Make env in ImageConfig a map

Fields in CUE were renamed to the lowercase version of Dockerfile instructions.

There's now opportunity to make other fields simpler to use (e.g., healthcheck), this commit is focused on env.

Signed-off-by: Helder Correia <174525+helderco@users.noreply.github.com>
This commit is contained in:
Helder Correia 2022-01-26 09:22:09 -01:00
parent c6d4ffb75e
commit c276a8b8ba
No known key found for this signature in database
GPG Key ID: C6490D872EF1DCA7
9 changed files with 184 additions and 19 deletions

View File

@ -35,15 +35,28 @@ package engine
// Container image config. See [OCI](https://www.opencontainers.org/).
// Spec left open on purpose to account for additional fields.
// [Image Spec](https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/config.go)
// [Docker Superset](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/dockerfile2llb/image.go)
#ImageConfig: {
Env?: [...string]
User?: string
Cmd?: [...string]
user?: string
expose?: [string]: {}
env?: [string]: string
entrypoint?: [...string]
cmd?: [...string]
volume?: [string]: {}
workdir?: string
label?: [string]: string
healthcheck?: #HealthCheck
shell?: [...string]
...
}
#HealthCheck: {
test?: [...string]
interval?: int
timeout?: int
startPeriod?: int
retries?: int
}
// Download a container image from a remote repository
#Pull: {
$dagger: task: _name: "Pull"

View File

@ -126,7 +126,7 @@ func (t *dockerfileTask) Run(ctx context.Context, pctx *plancontext.Context, s s
return compiler.NewValue().FillFields(map[string]interface{}{
"output": pctx.FS.New(solvedRef).MarshalCUE(),
"config": image.Config,
"config": ConvertImageConfig(image.Config),
})
}

154
plan/task/imageconfig.go Normal file
View File

