Skip to content
Permalink
Browse files
feat(staged-dockerfile): implement buidkit frontend instructions to d…
…ockerfile instructions conversion

Signed-off-by: Timofey Kirillov <timofey.kirillov@flant.com>
  • Loading branch information
distorhead committed Oct 13, 2022
1 parent 06f122b commit 2bc6c30d4c09b6034c8651adf7fc6828d412467c
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 39 deletions.
@@ -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}}"

@@ -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,
@@ -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
@@ -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
}
@@ -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
}
@@ -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{}
@@ -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 {
@@ -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"
}
@@ -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 {
@@ -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())
}

0 comments on commit 2bc6c30

Please sign in to comment.