/
dockerfile_stage.go
118 lines (102 loc) · 3.35 KB
/
dockerfile_stage.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package dockerfile
import (
"fmt"
"strconv"
"strings"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
)
func NewDockerfileStage(index int, baseName, stageName string, instructions []DockerfileStageInstructionInterface, platform string, expanderFactory ExpanderFactory) *DockerfileStage {
return &DockerfileStage{
ExpanderFactory: expanderFactory,
BaseName: baseName,
StageName: stageName,
Instructions: instructions,
Platform: platform,
}
}
type DockerfileStage struct {
Dockerfile *Dockerfile
Dependencies []*DockerfileStage
BaseStage *DockerfileStage
ExpanderFactory ExpanderFactory
BaseName string
Index int
StageName string
Platform string
Instructions []DockerfileStageInstructionInterface
}
func (stage *DockerfileStage) AppendDependencyStage(dep *DockerfileStage) {
for _, d := range stage.Dependencies {
if d.Index == dep.Index {
return
}
}
stage.Dependencies = append(stage.Dependencies, dep)
}
func (stage *DockerfileStage) WerfImageName() string {
if stage.HasStageName() {
return fmt.Sprintf("stage/%s", stage.StageName)
} else {
return fmt.Sprintf("stage/%d", stage.Index)
}
}
func (stage *DockerfileStage) LogName() string {
if stage.HasStageName() {
return stage.StageName
} else {
return fmt.Sprintf("<%d>", stage.Index)
}
}
func (stage *DockerfileStage) HasStageName() bool {
return stage.StageName != ""
}
func SetupDockerfileStagesDependencies(stages []*DockerfileStage) error {
stageByName := make(map[string]*DockerfileStage)
for _, stage := range stages {
if stage.HasStageName() {
stageByName[strings.ToLower(stage.StageName)] = stage
}
}
for _, stage := range stages {
// Base image dependency
if baseStage, hasKey := stageByName[strings.ToLower(stage.BaseName)]; hasKey {
stage.BaseStage = baseStage
stage.Dependencies = append(stage.Dependencies, baseStage)
}
for _, instr := range stage.Instructions {
switch typedInstr := instr.GetInstructionData().(type) {
case *instructions.CopyCommand:
if typedInstr.From != "" {
if dep := findStageByRef(typedInstr.From, stages, stageByName); dep != nil {
stage.AppendDependencyStage(dep)
instr.SetDependencyByStageRef(typedInstr.From, dep)
} else {
return fmt.Errorf("unable to resolve stage %q instruction %s --from=%q: no such stage", stage.LogName(), instr.GetInstructionData().Name(), typedInstr.From)
}
}
case *instructions.RunCommand:
mounts := instructions.GetMounts(typedInstr)
for _, mount := range mounts {
if mount.From != "" {
if dep := findStageByRef(mount.From, stages, stageByName); dep != nil {
stage.AppendDependencyStage(dep)
instr.SetDependencyByStageRef(mount.From, dep)
} else {
return fmt.Errorf("unable to resolve stage %q instruction %s --mount=from=%s: no such stage", stage.LogName(), instr.GetInstructionData().Name(), mount.From)
}
}
}
}
}
}
return nil
}
// findStageByRef finds stage by stage reference which is stage index or stage name
func findStageByRef(ref string, stages []*DockerfileStage, stageByName map[string]*DockerfileStage) *DockerfileStage {
if stg, found := stageByName[strings.ToLower(ref)]; found {
return stg
} else if ind, err := strconv.Atoi(ref); err == nil && ind >= 0 && ind < len(stages) {
return stages[ind]
}
return nil
}