From 33c10cbbfd5fe81aaae45b3c37b67bd4ef88d40e Mon Sep 17 00:00:00 2001 From: Ole Claussen Date: Wed, 12 Feb 2020 16:09:06 +0000 Subject: [PATCH] create protobuf definition for config types, and moved the config parsing into a separate package --- pkg/container/container.go | 6 +-- pkg/container/create.go | 77 ++++++++++++++++++++++++++++---------- pkg/container/run.go | 6 +-- pkg/image/build.go | 20 ++++------ pkg/image/context.go | 47 +++++++++++++++++++---- pkg/image/image.go | 4 +- 6 files changed, 110 insertions(+), 50 deletions(-) diff --git a/pkg/container/container.go b/pkg/container/container.go index 6350ae2..ddef6b8 100644 --- a/pkg/container/container.go +++ b/pkg/container/container.go @@ -53,9 +53,9 @@ func NewContainer(config *types.Backdrop, authConfigs map[string]dockerapi.AuthC } func (c *Container) Build() error { - c.config.Image.ForceRebuild = true + c.config.Build.ForceRebuild = true - img, err := image.NewImage(c.client, c.authConfigs, c.config.Image) + img, err := image.NewImage(c.client, c.authConfigs, c.config.Build) if err != nil { return err } @@ -68,7 +68,7 @@ func (c *Container) Build() error { } func (c *Container) Run() error { - img, err := image.NewImage(c.client, c.authConfigs, c.config.Image) + img, err := image.NewImage(c.client, c.authConfigs, c.config.Build) if err != nil { return err } diff --git a/pkg/container/create.go b/pkg/container/create.go index 4f8cbb6..193dd1e 100644 --- a/pkg/container/create.go +++ b/pkg/container/create.go @@ -1,10 +1,12 @@ package container import ( + "fmt" "path" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" + "github.com/docker/go-connections/nat" ) func (c *Container) create(image string) (string, error) { @@ -19,26 +21,19 @@ func (c *Container) create(image string) (string, error) { Tty: hasTTY() && !c.daemon, OpenStdin: !c.daemon, StdinOnce: !c.daemon, - Env: c.config.Environment.Strings(), + Env: c.dockerEnvironment(), Cmd: command, Image: image, WorkingDir: c.config.WorkingDir, Entrypoint: entrypoint, - ExposedPorts: c.config.Ports.PortSet(), + ExposedPorts: c.dockerPortSet(), }, &container.HostConfig{ AutoRemove: func() bool { - if c.daemon { - return false - } - if c.config.Remove == nil { - return true - } - return *c.config.Remove + return !c.daemon }(), - Binds: c.config.Volumes.Strings(), - VolumesFrom: c.config.VolumesFrom, - PortBindings: c.config.Ports.PortMap(), + Binds: c.dockerVolumes(), + PortBindings: c.dockerPortMap(), RestartPolicy: c.dockerRestartPolicy(), Resources: container.Resources{ Devices: c.dockerDevices(), @@ -52,8 +47,8 @@ func (c *Container) create(image string) (string, error) { return "", err } - if len(c.config.Script) > 0 { - if err := c.UploadFile(response.ID, "entrypoint", []byte(c.config.Script+"\n")); err != nil { + if len(c.config.Entrypoint.Script) > 0 { + if err := c.UploadFile(response.ID, "entrypoint", []byte(c.config.Entrypoint.Script+"\n")); err != nil { return "", err } } @@ -63,14 +58,14 @@ func (c *Container) create(image string) (string, error) { func (c *Container) dockerEntrypoint() ([]string, []string) { entrypoint := []string{"/bin/sh"} - command := c.config.Command + command := c.config.Entrypoint.Arguments - if c.config.Interpreter != nil { - entrypoint = c.config.Interpreter + if c.config.Entrypoint.Interpreter != nil { + entrypoint = c.config.Entrypoint.Interpreter } - if c.config.Interactive { + if c.config.Entrypoint.Interactive { command = nil - } else if len(c.config.Script) > 0 { + } else if len(c.config.Entrypoint.Script) > 0 { entrypoint = append(entrypoint, path.Join(c.tmpPath, "entrypoint")) } @@ -109,3 +104,47 @@ func (c *Container) dockerDeviceCgroupRules() []string { } return result } + +func (c *Container) dockerPortMap() nat.PortMap { + result := map[nat.Port][]nat.PortBinding{} + for _, port := range c.config.Ports { + portSpec, _ := nat.NewPort(port.Protocol, port.Target) + result[portSpec] = append(result[portSpec], nat.PortBinding{HostPort: port.Published}) + } + return result +} + +func (c *Container) dockerPortSet() nat.PortSet { + result := map[nat.Port]struct{}{} + for _, port := range c.config.Ports { + portSpec, _ := nat.NewPort(port.Protocol, port.Target) + result[portSpec] = struct{}{} + } + return result +} + +func (c *Container) dockerEnvironment() []string { + result := []string{} + for _, kv := range c.config.Environment { + result = append(result, fmt.Sprintf("%s=%s", kv.Key, kv.Value)) + } + return result +} + +func (c *Container) dockerVolumes() []string { + result := []string{} + for _, v := range c.config.Volumes { + var volumeString string + + if v.Target == "" && !v.Readonly { + volumeString = fmt.Sprintf("%s:%s", v.Source, v.Source) + } else if !v.Readonly { + volumeString = fmt.Sprintf("%s:%s", v.Source, v.Target) + } else { + volumeString = fmt.Sprintf("%s:%s:ro", v.Source, v.Target) + } + + result = append(result, volumeString) + } + return result +} diff --git a/pkg/container/run.go b/pkg/container/run.go index a7edfea..6c0d2e1 100644 --- a/pkg/container/run.go +++ b/pkg/container/run.go @@ -34,14 +34,10 @@ func (c *Container) run(containerID string, tty bool) error { streamErrorChannel := make(chan error, 1) go streamContainer(c.context, streamErrorChannel, attach, tty) - condition := container.WaitConditionNextExit - if c.config.Remove == nil || *c.config.Remove == true { - condition = container.WaitConditionRemoved - } waitChannel, waitErrorChannel := c.client.ContainerWait( c.context, containerID, - condition, + container.WaitConditionRemoved, ) err = c.client.ContainerStart( diff --git a/pkg/image/build.go b/pkg/image/build.go index c997fe4..37ee889 100644 --- a/pkg/image/build.go +++ b/pkg/image/build.go @@ -5,7 +5,6 @@ import ( "io" "net" "os" - "strings" "github.com/containerd/console" "github.com/docker/docker/api/types" @@ -23,14 +22,14 @@ import ( ) func (image *Image) Get() (string, error) { - if image.config.ForceRebuild || len(image.config.Name) == 0 { + if image.config.ForceRebuild || len(image.config.ImageName) == 0 { return image.Build() } imgs, err := image.client.ImageList( context.Background(), types.ImageListOptions{ - Filters: filters.NewArgs(filters.Arg("reference", image.config.Name)), + Filters: filters.NewArgs(filters.Arg("reference", image.config.ImageName)), }, ) if err != nil || len(imgs) == 0 { @@ -41,7 +40,7 @@ func (image *Image) Get() (string, error) { } func (image *Image) Build() (string, error) { - for _, name := range image.config.Requires { + for _, name := range image.config.Dependencies { // TODO: refactor here, the dependency on config is uncomfortable conf, err := config.LoadImage(name) if err != nil { @@ -114,18 +113,13 @@ func (image *Image) Build() (string, error) { func (image *Image) runBuild(contextData *contextData, displayCh chan *client.SolveStatus) (string, error) { args := map[string]*string{} - for _, arg := range image.config.Args.Strings() { - switch values := strings.SplitN(arg, "=", 2); len(values) { - case 1: - args[values[0]] = nil - case 2: - args[values[0]] = &values[1] - } + for _, arg := range image.config.Arguments { + args[arg.Key] = &arg.Value } var tags []string - if image.config.Name != "" { - tags = append(tags, image.config.Name) + if image.config.ImageName != "" { + tags = append(tags, image.config.ImageName) } response, err := image.client.ImageBuild( diff --git a/pkg/image/context.go b/pkg/image/context.go index b9c2c04..126f87c 100644 --- a/pkg/image/context.go +++ b/pkg/image/context.go @@ -9,8 +9,11 @@ import ( "path/filepath" "github.com/docker/docker/pkg/urlutil" + buildkit "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/auth/authprovider" "github.com/moby/buildkit/session/filesync" + "github.com/moby/buildkit/session/secrets/secretsprovider" + "github.com/moby/buildkit/session/sshforward/sshprovider" "github.com/oclaussen/dodo/pkg/types" "github.com/pkg/errors" fstypes "github.com/tonistiigi/fsutil/types" @@ -41,7 +44,7 @@ func (data *contextData) cleanup() { } } -func prepareContext(config *types.Image, session session) (*contextData, error) { +func prepareContext(config *types.BuildInfo, session session) (*contextData, error) { data := contextData{ remote: "", dockerfileName: config.Dockerfile, @@ -76,9 +79,9 @@ func prepareContext(config *types.Image, session session) (*contextData, error) return nil, errors.Errorf("Context directory does not exist: %v", config.Context) } - if len(config.Steps) > 0 { + if len(config.InlineDockerfile) > 0 { steps := "" - for _, step := range config.Steps { + for _, step := range config.InlineDockerfile { steps = steps + step + "\n" } @@ -108,14 +111,14 @@ func prepareContext(config *types.Image, session session) (*contextData, error) Dir: dockerfileDir, }) - } else if config.Name != "" && data.remote == clientSession { + } else if config.ImageName != "" && data.remote == clientSession { dir, err := data.tempdir() if err != nil { data.cleanup() return nil, err } tempfile := filepath.Join(dir, "Dockerfile") - if err := writeDockerfile(tempfile, fmt.Sprintf("FROM %s", config.Name)); err != nil { + if err := writeDockerfile(tempfile, fmt.Sprintf("FROM %s", config.ImageName)); err != nil { data.cleanup() return nil, err } @@ -134,14 +137,14 @@ func prepareContext(config *types.Image, session session) (*contextData, error) session.Allow(authprovider.NewDockerAuthProvider()) if len(config.Secrets) > 0 { - provider, err := config.Secrets.SecretsProvider() + provider, err := secretsProvider(config) if err != nil { return nil, err } session.Allow(provider) } - if len(config.SSHAgents) > 0 { - provider, err := config.SSHAgents.SSHAgentProvider() + if len(config.SshAgents) > 0 { + provider, err := sshAgentProvider(config) if err != nil { return nil, err } @@ -171,3 +174,31 @@ func writeDockerfile(path string, content string) error { return nil } + +func secretsProvider(config *types.BuildInfo) (buildkit.Attachable, error) { + sources := make([]secretsprovider.FileSource, 0, len(config.Secrets)) + for _, secret := range config.Secrets { + source := secretsprovider.FileSource{ + ID: secret.Id, + FilePath: secret.Path, + } + sources = append(sources, source) + } + store, err := secretsprovider.NewFileStore(sources) + if err != nil { + return nil, err + } + return secretsprovider.NewSecretProvider(store), nil +} + +func sshAgentProvider(config *types.BuildInfo) (buildkit.Attachable, error) { + configs := make([]sshprovider.AgentConfig, 0, len(config.SshAgents)) + for _, agent := range config.SshAgents { + config := sshprovider.AgentConfig{ + ID: agent.Id, + Paths: []string{agent.IdentityFile}, + } + configs = append(configs, config) + } + return sshprovider.NewSSHAgentProvider(configs) +} diff --git a/pkg/image/image.go b/pkg/image/image.go index 865f3af..557309e 100644 --- a/pkg/image/image.go +++ b/pkg/image/image.go @@ -17,7 +17,7 @@ var ( // Image represents the data necessary to build a docker image type Image struct { - config *dodotypes.Image + config *dodotypes.BuildInfo client Client authConfigs map[string]types.AuthConfig session session @@ -31,7 +31,7 @@ type Client interface { } // NewImage initializes and validates a new Image object -func NewImage(client Client, authConfigs map[string]types.AuthConfig, config *dodotypes.Image) (*Image, error) { +func NewImage(client Client, authConfigs map[string]types.AuthConfig, config *dodotypes.BuildInfo) (*Image, error) { if client == nil { return nil, errors.New("client may not be nil") }