-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
dockerfile.go
130 lines (120 loc) · 4.22 KB
/
dockerfile.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
119
120
121
122
123
124
125
126
127
128
129
130
package dockerfile
import (
"fmt"
"strings"
"github.com/docker/docker/builder/dockerfile/command"
"github.com/docker/docker/builder/dockerfile/parser"
)
// ParseTreeToDockerfile takes a Dockerfile AST node, as generated by
// parser.Parse, and returns a new, equivalent, Dockerfile.
func ParseTreeToDockerfile(node *parser.Node) []byte {
if node == nil {
return nil
}
buf := []byte(node.Original)
for _, child := range node.Children {
buf = append(buf, ParseTreeToDockerfile(child)...)
}
// Append a line break when needed.
if len(buf) > 0 && buf[len(buf)-1] != '\n' {
buf = append(buf, '\n')
}
return buf
}
// FindAll returns the indices of all children of node such that
// node.Children[i].Value == cmd. Valid values for cmd are defined in the
// package github.com/docker/docker/builder/dockerfile/command.
func FindAll(node *parser.Node, cmd string) []int {
if node == nil {
return nil
}
var indices []int
for i, child := range node.Children {
if child != nil && child.Value == cmd {
indices = append(indices, i)
}
}
return indices
}
// InsertInstructions inserts instructions starting from the pos-th child of
// node, moving other children as necessary. The instructions should be valid
// Dockerfile instructions. InsertInstructions mutates node in-place, and the
// final state of node is equivalent to what parser.Parse would return if the
// original Dockerfile represented by node contained the instructions at the
// specified position pos. If the returned error is non-nil, node is guaranteed
// to be unchanged.
func InsertInstructions(node *parser.Node, pos int, instructions string) error {
if node == nil {
return fmt.Errorf("cannot insert instructions in a nil node")
}
if pos < 0 || pos > len(node.Children) {
return fmt.Errorf("pos %d out of range [0, %d]", pos, len(node.Children)-1)
}
newChild, err := parser.Parse(strings.NewReader(instructions))
if err != nil {
return err
}
// InsertVector pattern (https://github.com/golang/go/wiki/SliceTricks)
node.Children = append(node.Children[:pos], append(newChild.AST.Children, node.Children[pos:]...)...)
return nil
}
// LastBaseImage takes a Dockerfile root node and returns the base image
// declared in the last FROM instruction.
func LastBaseImage(node *parser.Node) string {
baseImages := baseImages(node)
if len(baseImages) == 0 {
return ""
}
return baseImages[len(baseImages)-1]
}
// baseImages takes a Dockerfile root node and returns a list of all base images
// declared in the Dockerfile. Each base image is the argument of a FROM
// instruction.
func baseImages(node *parser.Node) []string {
var images []string
for _, pos := range FindAll(node, command.From) {
images = append(images, nextValues(node.Children[pos])...)
}
return images
}
// LastExposedPorts takes a Dockerfile root node and returns a list of ports
// exposed in the last image built by the Dockerfile, i.e., only the EXPOSE
// instructions after the last FROM instruction are considered.
func LastExposedPorts(node *parser.Node) []string {
exposedPorts := exposedPorts(node)
if len(exposedPorts) == 0 {
return nil
}
return exposedPorts[len(exposedPorts)-1]
}
// exposedPorts takes a Dockerfile root node and returns a list of all ports
// exposed in the Dockerfile, grouped by images that this Dockerfile produces.
// The number of port lists returned is the number of images produced by this
// Dockerfile, which is the same as the number of FROM instructions.
func exposedPorts(node *parser.Node) [][]string {
var allPorts [][]string
var ports []string
froms := FindAll(node, command.From)
exposes := FindAll(node, command.Expose)
for i, j := len(froms)-1, len(exposes)-1; i >= 0; i-- {
for ; j >= 0 && exposes[j] > froms[i]; j-- {
ports = append(nextValues(node.Children[exposes[j]]), ports...)
}
allPorts = append([][]string{ports}, allPorts...)
ports = nil
}
return allPorts
}
// nextValues returns a slice of values from the next nodes following node. This
// roughly translates to the arguments to the Docker builder instruction
// represented by node.
func nextValues(node *parser.Node) []string {
if node == nil {
return nil
}
var values []string
for next := node.Next; next != nil; next = next.Next {
values = append(values, next.Value)
}
return values
}