diff --git a/Taskfile.dist.yaml b/Taskfile.dist.yaml index 938265d042..068280ef2d 100644 --- a/Taskfile.dist.yaml +++ b/Taskfile.dist.yaml @@ -18,11 +18,11 @@ vars: withCoverageBinary: './bin/werf-with-coverage{{if (eq .targetOS "windows")}}.exe{{end}}' package: "github.com/werf/werf/cmd/werf" - cgoTags: "dfrunmount dfssh containers_image_openpgp osusergo exclude_graphdriver_devicemapper netgo no_devmapper static_build" + cgoTags: "dfrunsecurity dfrunnetwork dfrunmount dfssh containers_image_openpgp osusergo exclude_graphdriver_devicemapper netgo no_devmapper static_build" cgoDevLDFlags: "-linkmode external -extldflags=-static" cgoReleaseLDFlags: "-linkmode external -extldflags=-static -s -w -X github.com/werf/werf/pkg/werf.Version={{.version}}" - goTags: "dfrunmount dfssh containers_image_openpgp" + goTags: "dfrunsecurity dfrunnetwork dfrunmount dfssh containers_image_openpgp" goDevLDFlags: "" goReleaseLDFlags: "-s -w -X github.com/werf/werf/pkg/werf.Version={{.version}}" diff --git a/pkg/build/image/dockerfile.go b/pkg/build/image/dockerfile.go index dad63e8176..b4a3bda51c 100644 --- a/pkg/build/image/dockerfile.go +++ b/pkg/build/image/dockerfile.go @@ -83,7 +83,7 @@ func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile, return nil, fmt.Errorf("unable to create image %q: %w", "test", err) } - img.stages = append(img.stages, stage_instruction.NewRun(backend_instruction.NewRun(*dockerfile_instruction.NewRun([]string{"ls", "/"}, nil, nil, false)), nil, false, &stage.BaseStageOptions{ + img.stages = append(img.stages, stage_instruction.NewRun(backend_instruction.NewRun(*dockerfile_instruction.NewRun([]string{"ls", "/"}, false, nil, "", "")), nil, false, &stage.BaseStageOptions{ ImageName: img.Name, ImageTmpDir: img.TmpDir, ContainerWerfDir: img.ContainerWerfDir, diff --git a/pkg/container_backend/instruction/healthcheck.go b/pkg/container_backend/instruction/healthcheck.go index b816543a30..0898dbe0c7 100644 --- a/pkg/container_backend/instruction/healthcheck.go +++ b/pkg/container_backend/instruction/healthcheck.go @@ -8,12 +8,4 @@ type Healthcheck struct { dockerfile_instruction.Healthcheck } -type HealthcheckType string - -var ( - HealthcheckTypeNone HealthcheckType = "NONE" - HealthcheckTypeCmd HealthcheckType = "CMD" - HealthcheckTypeCmdShell HealthcheckType = "CMD-SHELL" -) - // TODO diff --git a/pkg/dockerfile/dockerfile_stage.go b/pkg/dockerfile/dockerfile_stage.go index e4019caf79..c3f315e67a 100644 --- a/pkg/dockerfile/dockerfile_stage.go +++ b/pkg/dockerfile/dockerfile_stage.go @@ -1,25 +1,10 @@ package dockerfile -import "github.com/moby/buildkit/frontend/dockerfile/instructions" - -func NewDockerfileStage(dockerStage instructions.Stage) *DockerfileStage { - return &DockerfileStage{dockerStage: dockerStage} +func NewDockerfileStage(dockerfile *Dockerfile, instructions []InstructionInterface) *DockerfileStage { + return &DockerfileStage{Dockerfile: dockerfile, Instructions: instructions} } type DockerfileStage struct { - Dockerfile *Dockerfile - DependenciesStages []*DockerfileStage - - dockerStage instructions.Stage -} - -func (stage *DockerfileStage) GetInstructions() []InstructionInterface { - // TODO(staged-dockerfile) - // for _, cmd := range stage.dockerStage.Commands { - // switch typedCmd := cmd.(type) { - // case *instructions.ArgCommand: - // } - // } - - return nil + Dockerfile *Dockerfile + Instructions []InstructionInterface } diff --git a/pkg/dockerfile/frontend/buildkit.go b/pkg/dockerfile/frontend/buildkit.go new file mode 100644 index 0000000000..c517cca1d2 --- /dev/null +++ b/pkg/dockerfile/frontend/buildkit.go @@ -0,0 +1,77 @@ +package frontend + +import ( + "fmt" + + "github.com/moby/buildkit/frontend/dockerfile/instructions" + + "github.com/werf/werf/pkg/dockerfile" + dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction" +) + +func DockerfileStageFromBuildkitStage(d *dockerfile.Dockerfile, stage instructions.Stage) (*dockerfile.DockerfileStage, error) { + var i []dockerfile.InstructionInterface + + for _, cmd := range stage.Commands { + switch typedCmd := cmd.(type) { + case *instructions.AddCommand: + src, dst := extractSrcAndDst(typedCmd.SourcesAndDest) + i = append(i, dockerfile_instruction.NewAdd(src, dst, typedCmd.Chown, typedCmd.Chmod)) + case *instructions.ArgCommand: + i = append(i, dockerfile_instruction.NewArg(typedCmd.Args)) + case *instructions.CmdCommand: + i = append(i, dockerfile_instruction.NewCmd(typedCmd.CmdLine, typedCmd.PrependShell)) + case *instructions.CopyCommand: + src, dst := extractSrcAndDst(typedCmd.SourcesAndDest) + i = append(i, dockerfile_instruction.NewCopy(typedCmd.From, src, dst, typedCmd.Chown, typedCmd.Chmod)) + case *instructions.EntrypointCommand: + i = append(i, dockerfile_instruction.NewEntrypoint(typedCmd.CmdLine, typedCmd.PrependShell)) + case *instructions.EnvCommand: + i = append(i, dockerfile_instruction.NewEnv(extractKeyValuePairsAsMap(typedCmd.Env))) + case *instructions.ExposeCommand: + i = append(i, dockerfile_instruction.NewExpose(typedCmd.Ports)) + case *instructions.HealthCheckCommand: + i = append(i, dockerfile_instruction.NewHealthcheck(typedCmd.Health)) + case *instructions.LabelCommand: + i = append(i, dockerfile_instruction.NewLabel(extractKeyValuePairsAsMap(typedCmd.Labels))) + case *instructions.MaintainerCommand: + i = append(i, dockerfile_instruction.NewMaintainer(typedCmd.Maintainer)) + case *instructions.OnbuildCommand: + i = append(i, dockerfile_instruction.NewOnBuild(typedCmd.Expression)) + case *instructions.RunCommand: + network := dockerfile_instruction.NewNetworkType(instructions.GetNetwork(typedCmd)) + security := dockerfile_instruction.NewSecurityType(instructions.GetSecurity(typedCmd)) + mounts := instructions.GetMounts(typedCmd) + i = append(i, dockerfile_instruction.NewRun(typedCmd.CmdLine, typedCmd.PrependShell, mounts, network, security)) + case *instructions.ShellCommand: + i = append(i, dockerfile_instruction.NewShell(typedCmd.Shell)) + case *instructions.StopSignalCommand: + i = append(i, dockerfile_instruction.NewStopSignal(typedCmd.Signal)) + case *instructions.UserCommand: + i = append(i, dockerfile_instruction.NewUser(typedCmd.User)) + case *instructions.VolumeCommand: + i = append(i, dockerfile_instruction.NewVolume(typedCmd.Volumes)) + case *instructions.WorkdirCommand: + i = append(i, dockerfile_instruction.NewWorkdir(typedCmd.Path)) + } + } + + return dockerfile.NewDockerfileStage(d, i), nil +} + +func extractSrcAndDst(sourcesAndDest instructions.SourcesAndDest) ([]string, string) { + if len(sourcesAndDest) < 2 { + panic(fmt.Sprintf("unexpected buildkit instruction source and destination: %#v", sourcesAndDest)) + } + dst := sourcesAndDest[len(sourcesAndDest)-1] + src := sourcesAndDest[0 : len(sourcesAndDest)-2] + return src, dst +} + +func extractKeyValuePairsAsMap(pairs instructions.KeyValuePairs) (res map[string]string) { + res = make(map[string]string) + for _, item := range pairs { + res[item.Key] = item.Value + } + return +} diff --git a/pkg/dockerfile/frontend/dockerfile_yaml.go b/pkg/dockerfile/frontend/dockerfile_yaml.go new file mode 100644 index 0000000000..5a1374150c --- /dev/null +++ b/pkg/dockerfile/frontend/dockerfile_yaml.go @@ -0,0 +1,4 @@ +package frontend + +// TODO: implement Dockerfile.yaml or werf.yaml section parser primitives and conversion to Dockerfile and DockerfileStage primitives +type DockerfileYaml struct{} diff --git a/pkg/dockerfile/instruction/healthcheck.go b/pkg/dockerfile/instruction/healthcheck.go index b04547d82d..cc765778ac 100644 --- a/pkg/dockerfile/instruction/healthcheck.go +++ b/pkg/dockerfile/instruction/healthcheck.go @@ -10,13 +10,22 @@ type Healthcheck struct { type HealthcheckType string var ( + HealthcheckTypeInherit HealthcheckType = "" HealthcheckTypeNone HealthcheckType = "NONE" HealthcheckTypeCmd HealthcheckType = "CMD" HealthcheckTypeCmdShell HealthcheckType = "CMD-SHELL" ) -func NewHealthcheck(t HealthcheckType, cfg *container.HealthConfig) *Healthcheck { - return &Healthcheck{Type: t, Config: cfg} +func NewHealthcheckType(cfg *container.HealthConfig) HealthcheckType { + if len(cfg.Test) == 0 { + return HealthcheckTypeInherit + } else { + return HealthcheckType(cfg.Test[0]) + } +} + +func NewHealthcheck(cfg *container.HealthConfig) *Healthcheck { + return &Healthcheck{Type: NewHealthcheckType(cfg), Config: cfg} } func (i *Healthcheck) Name() string { diff --git a/pkg/dockerfile/instruction/maintainer.go b/pkg/dockerfile/instruction/maintainer.go index d6a99eedfd..e3ea39239d 100644 --- a/pkg/dockerfile/instruction/maintainer.go +++ b/pkg/dockerfile/instruction/maintainer.go @@ -4,6 +4,10 @@ type Maintainer struct { Maintainer string } +func NewMaintainer(maintainer string) *Maintainer { + return &Maintainer{Maintainer: maintainer} +} + func (i *Maintainer) Name() string { return "MAINTAINER" } diff --git a/pkg/dockerfile/instruction/run.go b/pkg/dockerfile/instruction/run.go index fba0419147..b88831adc2 100644 --- a/pkg/dockerfile/instruction/run.go +++ b/pkg/dockerfile/instruction/run.go @@ -1,16 +1,56 @@ package instruction -import "github.com/opencontainers/runtime-spec/specs-go" +import ( + "fmt" + + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +type NetworkType string + +const ( + NetworkDefault NetworkType = "default" + NetworkNone NetworkType = "none" + NetworkHost NetworkType = "host" +) + +func NewNetworkType(network string) NetworkType { + v := NetworkType(network) + switch v { + case NetworkDefault, NetworkHost, NetworkNone: + return v + default: + panic(fmt.Sprintf("unknown network type %q", network)) + } +} + +type SecurityType string + +const ( + SecurityInsecure SecurityType = "insecure" + SecuritySandbox SecurityType = "sandbox" +) + +func NewSecurityType(security string) SecurityType { + v := SecurityType(security) + switch v { + case SecurityInsecure, SecuritySandbox: + return v + default: + panic(fmt.Sprintf("unknown security type %q", security)) + } +} type Run struct { Command []string - Args []string // runtime args like --security and --network - Mounts []specs.Mount // structured --mount args PrependShell bool + Mounts []*instructions.Mount + Network NetworkType + Security SecurityType } -func NewRun(command, args []string, mounts []specs.Mount, prependShell bool) *Run { - return &Run{Command: command, Args: args, Mounts: mounts, PrependShell: prependShell} +func NewRun(command []string, prependShell bool, mounts []*instructions.Mount, network NetworkType, security SecurityType) *Run { + return &Run{Command: command, PrependShell: prependShell, Mounts: mounts, Network: network} } func (i *Run) Name() string { diff --git a/test/pkg/suite_init/werf_binary_data.go b/test/pkg/suite_init/werf_binary_data.go index 3662999f2c..b752887d04 100644 --- a/test/pkg/suite_init/werf_binary_data.go +++ b/test/pkg/suite_init/werf_binary_data.go @@ -29,9 +29,9 @@ func ComputeWerfBinPath() []byte { // TODO: get rid of these hardcoded build instructions? if runtime.GOOS == "linux" { - werfBinPath, err = gexec.BuildWithEnvironment("github.com/werf/werf/cmd/werf", []string{"CGO_ENABLED=1"}, "-compiler", "gc", "-ldflags", "-linkmode external -extldflags=-static", "-tags", "dfrunmount dfssh containers_image_openpgp osusergo exclude_graphdriver_devicemapper netgo no_devmapper static_build") + werfBinPath, err = gexec.BuildWithEnvironment("github.com/werf/werf/cmd/werf", []string{"CGO_ENABLED=1"}, "-compiler", "gc", "-ldflags", "-linkmode external -extldflags=-static", "-tags", "dfrunsecurity dfrunnetwork dfrunmount dfssh containers_image_openpgp osusergo exclude_graphdriver_devicemapper netgo no_devmapper static_build") } else { - werfBinPath, err = gexec.BuildWithEnvironment("github.com/werf/werf/cmd/werf", nil, "-compiler", "gc", "-tags", "dfrunmount dfssh containers_image_openpgp") + werfBinPath, err = gexec.BuildWithEnvironment("github.com/werf/werf/cmd/werf", nil, "-compiler", "gc", "-tags", "dfrunsecurity dfrunnetwork dfrunmount dfssh containers_image_openpgp") } Ω(err).ShouldNot(HaveOccurred()) }