Skip to content

Commit

Permalink
feat(staged-dockerfile): basic support of all dockerfile stages at co…
Browse files Browse the repository at this point in the history
…nveyor level

Signed-off-by: Timofey Kirillov <timofey.kirillov@flant.com>
  • Loading branch information
distorhead committed Oct 19, 2022
1 parent f5f200e commit 306ed6c
Show file tree
Hide file tree
Showing 18 changed files with 559 additions and 15 deletions.
52 changes: 43 additions & 9 deletions pkg/build/image/dockerfile.go
Expand Up @@ -118,21 +118,55 @@ func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile,
}

for ind, instr := range stg.Instructions {
stageName := stage.StageName(fmt.Sprintf("%d-%s", ind, instr.GetInstructionData().Name()))
isFirstStage := (len(img.stages) == 0)
baseStageOptions := &stage.BaseStageOptions{
ImageName: img.Name,
ImageTmpDir: img.TmpDir,
ContainerWerfDir: img.ContainerWerfDir,
ProjectName: opts.ProjectName,
}

var stg stage.Interface
switch typedInstr := any(instr).(type) {
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Add]:
stg = stage_instruction.NewAdd(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Cmd]:
stg = stage_instruction.NewCmd(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Copy]:
stg = stage_instruction.NewCopy(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Entrypoint]:
stg = stage_instruction.NewEntrypoint(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Env]:
stg = stage_instruction.NewEnv(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Expose]:
stg = stage_instruction.NewExpose(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Healthcheck]:
stg = stage_instruction.NewHealthcheck(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Label]:
stg = stage_instruction.NewLabel(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Maintainer]:
stg = stage_instruction.NewMaintainer(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.OnBuild]:
stg = stage_instruction.NewOnBuild(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Run]:
isFirstStage := (len(img.stages) == 0)

img.stages = append(img.stages, stage_instruction.NewRun(stage.StageName(fmt.Sprintf("%d-%s", ind, typedInstr.Data.Name())), typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, &stage.BaseStageOptions{
ImageName: img.Name,
ImageTmpDir: img.TmpDir,
ContainerWerfDir: img.ContainerWerfDir,
ProjectName: opts.ProjectName,
}))

stg = stage_instruction.NewRun(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Shell]:
stg = stage_instruction.NewShell(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.StopSignal]:
stg = stage_instruction.NewStopSignal(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.User]:
stg = stage_instruction.NewUser(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Volume]:
stg = stage_instruction.NewVolume(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
case *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Workdir]:
stg = stage_instruction.NewWorkdir(stageName, typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, baseStageOptions)
default:
panic(fmt.Sprintf("unsupported instruction type %#v", instr))
}

img.stages = append(img.stages, stg)

for _, dep := range instr.GetDependenciesByStageRef() {
appendQueue(dep, item.Level+1)
}
Expand Down
35 changes: 35 additions & 0 deletions pkg/build/stage/instruction/add.go
@@ -0,0 +1,35 @@
package instruction

import (
"context"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
backend_instruction "github.com/werf/werf/pkg/container_backend/instruction"
"github.com/werf/werf/pkg/dockerfile"
dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction"
"github.com/werf/werf/pkg/util"
)

type Add struct {
*Base[*dockerfile_instruction.Add]
}

func NewAdd(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Add], dependencies []*config.Dependency, hasPrevStage bool, opts *stage.BaseStageOptions) *Add {
return &Add{Base: NewBase(name, i, dependencies, hasPrevStage, opts)}
}

func (stage *Add) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) {
var args []string
args = append(args, stage.instruction.Data.Name())
args = append(args, stage.instruction.Data.Src...)
args = append(args, stage.instruction.Data.Dst)
args = append(args, stage.instruction.Data.Chown)
args = append(args, stage.instruction.Data.Chmod)
return util.Sha256Hash(args...), nil
}

func (stage *Add) PrepareImage(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevBuiltImage, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) error {
return stage.Base.prepareInstruction(ctx, stageImage, buildContextArchive, backend_instruction.NewAdd(*stage.instruction.Data))
}
15 changes: 12 additions & 3 deletions pkg/build/stage/instruction/base.go
@@ -1,8 +1,11 @@
package instruction

import (
"context"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
"github.com/werf/werf/pkg/dockerfile"
)

Expand All @@ -23,10 +26,16 @@ func NewBase[T dockerfile.InstructionDataInterface](name stage.StageName, instru
}
}

