diff --git a/docs/tutorials/04-include-in-your-build-tool.md b/docs/tutorials/04-include-in-your-build-tool.md new file mode 100644 index 000000000..803318724 --- /dev/null +++ b/docs/tutorials/04-include-in-your-build-tool.md @@ -0,0 +1,156 @@ +![buildah logo](../../logos/buildah-logo_large.png) + +# Buildah Tutorial 4 +## Include Buildah in your build tool + +The purpose of this tutorial is to demonstrate how to include Buildah as a library in your build tool. + +You can take advantage of all features provided by Buildah, like using Dockerfiles and building using rootless mode. + +In this tutorial I'll show you how to create a simple CLI tool that creates an image containing NodeJS and a JS main file. + +## Bootstrap the project and install the dependencies + +Bootstrap the installation of development dependencies of Buildah by following the [Building from scratch](https://github.com/slinkydeveloper/buildah/blob/master/install.md#building-from-scratch) instructions and in particular creating a directory for the Buildah project by completing the instructions in the [Installation from GitHub](https://github.com/containers/buildah/blob/master/install.md#installation-from-github) section of that page. + +Now let's bootstrap our project. Assuming you are in the directory of the project, run: + + +```shell +go mod init +``` + +To initialize the go modules. Now import Buildah as a dependency: + +```shell +go get github.com/containers/buildah +``` + +## Build the image + +Now you can develop your application. To access to the build features of Buildah, you need to instantiate `buildah.Builder`. This struct has methods to configure the build, define the build steps and run it. + +To instantiate a `Builder`, you need a `storage.Store` (the Store interface found in [store.go](https://github.com/containers/storage/blob/master/store.go)) from [`github.com/containers/storage`](https://github.com/containers/storage), where the intermediate and result images will be stored: + +```go +buildStoreOptions, err := storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID()) +buildStore, err := storage.GetStore(buildStoreOptions) +``` + +Define the builder options: + +```go +builderOpts := buildah.BuilderOptions{ + FromImage: "node:12-alpine", // Starting image + Isolation: buildah.IsolationChroot, // Isolation environment + CommonBuildOpts: &buildah.CommonBuildOptions{}, + ConfigureNetwork: buildah.NetworkDefault, + SystemContext: &types.SystemContext {}, +} +``` + +Now instantiate the `Builder`: + +```go +builder, err := buildah.NewBuilder(context.TODO(), buildStore, builderOpts) +``` + +Let's add our JS file (assuming is in your local directory with name `script.js`): + +```go +err = builder.Add("/home/node/", false, buildah.AddAndCopyOptions{}, "script.js") +``` + +And configure the command to run: + +```go +builder.SetCmd([]string{"node", "/home/node/script.js"}) +``` + +Before completing the build, create the image reference: + +```go +imageRef, err := alltransports.ParseImageName("docker.io/myusername/my-image") +``` + +Now you can run commit the build: + +```go +imageId, _, _, err := builder.Commit(context.TODO(), imageRef, buildah.CommitOptions{}) +``` + +## Rootless mode + +To enable rootless mode, import `github.com/containers/buildah/pkg/unshare` and add this code at the beginning of your main method: + +```go +if buildah.InitReexec() { + return +} +unshare.MaybeReexecUsingUserNamespace(false) +``` + +This code ensures that your application is re executed in an isolated environment with root privileges. + +## Complete code + +```go +package main + +import ( + "context" + "fmt" + "github.com/containers/buildah" + "github.com/containers/buildah/pkg/unshare" + "github.com/containers/image/transports/alltransports" + "github.com/containers/image/types" + "github.com/containers/storage" +) + +func main() { + if buildah.InitReexec() { + return + } + unshare.MaybeReexecUsingUserNamespace(false) + + buildStoreOptions, err := storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID()) + + if err != nil { + panic(err) + } + + buildStore, err := storage.GetStore(buildStoreOptions) + + if err != nil { + panic(err) + } + + opts := buildah.BuilderOptions{ + FromImage: "node:12-alpine", + Isolation: buildah.IsolationChroot, + CommonBuildOpts: &buildah.CommonBuildOptions{}, + ConfigureNetwork: buildah.NetworkDefault, + SystemContext: &types.SystemContext {}, + } + + builder, err := buildah.NewBuilder(context.TODO(), buildStore, opts) + + if err != nil { + panic(err) + } + + err = builder.Add("/home/node/", false, buildah.AddAndCopyOptions{}, "script.js") + + if err != nil { + panic(err) + } + + builder.SetCmd([]string{"node", "/home/node/script.js"}) + + imageRef, err := alltransports.ParseImageName("docker.io/myusername/my-image") + + imageId, _, _, err := builder.Commit(context.TODO(), imageRef, buildah.CommitOptions{}) + + fmt.Printf("Image built! %s\n", imageId) +} +``` diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md index ea9d7501e..db77d6d3a 100644 --- a/docs/tutorials/README.md +++ b/docs/tutorials/README.md @@ -17,4 +17,6 @@ Learn how Buildah can be used to move OCI compliant images in and out of private Learn how Buildah can use the ONBUILD instruction in either a Dockerfile or via the `buildah config --onbuild` command to configure an image to run those instructions when the container is created. In this manner you can front load setup of the container inside the image and minimalize the steps needed to create one or more containers that share a number of initial settings, but need a few differentiators between each. +**[Include Buildah in your build tool](04-include-in-your-build-tool.md)** +Learn how to include Buildah as a library in your build tool.