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 |
} |