func (stage *Base[T]) HasPrevStage() bool {
return stage.hasPrevStage
func (stg *Base[T]) HasPrevStage() bool {
return stg.hasPrevStage
}

func (s *Base[T]) IsStapelStage() bool {
func (stg *Base[T]) IsStapelStage() bool {
return false
}

func (stg *Base[T]) prepareInstruction(ctx context.Context, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver, backendInstruction container_backend.InstructionInterface) error {
stageImage.Builder.DockerfileStageBuilder().SetBuildContextArchive(buildContextArchive) // FIXME(staged-dockerfile): set context at build-phase level
stageImage.Builder.DockerfileStageBuilder().AppendInstruction(backendInstruction)
return nil
}
34 changes: 34 additions & 0 deletions pkg/build/stage/instruction/cmd.go
@@ -0,0 +1,34 @@
package instruction

import (
"context"
"fmt"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
backend_instruction "github.com/werf/werf/pkg/container_backend/instruction"
"github.com/werf/werf/pkg/dockerfile"
dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction"
"github.com/werf/werf/pkg/util"
)

type Cmd struct {
*Base[*dockerfile_instruction.Cmd]
}

func NewCmd(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Cmd], dependencies []*config.Dependency, hasPrevStage bool, opts *stage.BaseStageOptions) *Cmd {
return &Cmd{Base: NewBase(name, i, dependencies, hasPrevStage, opts)}
}

func (stage *Cmd) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) {
var args []string
args = append(args, stage.instruction.Data.Name())
args = append(args, stage.instruction.Data.Cmd...)
args = append(args, fmt.Sprintf("%v", stage.instruction.Data.PrependShell))
return util.Sha256Hash(args...), nil
}

func (stage *Cmd) PrepareImage(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevBuiltImage, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) error {
return stage.Base.prepareInstruction(ctx, stageImage, buildContextArchive, backend_instruction.NewCmd(*stage.instruction.Data))
}
36 changes: 36 additions & 0 deletions pkg/build/stage/instruction/copy.go
@@ -0,0 +1,36 @@
package instruction

import (
"context"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
backend_instruction "github.com/werf/werf/pkg/container_backend/instruction"
"github.com/werf/werf/pkg/dockerfile"
dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction"
"github.com/werf/werf/pkg/util"
)

type Copy struct {
*Base[*dockerfile_instruction.Copy]
}

func NewCopy(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Copy], dependencies []*config.Dependency, hasPrevStage bool, opts *stage.BaseStageOptions) *Copy {
return &Copy{Base: NewBase(name, i, dependencies, hasPrevStage, opts)}
}

func (stage *Copy) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) {
var args []string
args = append(args, stage.instruction.Data.Name())
args = append(args, stage.instruction.Data.From)
args = append(args, stage.instruction.Data.Src...)
args = append(args, stage.instruction.Data.Dst)
args = append(args, stage.instruction.Data.Chown)
args = append(args, stage.instruction.Data.Chmod)
return util.Sha256Hash(args...), nil
}

func (stage *Copy) PrepareImage(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevBuiltImage, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) error {
return stage.Base.prepareInstruction(ctx, stageImage, buildContextArchive, backend_instruction.NewCopy(*stage.instruction.Data))
}
34 changes: 34 additions & 0 deletions pkg/build/stage/instruction/entrypoint.go
@@ -0,0 +1,34 @@
package instruction

import (
"context"
"fmt"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
backend_instruction "github.com/werf/werf/pkg/container_backend/instruction"
"github.com/werf/werf/pkg/dockerfile"
dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction"
"github.com/werf/werf/pkg/util"
)

type Entrypoint struct {
*Base[*dockerfile_instruction.Entrypoint]
}

func NewEntrypoint(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Entrypoint], dependencies []*config.Dependency, hasPrevStage bool, opts *stage.BaseStageOptions) *Entrypoint {
return &Entrypoint{Base: NewBase(name, i, dependencies, hasPrevStage, opts)}
}

func (stage *Entrypoint) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) {
var args []string
args = append(args, stage.instruction.Data.Name())
args = append(args, stage.instruction.Data.Entrypoint...)
args = append(args, fmt.Sprintf("%v", stage.instruction.Data.PrependShell))
return util.Sha256Hash(args...), nil
}

func (stage *Entrypoint) PrepareImage(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevBuiltImage, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) error {
return stage.Base.prepareInstruction(ctx, stageImage, buildContextArchive, backend_instruction.NewEntrypoint(*stage.instruction.Data))
}
35 changes: 35 additions & 0 deletions pkg/build/stage/instruction/env.go
@@ -0,0 +1,35 @@
package instruction

import (
"context"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
backend_instruction "github.com/werf/werf/pkg/container_backend/instruction"
"github.com/werf/werf/pkg/dockerfile"
dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction"
"github.com/werf/werf/pkg/util"
)

type Env struct {
*Base[*dockerfile_instruction.Env]
}

func NewEnv(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Env], dependencies []*config.Dependency, hasPrevStage bool, opts *stage.BaseStageOptions) *Env {
return &Env{Base: NewBase(name, i, dependencies, hasPrevStage, opts)}
}

