Skip to content

Commit

Permalink
Merge pull request #14 from smarterclayton/mount_secret
Browse files Browse the repository at this point in the history
Fix transient mounts in Docker 1.12
  • Loading branch information
smarterclayton authored Oct 27, 2016
2 parents 787a548 + 442191c commit 1382f05
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 96 deletions.
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ To download and install the library and the binary, set up a Golang build enviro
$ go get -u github.com/openshift/imagebuilder/cmd/imagebuilder
```

The included command line takes two arguments, a path to a directory containing a dockerfile, and the name to tag the output image with.
The included command line takes one argument, a path to a directory containing a Dockerfile. The `-t` option
can be used to specify an image to tag as.

```
$ imagebuilder path_to_directory output_image_name
$ imagebuilder [-t TAG] DIRECTORY
```

To mount a file into the image for build that will not be present in the final output image, run:
Expand All @@ -54,7 +55,18 @@ $ imagebuilder --mount ~/secrets/private.key:/etc/keys/private.key path/to/my/co

Any processes in the Dockerfile will have access to `/etc/keys/private.key`, but that file will not be part of the committed image.

Note that running `--mount` requires Docker 1.10 or newer, as it uses a Docker volume to hold the mounted files and the volume API was not available in earlier versions.
Running `--mount` requires Docker 1.10 or newer, as it uses a Docker volume to hold the mounted files and the volume API was not
available in earlier versions. In addition, there is currently a bug that blocks Docker 1.12 from using `--mount`.

You can also customize which Dockerfile is run, or run multiple Dockerfiles in sequenc (the FROM is ignored on
later files):

```
$ imagebuilder -f Dockerfile:Dockerfile.extra .
```

will build the current directory and combine the first Dockerfile with the second. The FROM in the second image
is ignored.


## Code Example
Expand Down
22 changes: 21 additions & 1 deletion builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package imagebuilder
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
Expand Down Expand Up @@ -86,7 +87,7 @@ type Builder struct {
PendingRuns []Run
PendingCopies []Copy

Executor Executor
//Executor Executor
}

func NewBuilder() *Builder {
Expand All @@ -100,6 +101,25 @@ func NewBuilder() *Builder {
}
}

func NewBuilderForReader(r io.Reader, args map[string]string) (*Builder, *parser.Node, error) {
b := NewBuilder()
b.Args = args
node, err := ParseDockerfile(r)
if err != nil {
return nil, nil, err
}
return b, node, err
}

func NewBuilderForFile(path string, args map[string]string) (*Builder, *parser.Node, error) {
f, err := os.Open(path)
if err != nil {
return nil, nil, err
}
defer f.Close()
return NewBuilderForReader(f, args)
}

// Step creates a new step from the current state.
func (b *Builder) Step() *Step {
dst := make([]string, len(b.Env)+len(b.RunConfig.Env))
Expand Down
59 changes: 45 additions & 14 deletions cmd/imagebuilder/imagebuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,34 @@ import (
docker "github.com/fsouza/go-dockerclient"
"github.com/golang/glog"

"github.com/openshift/imagebuilder"
"github.com/openshift/imagebuilder/dockerclient"
)

func main() {
log.SetFlags(0)
options := &dockerclient.ClientExecutor{}
options := dockerclient.NewClientExecutor(nil)
var tag string
var dockerfilePath string
var mountSpecs stringSliceFlag

flag.StringVar(&dockerfilePath, "dockerfile", dockerfilePath, "An optional path to a Dockerfile to use.")
flag.StringVar(&tag, "t", tag, "The name to assign this image, if any.")
flag.StringVar(&tag, "tag", tag, "The name to assign this image, if any.")
flag.StringVar(&dockerfilePath, "f", dockerfilePath, "An optional path to a Dockerfile to use. You may pass multiple docker files using the operating system delimiter.")
flag.StringVar(&dockerfilePath, "file", dockerfilePath, "An optional path to a Dockerfile to use. You may pass multiple docker files using the operating system delimiter.")
flag.Var(&mountSpecs, "mount", "An optional list of files and directories to mount during the build. Use SRC:DST syntax for each path.")
flag.BoolVar(&options.AllowPull, "allow-pull", true, "Pull the images that are not present.")
flag.BoolVar(&options.IgnoreUnrecognizedInstructions, "ignore-unrecognized-instructions", true, "If an unrecognized Docker instruction is encountered, warn but do not fail the build.")

flag.Parse()

args := flag.Args()
if len(args) != 2 {
log.Fatalf("You must provide two arguments: DIRECTORY and IMAGE_NAME")
if len(args) != 1 {
log.Fatalf("You must provide one argument, the name of a directory to build")
}

options.Directory = args[0]
options.Tag = args[1]
options.Tag = tag
if len(dockerfilePath) == 0 {
dockerfilePath = filepath.Join(options.Directory, "Dockerfile")
}
Expand Down Expand Up @@ -64,30 +69,56 @@ func main() {
// Accept ARGS on the command line
arguments := make(map[string]string)

if err := build(dockerfilePath, options, arguments); err != nil {
dockerfiles := filepath.SplitList(dockerfilePath)
if len(dockerfiles) == 0 {
dockerfiles = []string{filepath.Join(options.Directory, "Dockerfile")}
}

if err := build(dockerfiles[0], dockerfiles[1:], arguments, options); err != nil {
log.Fatal(err.Error())
}
}

func build(dockerfilePath string, e *dockerclient.ClientExecutor, arguments map[string]string) error {
func build(dockerfile string, additionalDockerfiles []string, arguments map[string]string, e *dockerclient.ClientExecutor) error {
if err := e.DefaultExcludes(); err != nil {
return fmt.Errorf("error: Could not parse default .dockerignore: %v", err)
}

client, err := docker.NewClientFromEnv()
if err != nil {
log.Fatalf("No connection to Docker available: %v", err)
return fmt.Errorf("error: No connection to Docker available: %v", err)
}
e.Client = client

f, err := os.Open(dockerfilePath)
// TODO: handle signals
defer func() {
for _, err := range e.Release() {
fmt.Fprintf(e.ErrOut, "error: Unable to clean up build: %v\n", err)
}
}()

b, node, err := imagebuilder.NewBuilderForFile(dockerfile, arguments)
if err != nil {
return err
}
defer f.Close()
if err := e.Prepare(b, node); err != nil {
return err
}
if err := e.Execute(b, node); err != nil {
return err
}

// TODO: handle signals
if err := e.Cleanup(); err != nil {
fmt.Fprintf(e.ErrOut, "error: Unable to clean up build: %v\n", err)
for _, s := range additionalDockerfiles {
_, node, err := imagebuilder.NewBuilderForFile(s, arguments)
if err != nil {
return err
}
if err := e.Execute(b, node); err != nil {
return err
}
}

return e.Build(f, arguments)
return e.Commit(b)
}

type stringSliceFlag []string
Expand Down
Loading

0 comments on commit 1382f05

Please sign in to comment.