@ -0,0 +1,154 @@
package task
import (
"time"
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
"github.com/moby/buildkit/frontend/dockerfile/shell"
)
// ImageConfig defines the execution parameters which should be used as a base when running a container using an image.
type ImageConfig struct {
// [Image Spec](https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/config.go)
// User defines the username or UID which the process in the container should run as.
User string `json:"user,omitempty"`
// ExposedPorts a set of ports to expose from a container running this image.
ExposedPorts map[string]struct{} `json:"expose,omitempty"`
// Env is a list of environment variables to be used in a container.
Env map[string]string `json:"env,omitempty"`
// Entrypoint defines a list of arguments to use as the command to execute when the container starts.
Entrypoint []string `json:"entrypoint,omitempty"`
// Cmd defines the default arguments to the entrypoint of the container.
Cmd []string `json:"cmd,omitempty"`
// Volumes is a set of directories describing where the process is likely write data specific to a container instance.
Volumes map[string]struct{} `json:"volume,omitempty"`
// WorkingDir sets the current working directory of the entrypoint process in the container.
WorkingDir string `json:"workdir,omitempty"`
// Labels contains arbitrary metadata for the container.
Labels map[string]string `json:"label,omitempty"`
// StopSignal contains the system call signal that will be sent to the container to exit.
StopSignal string `json:"stopsignal,omitempty"`
// [Docker Superset](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/dockerfile2llb/image.go)
Healthcheck *HealthConfig `json:"healthcheck,omitempty"` // Healthcheck describes how to check the container is healthy
ArgsEscaped bool `json:"argsescaped,omitempty"` // True if command is already escaped (Windows specific)
OnBuild []string `json:"onbuild,omitempty"` // ONBUILD metadata that were defined on the image Dockerfile
StopTimeout *int `json:"stoptimeout,omitempty"` // Timeout (in seconds) to stop a container
Shell []string `json:"shell,omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
}
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
type HealthConfig struct {
// Test is the test to perform to check that the container is healthy.
// An empty slice means to inherit the default.
// The options are:
// {} : inherit healthcheck
// {"NONE"} : disable healthcheck
// {"CMD", args...} : exec arguments directly
// {"CMD-SHELL", command} : run command with system's default shell
Test []string `json:"test,omitempty"`
// Zero means to inherit. Durations are expressed as integer nanoseconds.
Interval time.Duration `json:"interval,omitempty"` // Interval is the time to wait between checks.
Timeout time.Duration `json:"timeout,omitempty"` // Timeout is the time to wait before considering the check to have hung.
StartPeriod time.Duration `json:"startPeriod,omitempty"` // The start period for the container to initialize before the retries starts to count down.
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
// Zero means inherit.
Retries int `json:"retries,omitempty"`
}
func (ic ImageConfig) ToSpec() dockerfile2llb.ImageConfig {
cfg := dockerfile2llb.ImageConfig{}
cfg.User = ic.User
cfg.ExposedPorts = ic.ExposedPorts
cfg.Env = envToSpec(ic.Env)
cfg.Entrypoint = ic.Entrypoint
cfg.Cmd = ic.Cmd
cfg.Volumes = ic.Volumes
cfg.WorkingDir = ic.WorkingDir
cfg.Labels = ic.Labels
cfg.StopSignal = ic.StopSignal
cfg.Healthcheck = ic.Healthcheck.ToSpec()
cfg.ArgsEscaped = ic.ArgsEscaped
cfg.OnBuild = ic.OnBuild
cfg.StopTimeout = ic.StopTimeout
cfg.Shell = ic.Shell
return cfg
}
func ConvertImageConfig(spec dockerfile2llb.ImageConfig) ImageConfig {
cfg := ImageConfig{}
cfg.User = spec.User
cfg.ExposedPorts = spec.ExposedPorts
cfg.Env = shell.BuildEnvs(spec.Env)
cfg.Entrypoint = spec.Entrypoint
cfg.Cmd = spec.Cmd
cfg.Volumes = spec.Volumes
cfg.WorkingDir = spec.WorkingDir
cfg.Labels = spec.Labels
cfg.StopSignal = spec.StopSignal
cfg.Healthcheck = ConvertHealthConfig(spec.Healthcheck)
cfg.ArgsEscaped = spec.ArgsEscaped
cfg.OnBuild = spec.OnBuild
cfg.StopTimeout = spec.StopTimeout
cfg.Shell = spec.Shell
return cfg
}
func envToSpec(env map[string]string) []string {
envs := []string{}
for k, v := range env {
envs = append(envs, k+"="+v)
}
return envs
}
func (hc *HealthConfig) ToSpec() *dockerfile2llb.HealthConfig {
if hc == nil {
return nil
}
cfg := dockerfile2llb.HealthConfig{}
cfg.Test = hc.Test
cfg.Interval = hc.Interval
cfg.Timeout = hc.Timeout
cfg.StartPeriod = hc.StartPeriod
cfg.Retries = hc.Retries
return &cfg
}
func ConvertHealthConfig(spec *dockerfile2llb.HealthConfig) *HealthConfig {
if spec == nil {
return nil
}
cfg := HealthConfig{}
cfg.Test = spec.Test
cfg.Interval = spec.Interval
cfg.Timeout = spec.Timeout
cfg.StartPeriod = spec.StartPeriod
cfg.Retries = spec.Retries
return &cfg
}

View File

@ -68,6 +68,6 @@ func (c *pullTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.
return compiler.NewValue().FillFields(map[string]interface{}{
"output": fs.MarshalCUE(),
"digest": digest,
"config": image.Config,
"config": ConvertImageConfig(image.Config),
})
}

View File

@ -56,14 +56,14 @@ func (c *pushTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.
}
// Decode the image config
imageConfig := dockerfile2llb.ImageConfig{}
imageConfig := ImageConfig{}
if err := v.Lookup("config").Decode(&imageConfig); err != nil {
return nil, err
}
// Export image
lg.Debug().Str("dest", dest.String()).Msg("export image")
resp, err := s.Export(ctx, st, &dockerfile2llb.Image{Config: imageConfig}, bk.ExportEntry{
resp, err := s.Export(ctx, st, &dockerfile2llb.Image{Config: imageConfig.ToSpec()}, bk.ExportEntry{
Type: bk.ExporterImage,
Attrs: map[string]string{
"name": dest.String(),

View File

@ -18,8 +18,8 @@ engine.#Plan & {
"""
} & {
config: {
Env: ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "test=foobar"]
Cmd: ["/bin/sh", "-c", "/test-cmd"]
env: test: "foobar"
cmd: ["/bin/sh", "-c", "/test-cmd"]
}
}
}

View File

@ -11,8 +11,8 @@ engine.#Plan & {
// assert result
digest: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3"
config: {
Env: ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]
Cmd: ["/bin/sh"]
env: PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
cmd: ["/bin/sh"]
}
}
}

View File

@ -20,8 +20,8 @@ engine.#Plan & {
// assert result
digest: "sha256:c74f1b1166784193ea6c8f9440263b9be6cae07dfe35e32a5df7a31358ac2060"
config: {
Env: ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]
Cmd: ["/bin/sh"]
env: PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
cmd: ["/bin/sh"]
}
}
}

View File

@ -42,7 +42,7 @@ engine.#Plan & {
push: engine.#Push & {
dest: "daggerio/ci-test:\(randomString.output)"
input: randomString.image.output
config: Env: ["FOO=\(randomString.output)"]
config: env: FOO: randomString.output
auth: #auth
}
@ -54,9 +54,7 @@ engine.#Plan & {
// check digest
digest: strings.Split(push.result, "@")[1]
// check image config
config: {
Env: ["FOO=\(randomString.output)"]
}
config: env: FOO: randomString.output
}
pullOutputFile: engine.#ReadFile & {