-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocker.go
161 lines (134 loc) · 3.79 KB
/
docker.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package main
import (
stdtar "archive/tar"
"bytes"
"io"
"log"
"net/url"
"strings"
"github.com/Bowery/gopackages/docker"
"github.com/docker/docker/builder/command"
)
// createImage creates the given image from a base image.
func createImage(imageID, image, baseImage string) error {
log.Println("Creating build container using", baseImage, "for", imageID)
id, err := DockerClient.Create(new(docker.Config), baseImage, nil)
if err != nil {
return err
}
// Commit the empty container to the image name.
log.Println("Commit build container to image", image)
err = DockerClient.CommitImage(id, image)
if err != nil {
return err
}
// Clean up the container.
log.Println("Removing build container", imageID)
go DockerClient.Remove(id)
return nil
}
// buildImage builds an image for the repo from a list of paths that should
// include a Dockerfile. If strip is true, the Dockerfile found is stripped of
// unsafe instructions. Progress is sent across the given channel.
func buildImage(strip bool, paths map[string]string, vars map[string]string, repo string, progress chan float64) (string, error) {
dockerfile, ok := paths["Dockerfile"]
if strip && ok {
fileContents, err := stripInstructions(strings.NewReader(dockerfile))
if err != nil {
return "", err
}
paths["Dockerfile"] = fileContents.String()
dockerfile = paths["Dockerfile"]
}
input, err := createImageInput(paths, vars)
if err != nil {
return "", err
}
if progress == nil {
return DockerClient.BuildImage(input, "", repo, nil)
}
steps, err := docker.ParseDockerfile(strings.NewReader(dockerfile))
if err != nil {
return "", err
}
progChan := make(chan int)
stepsNum := float64(len(steps))
go func() {
for prog := range progChan {
progress <- (float64(prog) + 1) / stepsNum
}
}()
return DockerClient.BuildImage(input, "", repo, progChan)
}
// createImageInput creates a tar reader using the given templates as files.
// The given vars are replaced in all the templates encountered.
func createImageInput(tmpls, vars map[string]string) (io.Reader, error) {
var buf bytes.Buffer
tarW := stdtar.NewWriter(&buf)
header := &stdtar.Header{
Mode: 0644,
}
for path, tmpl := range tmpls {
if vars != nil {
for key, val := range vars {
tmpl = strings.Replace(tmpl, "{{"+key+"}}", val, -1)
}
}
header.Name = path
header.Size = int64(len(tmpl))
err := tarW.WriteHeader(header)
if err != nil {
return nil, err
}
_, err = io.Copy(tarW, strings.NewReader(tmpl))
if err != nil {
return nil, err
}
}
return &buf, tarW.Close()
}
// stripInstructions reads Dockerfile input and strips unsafe or disallowed
// instructions. It also only allows URL based sources for the ADD instruction.
func stripInstructions(contents io.Reader) (*bytes.Buffer, error) {
var buf bytes.Buffer
nodes, err := docker.ParseDockerfile(contents)
if err != nil {
return nil, err
}
for _, node := range nodes {
// Commands disabled, either for security or because they use features
// disallowed(like local file copying).
if node.Value == command.Cmd || node.Value == command.Copy ||
node.Value == command.Entrypoint || node.Value == command.Volume ||
node.Value == command.User || node.Value == command.Onbuild {
continue
}
// Only allow ADD instructions using URLs for src paths.
if node.Value == command.Add {
skip := false
n := node.Next
// Descend the src paths, Value field is the path.
for n != nil {
// Skip the dest(last) path.
if n.Next == nil {
break
}
parsedURL, err := url.Parse(n.Value)
if err != nil {
skip = true
break
}
if parsedURL.Scheme == "" || parsedURL.Scheme == "file" {
skip = true
break
}
n = n.Next
}
if skip {
continue
}
}
buf.Write([]byte(node.Original + "\n"))
}
return &buf, nil
}