func (stage *Env) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) {
var args []string
args = append(args, stage.instruction.Data.Name())
// FIXME(staged-dockerfile): sort envs
for k, v := range stage.instruction.Data.Envs {
args = append(args, k, v)
}
return util.Sha256Hash(args...), nil
}

func (stage *Env) PrepareImage(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevBuiltImage, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) error {
return stage.Base.prepareInstruction(ctx, stageImage, buildContextArchive, backend_instruction.NewEnv(*stage.instruction.Data))
}
32 changes: 32 additions & 0 deletions pkg/build/stage/instruction/expose.go
@@ -0,0 +1,32 @@
package instruction

import (
"context"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
backend_instruction "github.com/werf/werf/pkg/container_backend/instruction"
"github.com/werf/werf/pkg/dockerfile"
dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction"
"github.com/werf/werf/pkg/util"
)

type Expose struct {
*Base[*dockerfile_instruction.Expose]
}

func NewExpose(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Expose], dependencies []*config.Dependency, hasPrevStage bool, opts *stage.BaseStageOptions) *Expose {
return &Expose{Base: NewBase(name, i, dependencies, hasPrevStage, opts)}
}

func (stage *Expose) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) {
var args []string
args = append(args, stage.instruction.Data.Name())
args = append(args, stage.instruction.Data.Ports...)
return util.Sha256Hash(args...), nil
}

func (stage *Expose) PrepareImage(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevBuiltImage, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) error {
return stage.Base.prepareInstruction(ctx, stageImage, buildContextArchive, backend_instruction.NewExpose(*stage.instruction.Data))
}
38 changes: 38 additions & 0 deletions pkg/build/stage/instruction/healthcheck.go
@@ -0,0 +1,38 @@
package instruction

import (
"context"
"fmt"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
"github.com/werf/werf/pkg/dockerfile"
dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction"
"github.com/werf/werf/pkg/util"
)

type Healthcheck struct {
*Base[*dockerfile_instruction.Healthcheck]
}

func NewHealthcheck(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Healthcheck], dependencies []*config.Dependency, hasPrevStage bool, opts *stage.BaseStageOptions) *Healthcheck {
return &Healthcheck{Base: NewBase(name, i, dependencies, hasPrevStage, opts)}
}

func (stage *Healthcheck) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) {
var args []string
args = append(args, stage.instruction.Data.Name())
args = append(args, string(stage.instruction.Data.Type))
args = append(args, stage.instruction.Data.Config.Test...)
args = append(args, stage.instruction.Data.Config.Interval.String())
args = append(args, stage.instruction.Data.Config.Timeout.String())
args = append(args, stage.instruction.Data.Config.StartPeriod.String())
args = append(args, fmt.Sprintf("%d", stage.instruction.Data.Config.Retries))
return util.Sha256Hash(args...), nil
}

func (stage *Healthcheck) PrepareImage(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevBuiltImage, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) error {
// FIXME(staged-dockerfile): construct backend instruction
return stage.Base.prepareInstruction(ctx, stageImage, buildContextArchive, nil)
}
35 changes: 35 additions & 0 deletions pkg/build/stage/instruction/label.go
@@ -0,0 +1,35 @@
package instruction

import (
"context"

"github.com/werf/werf/pkg/build/stage"
"github.com/werf/werf/pkg/config"
"github.com/werf/werf/pkg/container_backend"
backend_instruction "github.com/werf/werf/pkg/container_backend/instruction"
"github.com/werf/werf/pkg/dockerfile"
dockerfile_instruction "github.com/werf/werf/pkg/dockerfile/instruction"
"github.com/werf/werf/pkg/util"
)

type Label struct {
*Base[*dockerfile_instruction.Label]
}

func NewLabel(name stage.StageName, i *dockerfile.DockerfileStageInstruction[*dockerfile_instruction.Label], dependencies []*config.Dependency, hasPrevStage bool, opts *stage.BaseStageOptions) *Label {
return &Label{Base: NewBase(name, i, dependencies, hasPrevStage, opts)}
}

func (stage *Label) GetDependencies(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevImage, prevBuiltImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) (string, error) {
var args []string
args = append(args, stage.instruction.Data.Name())
// FIXME(staged-dockerfile): sort labels map
for k, v := range stage.instruction.Data.Labels {
args = append(args, k, v)
}
return util.Sha256Hash(args...), nil
}

func (stage *Label) PrepareImage(ctx context.Context, c stage.Conveyor, cb container_backend.ContainerBackend, prevBuiltImage, stageImage *stage.StageImage, buildContextArchive container_backend.BuildContextArchiver) error {
return stage.Base.prepareInstruction(ctx, stageImage, buildContextArchive, backend_instruction.NewLabel(*stage.instruction.Data))
}

0 comments on commit 306ed6c

Please sign in to comment.