pkg/cmd/cli/cmd/dockerbuild/dockerbuild.go
fe7e661b
 package dockerbuild
 
 import (
 	"fmt"
 	"io"
 	"os"
 	"path/filepath"
 	"strings"
 
0ac1bca0
 	dockertypes "github.com/docker/engine-api/types"
fe7e661b
 	docker "github.com/fsouza/go-dockerclient"
32fcf23f
 	"github.com/golang/glog"
fe7e661b
 	"github.com/spf13/cobra"
 
 	"k8s.io/kubernetes/pkg/credentialprovider"
 	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
a593346b
 	"k8s.io/kubernetes/pkg/util/interrupt"
fe7e661b
 
0ac1bca0
 	dockerbuilder "github.com/openshift/imagebuilder/dockerclient"
6267dded
 	"github.com/openshift/origin/pkg/cmd/templates"
fe7e661b
 	cmdutil "github.com/openshift/origin/pkg/cmd/util"
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
53e4af76
 	"github.com/openshift/origin/pkg/generate/app"
fe7e661b
 )
 
6267dded
 var (
 	dockerbuildLong = templates.LongDesc(`
 		Build a Dockerfile into a single layer
fe7e661b
 
6267dded
 		Builds the provided directory with a Dockerfile into a single layered image.
 		Requires that you have a working connection to a Docker engine. You may mount
 		secrets or config into the build with the --mount flag - these files will not
 		be included in the final image.
26c9e032
 
6267dded
 		Experimental: This command is under active development and may change without notice.`)
fe7e661b
 
6267dded
 	dockerbuildExample = templates.Examples(`
 		# Build the current directory into a single layer and tag
 	  %[1]s ex dockerbuild . myimage:latest
26c9e032
 
6267dded
 	  # Mount a client secret into the build at a certain path
 	  %[1]s ex dockerbuild . myimage:latest --mount ~/mysecret.pem:/etc/pki/secret/mysecret.pem`)
fe7e661b
 )
 
 type DockerbuildOptions struct {
 	Out io.Writer
 	Err io.Writer
 
 	Client *docker.Client
 
26c9e032
 	MountSpecs []string
 
0ac1bca0
 	Mounts         []dockerbuilder.Mount
fe7e661b
 	Directory      string
 	Tag            string
 	DockerfilePath string
ed02b49a
 	AllowPull      bool
fe7e661b
 	Keyring        credentialprovider.DockerKeyring
53e4af76
 	Arguments      app.Environment
fe7e661b
 }
 
 func NewCmdDockerbuild(fullName string, f *clientcmd.Factory, out, errOut io.Writer) *cobra.Command {
 	options := &DockerbuildOptions{
 		Out: out,
 		Err: errOut,
 	}
 	cmd := &cobra.Command{
 		Use:     "dockerbuild DIRECTORY TAG [--dockerfile=PATH]",
 		Short:   "Perform a direct Docker build",
 		Long:    dockerbuildLong,
 		Example: fmt.Sprintf(dockerbuildExample, fullName),
 		Run: func(cmd *cobra.Command, args []string) {
 			kcmdutil.CheckErr(options.Complete(f, cmd, args))
 			kcmdutil.CheckErr(options.Validate())
 			if err := options.Run(); err != nil {
d7da290f
 				// TODO: move me to kcmdutil
fe7e661b
 				if err == cmdutil.ErrExit {
 					os.Exit(1)
 				}
 				kcmdutil.CheckErr(err)
 			}
 		},
 	}
 
26c9e032
 	cmd.Flags().StringSliceVar(&options.MountSpecs, "mount", options.MountSpecs, "An optional list of files and directories to mount during the build. Use SRC:DST syntax for each path.")
fe7e661b
 	cmd.Flags().StringVar(&options.DockerfilePath, "dockerfile", options.DockerfilePath, "An optional path to a Dockerfile to use.")
ed02b49a
 	cmd.Flags().BoolVar(&options.AllowPull, "allow-pull", true, "Pull the images that are not present.")
fe7e661b
 	cmd.MarkFlagFilename("dockerfile")
 
 	return cmd
 }
 
 func (o *DockerbuildOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string) error {
 	paths, envArgs, ok := cmdutil.SplitEnvironmentFromResources(args)
 	if !ok {
 		return kcmdutil.UsageError(cmd, "context directory must be specified before environment changes: %s", strings.Join(args, " "))
 	}
 	if len(paths) != 2 {
 		return kcmdutil.UsageError(cmd, "the directory to build and tag must be specified")
 	}
53e4af76
 	o.Arguments, _, _ = app.ParseEnvironment(envArgs...)
fe7e661b
 	o.Directory = paths[0]
 	o.Tag = paths[1]
 	if len(o.DockerfilePath) == 0 {
 		o.DockerfilePath = filepath.Join(o.Directory, "Dockerfile")
 	}
26c9e032
 
0ac1bca0
 	var mounts []dockerbuilder.Mount
26c9e032
 	for _, s := range o.MountSpecs {
 		segments := strings.Split(s, ":")
 		if len(segments) != 2 {
 			return kcmdutil.UsageError(cmd, "--mount must be of the form SOURCE:DEST")
 		}
0ac1bca0
 		mounts = append(mounts, dockerbuilder.Mount{SourcePath: segments[0], DestinationPath: segments[1]})
26c9e032
 	}
 	o.Mounts = mounts
 
fe7e661b
 	client, err := docker.NewClientFromEnv()
 	if err != nil {
 		return err
 	}
 	o.Client = client
 
 	o.Keyring = credentialprovider.NewDockerKeyring()
 
 	return nil
 }
 
 func (o *DockerbuildOptions) Validate() error {
 	return nil
 }
 
 func (o *DockerbuildOptions) Run() error {
 	f, err := os.Open(o.DockerfilePath)
 	if err != nil {
 		return err
 	}
 	defer f.Close()
0ac1bca0
 	e := dockerbuilder.NewClientExecutor(o.Client)
fe7e661b
 	e.Out, e.ErrOut = o.Out, o.Err
ed02b49a
 	e.AllowPull = o.AllowPull
fe7e661b
 	e.Directory = o.Directory
26c9e032
 	e.TransientMounts = o.Mounts
fe7e661b
 	e.Tag = o.Tag
0ac1bca0
 	e.AuthFn = func(image string) ([]dockertypes.AuthConfig, bool) {
 		auth, ok := o.Keyring.Lookup(image)
 		if !ok {
 			return nil, false
 		}
 		var engineAuth []dockertypes.AuthConfig
 		for _, c := range auth {
 			engineAuth = append(engineAuth, c.AuthConfig)
 		}
 		return engineAuth, true
 	}
32fcf23f
 	e.LogFn = func(format string, args ...interface{}) {
 		if glog.V(2) {
 			glog.Infof("Builder: "+format, args...)
 		} else {
a593346b
 			fmt.Fprintf(e.ErrOut, "--> %s\n", fmt.Sprintf(format, args...))
32fcf23f
 		}
 	}
a593346b
 	safe := interrupt.New(func(os.Signal) { os.Exit(1) }, func() {
 		glog.V(5).Infof("invoking cleanup")
 		if err := e.Cleanup(); err != nil {
 			fmt.Fprintf(o.Err, "error: Unable to clean up build: %v\n", err)
 		}
 	})
 	return safe.Run(func() error { return stripLeadingError(e.Build(f, o.Arguments)) })
32fcf23f
 }
 
 func stripLeadingError(err error) error {
 	if err == nil {
6d3f303e
 		return nil
32fcf23f
 	}
 	if strings.HasPrefix(err.Error(), "Error: ") {
 		return fmt.Errorf(strings.TrimPrefix(err.Error(), "Error: "))
 	}
 	return err
fe7e661b
 }