Browse code

Dockerfile builder moved to github.com/openshift/imagebuilder

Clayton Coleman authored on 2016/08/04 23:44:16
Showing 39 changed files
... ...
@@ -17,8 +17,8 @@ import (
17 17
 	"github.com/golang/glog"
18 18
 	"golang.org/x/net/context"
19 19
 
20
+	"github.com/openshift/imagebuilder/imageprogress"
20 21
 	starterrors "github.com/openshift/origin/pkg/bootstrap/docker/errors"
21
-	"github.com/openshift/origin/pkg/util/docker/dockerfile/builder/imageprogress"
22 22
 )
23 23
 
24 24
 const openShiftInsecureCIDR = "172.30.0.0/16"
... ...
@@ -15,7 +15,7 @@ import (
15 15
 	s2iapi "github.com/openshift/source-to-image/pkg/api"
16 16
 	"github.com/openshift/source-to-image/pkg/tar"
17 17
 
18
-	"github.com/openshift/origin/pkg/util/docker/dockerfile/builder/imageprogress"
18
+	"github.com/openshift/imagebuilder/imageprogress"
19 19
 )
20 20
 
21 21
 var (
22 22
deleted file mode 100644
... ...
@@ -1,192 +0,0 @@
1
-
2
-                                 Apache License
3
-                           Version 2.0, January 2004
4
-                        https://www.apache.org/licenses/
5
-
6
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
-
8
-   1. Definitions.
9
-
10
-      "License" shall mean the terms and conditions for use, reproduction,
11
-      and distribution as defined by Sections 1 through 9 of this document.
12
-
13
-      "Licensor" shall mean the copyright owner or entity authorized by
14
-      the copyright owner that is granting the License.
15
-
16
-      "Legal Entity" shall mean the union of the acting entity and all
17
-      other entities that control, are controlled by, or are under common
18
-      control with that entity. For the purposes of this definition,
19
-      "control" means (i) the power, direct or indirect, to cause the
20
-      direction or management of such entity, whether by contract or
21
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
-      outstanding shares, or (iii) beneficial ownership of such entity.
23
-
24
-      "You" (or "Your") shall mean an individual or Legal Entity
25
-      exercising permissions granted by this License.
26
-
27
-      "Source" form shall mean the preferred form for making modifications,
28
-      including but not limited to software source code, documentation
29
-      source, and configuration files.
30
-
31
-      "Object" form shall mean any form resulting from mechanical
32
-      transformation or translation of a Source form, including but
33
-      not limited to compiled object code, generated documentation,
34
-      and conversions to other media types.
35
-
36
-      "Work" shall mean the work of authorship, whether in Source or
37
-      Object form, made available under the License, as indicated by a
38
-      copyright notice that is included in or attached to the work
39
-      (an example is provided in the Appendix below).
40
-
41
-      "Derivative Works" shall mean any work, whether in Source or Object
42
-      form, that is based on (or derived from) the Work and for which the
43
-      editorial revisions, annotations, elaborations, or other modifications
44
-      represent, as a whole, an original work of authorship. For the purposes
45
-      of this License, Derivative Works shall not include works that remain
46
-      separable from, or merely link (or bind by name) to the interfaces of,
47
-      the Work and Derivative Works thereof.
48
-
49
-      "Contribution" shall mean any work of authorship, including
50
-      the original version of the Work and any modifications or additions
51
-      to that Work or Derivative Works thereof, that is intentionally
52
-      submitted to Licensor for inclusion in the Work by the copyright owner
53
-      or by an individual or Legal Entity authorized to submit on behalf of
54
-      the copyright owner. For the purposes of this definition, "submitted"
55
-      means any form of electronic, verbal, or written communication sent
56
-      to the Licensor or its representatives, including but not limited to
57
-      communication on electronic mailing lists, source code control systems,
58
-      and issue tracking systems that are managed by, or on behalf of, the
59
-      Licensor for the purpose of discussing and improving the Work, but
60
-      excluding communication that is conspicuously marked or otherwise
61
-      designated in writing by the copyright owner as "Not a Contribution."
62
-
63
-      "Contributor" shall mean Licensor and any individual or Legal Entity
64
-      on behalf of whom a Contribution has been received by Licensor and
65
-      subsequently incorporated within the Work.
66
-
67
-   2. Grant of Copyright License. Subject to the terms and conditions of
68
-      this License, each Contributor hereby grants to You a perpetual,
69
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
-      copyright license to reproduce, prepare Derivative Works of,
71
-      publicly display, publicly perform, sublicense, and distribute the
72
-      Work and such Derivative Works in Source or Object form.
73
-
74
-   3. Grant of Patent License. Subject to the terms and conditions of
75
-      this License, each Contributor hereby grants to You a perpetual,
76
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
-      (except as stated in this section) patent license to make, have made,
78
-      use, offer to sell, sell, import, and otherwise transfer the Work,
79
-      where such license applies only to those patent claims licensable
80
-      by such Contributor that are necessarily infringed by their
81
-      Contribution(s) alone or by combination of their Contribution(s)
82
-      with the Work to which such Contribution(s) was submitted. If You
83
-      institute patent litigation against any entity (including a
84
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
85
-      or a Contribution incorporated within the Work constitutes direct
86
-      or contributory patent infringement, then any patent licenses
87
-      granted to You under this License for that Work shall terminate
88
-      as of the date such litigation is filed.
89
-
90
-   4. Redistribution. You may reproduce and distribute copies of the
91
-      Work or Derivative Works thereof in any medium, with or without
92
-      modifications, and in Source or Object form, provided that You
93
-      meet the following conditions:
94
-
95
-      (a) You must give any other recipients of the Work or
96
-          Derivative Works a copy of this License; and
97
-
98
-      (b) You must cause any modified files to carry prominent notices
99
-          stating that You changed the files; and
100
-
101
-      (c) You must retain, in the Source form of any Derivative Works
102
-          that You distribute, all copyright, patent, trademark, and
103
-          attribution notices from the Source form of the Work,
104
-          excluding those notices that do not pertain to any part of
105
-          the Derivative Works; and
106
-
107
-      (d) If the Work includes a "NOTICE" text file as part of its
108
-          distribution, then any Derivative Works that You distribute must
109
-          include a readable copy of the attribution notices contained
110
-          within such NOTICE file, excluding those notices that do not
111
-          pertain to any part of the Derivative Works, in at least one
112
-          of the following places: within a NOTICE text file distributed
113
-          as part of the Derivative Works; within the Source form or
114
-          documentation, if provided along with the Derivative Works; or,
115
-          within a display generated by the Derivative Works, if and
116
-          wherever such third-party notices normally appear. The contents
117
-          of the NOTICE file are for informational purposes only and
118
-          do not modify the License. You may add Your own attribution
119
-          notices within Derivative Works that You distribute, alongside
120
-          or as an addendum to the NOTICE text from the Work, provided
121
-          that such additional attribution notices cannot be construed
122
-          as modifying the License.
123
-
124
-      You may add Your own copyright statement to Your modifications and
125
-      may provide additional or different license terms and conditions
126
-      for use, reproduction, or distribution of Your modifications, or
127
-      for any such Derivative Works as a whole, provided Your use,
128
-      reproduction, and distribution of the Work otherwise complies with
129
-      the conditions stated in this License.
130
-
131
-   5. Submission of Contributions. Unless You explicitly state otherwise,
132
-      any Contribution intentionally submitted for inclusion in the Work
133
-      by You to the Licensor shall be under the terms and conditions of
134
-      this License, without any additional terms or conditions.
135
-      Notwithstanding the above, nothing herein shall supersede or modify
136
-      the terms of any separate license agreement you may have executed
137
-      with Licensor regarding such Contributions.
138
-
139
-   6. Trademarks. This License does not grant permission to use the trade
140
-      names, trademarks, service marks, or product names of the Licensor,
141
-      except as required for reasonable and customary use in describing the
142
-      origin of the Work and reproducing the content of the NOTICE file.
143
-
144
-   7. Disclaimer of Warranty. Unless required by applicable law or
145
-      agreed to in writing, Licensor provides the Work (and each
146
-      Contributor provides its Contributions) on an "AS IS" BASIS,
147
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
-      implied, including, without limitation, any warranties or conditions
149
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
-      PARTICULAR PURPOSE. You are solely responsible for determining the
151
-      appropriateness of using or redistributing the Work and assume any
152
-      risks associated with Your exercise of permissions under this License.
153
-
154
-   8. Limitation of Liability. In no event and under no legal theory,
155
-      whether in tort (including negligence), contract, or otherwise,
156
-      unless required by applicable law (such as deliberate and grossly
157
-      negligent acts) or agreed to in writing, shall any Contributor be
158
-      liable to You for damages, including any direct, indirect, special,
159
-      incidental, or consequential damages of any character arising as a
160
-      result of this License or out of the use or inability to use the
161
-      Work (including but not limited to damages for loss of goodwill,
162
-      work stoppage, computer failure or malfunction, or any and all
163
-      other commercial damages or losses), even if such Contributor
164
-      has been advised of the possibility of such damages.
165
-
166
-   9. Accepting Warranty or Additional Liability. While redistributing
167
-      the Work or Derivative Works thereof, You may choose to offer,
168
-      and charge a fee for, acceptance of support, warranty, indemnity,
169
-      or other liability obligations and/or rights consistent with this
170
-      License. However, in accepting such obligations, You may act only
171
-      on Your own behalf and on Your sole responsibility, not on behalf
172
-      of any other Contributor, and only if You agree to indemnify,
173
-      defend, and hold each Contributor harmless for any liability
174
-      incurred by, or claims asserted against, such Contributor by reason
175
-      of your accepting any such warranty or additional liability.
176
-
177
-   END OF TERMS AND CONDITIONS
178
-
179
-   Copyright 2013-2016 Docker, Inc.
180
-   Copyright 2016 The OpenShift Authors
181
-
182
-   Licensed under the Apache License, Version 2.0 (the "License");
183
-   you may not use this file except in compliance with the License.
184
-   You may obtain a copy of the License at
185
-
186
-       https://www.apache.org/licenses/LICENSE-2.0
187
-
188
-   Unless required by applicable law or agreed to in writing, software
189
-   distributed under the License is distributed on an "AS IS" BASIS,
190
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
191
-   See the License for the specific language governing permissions and
192
-   limitations under the License.
193 1
deleted file mode 100644
... ...
@@ -1,293 +0,0 @@
1
-package builder
2
-
3
-import (
4
-	"bytes"
5
-	"fmt"
6
-	"io/ioutil"
7
-	"log"
8
-	"os"
9
-	"path/filepath"
10
-	"runtime"
11
-	"strings"
12
-
13
-	docker "github.com/fsouza/go-dockerclient"
14
-
15
-	"github.com/docker/docker/builder/command"
16
-	"github.com/docker/docker/builder/parser"
17
-)
18
-
19
-// Copy defines a copy operation required on the container.
20
-type Copy struct {
21
-	Src      string
22
-	Dest     []string
23
-	Download bool
24
-}
25
-
26
-// Run defines a run operation required in the container.
27
-type Run struct {
28
-	Shell bool
29
-	Args  []string
30
-}
31
-
32
-type Executor interface {
33
-	Copy(copies ...Copy) error
34
-	Run(run Run, config docker.Config) error
35
-}
36
-
37
-type logExecutor struct{}
38
-
39
-func (logExecutor) Copy(copies ...Copy) error {
40
-	for _, c := range copies {
41
-		log.Printf("COPY %s -> %v (download:%t)", c.Src, c.Dest, c.Download)
42
-	}
43
-	return nil
44
-}
45
-
46
-func (logExecutor) Run(run Run, config docker.Config) error {
47
-	log.Printf("RUN %v %t (%v)", run.Args, run.Shell, config.Env)
48
-	return nil
49
-}
50
-
51
-type noopExecutor struct{}
52
-
53
-func (noopExecutor) Copy(copies ...Copy) error {
54
-	return nil
55
-}
56
-
57
-func (noopExecutor) Run(run Run, config docker.Config) error {
58
-	return nil
59
-}
60
-
61
-var (
62
-	LogExecutor  = logExecutor{}
63
-	NoopExecutor = noopExecutor{}
64
-)
65
-
66
-type Builder struct {
67
-	RunConfig docker.Config
68
-
69
-	Env    []string
70
-	Args   map[string]string
71
-	CmdSet bool
72
-	Author string
73
-
74
-	AllowedArgs map[string]bool
75
-
76
-	PendingRuns   []Run
77
-	PendingCopies []Copy
78
-
79
-	Executor Executor
80
-}
81
-
82
-func NewBuilder() *Builder {
83
-	args := make(map[string]bool)
84
-	for k, v := range builtinAllowedBuildArgs {
85
-		args[k] = v
86
-	}
87
-	return &Builder{
88
-		Args:        make(map[string]string),
89
-		AllowedArgs: args,
90
-	}
91
-}
92
-
93
-// Step creates a new step from the current state.
94
-func (b *Builder) Step() *Step {
95
-	dst := make([]string, len(b.Env)+len(b.RunConfig.Env))
96
-	copy(dst, b.Env)
97
-	dst = append(dst, b.RunConfig.Env...)
98
-	return &Step{Env: dst}
99
-}
100
-
101
-// Run executes a step, transforming the current builder and
102
-// invoking any Copy or Run operations.
103
-func (b *Builder) Run(step *Step, exec Executor) error {
104
-	fn, ok := evaluateTable[step.Command]
105
-	if !ok {
106
-		return fmt.Errorf("Unknown instruction: %s", strings.ToUpper(step.Command))
107
-	}
108
-	if err := fn(b, step.Args, step.Attrs, step.Original); err != nil {
109
-		return err
110
-	}
111
-
112
-	copies := b.PendingCopies
113
-	b.PendingCopies = nil
114
-	runs := b.PendingRuns
115
-	b.PendingRuns = nil
116
-
117
-	if err := exec.Copy(copies...); err != nil {
118
-		return err
119
-	}
120
-	for _, run := range runs {
121
-		config := b.Config()
122
-		if err := exec.Run(run, *config); err != nil {
123
-			return err
124
-		}
125
-	}
126
-
127
-	return nil
128
-}
129
-
130
-// RequiresStart returns true if a running container environment is necessary
131
-// to invoke the provided commands
132
-func (b *Builder) RequiresStart(node *parser.Node) bool {
133
-	for _, child := range node.Children {
134
-		if child.Value == command.Run {
135
-			return true
136
-		}
137
-	}
138
-	return false
139
-}
140
-
141
-// Config returns a snapshot of the current RunConfig intended for
142
-// use with a container commit.
143
-func (b *Builder) Config() *docker.Config {
144
-	config := b.RunConfig
145
-	if config.OnBuild == nil {
146
-		config.OnBuild = []string{}
147
-	}
148
-	if config.Entrypoint == nil {
149
-		config.Entrypoint = []string{}
150
-	}
151
-	config.Image = ""
152
-	return &config
153
-}
154
-
155
-// Arguments returns the currently active arguments.
156
-func (b *Builder) Arguments() []string {
157
-	var envs []string
158
-	for key, val := range b.Args {
159
-		if _, ok := b.AllowedArgs[key]; ok {
160
-			envs = append(envs, fmt.Sprintf("%s=%s", key, val))
161
-		}
162
-	}
163
-	return envs
164
-}
165
-
166
-// ErrNoFROM is returned if the Dockerfile did not contain a FROM
167
-// statement.
168
-var ErrNoFROM = fmt.Errorf("no FROM statement found")
169
-
170
-// From returns the image this dockerfile depends on, or an error
171
-// if no FROM is found or if multiple FROM are specified. If a
172
-// single from is found the passed node is updated with only
173
-// the remaining statements.  The builder's RunConfig.Image field
174
-// is set to the first From found, or left unchanged if already
175
-// set.
176
-func (b *Builder) From(node *parser.Node) (string, error) {
177
-	children := SplitChildren(node, command.From)
178
-	switch {
179
-	case len(children) == 0:
180
-		return "", ErrNoFROM
181
-	case len(children) > 1:
182
-		return "", fmt.Errorf("multiple FROM statements are not supported")
183
-	default:
184
-		step := b.Step()
185
-		if err := step.Resolve(children[0]); err != nil {
186
-			return "", err
187
-		}
188
-		if err := b.Run(step, NoopExecutor); err != nil {
189
-			return "", err
190
-		}
191
-		return b.RunConfig.Image, nil
192
-	}
193
-}
194
-
195
-// FromImage updates the builder to use the provided image (resetting RunConfig
196
-// and recording the image environment), and updates the node with any ONBUILD
197
-// statements extracted from the parent image.
198
-func (b *Builder) FromImage(image *docker.Image, node *parser.Node) error {
199
-	SplitChildren(node, command.From)
200
-
201
-	b.RunConfig = *image.Config
202
-	b.Env = b.RunConfig.Env
203
-	b.RunConfig.Env = nil
204
-
205
-	// Check to see if we have a default PATH, note that windows won't
206
-	// have one as its set by HCS
207
-	if runtime.GOOS != "windows" && !hasEnvName(b.Env, "PATH") {
208
-		b.RunConfig.Env = append(b.RunConfig.Env, "PATH="+defaultPathEnv)
209
-	}
210
-
211
-	// Join the image onbuild statements into node
212
-	if image.Config == nil || len(image.Config.OnBuild) == 0 {
213
-		return nil
214
-	}
215
-	extra, err := parser.Parse(bytes.NewBufferString(strings.Join(image.Config.OnBuild, "\n")))
216
-	if err != nil {
217
-		return err
218
-	}
219
-	for _, child := range extra.Children {
220
-		switch strings.ToUpper(child.Value) {
221
-		case "ONBUILD":
222
-			return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
223
-		case "MAINTAINER", "FROM":
224
-			return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", child.Value)
225
-		}
226
-	}
227
-	node.Children = append(extra.Children, node.Children...)
228
-	// Since we've processed the OnBuild statements, clear them from the runconfig state.
229
-	b.RunConfig.OnBuild = nil
230
-	return nil
231
-}
232
-
233
-// SplitChildren removes any children with the provided value from node
234
-// and returns them as an array. node.Children is updated.
235
-func SplitChildren(node *parser.Node, value string) []*parser.Node {
236
-	var split []*parser.Node
237
-	var children []*parser.Node
238
-	for _, child := range node.Children {
239
-		if child.Value == value {
240
-			split = append(split, child)
241
-		} else {
242
-			children = append(children, child)
243
-		}
244
-	}
245
-	node.Children = children
246
-	return split
247
-}
248
-
249
-// StepFunc is invoked with the result of a resolved step.
250
-type StepFunc func(*Builder, []string, map[string]bool, string) error
251
-
252
-var evaluateTable = map[string]StepFunc{
253
-	command.Env:        env,
254
-	command.Label:      label,
255
-	command.Maintainer: maintainer,
256
-	command.Add:        add,
257
-	command.Copy:       dispatchCopy, // copy() is a go builtin
258
-	command.From:       from,
259
-	command.Onbuild:    onbuild,
260
-	command.Workdir:    workdir,
261
-	command.Run:        run,
262
-	command.Cmd:        cmd,
263
-	command.Entrypoint: entrypoint,
264
-	command.Expose:     expose,
265
-	command.Volume:     volume,
266
-	command.User:       user,
267
-	// TODO: use the public constants for these when we update dockerfile/
268
-	commandStopSignal: stopSignal,
269
-	commandArg:        arg,
270
-}
271
-
272
-// builtinAllowedBuildArgs is list of built-in allowed build args
273
-var builtinAllowedBuildArgs = map[string]bool{
274
-	"HTTP_PROXY":  true,
275
-	"http_proxy":  true,
276
-	"HTTPS_PROXY": true,
277
-	"https_proxy": true,
278
-	"FTP_PROXY":   true,
279
-	"ftp_proxy":   true,
280
-	"NO_PROXY":    true,
281
-	"no_proxy":    true,
282
-}
283
-
284
-// ParseDockerIgnore returns a list of the excludes in the .dockerignore file.
285
-// extracted from fsouza/go-dockerclient.
286
-func ParseDockerignore(root string) ([]string, error) {
287
-	var excludes []string
288
-	ignore, err := ioutil.ReadFile(filepath.Join(root, ".dockerignore"))
289
-	if err != nil && !os.IsNotExist(err) {
290
-		return excludes, fmt.Errorf("error reading .dockerignore: '%s'", err)
291
-	}
292
-	return strings.Split(string(ignore), "\n"), nil
293
-}
294 1
deleted file mode 100644
... ...
@@ -1,350 +0,0 @@
1
-package builder
2
-
3
-import (
4
-	"bytes"
5
-	"io/ioutil"
6
-	"os"
7
-	"testing"
8
-
9
-	"fmt"
10
-	"github.com/docker/docker/builder/parser"
11
-	docker "github.com/fsouza/go-dockerclient"
12
-	"reflect"
13
-)
14
-
15
-func TestRun(t *testing.T) {
16
-	f, err := os.Open("../../../../../images/dockerregistry/Dockerfile")
17
-	if err != nil {
18
-		t.Fatal(err)
19
-	}
20
-	node, err := parser.Parse(f)
21
-	if err != nil {
22
-		t.Fatal(err)
23
-	}
24
-	b := NewBuilder()
25
-	from, err := b.From(node)
26
-	if err != nil {
27
-		t.Fatal(err)
28
-	}
29
-	if from != "openshift/origin-base" {
30
-		t.Fatalf("unexpected from: %s", from)
31
-	}
32
-	for _, child := range node.Children {
33
-		step := b.Step()
34
-		if err := step.Resolve(child); err != nil {
35
-			t.Fatal(err)
36
-		}
37
-		if err := b.Run(step, LogExecutor); err != nil {
38
-			t.Fatal(err)
39
-		}
40
-	}
41
-	t.Logf("config: %#v", b.Config())
42
-	t.Logf(node.Dump())
43
-}
44
-
45
-type testExecutor struct {
46
-	Copies  []Copy
47
-	Runs    []Run
48
-	Configs []docker.Config
49
-	Err     error
50
-}
51
-
52
-func (e *testExecutor) Copy(copies ...Copy) error {
53
-	e.Copies = append(e.Copies, copies...)
54
-	return e.Err
55
-}
56
-func (e *testExecutor) Run(run Run, config docker.Config) error {
57
-	e.Runs = append(e.Runs, run)
58
-	e.Configs = append(e.Configs, config)
59
-	return e.Err
60
-}
61
-
62
-func TestBuilder(t *testing.T) {
63
-	testCases := []struct {
64
-		Dockerfile string
65
-		From       string
66
-		Copies     []Copy
67
-		Runs       []Run
68
-		Config     docker.Config
69
-		ErrFn      func(err error) bool
70
-	}{
71
-		{
72
-			Dockerfile: "testdata/dir/Dockerfile",
73
-			From:       "busybox",
74
-			Copies: []Copy{
75
-				{Src: ".", Dest: []string{"/"}, Download: false},
76
-				{Src: ".", Dest: []string{"/dir"}},
77
-				{Src: "subdir/", Dest: []string{"/test/"}, Download: false},
78
-			},
79
-			Config: docker.Config{
80
-				Image: "busybox",
81
-			},
82
-		},
83
-		{
84
-			Dockerfile: "testdata/ignore/Dockerfile",
85
-			From:       "busybox",
86
-			Copies: []Copy{
87
-				{Src: ".", Dest: []string{"/"}},
88
-			},
89
-			Config: docker.Config{
90
-				Image: "busybox",
91
-			},
92
-		},
93
-		{
94
-			Dockerfile: "testdata/Dockerfile.env",
95
-			From:       "busybox",
96
-			Config: docker.Config{
97
-				Env:   []string{"name=value", "name2=value2a            value2b", "name1=value1", "name3=value3a\\n\"value3b\"", "name4=value4a\\\\nvalue4b"},
98
-				Image: "busybox",
99
-			},
100
-		},
101
-		{
102
-			Dockerfile: "testdata/Dockerfile.edgecases",
103
-			From:       "busybox",
104
-			Copies: []Copy{
105
-				{Src: ".", Dest: []string{"/"}, Download: true},
106
-				{Src: ".", Dest: []string{"/test/copy"}},
107
-			},
108
-			Runs: []Run{
109
-				{Shell: false, Args: []string{"ls", "-la"}},
110
-				{Shell: false, Args: []string{"echo", "'1234'"}},
111
-				{Shell: true, Args: []string{"echo \"1234\""}},
112
-				{Shell: true, Args: []string{"echo 1234"}},
113
-				{Shell: true, Args: []string{"echo '1234' &&     echo \"456\" &&     echo 789"}},
114
-				{Shell: true, Args: []string{"sh -c 'echo root:testpass         > /tmp/passwd'"}},
115
-				{Shell: true, Args: []string{"mkdir -p /test /test2 /test3/test"}},
116
-			},
117
-			Config: docker.Config{
118
-				User:         "docker:root",
119
-				ExposedPorts: map[docker.Port]struct{}{"6000/tcp": {}, "3000/tcp": {}, "9000/tcp": {}, "5000/tcp": {}},
120
-				Env:          []string{"SCUBA=1 DUBA 3"},
121
-				Cmd:          []string{"/bin/sh", "-c", "echo 'test' | wc -"},
122
-				Image:        "busybox",
123
-				Volumes:      map[string]struct{}{"/test2": {}, "/test3": {}, "/test": {}},
124
-				WorkingDir:   "/test",
125
-				OnBuild:      []string{"RUN [\"echo\", \"test\"]", "RUN echo test", "COPY . /"},
126
-			},
127
-		},
128
-		{
129
-			Dockerfile: "testdata/Dockerfile.exposedefault",
130
-			From:       "busybox",
131
-			Config: docker.Config{
132
-				ExposedPorts: map[docker.Port]struct{}{"3469/tcp": {}},
133
-				Image:        "busybox",
134
-			},
135
-		},
136
-		{
137
-			Dockerfile: "testdata/Dockerfile.add",
138
-			From:       "busybox",
139
-			Copies: []Copy{
140
-				{Src: "https://github.com/openshift/origin/raw/master/README.md", Dest: []string{"/README.md"}, Download: true},
141
-				{Src: "https://github.com/openshift/origin/raw/master/LICENSE", Dest: []string{"/"}, Download: true},
142
-				{Src: "https://github.com/openshift/origin/raw/master/LICENSE", Dest: []string{"/A"}, Download: true},
143
-				{Src: "https://github.com/openshift/origin/raw/master/LICENSE", Dest: []string{"/a"}, Download: true},
144
-				{Src: "https://github.com/openshift/origin/raw/master/LICENSE", Dest: []string{"/b/a"}, Download: true},
145
-				{Src: "https://github.com/openshift/origin/raw/master/LICENSE", Dest: []string{"/b/"}, Download: true},
146
-				{Src: "https://github.com/openshift/ruby-hello-world/archive/master.zip", Dest: []string{"/tmp/"}, Download: true},
147
-			},
148
-			Runs: []Run{
149
-				{Shell: true, Args: []string{"mkdir ./b"}},
150
-			},
151
-			Config: docker.Config{
152
-				Image: "busybox",
153
-				User:  "root",
154
-			},
155
-		},
156
-	}
157
-	for i, test := range testCases {
158
-		data, err := ioutil.ReadFile(test.Dockerfile)
159
-		if err != nil {
160
-			t.Errorf("%d: %v", i, err)
161
-			continue
162
-		}
163
-		node, err := parser.Parse(bytes.NewBuffer(data))
164
-		if err != nil {
165
-			t.Errorf("%d: %v", i, err)
166
-			continue
167
-		}
168
-		b := NewBuilder()
169
-		from, err := b.From(node)
170
-		if err != nil {
171
-			t.Errorf("%d: %v", i, err)
172
-			continue
173
-		}
174
-		if from != test.From {
175
-			t.Errorf("%d: unexpected FROM: %s", i, from)
176
-		}
177
-		e := &testExecutor{}
178
-		var lastErr error
179
-		for j, child := range node.Children {
180
-			step := b.Step()
181
-			if err := step.Resolve(child); err != nil {
182
-				lastErr = fmt.Errorf("%d: %d: %s: resolve: %v", i, j, step.Original, err)
183
-				break
184
-			}
185
-			if err := b.Run(step, e); err != nil {
186
-				lastErr = fmt.Errorf("%d: %d: %s: run: %v", i, j, step.Original, err)
187
-				break
188
-			}
189
-		}
190
-		if lastErr != nil {
191
-			if test.ErrFn == nil || !test.ErrFn(lastErr) {
192
-				t.Errorf("%d: unexpected error: %v", i, lastErr)
193
-			}
194
-			continue
195
-		}
196
-		if !reflect.DeepEqual(test.Copies, e.Copies) {
197
-			t.Errorf("%d: unexpected copies: %#v", i, e.Copies)
198
-		}
199
-		if !reflect.DeepEqual(test.Runs, e.Runs) {
200
-			t.Errorf("%d: unexpected runs: %#v", i, e.Runs)
201
-		}
202
-		lastConfig := b.RunConfig
203
-		if !reflect.DeepEqual(test.Config, lastConfig) {
204
-			t.Errorf("%d: unexpected config: %#v", i, lastConfig)
205
-		}
206
-	}
207
-}
208
-
209
-func TestCalcCopyInfo(t *testing.T) {
210
-	nilErr := func(err error) bool { return err == nil }
211
-	tests := []struct {
212
-		origPath       string
213
-		rootPath       string
214
-		dstPath        string
215
-		allowWildcards bool
216
-		errFn          func(err error) bool
217
-		paths          map[string]struct{}
218
-		excludes       []string
219
-		rebaseNames    map[string]string
220
-	}{
221
-		{
222
-			origPath:       "subdir/*",
223
-			rootPath:       "testdata/dir",
224
-			allowWildcards: true,
225
-			errFn:          nilErr,
226
-			paths:          map[string]struct{}{"subdir/file2": {}},
227
-		},
228
-		{
229
-			origPath:       "*",
230
-			rootPath:       "testdata/dir",
231
-			allowWildcards: true,
232
-			errFn:          nilErr,
233
-			paths: map[string]struct{}{
234
-				"Dockerfile": {},
235
-				"file":       {},
236
-				"subdir":     {},
237
-			},
238
-		},
239
-		{
240
-			origPath:       ".",
241
-			rootPath:       "testdata/dir",
242
-			allowWildcards: true,
243
-			errFn:          nilErr,
244
-			paths: map[string]struct{}{
245
-				"Dockerfile": {},
246
-				"file":       {},
247
-				"subdir":     {},
248
-			},
249
-		},
250
-		{
251
-			origPath:       "/.",
252
-			rootPath:       "testdata/dir",
253
-			allowWildcards: true,
254
-			errFn:          nilErr,
255
-			paths: map[string]struct{}{
256
-				"Dockerfile": {},
257
-				"file":       {},
258
-				"subdir":     {},
259
-			},
260
-		},
261
-		{
262
-			origPath:       "subdir/",
263
-			rootPath:       "testdata/dir",
264
-			allowWildcards: true,
265
-			errFn:          nilErr,
266
-			paths: map[string]struct{}{
267
-				"subdir/": {},
268
-			},
269
-		},
270
-		{
271
-			origPath:       "subdir",
272
-			rootPath:       "testdata/dir",
273
-			allowWildcards: true,
274
-			errFn:          nilErr,
275
-			paths: map[string]struct{}{
276
-				"subdir": {},
277
-			},
278
-		},
279
-		{
280
-			origPath:       "subdir/.",
281
-			rootPath:       "testdata/dir",
282
-			allowWildcards: true,
283
-			errFn:          nilErr,
284
-			paths: map[string]struct{}{
285
-				"subdir/": {},
286
-			},
287
-		},
288
-		{
289
-			origPath:       "testdata/dir/subdir/.",
290
-			rootPath:       "",
291
-			allowWildcards: true,
292
-			errFn:          nilErr,
293
-			paths: map[string]struct{}{
294
-				"testdata/dir/subdir/": {},
295
-			},
296
-		},
297
-		{
298
-			origPath:       "subdir/",
299
-			rootPath:       "testdata/dir",
300
-			allowWildcards: true,
301
-			errFn:          nilErr,
302
-			paths: map[string]struct{}{
303
-				"subdir/": {},
304
-			},
305
-		},
306
-		{
307
-			origPath:       "subdir/",
308
-			rootPath:       "testdata/dir",
309
-			allowWildcards: true,
310
-			errFn:          nilErr,
311
-			paths: map[string]struct{}{
312
-				"subdir/": {},
313
-			},
314
-			dstPath: "test/",
315
-			rebaseNames: map[string]string{
316
-				"subdir/": "test/",
317
-			},
318
-		},
319
-	}
320
-
321
-	for i, test := range tests {
322
-		infos, err := CalcCopyInfo(test.origPath, test.rootPath, false, test.allowWildcards)
323
-		if !test.errFn(err) {
324
-			t.Errorf("%d: unexpected error: %v", i, err)
325
-			continue
326
-		}
327
-		if err != nil {
328
-			continue
329
-		}
330
-		expect := make(map[string]struct{})
331
-		for k := range test.paths {
332
-			expect[k] = struct{}{}
333
-		}
334
-		for _, info := range infos {
335
-			if _, ok := expect[info.Path]; ok {
336
-				delete(expect, info.Path)
337
-			} else {
338
-				t.Errorf("%d: did not expect path %s", i, info.Path)
339
-			}
340
-		}
341
-		if len(expect) > 0 {
342
-			t.Errorf("%d: did not see paths: %#v", i, expect)
343
-		}
344
-
345
-		options := archiveOptionsFor(infos, test.dstPath, test.excludes)
346
-		if !reflect.DeepEqual(test.rebaseNames, options.RebaseNames) {
347
-			t.Errorf("%d: rebase names did not match: %#v", i, options.RebaseNames)
348
-		}
349
-	}
350
-}
351 1
deleted file mode 100644
... ...
@@ -1,570 +0,0 @@
1
-package builder
2
-
3
-import (
4
-	"archive/tar"
5
-	"bytes"
6
-	"crypto/rand"
7
-	"fmt"
8
-	"io"
9
-	"os"
10
-	"path"
11
-	"path/filepath"
12
-	"runtime"
13
-	"strconv"
14
-	"strings"
15
-
16
-	"k8s.io/kubernetes/pkg/credentialprovider"
17
-
18
-	"github.com/docker/docker/builder/parser"
19
-	docker "github.com/fsouza/go-dockerclient"
20
-	"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive"
21
-	"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/fileutils"
22
-	"github.com/golang/glog"
23
-
24
-	"github.com/openshift/origin/pkg/util/docker/dockerfile/builder/imageprogress"
25
-)
26
-
27
-// Mount represents a binding between the current system and the destination client
28
-type Mount struct {
29
-	SourcePath      string
30
-	DestinationPath string
31
-}
32
-
33
-// ClientExecutor can run Docker builds from a Docker client.
34
-type ClientExecutor struct {
35
-	// Client is a client to a Docker daemon.
36
-	Client *docker.Client
37
-	// Directory is the context directory to build from, will use
38
-	// the current working directory if not set.
39
-	Directory string
40
-	// Excludes are a list of file patterns that should be excluded
41
-	// from the context. Will be set to the contents of the
42
-	// .dockerignore file if nil.
43
-	Excludes []string
44
-	// Tag is an optional value to tag the resulting built image.
45
-	Tag string
46
-	// AllowPull when set will pull images that are not present on
47
-	// the daemon.
48
-	AllowPull bool
49
-	// TransientMounts are a set of mounts from outside the build
50
-	// to the inside that will not be part of the final image. Any
51
-	// content created inside the mount's destinationPath will be
52
-	// omitted from the final image.
53
-	TransientMounts []Mount
54
-
55
-	Out, ErrOut io.Writer
56
-
57
-	// Container is optional and can be set to a container to use as
58
-	// the execution environment for a build.
59
-	Container *docker.Container
60
-	// Command, if set, will be used as the entrypoint for the new
61
-	// container. This is ignored if Container is set.
62
-	Command []string
63
-	// Image is optional and may be set to control which image is used
64
-	// as a base for this build. Otherwise the FROM value from the
65
-	// Dockerfile is read (will be pulled if not locally present).
66
-	Image *docker.Image
67
-
68
-	// AuthFn will handle authenticating any docker pulls if Image
69
-	// is set to nil.
70
-	AuthFn func(name string) ([]credentialprovider.LazyAuthConfiguration, bool)
71
-	// HostConfig is used to start the container (if necessary).
72
-	HostConfig *docker.HostConfig
73
-	// LogFn is an optional command to log information to the end user
74
-	LogFn func(format string, args ...interface{})
75
-}
76
-
77
-// NewClientExecutor creates a client executor.
78
-func NewClientExecutor(client *docker.Client) *ClientExecutor {
79
-	return &ClientExecutor{Client: client}
80
-}
81
-
82
-// Build is a helper method to perform a Docker build against the
83
-// provided Docker client. It will load the image if not specified,
84
-// create a container if one does not already exist, and start a
85
-// container if the Dockerfile contains RUN commands. It will cleanup
86
-// any containers it creates directly, and set the e.Image.ID field
87
-// to the generated image.
88
-func (e *ClientExecutor) Build(r io.Reader, args map[string]string) error {
89
-	b := NewBuilder()
90
-	b.Args = args
91
-
92
-	if e.Excludes == nil {
93
-		excludes, err := ParseDockerignore(e.Directory)
94
-		if err != nil {
95
-			return err
96
-		}
97
-		e.Excludes = append(excludes, ".dockerignore")
98
-	}
99
-
100
-	// TODO: check the Docker daemon version (1.20 is required for Upload)
101
-
102
-	node, err := parser.Parse(r)
103
-	if err != nil {
104
-		return err
105
-	}
106
-
107
-	// identify the base image
108
-	from, err := b.From(node)
109
-	if err != nil {
110
-		return err
111
-	}
112
-	// load the image
113
-	if e.Image == nil {
114
-		if from == NoBaseImageSpecifier {
115
-			if runtime.GOOS == "windows" {
116
-				return fmt.Errorf("building from scratch images is not supported")
117
-			}
118
-			from, err = e.CreateScratchImage()
119
-			if err != nil {
120
-				return fmt.Errorf("unable to create a scratch image for this build: %v", err)
121
-			}
122
-			defer e.CleanupImage(from)
123
-		}
124
-		glog.V(4).Infof("Retrieving image %q", from)
125
-		e.Image, err = e.LoadImage(from)
126
-		if err != nil {
127
-			return err
128
-		}
129
-	}
130
-
131
-	// update the builder with any information from the image, including ONBUILD
132
-	// statements
133
-	if err := b.FromImage(e.Image, node); err != nil {
134
-		return err
135
-	}
136
-
137
-	b.RunConfig.Image = from
138
-	e.LogFn("FROM %s", from)
139
-	glog.V(4).Infof("step: FROM %s", from)
140
-
141
-	var sharedMount string
142
-
143
-	// create a container to execute in, if necessary
144
-	mustStart := b.RequiresStart(node)
145
-	if e.Container == nil {
146
-		opts := docker.CreateContainerOptions{
147
-			Config: &docker.Config{
148
-				Image: from,
149
-			},
150
-		}
151
-		if mustStart {
152
-			// Transient mounts only make sense on images that will be running processes
153
-			if len(e.TransientMounts) > 0 {
154
-				volumeName, err := randSeq(imageSafeCharacters, 24)
155
-				if err != nil {
156
-					return err
157
-				}
158
-				v, err := e.Client.CreateVolume(docker.CreateVolumeOptions{Name: volumeName})
159
-				if err != nil {
160
-					return fmt.Errorf("unable to create volume to mount secrets: %v", err)
161
-				}
162
-				defer e.cleanupVolume(volumeName)
163
-				sharedMount = v.Mountpoint
164
-				opts.HostConfig = &docker.HostConfig{
165
-					Binds: []string{sharedMount + ":/tmp/__temporarymount"},
166
-				}
167
-			}
168
-
169
-			// TODO: windows support
170
-			if len(e.Command) > 0 {
171
-				opts.Config.Cmd = e.Command
172
-				opts.Config.Entrypoint = nil
173
-			} else {
174
-				// TODO; replace me with a better default command
175
-				opts.Config.Cmd = []string{"sleep 86400"}
176
-				opts.Config.Entrypoint = []string{"/bin/sh", "-c"}
177
-			}
178
-		}
179
-		if len(opts.Config.Cmd) == 0 {
180
-			opts.Config.Entrypoint = []string{"/bin/sh", "-c", "# NOP"}
181
-		}
182
-		container, err := e.Client.CreateContainer(opts)
183
-		if err != nil {
184
-			return fmt.Errorf("unable to create build container: %v", err)
185
-		}
186
-		e.Container = container
187
-
188
-		// if we create the container, take responsibilty for cleaning up
189
-		defer e.Cleanup()
190
-	}
191
-
192
-	// copy any source content into the temporary mount path
193
-	if mustStart && len(e.TransientMounts) > 0 {
194
-		var copies []Copy
195
-		for i, mount := range e.TransientMounts {
196
-			source := mount.SourcePath
197
-			copies = append(copies, Copy{
198
-				Src:  source,
199
-				Dest: []string{path.Join("/tmp/__temporarymount", strconv.Itoa(i))},
200
-			})
201
-		}
202
-		if err := e.Copy(copies...); err != nil {
203
-			return fmt.Errorf("unable to copy build context into container: %v", err)
204
-		}
205
-	}
206
-
207
-	// TODO: lazy start
208
-	if mustStart && !e.Container.State.Running {
209
-		var hostConfig docker.HostConfig
210
-		if e.HostConfig != nil {
211
-			hostConfig = *e.HostConfig
212
-		}
213
-
214
-		// mount individual items temporarily
215
-		for i, mount := range e.TransientMounts {
216
-			if len(sharedMount) == 0 {
217
-				return fmt.Errorf("no mount point available for temporary mounts")
218
-			}
219
-			hostConfig.Binds = append(
220
-				hostConfig.Binds,
221
-				fmt.Sprintf("%s:%s:%s", path.Join(sharedMount, strconv.Itoa(i)), mount.DestinationPath, "ro"),
222
-			)
223
-		}
224
-
225
-		if err := e.Client.StartContainer(e.Container.ID, &hostConfig); err != nil {
226
-			return fmt.Errorf("unable to start build container: %v", err)
227
-		}
228
-		// TODO: is this racy? may have to loop wait in the actual run step
229
-	}
230
-
231
-	for _, child := range node.Children {
232
-		step := b.Step()
233
-		if err := step.Resolve(child); err != nil {
234
-			return err
235
-		}
236
-		glog.V(4).Infof("step: %s", step.Original)
237
-		if e.LogFn != nil {
238
-			e.LogFn(step.Original)
239
-		}
240
-		if err := b.Run(step, e); err != nil {
241
-			return err
242
-		}
243
-	}
244
-
245
-	if mustStart {
246
-		glog.V(4).Infof("Stopping container %s ...", e.Container.ID)
247
-		if err := e.Client.StopContainer(e.Container.ID, 0); err != nil {
248
-			return fmt.Errorf("unable to stop build container: %v", err)
249
-		}
250
-	}
251
-
252
-	config := b.Config()
253
-	var repository, tag string
254
-	if len(e.Tag) > 0 {
255
-		repository, tag = docker.ParseRepositoryTag(e.Tag)
256
-		glog.V(4).Infof("Committing built container %s as image %q: %#v", e.Container.ID, e.Tag, config)
257
-		if e.LogFn != nil {
258
-			e.LogFn("Committing changes to %s ...", e.Tag)
259
-		}
260
-	} else {
261
-		glog.V(4).Infof("Committing built container %s: %#v", e.Container.ID, config)
262
-		if e.LogFn != nil {
263
-			e.LogFn("Committing changes ...")
264
-		}
265
-	}
266
-
267
-	image, err := e.Client.CommitContainer(docker.CommitContainerOptions{
268
-		Author:     b.Author,
269
-		Container:  e.Container.ID,
270
-		Run:        config,
271
-		Repository: repository,
272
-		Tag:        tag,
273
-	})
274
-	if err != nil {
275
-		return fmt.Errorf("unable to commit build container: %v", err)
276
-	}
277
-	e.Image = image
278
-	glog.V(4).Infof("Committed %s to %s", e.Container.ID, e.Image.ID)
279
-	if e.LogFn != nil {
280
-		e.LogFn("Done")
281
-	}
282
-	return nil
283
-}
284
-
285
-// Cleanup will remove the container that created the build.
286
-func (e *ClientExecutor) Cleanup() error {
287
-	if e.Container == nil {
288
-		return nil
289
-	}
290
-	err := e.Client.RemoveContainer(docker.RemoveContainerOptions{
291
-		ID:            e.Container.ID,
292
-		RemoveVolumes: true,
293
-		Force:         true,
294
-	})
295
-	if _, ok := err.(*docker.NoSuchContainer); err != nil && !ok {
296
-		return fmt.Errorf("unable to cleanup build container: %v", err)
297
-	}
298
-	e.Container = nil
299
-	return nil
300
-}
301
-
302
-// CreateScratchImage creates a new, zero byte layer that is identical to "scratch"
303
-// except that the resulting image will have two layers.
304
-func (e *ClientExecutor) CreateScratchImage() (string, error) {
305
-	random, err := randSeq(imageSafeCharacters, 24)
306
-	if err != nil {
307
-		return "", err
308
-	}
309
-	name := fmt.Sprintf("scratch%s", random)
310
-
311
-	buf := &bytes.Buffer{}
312
-	w := tar.NewWriter(buf)
313
-	w.Close()
314
-
315
-	return name, e.Client.ImportImage(docker.ImportImageOptions{
316
-		Repository:  name,
317
-		Source:      "-",
318
-		InputStream: buf,
319
-	})
320
-}
321
-
322
-// imageSafeCharacters are characters allowed to be part of a Docker image name.
323
-const imageSafeCharacters = "abcdefghijklmnopqrstuvwxyz0123456789"
324
-
325
-// randSeq returns a sequence of random characters drawn from source. It returns
326
-// an error if cryptographic randomness is not available or source is more than 255
327
-// characters.
328
-func randSeq(source string, n int) (string, error) {
329
-	if len(source) > 255 {
330
-		return "", fmt.Errorf("source must be less than 256 bytes long")
331
-	}
332
-	random := make([]byte, n)
333
-	if _, err := io.ReadFull(rand.Reader, random); err != nil {
334
-		return "", err
335
-	}
336
-	for i := range random {
337
-		random[i] = source[random[i]%byte(len(source))]
338
-	}
339
-	return string(random), nil
340
-}
341
-
342
-// cleanupVolume attempts to remove the provided volume
343
-func (e *ClientExecutor) cleanupVolume(name string) error {
344
-	return e.Client.RemoveVolume(name)
345
-}
346
-
347
-// CleanupImage attempts to remove the provided image.
348
-func (e *ClientExecutor) CleanupImage(name string) error {
349
-	return e.Client.RemoveImage(name)
350
-}
351
-
352
-// LoadImage checks the client for an image matching from. If not found,
353
-// attempts to pull the image and then tries to inspect again.
354
-func (e *ClientExecutor) LoadImage(from string) (*docker.Image, error) {
355
-	image, err := e.Client.InspectImage(from)
356
-	if err == nil {
357
-		return image, nil
358
-	}
359
-	if err != docker.ErrNoSuchImage {
360
-		return nil, err
361
-	}
362
-
363
-	if !e.AllowPull {
364
-		glog.V(4).Infof("image %s did not exist", from)
365
-		return nil, docker.ErrNoSuchImage
366
-	}
367
-
368
-	repository, tag := docker.ParseRepositoryTag(from)
369
-	if len(tag) == 0 {
370
-		tag = "latest"
371
-	}
372
-
373
-	glog.V(4).Infof("attempting to pull %s with auth from repository %s:%s", from, repository, tag)
374
-
375
-	// TODO: we may want to abstract looping over multiple credentials
376
-	auth, _ := e.AuthFn(repository)
377
-	if len(auth) == 0 {
378
-		auth = append(auth, credentialprovider.LazyAuthConfiguration{})
379
-	}
380
-
381
-	if e.LogFn != nil {
382
-		e.LogFn("Image %s was not found, pulling ...", from)
383
-	}
384
-
385
-	var lastErr error
386
-	outputProgress := func(s string) {
387
-		e.LogFn("%s", s)
388
-	}
389
-	for _, config := range auth {
390
-		// TODO: handle IDs?
391
-		pullImageOptions := docker.PullImageOptions{
392
-			Repository:    repository,
393
-			Tag:           tag,
394
-			OutputStream:  imageprogress.NewPullWriter(outputProgress),
395
-			RawJSONStream: true,
396
-		}
397
-		if glog.V(5) {
398
-			pullImageOptions.OutputStream = os.Stderr
399
-			pullImageOptions.RawJSONStream = false
400
-		}
401
-		authConfig := docker.AuthConfiguration{Username: config.Username, ServerAddress: config.ServerAddress, Password: config.Password}
402
-		if err = e.Client.PullImage(pullImageOptions, authConfig); err == nil {
403
-			break
404
-		}
405
-		lastErr = err
406
-		continue
407
-	}
408
-	if lastErr != nil {
409
-		return nil, fmt.Errorf("unable to pull image (from: %s, tag: %s): %v", repository, tag, lastErr)
410
-	}
411
-
412
-	return e.Client.InspectImage(from)
413
-}
414
-
415
-// Run executes a single Run command against the current container using exec().
416
-// Since exec does not allow ENV or WORKINGDIR to be set, we force the execution of
417
-// the user command into a shell and perform those operations before. Since RUN
418
-// requires /bin/sh, we can use both 'cd' and 'export'.
419
-func (e *ClientExecutor) Run(run Run, config docker.Config) error {
420
-	args := make([]string, len(run.Args))
421
-	copy(args, run.Args)
422
-
423
-	if runtime.GOOS == "windows" {
424
-		if len(config.WorkingDir) > 0 {
425
-			args[0] = fmt.Sprintf("cd %s && %s", bashQuote(config.WorkingDir), args[0])
426
-		}
427
-		// TODO: implement windows ENV
428
-		args = append([]string{"cmd", "/S", "/C"}, args...)
429
-	} else {
430
-		if len(config.WorkingDir) > 0 {
431
-			args[0] = fmt.Sprintf("cd %s && %s", bashQuote(config.WorkingDir), args[0])
432
-		}
433
-		if len(config.Env) > 0 {
434
-			args[0] = exportEnv(config.Env) + args[0]
435
-		}
436
-		args = append([]string{"/bin/sh", "-c"}, args...)
437
-	}
438
-
439
-	config.Cmd = args
440
-
441
-	exec, err := e.Client.CreateExec(docker.CreateExecOptions{
442
-		Cmd:          config.Cmd,
443
-		Container:    e.Container.ID,
444
-		AttachStdout: true,
445
-		AttachStderr: true,
446
-		User:         config.User,
447
-	})
448
-	if err != nil {
449
-		return err
450
-	}
451
-	if err := e.Client.StartExec(exec.ID, docker.StartExecOptions{
452
-		OutputStream: e.Out,
453
-		ErrorStream:  e.ErrOut,
454
-	}); err != nil {
455
-		return err
456
-	}
457
-	status, err := e.Client.InspectExec(exec.ID)
458
-	if err != nil {
459
-		return err
460
-	}
461
-	if status.ExitCode != 0 {
462
-		return fmt.Errorf("running '%s' failed with exit code %d", strings.Join(args, " "), status.ExitCode)
463
-	}
464
-	return nil
465
-}
466
-
467
-func (e *ClientExecutor) Copy(copies ...Copy) error {
468
-	container := e.Container
469
-	for _, c := range copies {
470
-		// TODO: reuse source
471
-		for _, dst := range c.Dest {
472
-			glog.V(4).Infof("Archiving %s %t", c.Src, c.Download)
473
-			r, closer, err := e.Archive(c.Src, dst, c.Download, c.Download)
474
-			if err != nil {
475
-				return err
476
-			}
477
-			glog.V(5).Infof("Uploading to %s at %s", container.ID, dst)
478
-			err = e.Client.UploadToContainer(container.ID, docker.UploadToContainerOptions{
479
-				InputStream: r,
480
-				Path:        "/",
481
-			})
482
-			if err := closer.Close(); err != nil {
483
-				glog.Errorf("Error while closing stream container copy stream %s: %v", container.ID, err)
484
-			}
485
-			if err != nil {
486
-				return err
487
-			}
488
-		}
489
-	}
490
-	return nil
491
-}
492
-
493
-type closers []func() error
494
-
495
-func (c closers) Close() error {
496
-	var lastErr error
497
-	for _, fn := range c {
498
-		if err := fn(); err != nil {
499
-			lastErr = err
500
-		}
501
-	}
502
-	return lastErr
503
-}
504
-
505
-func (e *ClientExecutor) Archive(src, dst string, allowDecompression, allowDownload bool) (io.Reader, io.Closer, error) {
506
-	var closer closers
507
-	var base string
508
-	var infos []CopyInfo
509
-	var err error
510
-	if isURL(src) {
511
-		if !allowDownload {
512
-			return nil, nil, fmt.Errorf("source can't be a URL")
513
-		}
514
-		infos, base, err = DownloadURL(src, dst)
515
-		if len(base) > 0 {
516
-			closer = append(closer, func() error { return os.RemoveAll(base) })
517
-		}
518
-	} else {
519
-		if filepath.IsAbs(src) {
520
-			base = filepath.Dir(src)
521
-			src, err = filepath.Rel(base, src)
522
-			if err != nil {
523
-				return nil, nil, err
524
-			}
525
-		} else {
526
-			base = e.Directory
527
-		}
528
-		infos, err = CalcCopyInfo(src, base, allowDecompression, true)
529
-	}
530
-	if err != nil {
531
-		closer.Close()
532
-		return nil, nil, err
533
-	}
534
-
535
-	options := archiveOptionsFor(infos, dst, e.Excludes)
536
-
537
-	glog.V(4).Infof("Tar of directory %s %#v", base, options)
538
-	rc, err := archive.TarWithOptions(base, options)
539
-	closer = append(closer, rc.Close)
540
-	return rc, closer, err
541
-}
542
-
543
-func archiveOptionsFor(infos []CopyInfo, dst string, excludes []string) *archive.TarOptions {
544
-	dst = trimLeadingPath(dst)
545
-	patterns, patDirs, _, _ := fileutils.CleanPatterns(excludes)
546
-	options := &archive.TarOptions{}
547
-	for _, info := range infos {
548
-		if ok, _ := fileutils.OptimizedMatches(info.Path, patterns, patDirs); ok {
549
-			continue
550
-		}
551
-		options.IncludeFiles = append(options.IncludeFiles, info.Path)
552
-		if len(dst) == 0 {
553
-			continue
554
-		}
555
-		if options.RebaseNames == nil {
556
-			options.RebaseNames = make(map[string]string)
557
-		}
558
-		if info.FromDir || strings.HasSuffix(dst, "/") || strings.HasSuffix(dst, "/.") || dst == "." {
559
-			if strings.HasSuffix(info.Path, "/") {
560
-				options.RebaseNames[info.Path] = dst
561
-			} else {
562
-				options.RebaseNames[info.Path] = path.Join(dst, path.Base(info.Path))
563
-			}
564
-		} else {
565
-			options.RebaseNames[info.Path] = dst
566
-		}
567
-	}
568
-	options.ExcludePatterns = excludes
569
-	return options
570
-}
571 1
deleted file mode 100644
... ...
@@ -1,583 +0,0 @@
1
-// +build conformance
2
-
3
-package builder
4
-
5
-import (
6
-	"archive/tar"
7
-	"bytes"
8
-	"flag"
9
-	"fmt"
10
-	"io"
11
-	"io/ioutil"
12
-	"os"
13
-	"os/exec"
14
-	"path/filepath"
15
-	"reflect"
16
-	"strings"
17
-	"testing"
18
-	"time"
19
-
20
-	"github.com/docker/docker/builder/command"
21
-	"github.com/docker/docker/builder/parser"
22
-	docker "github.com/fsouza/go-dockerclient"
23
-	"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive"
24
-	"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/fileutils"
25
-
26
-	"k8s.io/kubernetes/pkg/conversion"
27
-	"k8s.io/kubernetes/pkg/util/diff"
28
-)
29
-
30
-var compareLayers = flag.Bool("compare-layers", false, "If true, compare each generated layer for equivalence")
31
-
32
-type conformanceTest struct {
33
-	Dockerfile string
34
-	Git        string
35
-	ContextDir string
36
-	Ignore     []ignoreFunc
37
-	PostClone  func(dir string) error
38
-}
39
-
40
-// TestConformance* compares the result of running the direct build against a
41
-// sequential docker build. A dockerfile and git repo is loaded, then each step
42
-// in the file is run sequentially, committing after each step. The generated
43
-// image.Config and the resulting filesystems are compared. The next step reuses
44
-// the previously generated layer and performs an incremental diff. This ensures
45
-// that each step is functionally equivalent.
46
-//
47
-// Deviations:
48
-// * Builds run at different times
49
-//   * Modification timestamps are ignored on files
50
-//   * Some processes (gem install) result in files created in the image that
51
-//     have different content because of that (timestamps in files). We treat
52
-//     a file that is identical except for size within 10 bytes and neither old
53
-//     or new is zero bytes to be identical.
54
-// * Docker container commit with ENV FOO=BAR and a Docker build with line
55
-//   ENV FOO=BAR will generate an image with FOO=BAR in different positions
56
-//   (commit places the variable first, build: last). We try to align the
57
-//   generated environment variable to ensure they are equal.
58
-// * The parent image ID is ignored.
59
-//
60
-// TODO: .dockerignore
61
-// TODO: check context dir
62
-// TODO: ONBUILD
63
-// TODO: ensure that the final built image has the right UIDs
64
-//
65
-func TestConformanceInternal(t *testing.T) {
66
-	testCases := []conformanceTest{
67
-		{
68
-			ContextDir: "testdata/dir",
69
-		},
70
-		// TODO: Fix this test
71
-		// {
72
-		// 	ContextDir: "testdata/ignore",
73
-		// },
74
-		{
75
-			Dockerfile: "testdata/Dockerfile.env",
76
-		},
77
-		{
78
-			Dockerfile: "testdata/Dockerfile.edgecases",
79
-		},
80
-		{
81
-			Dockerfile: "testdata/Dockerfile.exposedefault",
82
-		},
83
-		{
84
-			Dockerfile: "testdata/Dockerfile.add",
85
-		},
86
-	}
87
-
88
-	c, err := docker.NewClientFromEnv()
89
-	if err != nil {
90
-		t.Fatal(err)
91
-	}
92
-
93
-	for i, test := range testCases {
94
-		conformanceTester(t, c, test, i, *compareLayers)
95
-	}
96
-}
97
-
98
-// TestConformanceExternal applies external repo testing that may be more expensive or
99
-// change more frequently.
100
-func TestConformanceExternal(t *testing.T) {
101
-	testCases := []conformanceTest{
102
-		{
103
-			// Tests user ownership change under COPY
104
-			Git: "https://github.com/openshift/ruby-hello-world.git",
105
-		},
106
-		{
107
-			// Tests Non-default location dockerfile
108
-			Dockerfile: "Dockerfile.build",
109
-			Git:        "https://github.com/docker-library/hello-world.git",
110
-			PostClone: func(dir string) error {
111
-				return os.Remove(filepath.Join(dir, ".dockerignore"))
112
-			},
113
-		},
114
-		{
115
-			// Tests COPY and other complex interactions of ENV
116
-			ContextDir: "9.3",
117
-			Dockerfile: "9.3/Dockerfile",
118
-			Git:        "https://github.com/docker-library/postgres.git",
119
-			Ignore: []ignoreFunc{
120
-				func(a, b *tar.Header) bool {
121
-					switch {
122
-					case (a != nil) == (b != nil):
123
-						return false
124
-					case a != nil:
125
-						return strings.HasPrefix(a.Name, "etc/ssl/certs/")
126
-					case b != nil:
127
-						return strings.HasPrefix(b.Name, "etc/ssl/certs/")
128
-					default:
129
-						return false
130
-					}
131
-				},
132
-			},
133
-		},
134
-	}
135
-
136
-	c, err := docker.NewClientFromEnv()
137
-	if err != nil {
138
-		t.Fatal(err)
139
-	}
140
-
141
-	for i, test := range testCases {
142
-		conformanceTester(t, c, test, i, *compareLayers)
143
-	}
144
-}
145
-
146
-func conformanceTester(t *testing.T, c *docker.Client, test conformanceTest, i int, deep bool) {
147
-	dockerfile := test.Dockerfile
148
-	if len(dockerfile) == 0 {
149
-		dockerfile = "Dockerfile"
150
-	}
151
-	tmpDir, err := ioutil.TempDir("", "dockerbuild-conformance-")
152
-	if err != nil {
153
-		t.Fatal(err)
154
-	}
155
-	defer os.RemoveAll(tmpDir)
156
-
157
-	dir := tmpDir
158
-	contextDir := filepath.Join(dir, test.ContextDir)
159
-	dockerfilePath := filepath.Join(dir, dockerfile)
160
-
161
-	// clone repo or copy the Dockerfile
162
-	var input string
163
-	switch {
164
-	case len(test.Git) > 0:
165
-		input = test.Git
166
-		cmd := exec.Command("git", "clone", test.Git, dir)
167
-		out, err := cmd.CombinedOutput()
168
-		if err != nil {
169
-			t.Errorf("unable to clone %q: %v\n%s", test.Git, err, out)
170
-			return
171
-		}
172
-
173
-		if test.PostClone != nil {
174
-			if err := test.PostClone(dir); err != nil {
175
-				t.Errorf("unable to fixup clone: %v", err)
176
-				return
177
-			}
178
-		}
179
-
180
-	case len(test.Dockerfile) > 0:
181
-		input = dockerfile
182
-		dockerfilePath = filepath.Join(dir, "Dockerfile")
183
-		if _, err := fileutils.CopyFile(filepath.Join("", dockerfile), dockerfilePath); err != nil {
184
-			t.Fatal(err)
185
-		}
186
-		dockerfile = "Dockerfile"
187
-
188
-	default:
189
-		input = filepath.Join(test.ContextDir, dockerfile)
190
-		dockerfilePath = input
191
-		contextDir = test.ContextDir
192
-		dir = test.ContextDir
193
-	}
194
-
195
-	// read the dockerfile
196
-	data, err := ioutil.ReadFile(dockerfilePath)
197
-	if err != nil {
198
-		t.Errorf("%d: unable to read Dockerfile %q: %v", i, input, err)
199
-		return
200
-	}
201
-	node, err := parser.Parse(bytes.NewBuffer(data))
202
-	if err != nil {
203
-		t.Errorf("%d: can't parse Dockerfile %q: %v", i, input, err)
204
-		return
205
-	}
206
-	from, err := NewBuilder().From(node)
207
-	if err != nil {
208
-		t.Errorf("%d: can't get base FROM %q: %v", i, input, err)
209
-		return
210
-	}
211
-	nameFormat := "conformance-dockerbuild-%d-%s-%d"
212
-
213
-	var toDelete []string
214
-	steps := node.Children
215
-	lastImage := from
216
-
217
-	ignoreSmallFileChange := func(a, b *tar.Header) bool {
218
-		if a == nil || b == nil {
219
-			return false
220
-		}
221
-		diff := a.Size - b.Size
222
-		if differOnlyByFileSize(a, b, 10) {
223
-			t.Logf("WARNING: %s differs only in size by %d bytes, probably a timestamp value change", a.Name, diff)
224
-			return true
225
-		}
226
-		return false
227
-	}
228
-
229
-	if deep {
230
-		// execute each step on both Docker build and the direct builder, comparing as we
231
-		// go
232
-		fail := false
233
-		for j := range steps {
234
-			testFile := dockerfileWithFrom(lastImage, steps[j:j+1])
235
-
236
-			nameDirect := fmt.Sprintf(nameFormat, i, "direct", j)
237
-			nameDocker := fmt.Sprintf(nameFormat, i, "docker", j)
238
-
239
-			// run docker build
240
-			if err := ioutil.WriteFile(dockerfilePath, []byte(testFile), 0600); err != nil {
241
-				t.Errorf("%d: unable to update Dockerfile %q: %v", i, dockerfilePath, err)
242
-				break
243
-			}
244
-			in, err := archive.TarWithOptions(dir, &archive.TarOptions{IncludeFiles: []string{"."}})
245
-			if err != nil {
246
-				t.Errorf("%d: unable to generate build context %q: %v", i, dockerfilePath, err)
247
-				break
248
-			}
249
-			out := &bytes.Buffer{}
250
-			if err := c.BuildImage(docker.BuildImageOptions{
251
-				Name:                nameDocker,
252
-				Dockerfile:          dockerfile,
253
-				RmTmpContainer:      true,
254
-				ForceRmTmpContainer: true,
255
-				InputStream:         in,
256
-				OutputStream:        out,
257
-			}); err != nil {
258
-				in.Close()
259
-				t.Errorf("%d: unable to build Docker image %q: %v\n%s", i, test.Git, err, out)
260
-				break
261
-			}
262
-			toDelete = append(toDelete, nameDocker)
263
-
264
-			// run direct build
265
-			e := NewClientExecutor(c)
266
-			out = &bytes.Buffer{}
267
-			e.Out, e.ErrOut = out, out
268
-			e.Directory = contextDir
269
-			e.Tag = nameDirect
270
-			if err := e.Build(bytes.NewBufferString(testFile), nil); err != nil {
271
-				t.Errorf("%d: failed to build step %d in dockerfile %q: %s\n%s", i, j, dockerfilePath, steps[j].Original, out)
272
-				break
273
-			}
274
-			toDelete = append(toDelete, nameDirect)
275
-
276
-			// only compare filesystem on layers that change the filesystem
277
-			mutation := steps[j].Value == command.Add || steps[j].Value == command.Copy || steps[j].Value == command.Run
278
-			// metadata must be strictly equal
279
-			if !equivalentImages(
280
-				t, c, nameDocker, nameDirect, mutation,
281
-				metadataEqual,
282
-				append(ignoreFuncs{ignoreSmallFileChange}, test.Ignore...)...,
283
-			) {
284
-				t.Errorf("%d: layered Docker build was not equivalent to direct layer image metadata %s", i, input)
285
-				fail = true
286
-			}
287
-
288
-			lastImage = nameDocker
289
-		}
290
-
291
-		if fail {
292
-			t.Fatalf("%d: Conformance test failed for %s", i, input)
293
-		}
294
-
295
-	} else {
296
-		exclude, _ := ParseDockerignore(dir)
297
-		//exclude = append(filtered, ".dockerignore")
298
-		in, err := archive.TarWithOptions(dir, &archive.TarOptions{IncludeFiles: []string{"."}, ExcludePatterns: exclude})
299
-		if err != nil {
300
-			t.Errorf("%d: unable to generate build context %q: %v", i, dockerfilePath, err)
301
-			return
302
-		}
303
-		out := &bytes.Buffer{}
304
-		nameDocker := fmt.Sprintf(nameFormat, i, "docker", 0)
305
-		if err := c.BuildImage(docker.BuildImageOptions{
306
-			Name:                nameDocker,
307
-			Dockerfile:          dockerfile,
308
-			RmTmpContainer:      true,
309
-			ForceRmTmpContainer: true,
310
-			InputStream:         in,
311
-			OutputStream:        out,
312
-		}); err != nil {
313
-			in.Close()
314
-			t.Errorf("%d: unable to build Docker image %q: %v\n%s", i, test.Git, err, out)
315
-			return
316
-		}
317
-		lastImage = nameDocker
318
-		toDelete = append(toDelete, nameDocker)
319
-	}
320
-
321
-	// if we ran more than one step, compare the squashed output with the docker build output
322
-	if len(steps) > 1 || !deep {
323
-		nameDirect := fmt.Sprintf(nameFormat, i, "direct", len(steps)-1)
324
-		e := NewClientExecutor(c)
325
-		out := &bytes.Buffer{}
326
-		e.Out, e.ErrOut = out, out
327
-		e.Directory = contextDir
328
-		e.Tag = nameDirect
329
-		if err := e.Build(bytes.NewBuffer(data), nil); err != nil {
330
-			t.Errorf("%d: failed to build complete image in %q: %v\n%s", i, input, err, out)
331
-		} else {
332
-			if !equivalentImages(
333
-				t, c, lastImage, nameDirect, true,
334
-				// metadata should be loosely equivalent, but because we squash and because of limitations
335
-				// in docker commit, there are some differences
336
-				metadataLayerEquivalent,
337
-				append(ignoreFuncs{
338
-					ignoreSmallFileChange,
339
-					// the direct dockerfile contains all steps, the layered image is synthetic from our previous
340
-					// test and so only contains the last layer
341
-					ignoreDockerfileSize(dockerfile),
342
-				}, test.Ignore...)...,
343
-			) {
344
-				t.Errorf("%d: full Docker build was not equivalent to squashed image metadata %s", i, input)
345
-			}
346
-		}
347
-	}
348
-
349
-	for _, s := range toDelete {
350
-		c.RemoveImageExtended(s, docker.RemoveImageOptions{Force: true})
351
-	}
352
-}
353
-
354
-// ignoreFunc returns true if the difference between the two can be ignored
355
-type ignoreFunc func(a, b *tar.Header) bool
356
-
357
-type ignoreFuncs []ignoreFunc
358
-
359
-func (fns ignoreFuncs) Ignore(a, b *tar.Header) bool {
360
-	for _, fn := range fns {
361
-		if fn(a, b) {
362
-			return true
363
-		}
364
-	}
365
-	return false
366
-}
367
-
368
-// metadataFunc returns true if the metadata is equivalent
369
-type metadataFunc func(a, b *docker.Config) bool
370
-
371
-// metadataEqual checks that the metadata of two images is directly equivalent.
372
-func metadataEqual(a, b *docker.Config) bool {
373
-	// compare output metadata
374
-	a.Image, b.Image = "", ""
375
-	e1, e2 := envMap(a.Env), envMap(b.Env)
376
-	if !conversion.EqualitiesOrDie().DeepEqual(e1, e2) {
377
-		return false
378
-	}
379
-	a.Env, b.Env = nil, nil
380
-	if !conversion.EqualitiesOrDie().DeepEqual(a, b) {
381
-		return false
382
-	}
383
-	return true
384
-}
385
-
386
-// metadataLayerEquivalent returns true if the last layer of a is equivalent to b, assuming
387
-// that b is squashed over multiple layers, and a is not. b, for instance, will have an empty
388
-// slice entrypoint, while a would have a nil entrypoint.
389
-func metadataLayerEquivalent(a, b *docker.Config) bool {
390
-	if a.Entrypoint == nil && len(b.Entrypoint) == 0 {
391
-		// we are forced to set Entrypoint [] to reset the entrypoint
392
-		b.Entrypoint = nil
393
-	}
394
-	if len(a.OnBuild) == 1 && len(b.OnBuild) > 0 && a.OnBuild[0] == b.OnBuild[len(b.OnBuild)-1] {
395
-		// a layered file will only contain the last OnBuild statement
396
-		b.OnBuild = a.OnBuild
397
-	}
398
-	return metadataEqual(a, b)
399
-}
400
-
401
-// equivalentImages executes the provided checks against two docker images, returning true
402
-// if the images are equivalent, and recording a test suite error in any other condition.
403
-func equivalentImages(t *testing.T, c *docker.Client, a, b string, testFilesystem bool, metadataFn metadataFunc, ignoreFns ...ignoreFunc) bool {
404
-	imageA, err := c.InspectImage(a)
405
-	if err != nil {
406
-		t.Errorf("can't get image %q: %v", a, err)
407
-		return false
408
-	}
409
-	imageB, err := c.InspectImage(b)
410
-	if err != nil {
411
-		t.Errorf("can't get image %q: %v", b, err)
412
-		return false
413
-	}
414
-
415
-	if !metadataFn(imageA.Config, imageB.Config) {
416
-		t.Errorf("generated image metadata did not match: %s", diff.ObjectDiff(imageA.Config, imageB.Config))
417
-		return false
418
-	}
419
-
420
-	// for mutation commands, check the layer diff
421
-	if testFilesystem {
422
-		differs, onlyA, onlyB, err := compareImageFS(c, a, b)
423
-		if err != nil {
424
-			t.Errorf("can't calculate FS differences %q: %v", a, err)
425
-			return false
426
-		}
427
-		for k, v := range differs {
428
-			if ignoreFuncs(ignoreFns).Ignore(v[0], v[1]) {
429
-				delete(differs, k)
430
-				continue
431
-			}
432
-			t.Errorf("%s %s differs: %s", a, k, diff.ObjectDiff(v[0], v[1]))
433
-		}
434
-		for k, v := range onlyA {
435
-			if ignoreFuncs(ignoreFns).Ignore(v, nil) {
436
-				delete(onlyA, k)
437
-				continue
438
-			}
439
-		}
440
-		for k, v := range onlyB {
441
-			if ignoreFuncs(ignoreFns).Ignore(nil, v) {
442
-				delete(onlyB, k)
443
-				continue
444
-			}
445
-		}
446
-		if len(onlyA)+len(onlyB)+len(differs) > 0 {
447
-			t.Errorf("a=%v b=%v diff=%v", onlyA, onlyB, differs)
448
-			return false
449
-		}
450
-	}
451
-	return true
452
-}
453
-
454
-// dockerfileWithFrom returns the contents of a new docker file with a different
455
-// FROM as the first line.
456
-func dockerfileWithFrom(from string, steps []*parser.Node) string {
457
-	lines := []string{}
458
-	lines = append(lines, fmt.Sprintf("FROM %s", from))
459
-	for _, step := range steps {
460
-		lines = append(lines, step.Original)
461
-	}
462
-	return strings.Join(lines, "\n")
463
-}
464
-
465
-// envMap returns a map from a list of environment variables.
466
-func envMap(env []string) map[string]string {
467
-	out := make(map[string]string)
468
-	for _, envVar := range env {
469
-		parts := strings.SplitN(envVar, "=", 2)
470
-		if len(parts) != 2 {
471
-			out[envVar] = ""
472
-			continue
473
-		}
474
-		out[parts[0]] = parts[1]
475
-	}
476
-	return out
477
-}
478
-
479
-// differOnlyByFileSize returns true iff the headers differ only by size, but
480
-// that differences is less than within bytes.
481
-func differOnlyByFileSize(a, b *tar.Header, within int64) bool {
482
-	if a == nil || b == nil {
483
-		return false
484
-	}
485
-	if a.Size == b.Size {
486
-		return false
487
-	}
488
-
489
-	diff := a.Size - b.Size
490
-	if diff < 0 {
491
-		diff = diff * -1
492
-	}
493
-	if diff < within && a.Size != 0 && b.Size != 0 {
494
-		a.Size = b.Size
495
-		if reflect.DeepEqual(a, b) {
496
-			return true
497
-		}
498
-	}
499
-	return false
500
-}
501
-
502
-// ignore Dockerfile being different, artifact of this test
503
-func ignoreDockerfileSize(dockerfile string) ignoreFunc {
504
-	return func(a, b *tar.Header) bool {
505
-		if a == nil || b == nil {
506
-			return false
507
-		}
508
-		if !strings.HasSuffix(a.Name, dockerfile) {
509
-			return false
510
-		}
511
-		if a.Size != b.Size {
512
-			a.Size = b.Size
513
-			return reflect.DeepEqual(a, b)
514
-		}
515
-		return false
516
-	}
517
-}
518
-
519
-// compareImageFS exports the file systems of two images and returns a map
520
-// of files that differ in any way (modification time excluded), only exist in
521
-// image A, or only existing in image B.
522
-func compareImageFS(c *docker.Client, a, b string) (differ map[string][]*tar.Header, onlyA, onlyB map[string]*tar.Header, err error) {
523
-	fsA, err := imageFSMetadata(c, a)
524
-	if err != nil {
525
-		return nil, nil, nil, err
526
-	}
527
-	fsB, err := imageFSMetadata(c, b)
528
-	if err != nil {
529
-		return nil, nil, nil, err
530
-	}
531
-	differ = make(map[string][]*tar.Header)
532
-	onlyA = make(map[string]*tar.Header)
533
-	onlyB = fsB
534
-	for k, v1 := range fsA {
535
-		v2, ok := fsB[k]
536
-		if !ok {
537
-			onlyA[k] = v1
538
-			continue
539
-		}
540
-		delete(onlyB, k)
541
-		// we ignore modification time differences
542
-		v1.ModTime = time.Time{}
543
-		v2.ModTime = time.Time{}
544
-		if !reflect.DeepEqual(v1, v2) {
545
-			differ[k] = []*tar.Header{v1, v2}
546
-		}
547
-	}
548
-	return differ, onlyA, onlyB, nil
549
-}
550
-
551
-// imageFSMetadata creates a container and reads the filesystem metadata out of the archive.
552
-func imageFSMetadata(c *docker.Client, name string) (map[string]*tar.Header, error) {
553
-	container, err := c.CreateContainer(docker.CreateContainerOptions{Name: name + "-export", Config: &docker.Config{Image: name}})
554
-	if err != nil {
555
-		return nil, err
556
-	}
557
-	defer c.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID, RemoveVolumes: true, Force: true})
558
-
559
-	ch := make(chan struct{})
560
-	result := make(map[string]*tar.Header)
561
-	r, w := io.Pipe()
562
-	go func() {
563
-		defer close(ch)
564
-		out := tar.NewReader(r)
565
-		for {
566
-			h, err := out.Next()
567
-			if err != nil {
568
-				if err == io.EOF {
569
-					w.Close()
570
-				} else {
571
-					w.CloseWithError(err)
572
-				}
573
-				break
574
-			}
575
-			result[h.Name] = h
576
-		}
577
-	}()
578
-	if err := c.ExportContainer(docker.ExportContainerOptions{ID: container.ID, OutputStream: w}); err != nil {
579
-		return nil, err
580
-	}
581
-	<-ch
582
-	return result, nil
583
-}
584 1
deleted file mode 100644
... ...
@@ -1,13 +0,0 @@
1
-package builder
2
-
3
-const (
4
-	// in docker/system
5
-	NoBaseImageSpecifier = "scratch"
6
-
7
-	// not yet part of our import
8
-	commandArg        = "arg"
9
-	commandStopSignal = "stopsignal"
10
-
11
-	// in docker/system
12
-	defaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
13
-)
14 1
deleted file mode 100644
... ...
@@ -1,152 +0,0 @@
1
-package builder
2
-
3
-import (
4
-	"fmt"
5
-	"io"
6
-	"io/ioutil"
7
-	"net/http"
8
-	"net/url"
9
-	"os"
10
-	"path"
11
-	"path/filepath"
12
-	"strings"
13
-)
14
-
15
-type CopyInfo struct {
16
-	os.FileInfo
17
-	Path       string
18
-	Decompress bool
19
-	FromDir    bool
20
-}
21
-
22
-// CalcCopyInfo identifies the source files selected by a Dockerfile ADD or COPY instruction.
23
-func CalcCopyInfo(origPath, rootPath string, allowLocalDecompression, allowWildcards bool) ([]CopyInfo, error) {
24
-	origPath = trimLeadingPath(origPath)
25
-	// Deal with wildcards
26
-	if allowWildcards && containsWildcards(origPath) {
27
-		matchPath := filepath.Join(rootPath, origPath)
28
-		var copyInfos []CopyInfo
29
-		if err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
30
-			if err != nil {
31
-				return err
32
-			}
33
-			if info.Name() == "" {
34
-				// Why are we doing this check?
35
-				return nil
36
-			}
37
-			if match, _ := filepath.Match(matchPath, path); !match {
38
-				return nil
39
-			}
40
-
41
-			// Note we set allowWildcards to false in case the name has
42
-			// a * in it
43
-			subInfos, err := CalcCopyInfo(trimLeadingPath(strings.TrimPrefix(path, rootPath)), rootPath, allowLocalDecompression, false)
44
-			if err != nil {
45
-				return err
46
-			}
47
-			copyInfos = append(copyInfos, subInfos...)
48
-			return nil
49
-		}); err != nil {
50
-			return nil, err
51
-		}
52
-		return copyInfos, nil
53
-	}
54
-
55
-	// flatten the root directory so we can rebase it
56
-	if origPath == "." {
57
-		var copyInfos []CopyInfo
58
-		infos, err := ioutil.ReadDir(rootPath)
59
-		if err != nil {
60
-			return nil, err
61
-		}
62
-		for _, info := range infos {
63
-			copyInfos = append(copyInfos, CopyInfo{FileInfo: info, Path: info.Name(), Decompress: allowLocalDecompression, FromDir: true})
64
-		}
65
-		return copyInfos, nil
66
-	}
67
-
68
-	// Must be a dir or a file
69
-	fi, err := os.Stat(filepath.Join(rootPath, origPath))
70
-	if err != nil {
71
-		return nil, err
72
-	}
73
-
74
-	origPath = trimTrailingDot(origPath)
75
-	return []CopyInfo{{FileInfo: fi, Path: origPath, Decompress: allowLocalDecompression}}, nil
76
-}
77
-
78
-func DownloadURL(src, dst string) ([]CopyInfo, string, error) {
79
-	// get filename from URL
80
-	u, err := url.Parse(src)
81
-	if err != nil {
82
-		return nil, "", err
83
-	}
84
-	base := path.Base(u.Path)
85
-	if base == "." {
86
-		return nil, "", fmt.Errorf("cannot determine filename from url: %s", u)
87
-	}
88
-
89
-	resp, err := http.Get(src)
90
-	if err != nil {
91
-		return nil, "", err
92
-	}
93
-	defer resp.Body.Close()
94
-	if resp.StatusCode >= 400 {
95
-		return nil, "", fmt.Errorf("server returned a status code >= 400: %s", resp.Status)
96
-	}
97
-
98
-	tmpDir, err := ioutil.TempDir("", "dockerbuildurl-")
99
-	if err != nil {
100
-		return nil, "", err
101
-	}
102
-	tmpFileName := filepath.Join(tmpDir, base)
103
-	tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
104
-	if err != nil {
105
-		os.RemoveAll(tmpDir)
106
-		return nil, "", err
107
-	}
108
-	if _, err := io.Copy(tmpFile, resp.Body); err != nil {
109
-		os.RemoveAll(tmpDir)
110
-		return nil, "", err
111
-	}
112
-	if err := tmpFile.Close(); err != nil {
113
-		os.RemoveAll(tmpDir)
114
-		return nil, "", err
115
-	}
116
-	info, err := os.Stat(tmpFileName)
117
-	if err != nil {
118
-		os.RemoveAll(tmpDir)
119
-		return nil, "", err
120
-	}
121
-	return []CopyInfo{{FileInfo: info, Path: base}}, tmpDir, nil
122
-}
123
-
124
-func trimLeadingPath(origPath string) string {
125
-	// Work in daemon-specific OS filepath semantics
126
-	origPath = filepath.FromSlash(origPath)
127
-	if origPath != "" && origPath[0] == os.PathSeparator && len(origPath) > 1 {
128
-		origPath = origPath[1:]
129
-	}
130
-	origPath = strings.TrimPrefix(origPath, "."+string(os.PathSeparator))
131
-	return origPath
132
-}
133
-
134
-func trimTrailingDot(origPath string) string {
135
-	if strings.HasSuffix(origPath, string(os.PathSeparator)+".") {
136
-		return strings.TrimSuffix(origPath, ".")
137
-	}
138
-	return origPath
139
-}
140
-
141
-// containsWildcards checks whether the provided name has a wildcard.
142
-func containsWildcards(name string) bool {
143
-	for i := 0; i < len(name); i++ {
144
-		ch := name[i]
145
-		if ch == '\\' {
146
-			i++
147
-		} else if ch == '*' || ch == '?' || ch == '[' {
148
-			return true
149
-		}
150
-	}
151
-	return false
152
-}
153 1
deleted file mode 100644
... ...
@@ -1,441 +0,0 @@
1
-package builder
2
-
3
-// This file contains the dispatchers for each command. Note that
4
-// `nullDispatch` is not actually a command, but support for commands we parse
5
-// but do nothing with.
6
-//
7
-// See evaluator.go for a higher level discussion of the whole evaluator
8
-// package.
9
-
10
-import (
11
-	"fmt"
12
-	"os"
13
-	"path/filepath"
14
-	"regexp"
15
-	"runtime"
16
-	"strings"
17
-
18
-	docker "github.com/fsouza/go-dockerclient"
19
-
20
-	"github.com/openshift/origin/pkg/util/docker/dockerfile/builder/signal"
21
-	"github.com/openshift/origin/pkg/util/docker/dockerfile/builder/strslice"
22
-)
23
-
24
-// dispatch with no layer / parsing. This is effectively not a command.
25
-func nullDispatch(b *Builder, args []string, attributes map[string]bool, original string) error {
26
-	return nil
27
-}
28
-
29
-// ENV foo bar
30
-//
31
-// Sets the environment variable foo to bar, also makes interpolation
32
-// in the dockerfile available from the next statement on via ${foo}.
33
-//
34
-func env(b *Builder, args []string, attributes map[string]bool, original string) error {
35
-	if len(args) == 0 {
36
-		return errAtLeastOneArgument("ENV")
37
-	}
38
-
39
-	if len(args)%2 != 0 {
40
-		// should never get here, but just in case
41
-		return errTooManyArguments("ENV")
42
-	}
43
-
44
-	// TODO/FIXME/NOT USED
45
-	// Just here to show how to use the builder flags stuff within the
46
-	// context of a builder command. Will remove once we actually add
47
-	// a builder command to something!
48
-	/*
49
-		flBool1 := b.flags.AddBool("bool1", false)
50
-		flStr1 := b.flags.AddString("str1", "HI")
51
-
52
-		if err := b.flags.Parse(); err != nil {
53
-			return err
54
-		}
55
-
56
-		fmt.Printf("Bool1:%v\n", flBool1)
57
-		fmt.Printf("Str1:%v\n", flStr1)
58
-	*/
59
-
60
-	for j := 0; j < len(args); j++ {
61
-		// name  ==> args[j]
62
-		// value ==> args[j+1]
63
-		newVar := args[j] + "=" + args[j+1] + ""
64
-		gotOne := false
65
-		for i, envVar := range b.RunConfig.Env {
66
-			envParts := strings.SplitN(envVar, "=", 2)
67
-			if envParts[0] == args[j] {
68
-				b.RunConfig.Env[i] = newVar
69
-				gotOne = true
70
-				break
71
-			}
72
-		}
73
-		if !gotOne {
74
-			b.RunConfig.Env = append(b.RunConfig.Env, newVar)
75
-		}
76
-		j++
77
-	}
78
-
79
-	return nil
80
-}
81
-
82
-// MAINTAINER some text <maybe@an.email.address>
83
-//
84
-// Sets the maintainer metadata.
85
-func maintainer(b *Builder, args []string, attributes map[string]bool, original string) error {
86
-	if len(args) != 1 {
87
-		return errExactlyOneArgument("MAINTAINER")
88
-	}
89
-	b.Author = args[0]
90
-	return nil
91
-}
92
-
93
-// LABEL some json data describing the image
94
-//
95
-// Sets the Label variable foo to bar,
96
-//
97
-func label(b *Builder, args []string, attributes map[string]bool, original string) error {
98
-	if len(args) == 0 {
99
-		return errAtLeastOneArgument("LABEL")
100
-	}
101
-	if len(args)%2 != 0 {
102
-		// should never get here, but just in case
103
-		return errTooManyArguments("LABEL")
104
-	}
105
-
106
-	if b.RunConfig.Labels == nil {
107
-		b.RunConfig.Labels = map[string]string{}
108
-	}
109
-
110
-	for j := 0; j < len(args); j++ {
111
-		// name  ==> args[j]
112
-		// value ==> args[j+1]
113
-		b.RunConfig.Labels[args[j]] = args[j+1]
114
-		j++
115
-	}
116
-	return nil
117
-}
118
-
119
-// ADD foo /path
120
-//
121
-// Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
122
-// exist here. If you do not wish to have this automatic handling, use COPY.
123
-//
124
-func add(b *Builder, args []string, attributes map[string]bool, original string) error {
125
-	if len(args) < 2 {
126
-		return errAtLeastOneArgument("ADD")
127
-	}
128
-	for i := 1; i < len(args); i++ {
129
-		args[i] = makeAbsolute(args[i], b.RunConfig.WorkingDir)
130
-	}
131
-	b.PendingCopies = append(b.PendingCopies, Copy{Src: args[0], Dest: args[1:], Download: true})
132
-	return nil
133
-}
134
-
135
-// COPY foo /path
136
-//
137
-// Same as 'ADD' but without the tar and remote url handling.
138
-//
139
-func dispatchCopy(b *Builder, args []string, attributes map[string]bool, original string) error {
140
-	if len(args) < 2 {
141
-		return errAtLeastOneArgument("COPY")
142
-	}
143
-	for i := 1; i < len(args); i++ {
144
-		args[i] = makeAbsolute(args[i], b.RunConfig.WorkingDir)
145
-	}
146
-	b.PendingCopies = append(b.PendingCopies, Copy{Src: args[0], Dest: args[1:], Download: false})
147
-	return nil
148
-}
149
-
150
-// FROM imagename
151
-//
152
-// This sets the image the dockerfile will build on top of.
153
-//
154
-func from(b *Builder, args []string, attributes map[string]bool, original string) error {
155
-	if len(args) != 1 {
156
-		return errExactlyOneArgument("FROM")
157
-	}
158
-
159
-	name := args[0]
160
-	// Windows cannot support a container with no base image.
161
-	if name == NoBaseImageSpecifier {
162
-		if runtime.GOOS == "windows" {
163
-			return fmt.Errorf("Windows does not support FROM scratch")
164
-		}
165
-	}
166
-	b.RunConfig.Image = name
167
-	// TODO: handle onbuild
168
-	return nil
169
-}
170
-
171
-// ONBUILD RUN echo yo
172
-//
173
-// ONBUILD triggers run when the image is used in a FROM statement.
174
-//
175
-// ONBUILD handling has a lot of special-case functionality, the heading in
176
-// evaluator.go and comments around dispatch() in the same file explain the
177
-// special cases. search for 'OnBuild' in internals.go for additional special
178
-// cases.
179
-//
180
-func onbuild(b *Builder, args []string, attributes map[string]bool, original string) error {
181
-	if len(args) == 0 {
182
-		return errAtLeastOneArgument("ONBUILD")
183
-	}
184
-
185
-	triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
186
-	switch triggerInstruction {
187
-	case "ONBUILD":
188
-		return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
189
-	case "MAINTAINER", "FROM":
190
-		return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
191
-	}
192
-
193
-	original = regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(original, "")
194
-
195
-	b.RunConfig.OnBuild = append(b.RunConfig.OnBuild, original)
196
-	return nil
197
-}
198
-
199
-// WORKDIR /tmp
200
-//
201
-// Set the working directory for future RUN/CMD/etc statements.
202
-//
203
-func workdir(b *Builder, args []string, attributes map[string]bool, original string) error {
204
-	if len(args) != 1 {
205
-		return errExactlyOneArgument("WORKDIR")
206
-	}
207
-
208
-	// This is from the Dockerfile and will not necessarily be in platform
209
-	// specific semantics, hence ensure it is converted.
210
-	workdir := filepath.FromSlash(args[0])
211
-
212
-	if !filepath.IsAbs(workdir) {
213
-		current := filepath.FromSlash(b.RunConfig.WorkingDir)
214
-		workdir = filepath.Join(string(os.PathSeparator), current, workdir)
215
-	}
216
-
217
-	b.RunConfig.WorkingDir = workdir
218
-	return nil
219
-}
220
-
221
-// RUN some command yo
222
-//
223
-// run a command and commit the image. Args are automatically prepended with
224
-// 'sh -c' under linux or 'cmd /S /C' under Windows, in the event there is
225
-// only one argument. The difference in processing:
226
-//
227
-// RUN echo hi          # sh -c echo hi       (Linux)
228
-// RUN echo hi          # cmd /S /C echo hi   (Windows)
229
-// RUN [ "echo", "hi" ] # echo hi
230
-//
231
-func run(b *Builder, args []string, attributes map[string]bool, original string) error {
232
-	if b.RunConfig.Image == "" {
233
-		return fmt.Errorf("Please provide a source image with `from` prior to run")
234
-	}
235
-
236
-	args = handleJSONArgs(args, attributes)
237
-
238
-	run := Run{Args: args}
239
-
240
-	if !attributes["json"] {
241
-		run.Shell = true
242
-	}
243
-	b.PendingRuns = append(b.PendingRuns, run)
244
-	return nil
245
-}
246
-
247
-// CMD foo
248
-//
249
-// Set the default command to run in the container (which may be empty).
250
-// Argument handling is the same as RUN.
251
-//
252
-func cmd(b *Builder, args []string, attributes map[string]bool, original string) error {
253
-	cmdSlice := handleJSONArgs(args, attributes)
254
-
255
-	if !attributes["json"] {
256
-		if runtime.GOOS != "windows" {
257
-			cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...)
258
-		} else {
259
-			cmdSlice = append([]string{"cmd", "/S", "/C"}, cmdSlice...)
260
-		}
261
-	}
262
-
263
-	b.RunConfig.Cmd = strslice.StrSlice(cmdSlice)
264
-	if len(args) != 0 {
265
-		b.CmdSet = true
266
-	}
267
-	return nil
268
-}
269
-
270
-// ENTRYPOINT /usr/sbin/nginx
271
-//
272
-// Set the entrypoint (which defaults to sh -c on linux, or cmd /S /C on Windows) to
273
-// /usr/sbin/nginx. Will accept the CMD as the arguments to /usr/sbin/nginx.
274
-//
275
-// Handles command processing similar to CMD and RUN, only b.RunConfig.Entrypoint
276
-// is initialized at NewBuilder time instead of through argument parsing.
277
-//
278
-func entrypoint(b *Builder, args []string, attributes map[string]bool, original string) error {
279
-	parsed := handleJSONArgs(args, attributes)
280
-
281
-	switch {
282
-	case attributes["json"]:
283
-		// ENTRYPOINT ["echo", "hi"]
284
-		b.RunConfig.Entrypoint = strslice.StrSlice(parsed)
285
-	case len(parsed) == 0:
286
-		// ENTRYPOINT []
287
-		b.RunConfig.Entrypoint = nil
288
-	default:
289
-		// ENTRYPOINT echo hi
290
-		if runtime.GOOS != "windows" {
291
-			b.RunConfig.Entrypoint = strslice.StrSlice{"/bin/sh", "-c", parsed[0]}
292
-		} else {
293
-			b.RunConfig.Entrypoint = strslice.StrSlice{"cmd", "/S", "/C", parsed[0]}
294
-		}
295
-	}
296
-
297
-	// when setting the entrypoint if a CMD was not explicitly set then
298
-	// set the command to nil
299
-	if !b.CmdSet {
300
-		b.RunConfig.Cmd = nil
301
-	}
302
-	return nil
303
-}
304
-
305
-// EXPOSE 6667/tcp 7000/tcp
306
-//
307
-// Expose ports for links and port mappings. This all ends up in
308
-// b.RunConfig.ExposedPorts for runconfig.
309
-//
310
-func expose(b *Builder, args []string, attributes map[string]bool, original string) error {
311
-	if len(args) == 0 {
312
-		return errAtLeastOneArgument("EXPOSE")
313
-	}
314
-
315
-	if b.RunConfig.ExposedPorts == nil {
316
-		b.RunConfig.ExposedPorts = make(map[docker.Port]struct{})
317
-	}
318
-
319
-	existing := map[string]struct{}{}
320
-	for k := range b.RunConfig.ExposedPorts {
321
-		existing[k.Port()] = struct{}{}
322
-	}
323
-
324
-	for _, port := range args {
325
-		dp := docker.Port(port)
326
-		if _, exists := existing[dp.Port()]; !exists {
327
-			b.RunConfig.ExposedPorts[docker.Port(fmt.Sprintf("%s/%s", dp.Port(), dp.Proto()))] = struct{}{}
328
-		}
329
-	}
330
-	return nil
331
-}
332
-
333
-// USER foo
334
-//
335
-// Set the user to 'foo' for future commands and when running the
336
-// ENTRYPOINT/CMD at container run time.
337
-//
338
-func user(b *Builder, args []string, attributes map[string]bool, original string) error {
339
-	if len(args) != 1 {
340
-		return errExactlyOneArgument("USER")
341
-	}
342
-
343
-	b.RunConfig.User = args[0]
344
-	return nil
345
-}
346
-
347
-// VOLUME /foo
348
-//
349
-// Expose the volume /foo for use. Will also accept the JSON array form.
350
-//
351
-func volume(b *Builder, args []string, attributes map[string]bool, original string) error {
352
-	if len(args) == 0 {
353
-		return errAtLeastOneArgument("VOLUME")
354
-	}
355
-
356
-	if b.RunConfig.Volumes == nil {
357
-		b.RunConfig.Volumes = map[string]struct{}{}
358
-	}
359
-	for _, v := range args {
360
-		v = strings.TrimSpace(v)
361
-		if v == "" {
362
-			return fmt.Errorf("Volume specified can not be an empty string")
363
-		}
364
-		b.RunConfig.Volumes[v] = struct{}{}
365
-	}
366
-	return nil
367
-}
368
-
369
-// STOPSIGNAL signal
370
-//
371
-// Set the signal that will be used to kill the container.
372
-func stopSignal(b *Builder, args []string, attributes map[string]bool, original string) error {
373
-	if len(args) != 1 {
374
-		return fmt.Errorf("STOPSIGNAL requires exactly one argument")
375
-	}
376
-
377
-	sig := args[0]
378
-	_, err := signal.ParseSignal(sig)
379
-	if err != nil {
380
-		return err
381
-	}
382
-
383
-	b.RunConfig.StopSignal = sig
384
-	return nil
385
-}
386
-
387
-// ARG name[=value]
388
-//
389
-// Adds the variable foo to the trusted list of variables that can be passed
390
-// to builder using the --build-arg flag for expansion/subsitution or passing to 'run'.
391
-// Dockerfile author may optionally set a default value of this variable.
392
-func arg(b *Builder, args []string, attributes map[string]bool, original string) error {
393
-	if len(args) != 1 {
394
-		return fmt.Errorf("ARG requires exactly one argument definition")
395
-	}
396
-
397
-	var (
398
-		name       string
399
-		value      string
400
-		hasDefault bool
401
-	)
402
-
403
-	arg := args[0]
404
-	// 'arg' can just be a name or name-value pair. Note that this is different
405
-	// from 'env' that handles the split of name and value at the parser level.
406
-	// The reason for doing it differently for 'arg' is that we support just
407
-	// defining an arg and not assign it a value (while 'env' always expects a
408
-	// name-value pair). If possible, it will be good to harmonize the two.
409
-	if strings.Contains(arg, "=") {
410
-		parts := strings.SplitN(arg, "=", 2)
411
-		name = parts[0]
412
-		value = parts[1]
413
-		hasDefault = true
414
-	} else {
415
-		name = arg
416
-		hasDefault = false
417
-	}
418
-	// add the arg to allowed list of build-time args from this step on.
419
-	b.AllowedArgs[name] = true
420
-
421
-	// If there is a default value associated with this arg then add it to the
422
-	// b.buildArgs if one is not already passed to the builder. The args passed
423
-	// to builder override the default value of 'arg'.
424
-	if _, ok := b.Args[name]; !ok && hasDefault {
425
-		b.Args[name] = value
426
-	}
427
-
428
-	return nil
429
-}
430
-
431
-func errAtLeastOneArgument(command string) error {
432
-	return fmt.Errorf("%s requires at least one argument", command)
433
-}
434
-
435
-func errExactlyOneArgument(command string) error {
436
-	return fmt.Errorf("%s requires exactly one argument", command)
437
-}
438
-
439
-func errTooManyArguments(command string) error {
440
-	return fmt.Errorf("Bad input to %s, too many arguments", command)
441
-}
442 1
deleted file mode 100644
... ...
@@ -1,6 +0,0 @@
1
-// Package builder uses code from github.com/docker/docker/builder/* to implement
2
-// a Docker builder that does not create individual layers, but instead creates a
3
-// single layer.
4
-//
5
-// TODO: full windows support
6
-package builder
7 1
deleted file mode 100644
... ...
@@ -1,150 +0,0 @@
1
-package builder
2
-
3
-import (
4
-	"fmt"
5
-	"strings"
6
-
7
-	"github.com/docker/docker/builder/command"
8
-	"github.com/docker/docker/builder/parser"
9
-)
10
-
11
-// Environment variable interpolation will happen on these statements only.
12
-var replaceEnvAllowed = map[string]bool{
13
-	command.Env:       true,
14
-	command.Label:     true,
15
-	command.Add:       true,
16
-	command.Copy:      true,
17
-	command.Workdir:   true,
18
-	command.Expose:    true,
19
-	command.Volume:    true,
20
-	command.User:      true,
21
-	commandStopSignal: true,
22
-	commandArg:        true,
23
-}
24
-
25
-// Certain commands are allowed to have their args split into more
26
-// words after env var replacements. Meaning:
27
-//   ENV foo="123 456"
28
-//   EXPOSE $foo
29
-// should result in the same thing as:
30
-//   EXPOSE 123 456
31
-// and not treat "123 456" as a single word.
32
-// Note that: EXPOSE "$foo" and EXPOSE $foo are not the same thing.
33
-// Quotes will cause it to still be treated as single word.
34
-var allowWordExpansion = map[string]bool{
35
-	command.Expose: true,
36
-}
37
-
38
-// Step represents the input Env and the output command after all
39
-// post processing of the command arguments is done.
40
-type Step struct {
41
-	Env []string
42
-
43
-	Command  string
44
-	Args     []string
45
-	Flags    []string
46
-	Attrs    map[string]bool
47
-	Message  string
48
-	Original string
49
-}
50
-
51
-// Resolve transforms a parsed Dockerfile line into a command to execute,
52
-// resolving any arguments.
53
-//
54
-// Almost all nodes will have this structure:
55
-// Child[Node, Node, Node] where Child is from parser.Node.Children and each
56
-// node comes from parser.Node.Next. This forms a "line" with a statement and
57
-// arguments and we process them in this normalized form by hitting
58
-// evaluateTable with the leaf nodes of the command and the Builder object.
59
-//
60
-// ONBUILD is a special case; in this case the parser will emit:
61
-// Child[Node, Child[Node, Node...]] where the first node is the literal
62
-// "onbuild" and the child entrypoint is the command of the ONBUILD statement,
63
-// such as `RUN` in ONBUILD RUN foo. There is special case logic in here to
64
-// deal with that, at least until it becomes more of a general concern with new
65
-// features.
66
-func (b *Step) Resolve(ast *parser.Node) error {
67
-	cmd := ast.Value
68
-	upperCasedCmd := strings.ToUpper(cmd)
69
-
70
-	// To ensure the user is given a decent error message if the platform
71
-	// on which the daemon is running does not support a builder command.
72
-	if err := platformSupports(strings.ToLower(cmd)); err != nil {
73
-		return err
74
-	}
75
-
76
-	attrs := ast.Attributes
77
-	original := ast.Original
78
-	flags := ast.Flags
79
-	strList := []string{}
80
-	msg := upperCasedCmd
81
-
82
-	if len(ast.Flags) > 0 {
83
-		msg += " " + strings.Join(ast.Flags, " ")
84
-	}
85
-
86
-	if cmd == "onbuild" {
87
-		if ast.Next == nil {
88
-			return fmt.Errorf("ONBUILD requires at least one argument")
89
-		}
90
-		ast = ast.Next.Children[0]
91
-		strList = append(strList, ast.Value)
92
-		msg += " " + ast.Value
93
-
94
-		if len(ast.Flags) > 0 {
95
-			msg += " " + strings.Join(ast.Flags, " ")
96
-		}
97
-
98
-	}
99
-
100
-	// count the number of nodes that we are going to traverse first
101
-	// so we can pre-create the argument and message array. This speeds up the
102
-	// allocation of those list a lot when they have a lot of arguments
103
-	cursor := ast
104
-	var n int
105
-	for cursor.Next != nil {
106
-		cursor = cursor.Next
107
-		n++
108
-	}
109
-	msgList := make([]string, n)
110
-
111
-	var i int
112
-	envs := b.Env
113
-	for ast.Next != nil {
114
-		ast = ast.Next
115
-		var str string
116
-		str = ast.Value
117
-		if replaceEnvAllowed[cmd] {
118
-			var err error
119
-			var words []string
120
-
121
-			if allowWordExpansion[cmd] {
122
-				words, err = ProcessWords(str, envs)
123
-				if err != nil {
124
-					return err
125
-				}
126
-				strList = append(strList, words...)
127
-			} else {
128
-				str, err = ProcessWord(str, envs)
129
-				if err != nil {
130
-					return err
131
-				}
132
-				strList = append(strList, str)
133
-			}
134
-		} else {
135
-			strList = append(strList, str)
136
-		}
137
-		msgList[i] = ast.Value
138
-		i++
139
-	}
140
-
141
-	msg += " " + strings.Join(msgList, " ")
142
-
143
-	b.Message = msg
144
-	b.Command = cmd
145
-	b.Args = strList
146
-	b.Original = original
147
-	b.Attrs = attrs
148
-	b.Flags = flags
149
-	return nil
150
-}
151 1
deleted file mode 100644
... ...
@@ -1,292 +0,0 @@
1
-package imageprogress
2
-
3
-import (
4
-	"bytes"
5
-	"encoding/json"
6
-	"errors"
7
-	"fmt"
8
-	"io"
9
-	"regexp"
10
-	"strings"
11
-	"sync"
12
-	"time"
13
-)
14
-
15
-const (
16
-	defaultProgressTimeThreshhold = 30 * time.Second
17
-	defaultStableThreshhold       = 10
18
-)
19
-
20
-// progressLine is a structure representation of a Docker pull progress line
21
-type progressLine struct {
22
-	ID     string          `json:"id"`
23
-	Status string          `json:"status"`
24
-	Detail *progressDetail `json:"progressDetail"`
25
-	Error  string          `json:"error"`
26
-}
27
-
28
-// progressDetail is the progressDetail structure in a Docker pull progress line
29
-type progressDetail struct {
30
-	Current int64 `json:"current"`
31
-	Total   int64 `json:"total"`
32
-}
33
-
34
-// layerDetail is layer information associated with a specific layerStatus
35
-type layerDetail struct {
36
-	Count   int
37
-	Current int64
38
-	Total   int64
39
-}
40
-
41
-// layerStatus is one of different possible status for layers detected by
42
-// the ProgressWriter
43
-type layerStatus int
44
-
45
-const (
46
-	statusPending layerStatus = iota
47
-	statusDownloading
48
-	statusExtracting
49
-	statusComplete
50
-	statusPushing
51
-)
52
-
53
-// layerStatusFromDockerString translates a string in a Docker status
54
-// line to a layerStatus
55
-func layerStatusFromDockerString(dockerStatus string) layerStatus {
56
-	switch dockerStatus {
57
-	case "Pushing":
58
-		return statusPushing
59
-	case "Downloading":
60
-		return statusDownloading
61
-	case "Extracting", "Verifying Checksum", "Download complete":
62
-		return statusExtracting
63
-	case "Pull complete", "Already exists", "Pushed", "Layer already exists":
64
-		return statusComplete
65
-	default:
66
-		return statusPending
67
-	}
68
-}
69
-
70
-type report map[layerStatus]*layerDetail
71
-
72
-func (r report) count(status layerStatus) int {
73
-	detail, ok := r[status]
74
-	if !ok {
75
-		return 0
76
-	}
77
-	return detail.Count
78
-}
79
-
80
-func (r report) percentProgress(status layerStatus) float32 {
81
-	detail, ok := r[status]
82
-	if !ok {
83
-		return 0
84
-	}
85
-	if detail.Total == 0 {
86
-		return 0
87
-	}
88
-	pct := float32(detail.Current) / float32(detail.Total) * 100.0
89
-	if pct > 100.0 {
90
-		pct = 100.0
91
-	}
92
-	return pct
93
-}
94
-
95
-func (r report) totalCount() int {
96
-	cnt := 0
97
-	for _, detail := range r {
98
-		cnt += detail.Count
99
-	}
100
-	return cnt
101
-}
102
-
103
-// String is used for test output
104
-func (r report) String() string {
105
-	result := &bytes.Buffer{}
106
-	fmt.Fprintf(result, "{")
107
-	for k := range r {
108
-		var status string
109
-		switch k {
110
-		case statusPending:
111
-			status = "pending"
112
-		case statusDownloading:
113
-			status = "downloading"
114
-		case statusExtracting:
115
-			status = "extracting"
116
-		case statusComplete:
117
-			status = "complete"
118
-		}
119
-		fmt.Fprintf(result, "%s:{Count: %d, Current: %d, Total: %d}, ", status, r[k].Count, r[k].Current, r[k].Total)
120
-	}
121
-	fmt.Fprintf(result, "}")
122
-	return result.String()
123
-}
124
-
125
-// newWriter creates a writer that periodically reports
126
-// on pull/push progress of a Docker image. It only reports when the state of the
127
-// different layers has changed and uses time thresholds to limit the
128
-// rate of the reports.
129
-func newWriter(reportFn func(report), layersChangedFn func(report, report) bool) io.Writer {
130
-	writer := &imageProgressWriter{
131
-		mutex:                  &sync.Mutex{},
132
-		layerStatus:            map[string]progressLine{},
133
-		reportFn:               reportFn,
134
-		layersChangedFn:        layersChangedFn,
135
-		progressTimeThreshhold: defaultProgressTimeThreshhold,
136
-		stableThreshhold:       defaultStableThreshhold,
137
-	}
138
-	return writer
139
-}
140
-
141
-type imageProgressWriter struct {
142
-	mutex                  *sync.Mutex
143
-	internalWriter         io.Writer
144
-	layerStatus            map[string]progressLine
145
-	lastLayerCount         int
146
-	stableLines            int
147
-	stableThreshhold       int
148
-	progressTimeThreshhold time.Duration
149
-	lastReport             report
150
-	lastReportTime         time.Time
151
-	reportFn               func(report)
152
-	layersChangedFn        func(report, report) bool
153
-}
154
-
155
-func (w *imageProgressWriter) ReadFrom(reader io.Reader) (int64, error) {
156
-	decoder := json.NewDecoder(reader)
157
-	return 0, w.readProgress(decoder)
158
-}
159
-
160
-func (w *imageProgressWriter) Write(data []byte) (int, error) {
161
-	w.mutex.Lock()
162
-	defer w.mutex.Unlock()
163
-	if w.internalWriter == nil {
164
-		var pipeIn *io.PipeReader
165
-		pipeIn, w.internalWriter = io.Pipe()
166
-		decoder := json.NewDecoder(pipeIn)
167
-		go func() {
168
-			err := w.readProgress(decoder)
169
-			if err != nil {
170
-				pipeIn.CloseWithError(err)
171
-			}
172
-		}()
173
-	}
174
-	return w.internalWriter.Write(data)
175
-}
176
-
177
-func (w *imageProgressWriter) readProgress(decoder *json.Decoder) error {
178
-	for {
179
-		line := &progressLine{}
180
-		err := decoder.Decode(line)
181
-		if err == io.EOF {
182
-			break
183
-		}
184
-		if err != nil {
185
-			return err
186
-		}
187
-		err = w.processLine(line)
188
-		if err != nil {
189
-			return err
190
-		}
191
-	}
192
-	return nil
193
-}
194
-
195
-func (w *imageProgressWriter) processLine(line *progressLine) error {
196
-
197
-	if err := getError(line); err != nil {
198
-		return err
199
-	}
200
-
201
-	// determine if it's a line we want to process
202
-	if !islayerStatus(line) {
203
-		return nil
204
-	}
205
-
206
-	w.layerStatus[line.ID] = *line
207
-
208
-	// if the number of layers has not stabilized yet, return and wait for more
209
-	// progress
210
-	if !w.isStableLayerCount() {
211
-		return nil
212
-	}
213
-
214
-	r := createReport(w.layerStatus)
215
-
216
-	// check if the count of layers in each state has changed
217
-	if w.layersChangedFn(w.lastReport, r) {
218
-		w.lastReport = r
219
-		w.lastReportTime = time.Now()
220
-		w.reportFn(r)
221
-		return nil
222
-	}
223
-	// If layer counts haven't changed, but enough time has passed (30 sec by default),
224
-	// at least report on download/push progress
225
-	if time.Since(w.lastReportTime) > w.progressTimeThreshhold {
226
-		w.lastReport = r
227
-		w.lastReportTime = time.Now()
228
-		w.reportFn(r)
229
-	}
230
-	return nil
231
-}
232
-
233
-func (w *imageProgressWriter) isStableLayerCount() bool {
234
-	// If the number of layers has changed since last status, we're not stable
235
-	if w.lastLayerCount != len(w.layerStatus) {
236
-		w.lastLayerCount = len(w.layerStatus)
237
-		w.stableLines = 0
238
-		return false
239
-	}
240
-	// Only proceed after we've received status for the same number
241
-	// of layers at least stableThreshhold times. If not, they're still increasing
242
-	w.stableLines++
243
-	if w.stableLines < w.stableThreshhold {
244
-		// We're not stable enough yet
245
-		return false
246
-	}
247
-
248
-	return true
249
-}
250
-
251
-var layerIDRegexp = regexp.MustCompile("^[a-f,0-9]*$")
252
-
253
-func islayerStatus(line *progressLine) bool {
254
-	// ignore status lines with no layer id
255
-	if len(line.ID) == 0 {
256
-		return false
257
-	}
258
-	// ignore layer ids that are not hex string
259
-	if !layerIDRegexp.MatchString(line.ID) {
260
-		return false
261
-	}
262
-	// ignore retrying status
263
-	if strings.HasPrefix(line.Status, "Retrying") {
264
-		return false
265
-	}
266
-	return true
267
-}
268
-
269
-func getError(line *progressLine) error {
270
-	if len(line.Error) > 0 {
271
-		return errors.New(line.Error)
272
-	}
273
-	return nil
274
-}
275
-
276
-func createReport(dockerProgress map[string]progressLine) report {
277
-	r := report{}
278
-	for _, line := range dockerProgress {
279
-		layerStatus := layerStatusFromDockerString(line.Status)
280
-		detail, exists := r[layerStatus]
281
-		if !exists {
282
-			detail = &layerDetail{}
283
-			r[layerStatus] = detail
284
-		}
285
-		detail.Count++
286
-		if line.Detail != nil {
287
-			detail.Current += line.Detail.Current
288
-			detail.Total += line.Detail.Total
289
-		}
290
-	}
291
-	return r
292
-}
293 1
deleted file mode 100644
... ...
@@ -1,208 +0,0 @@
1
-package imageprogress
2
-
3
-import (
4
-	"encoding/json"
5
-	"io"
6
-	"reflect"
7
-	"strconv"
8
-	"testing"
9
-)
10
-
11
-func TestReports(t *testing.T) {
12
-	tests := []struct {
13
-		name        string
14
-		gen         func(*progressGenerator)
15
-		errExpected bool
16
-		expected    report
17
-	}{
18
-		{
19
-			name: "simple report",
20
-			gen: func(p *progressGenerator) {
21
-				p.status("1", "Extracting")
22
-				p.status("2", "Downloading")
23
-				p.status("1", "Downloading")
24
-				p.status("2", "Pull complete")
25
-			},
26
-			expected: report{
27
-				statusDownloading: &layerDetail{Count: 1},
28
-				statusComplete:    &layerDetail{Count: 1},
29
-			},
30
-		},
31
-		{
32
-			name: "ignore invalid layer id",
33
-			gen: func(p *progressGenerator) {
34
-				p.status("1", "Downloading")
35
-				p.status("hello", "testing")
36
-				p.status("1", "Downloading")
37
-			},
38
-			expected: report{
39
-				statusDownloading: &layerDetail{Count: 1},
40
-			},
41
-		},
42
-		{
43
-			name: "ignore retrying status",
44
-			gen: func(p *progressGenerator) {
45
-				p.status("1", "Downloading")
46
-				p.status("2", "Pull complete")
47
-				p.status("1", "Downloading")
48
-				p.status("3", "Retrying")
49
-			},
50
-			expected: report{
51
-				statusDownloading: &layerDetail{Count: 1},
52
-				statusComplete:    &layerDetail{Count: 1},
53
-			},
54
-		},
55
-		{
56
-			name: "detect error",
57
-			gen: func(p *progressGenerator) {
58
-				p.status("1", "Downloading")
59
-				p.err("an error")
60
-			},
61
-			errExpected: true,
62
-		},
63
-	}
64
-
65
-	for _, test := range tests {
66
-		pipeIn, pipeOut := io.Pipe()
67
-		go func() {
68
-			p := newProgressGenerator(pipeOut)
69
-			test.gen(p)
70
-			pipeOut.Close()
71
-		}()
72
-		var lastReport report
73
-		w := newWriter(
74
-			func(r report) {
75
-				lastReport = r
76
-			},
77
-			func(a report, b report) bool {
78
-				return true
79
-			},
80
-		)
81
-		w.(*imageProgressWriter).stableThreshhold = 0
82
-		_, err := io.Copy(w, pipeIn)
83
-		if err != nil {
84
-			if !test.errExpected {
85
-				t.Errorf("%s: unexpected: %v", test.name, err)
86
-			}
87
-			continue
88
-		}
89
-		if test.errExpected {
90
-			t.Errorf("%s: did not get expected error", test.name)
91
-			continue
92
-		}
93
-		if !compareReport(lastReport, test.expected) {
94
-			t.Errorf("%s: unexpected report, got: %v, expected: %v", test.name, lastReport, test.expected)
95
-		}
96
-	}
97
-}
98
-
99
-func TestErrorOnCopy(t *testing.T) {
100
-	// Producer pipe
101
-	genIn, genOut := io.Pipe()
102
-	p := newProgressGenerator(genOut)
103
-
104
-	// generate some data
105
-	go func() {
106
-		for i := 0; i < 100; i++ {
107
-			p.status("1", "Downloading")
108
-			p.status("2", "Downloading")
109
-			p.status("3", "Downloading")
110
-		}
111
-		p.err("data error")
112
-		genOut.Close()
113
-	}()
114
-
115
-	w := newWriter(func(r report) {}, func(a, b report) bool { return true })
116
-
117
-	// Ensure that the error is propagated to the copy
118
-	_, err := io.Copy(w, genIn)
119
-	if err == nil {
120
-		t.Errorf("Did not get an error when copying to writer")
121
-	}
122
-	if err.Error() != "data error" {
123
-		t.Errorf("Did not get expected error: %v", err)
124
-	}
125
-}
126
-
127
-func TestStableLayerCount(t *testing.T) {
128
-
129
-	tests := []struct {
130
-		name             string
131
-		lastLayerCount   int
132
-		layerStatusCount int
133
-		stableThreshhold int
134
-		callCount        int
135
-		expectStable     bool
136
-	}{
137
-		{
138
-			name:             "increasing layer count",
139
-			lastLayerCount:   3,
140
-			layerStatusCount: 4,
141
-			callCount:        1,
142
-			expectStable:     false,
143
-		},
144
-		{
145
-			name:             "has not met stable threshhold",
146
-			lastLayerCount:   3,
147
-			layerStatusCount: 3,
148
-			callCount:        2,
149
-			stableThreshhold: 3,
150
-			expectStable:     false,
151
-		},
152
-		{
153
-			name:             "met stable threshhold",
154
-			lastLayerCount:   3,
155
-			layerStatusCount: 3,
156
-			callCount:        4,
157
-			stableThreshhold: 3,
158
-			expectStable:     true,
159
-		},
160
-	}
161
-	for _, test := range tests {
162
-		w := newWriter(func(report) {}, func(a, b report) bool { return true }).(*imageProgressWriter)
163
-		w.lastLayerCount = test.lastLayerCount
164
-		w.layerStatus = map[string]progressLine{}
165
-		w.stableThreshhold = test.stableThreshhold
166
-		for i := 0; i < test.layerStatusCount; i++ {
167
-			w.layerStatus[strconv.Itoa(i)] = progressLine{}
168
-		}
169
-		var result bool
170
-		for i := 0; i < test.callCount; i++ {
171
-			result = w.isStableLayerCount()
172
-		}
173
-		if result != test.expectStable {
174
-			t.Errorf("%s: expected %v, got %v", test.name, test.expectStable, result)
175
-		}
176
-	}
177
-}
178
-
179
-func compareReport(a, b report) bool {
180
-	if len(a) != len(b) {
181
-		return false
182
-	}
183
-	for k := range a {
184
-		if _, ok := b[k]; !ok {
185
-			return false
186
-		}
187
-		if !reflect.DeepEqual(*a[k], *b[k]) {
188
-			return false
189
-		}
190
-	}
191
-	return true
192
-}
193
-
194
-type progressGenerator json.Encoder
195
-
196
-func newProgressGenerator(w io.Writer) *progressGenerator {
197
-	return (*progressGenerator)(json.NewEncoder(w))
198
-}
199
-
200
-func (p *progressGenerator) status(id, status string) {
201
-	(*json.Encoder)(p).Encode(&progressLine{ID: id, Status: status})
202
-}
203
-func (p *progressGenerator) detail(id, status string, current, total int64) {
204
-	(*json.Encoder)(p).Encode(&progressLine{ID: id, Status: status, Detail: &progressDetail{Current: current, Total: total}})
205
-}
206
-func (p *progressGenerator) err(msg string) {
207
-	(*json.Encoder)(p).Encode(&progressLine{Error: msg})
208
-}
209 1
deleted file mode 100644
... ...
@@ -1,45 +0,0 @@
1
-package imageprogress
2
-
3
-import (
4
-	"fmt"
5
-	"io"
6
-)
7
-
8
-// NewPullWriter creates a writer that periodically reports
9
-// on pull progress of a Docker image. It only reports when the state of the
10
-// different layers has changed and uses time thresholds to limit the
11
-// rate of the reports.
12
-func NewPullWriter(printFn func(string)) io.Writer {
13
-	return newWriter(pullReporter(printFn), pullLayersChanged)
14
-}
15
-
16
-func pullReporter(printFn func(string)) func(report) {
17
-	extracting := false
18
-	return func(r report) {
19
-		if extracting {
20
-			return
21
-		}
22
-		if r.count(statusDownloading) == 0 &&
23
-			r.count(statusPending) == 0 &&
24
-			r.count(statusExtracting) > 0 {
25
-
26
-			printFn(fmt.Sprintf("Pulled %[1]d/%[1]d layers, 100%% complete", r.totalCount()))
27
-			printFn("Extracting")
28
-			extracting = true
29
-			return
30
-		}
31
-
32
-		completeCount := r.count(statusComplete) + r.count(statusExtracting)
33
-		var pctComplete float32 = 0.0
34
-		pctComplete += float32(completeCount) / float32(r.totalCount())
35
-		pctComplete += float32(r.count(statusDownloading)) / float32(r.totalCount()) * r.percentProgress(statusDownloading) / 100.0
36
-		pctComplete *= 100.0
37
-		printFn(fmt.Sprintf("Pulled %d/%d layers, %.0f%% complete", completeCount, r.totalCount(), pctComplete))
38
-	}
39
-}
40
-
41
-func pullLayersChanged(older, newer report) bool {
42
-	olderCompleteCount := older.count(statusComplete) + older.count(statusExtracting)
43
-	newerCompleteCount := newer.count(statusComplete) + newer.count(statusExtracting)
44
-	return olderCompleteCount != newerCompleteCount
45
-}
46 1
deleted file mode 100644
... ...
@@ -1,29 +0,0 @@
1
-package imageprogress
2
-
3
-import (
4
-	"fmt"
5
-	"io"
6
-)
7
-
8
-// NewPushWriter creates a writer that periodically reports
9
-// on push progress of a Docker image. It only reports when the state of the
10
-// different layers has changed and uses time thresholds to limit the
11
-// rate of the reports.
12
-func NewPushWriter(printFn func(string)) io.Writer {
13
-	return newWriter(pushReporter(printFn), pushLayersChanged)
14
-}
15
-
16
-func pushReporter(printFn func(string)) func(report) {
17
-	return func(r report) {
18
-		var pctComplete float32 = 0.0
19
-		pctComplete += float32(r.count(statusComplete)) / float32(r.totalCount())
20
-		pctComplete += float32(r.count(statusPushing)) / float32(r.totalCount()) * r.percentProgress(statusPushing) / 100.0
21
-		pctComplete *= 100.0
22
-
23
-		printFn(fmt.Sprintf("Pushed %d/%d layers, %.0f%% complete", r.count(statusComplete), r.totalCount(), pctComplete))
24
-	}
25
-}
26
-
27
-func pushLayersChanged(older, newer report) bool {
28
-	return older.count(statusComplete) != newer.count(statusComplete)
29
-}
30 1
deleted file mode 100644
... ...
@@ -1,95 +0,0 @@
1
-package builder
2
-
3
-import (
4
-	"fmt"
5
-	"os"
6
-	"path/filepath"
7
-	"runtime"
8
-	"strings"
9
-)
10
-
11
-// isURL returns true if the string appears to be a URL.
12
-func isURL(s string) bool {
13
-	return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://")
14
-}
15
-
16
-// exportEnv creates an export statement for a shell that contains all of the
17
-// provided environment.
18
-func exportEnv(env []string) string {
19
-	if len(env) == 0 {
20
-		return ""
21
-	}
22
-	out := "export"
23
-	for _, e := range env {
24
-		out += " " + bashQuote(e)
25
-	}
26
-	return out + "; "
27
-}
28
-
29
-// bashQuote escapes the provided string and surrounds it with double quotes.
30
-// TODO: verify that these are all we have to escape.
31
-func bashQuote(env string) string {
32
-	out := []rune{'"'}
33
-	for _, r := range env {
34
-		switch r {
35
-		case '$', '\\', '"':
36
-			out = append(out, '\\', r)
37
-		default:
38
-			out = append(out, r)
39
-		}
40
-	}
41
-	out = append(out, '"')
42
-	return string(out)
43
-}
44
-
45
-// hasEnvName returns true if the provided environment contains the named ENV var.
46
-func hasEnvName(env []string, name string) bool {
47
-	for _, e := range env {
48
-		if strings.HasPrefix(e, name+"=") {
49
-			return true
50
-		}
51
-	}
52
-	return false
53
-}
54
-
55
-// platformSupports is a short-term function to give users a quality error
56
-// message if a Dockerfile uses a command not supported on the platform.
57
-func platformSupports(command string) error {
58
-	if runtime.GOOS != "windows" {
59
-		return nil
60
-	}
61
-	switch command {
62
-	case "expose", "user", "stopsignal", "arg":
63
-		return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
64
-	}
65
-	return nil
66
-}
67
-
68
-func handleJSONArgs(args []string, attributes map[string]bool) []string {
69
-	if len(args) == 0 {
70
-		return []string{}
71
-	}
72
-
73
-	if attributes != nil && attributes["json"] {
74
-		return args
75
-	}
76
-
77
-	// literal string command, not an exec array
78
-	return []string{strings.Join(args, " ")}
79
-}
80
-
81
-// makeAbsolute ensures that the provided path is absolute.
82
-func makeAbsolute(dest, workingDir string) string {
83
-	// Twiddle the destination when its a relative path - meaning, make it
84
-	// relative to the WORKINGDIR
85
-	if !filepath.IsAbs(dest) {
86
-		hasSlash := strings.HasSuffix(dest, string(os.PathSeparator)) || strings.HasSuffix(dest, string(os.PathSeparator)+".")
87
-		dest = filepath.Join(string(os.PathSeparator), filepath.FromSlash(workingDir), dest)
88
-
89
-		// Make sure we preserve any trailing slash
90
-		if hasSlash {
91
-			dest += string(os.PathSeparator)
92
-		}
93
-	}
94
-	return dest
95
-}
96 1
deleted file mode 100644
... ...
@@ -1,314 +0,0 @@
1
-package builder
2
-
3
-// This will take a single word and an array of env variables and
4
-// process all quotes (" and ') as well as $xxx and ${xxx} env variable
5
-// tokens.  Tries to mimic bash shell process.
6
-// It doesn't support all flavors of ${xx:...} formats but new ones can
7
-// be added by adding code to the "special ${} format processing" section
8
-
9
-import (
10
-	"fmt"
11
-	"strings"
12
-	"text/scanner"
13
-	"unicode"
14
-)
15
-
16
-type shellWord struct {
17
-	word    string
18
-	scanner scanner.Scanner
19
-	envs    []string
20
-	pos     int
21
-}
22
-
23
-// ProcessWord will use the 'env' list of environment variables,
24
-// and replace any env var references in 'word'.
25
-func ProcessWord(word string, env []string) (string, error) {
26
-	sw := &shellWord{
27
-		word: word,
28
-		envs: env,
29
-		pos:  0,
30
-	}
31
-	sw.scanner.Init(strings.NewReader(word))
32
-	word, _, err := sw.process()
33
-	return word, err
34
-}
35
-
36
-// ProcessWords will use the 'env' list of environment variables,
37
-// and replace any env var references in 'word' then it will also
38
-// return a slice of strings which represents the 'word'
39
-// split up based on spaces - taking into account quotes.  Note that
40
-// this splitting is done **after** the env var substitutions are done.
41
-// Note, each one is trimmed to remove leading and trailing spaces (unless
42
-// they are quoted", but ProcessWord retains spaces between words.
43
-func ProcessWords(word string, env []string) ([]string, error) {
44
-	sw := &shellWord{
45
-		word: word,
46
-		envs: env,
47
-		pos:  0,
48
-	}
49
-	sw.scanner.Init(strings.NewReader(word))
50
-	_, words, err := sw.process()
51
-	return words, err
52
-}
53
-
54
-func (sw *shellWord) process() (string, []string, error) {
55
-	return sw.processStopOn(scanner.EOF)
56
-}
57
-
58
-type wordsStruct struct {
59
-	word   string
60
-	words  []string
61
-	inWord bool
62
-}
63
-
64
-func (w *wordsStruct) addChar(ch rune) {
65
-	if unicode.IsSpace(ch) && w.inWord {
66
-		if len(w.word) != 0 {
67
-			w.words = append(w.words, w.word)
68
-			w.word = ""
69
-			w.inWord = false
70
-		}
71
-	} else if !unicode.IsSpace(ch) {
72
-		w.addRawChar(ch)
73
-	}
74
-}
75
-
76
-func (w *wordsStruct) addRawChar(ch rune) {
77
-	w.word += string(ch)
78
-	w.inWord = true
79
-}
80
-
81
-func (w *wordsStruct) addString(str string) {
82
-	var scan scanner.Scanner
83
-	scan.Init(strings.NewReader(str))
84
-	for scan.Peek() != scanner.EOF {
85
-		w.addChar(scan.Next())
86
-	}
87
-}
88
-
89
-func (w *wordsStruct) addRawString(str string) {
90
-	w.word += str
91
-	w.inWord = true
92
-}
93
-
94
-func (w *wordsStruct) getWords() []string {
95
-	if len(w.word) > 0 {
96
-		w.words = append(w.words, w.word)
97
-
98
-		// Just in case we're called again by mistake
99
-		w.word = ""
100
-		w.inWord = false
101
-	}
102
-	return w.words
103
-}
104
-
105
-// Process the word, starting at 'pos', and stop when we get to the
106
-// end of the word or the 'stopChar' character
107
-func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
108
-	var result string
109
-	var words wordsStruct
110
-
111
-	var charFuncMapping = map[rune]func() (string, error){
112
-		'\'': sw.processSingleQuote,
113
-		'"':  sw.processDoubleQuote,
114
-		'$':  sw.processDollar,
115
-	}
116
-
117
-	for sw.scanner.Peek() != scanner.EOF {
118
-		ch := sw.scanner.Peek()
119
-
120
-		if stopChar != scanner.EOF && ch == stopChar {
121
-			sw.scanner.Next()
122
-			break
123
-		}
124
-		if fn, ok := charFuncMapping[ch]; ok {
125
-			// Call special processing func for certain chars
126
-			tmp, err := fn()
127
-			if err != nil {
128
-				return "", []string{}, err
129
-			}
130
-			result += tmp
131
-
132
-			if ch == rune('$') {
133
-				words.addString(tmp)
134
-			} else {
135
-				words.addRawString(tmp)
136
-			}
137
-		} else {
138
-			// Not special, just add it to the result
139
-			ch = sw.scanner.Next()
140
-
141
-			if ch == '\\' {
142
-				// '\' escapes, except end of line
143
-
144
-				ch = sw.scanner.Next()
145
-
146
-				if ch == scanner.EOF {
147
-					break
148
-				}
149
-
150
-				words.addRawChar(ch)
151
-			} else {
152
-				words.addChar(ch)
153
-			}
154
-
155
-			result += string(ch)
156
-		}
157
-	}
158
-
159
-	return result, words.getWords(), nil
160
-}
161
-
162
-func (sw *shellWord) processSingleQuote() (string, error) {
163
-	// All chars between single quotes are taken as-is
164
-	// Note, you can't escape '
165
-	var result string
166
-
167
-	sw.scanner.Next()
168
-
169
-	for {
170
-		ch := sw.scanner.Next()
171
-		if ch == '\'' || ch == scanner.EOF {
172
-			break
173
-		}
174
-		result += string(ch)
175
-	}
176
-
177
-	return result, nil
178
-}
179
-
180
-func (sw *shellWord) processDoubleQuote() (string, error) {
181
-	// All chars up to the next " are taken as-is, even ', except any $ chars
182
-	// But you can escape " with a \
183
-	var result string
184
-
185
-	sw.scanner.Next()
186
-
187
-	for sw.scanner.Peek() != scanner.EOF {
188
-		ch := sw.scanner.Peek()
189
-		if ch == '"' {
190
-			sw.scanner.Next()
191
-			break
192
-		}
193
-		if ch == '$' {
194
-			tmp, err := sw.processDollar()
195
-			if err != nil {
196
-				return "", err
197
-			}
198
-			result += tmp
199
-		} else {
200
-			ch = sw.scanner.Next()
201
-			if ch == '\\' {
202
-				chNext := sw.scanner.Peek()
203
-
204
-				if chNext == scanner.EOF {
205
-					// Ignore \ at end of word
206
-					continue
207
-				}
208
-
209
-				if chNext == '"' || chNext == '$' {
210
-					// \" and \$ can be escaped, all other \'s are left as-is
211
-					ch = sw.scanner.Next()
212
-				}
213
-			}
214
-			result += string(ch)
215
-		}
216
-	}
217
-
218
-	return result, nil
219
-}
220
-
221
-func (sw *shellWord) processDollar() (string, error) {
222
-	sw.scanner.Next()
223
-	ch := sw.scanner.Peek()
224
-	if ch == '{' {
225
-		sw.scanner.Next()
226
-		name := sw.processName()
227
-		ch = sw.scanner.Peek()
228
-		if ch == '}' {
229
-			// Normal ${xx} case
230
-			sw.scanner.Next()
231
-			return sw.getEnv(name), nil
232
-		}
233
-		if ch == ':' {
234
-			// Special ${xx:...} format processing
235
-			// Yes it allows for recursive $'s in the ... spot
236
-
237
-			sw.scanner.Next() // skip over :
238
-			modifier := sw.scanner.Next()
239
-
240
-			word, _, err := sw.processStopOn('}')
241
-			if err != nil {
242
-				return "", err
243
-			}
244
-
245
-			// Grab the current value of the variable in question so we
246
-			// can use to to determine what to do based on the modifier
247
-			newValue := sw.getEnv(name)
248
-
249
-			switch modifier {
250
-			case '+':
251
-				if newValue != "" {
252
-					newValue = word
253
-				}
254
-				return newValue, nil
255
-
256
-			case '-':
257
-				if newValue == "" {
258
-					newValue = word
259
-				}
260
-				return newValue, nil
261
-
262
-			default:
263
-				return "", fmt.Errorf("Unsupported modifier (%c) in substitution: %s", modifier, sw.word)
264
-			}
265
-		}
266
-		return "", fmt.Errorf("Missing ':' in substitution: %s", sw.word)
267
-	}
268
-	// $xxx case
269
-	name := sw.processName()
270
-	if name == "" {
271
-		return "$", nil
272
-	}
273
-	return sw.getEnv(name), nil
274
-}
275
-
276
-func (sw *shellWord) processName() string {
277
-	// Read in a name (alphanumeric or _)
278
-	// If it starts with a numeric then just return $#
279
-	var name string
280
-
281
-	for sw.scanner.Peek() != scanner.EOF {
282
-		ch := sw.scanner.Peek()
283
-		if len(name) == 0 && unicode.IsDigit(ch) {
284
-			ch = sw.scanner.Next()
285
-			return string(ch)
286
-		}
287
-		if !unicode.IsLetter(ch) && !unicode.IsDigit(ch) && ch != '_' {
288
-			break
289
-		}
290
-		ch = sw.scanner.Next()
291
-		name += string(ch)
292
-	}
293
-
294
-	return name
295
-}
296
-
297
-func (sw *shellWord) getEnv(name string) string {
298
-	for _, env := range sw.envs {
299
-		i := strings.Index(env, "=")
300
-		if i < 0 {
301
-			if name == env {
302
-				// Should probably never get here, but just in case treat
303
-				// it like "var" and "var=" are the same
304
-				return ""
305
-			}
306
-			continue
307
-		}
308
-		if name != env[:i] {
309
-			continue
310
-		}
311
-		return env[i+1:]
312
-	}
313
-	return ""
314
-}
315 1
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-This package provides helper functions for dealing with signals across various operating systems
2 1
\ No newline at end of file
3 2
deleted file mode 100644
... ...
@@ -1,37 +0,0 @@
1
-// Package signal provides helper functions for dealing with signals across
2
-// various operating systems.
3
-package signal
4
-
5
-import (
6
-	"fmt"
7
-	"strconv"
8
-	"strings"
9
-	"syscall"
10
-)
11
-
12
-// ParseSignal translates a string to a valid syscall signal.
13
-// It returns an error if the signal map doesn't include the given signal.
14
-func ParseSignal(rawSignal string) (syscall.Signal, error) {
15
-	s, err := strconv.Atoi(rawSignal)
16
-	if err == nil {
17
-		if s == 0 {
18
-			return -1, fmt.Errorf("Invalid signal: %s", rawSignal)
19
-		}
20
-		return syscall.Signal(s), nil
21
-	}
22
-	signal, ok := SignalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]
23
-	if !ok {
24
-		return -1, fmt.Errorf("Invalid signal: %s", rawSignal)
25
-	}
26
-	return signal, nil
27
-}
28
-
29
-// ValidSignalForPlatform returns true if a signal is valid on the platform
30
-func ValidSignalForPlatform(sig syscall.Signal) bool {
31
-	for _, v := range SignalMap {
32
-		if v == sig {
33
-			return true
34
-		}
35
-	}
36
-	return false
37
-}
38 1
deleted file mode 100644
... ...
@@ -1,41 +0,0 @@
1
-package signal
2
-
3
-import (
4
-	"syscall"
5
-)
6
-
7
-// SignalMap is a map of Darwin signals.
8
-var SignalMap = map[string]syscall.Signal{
9
-	"ABRT":   syscall.SIGABRT,
10
-	"ALRM":   syscall.SIGALRM,
11
-	"BUG":    syscall.SIGBUS,
12
-	"CHLD":   syscall.SIGCHLD,
13
-	"CONT":   syscall.SIGCONT,
14
-	"EMT":    syscall.SIGEMT,
15
-	"FPE":    syscall.SIGFPE,
16
-	"HUP":    syscall.SIGHUP,
17
-	"ILL":    syscall.SIGILL,
18
-	"INFO":   syscall.SIGINFO,
19
-	"INT":    syscall.SIGINT,
20
-	"IO":     syscall.SIGIO,
21
-	"IOT":    syscall.SIGIOT,
22
-	"KILL":   syscall.SIGKILL,
23
-	"PIPE":   syscall.SIGPIPE,
24
-	"PROF":   syscall.SIGPROF,
25
-	"QUIT":   syscall.SIGQUIT,
26
-	"SEGV":   syscall.SIGSEGV,
27
-	"STOP":   syscall.SIGSTOP,
28
-	"SYS":    syscall.SIGSYS,
29
-	"TERM":   syscall.SIGTERM,
30
-	"TRAP":   syscall.SIGTRAP,
31
-	"TSTP":   syscall.SIGTSTP,
32
-	"TTIN":   syscall.SIGTTIN,
33
-	"TTOU":   syscall.SIGTTOU,
34
-	"URG":    syscall.SIGURG,
35
-	"USR1":   syscall.SIGUSR1,
36
-	"USR2":   syscall.SIGUSR2,
37
-	"VTALRM": syscall.SIGVTALRM,
38
-	"WINCH":  syscall.SIGWINCH,
39
-	"XCPU":   syscall.SIGXCPU,
40
-	"XFSZ":   syscall.SIGXFSZ,
41
-}
42 1
deleted file mode 100644
... ...
@@ -1,43 +0,0 @@
1
-package signal
2
-
3
-import (
4
-	"syscall"
5
-)
6
-
7
-// SignalMap is a map of FreeBSD signals.
8
-var SignalMap = map[string]syscall.Signal{
9
-	"ABRT":   syscall.SIGABRT,
10
-	"ALRM":   syscall.SIGALRM,
11
-	"BUF":    syscall.SIGBUS,
12
-	"CHLD":   syscall.SIGCHLD,
13
-	"CONT":   syscall.SIGCONT,
14
-	"EMT":    syscall.SIGEMT,
15
-	"FPE":    syscall.SIGFPE,
16
-	"HUP":    syscall.SIGHUP,
17
-	"ILL":    syscall.SIGILL,
18
-	"INFO":   syscall.SIGINFO,
19
-	"INT":    syscall.SIGINT,
20
-	"IO":     syscall.SIGIO,
21
-	"IOT":    syscall.SIGIOT,
22
-	"KILL":   syscall.SIGKILL,
23
-	"LWP":    syscall.SIGLWP,
24
-	"PIPE":   syscall.SIGPIPE,
25
-	"PROF":   syscall.SIGPROF,
26
-	"QUIT":   syscall.SIGQUIT,
27
-	"SEGV":   syscall.SIGSEGV,
28
-	"STOP":   syscall.SIGSTOP,
29
-	"SYS":    syscall.SIGSYS,
30
-	"TERM":   syscall.SIGTERM,
31
-	"THR":    syscall.SIGTHR,
32
-	"TRAP":   syscall.SIGTRAP,
33
-	"TSTP":   syscall.SIGTSTP,
34
-	"TTIN":   syscall.SIGTTIN,
35
-	"TTOU":   syscall.SIGTTOU,
36
-	"URG":    syscall.SIGURG,
37
-	"USR1":   syscall.SIGUSR1,
38
-	"USR2":   syscall.SIGUSR2,
39
-	"VTALRM": syscall.SIGVTALRM,
40
-	"WINCH":  syscall.SIGWINCH,
41
-	"XCPU":   syscall.SIGXCPU,
42
-	"XFSZ":   syscall.SIGXFSZ,
43
-}
44 1
deleted file mode 100644
... ...
@@ -1,80 +0,0 @@
1
-package signal
2
-
3
-import (
4
-	"syscall"
5
-)
6
-
7
-const (
8
-	sigrtmin = 34
9
-	sigrtmax = 64
10
-)
11
-
12
-// SignalMap is a map of Linux signals.
13
-var SignalMap = map[string]syscall.Signal{
14
-	"ABRT":     syscall.SIGABRT,
15
-	"ALRM":     syscall.SIGALRM,
16
-	"BUS":      syscall.SIGBUS,
17
-	"CHLD":     syscall.SIGCHLD,
18
-	"CLD":      syscall.SIGCLD,
19
-	"CONT":     syscall.SIGCONT,
20
-	"FPE":      syscall.SIGFPE,
21
-	"HUP":      syscall.SIGHUP,
22
-	"ILL":      syscall.SIGILL,
23
-	"INT":      syscall.SIGINT,
24
-	"IO":       syscall.SIGIO,
25
-	"IOT":      syscall.SIGIOT,
26
-	"KILL":     syscall.SIGKILL,
27
-	"PIPE":     syscall.SIGPIPE,
28
-	"POLL":     syscall.SIGPOLL,
29
-	"PROF":     syscall.SIGPROF,
30
-	"PWR":      syscall.SIGPWR,
31
-	"QUIT":     syscall.SIGQUIT,
32
-	"SEGV":     syscall.SIGSEGV,
33
-	"STKFLT":   syscall.SIGSTKFLT,
34
-	"STOP":     syscall.SIGSTOP,
35
-	"SYS":      syscall.SIGSYS,
36
-	"TERM":     syscall.SIGTERM,
37
-	"TRAP":     syscall.SIGTRAP,
38
-	"TSTP":     syscall.SIGTSTP,
39
-	"TTIN":     syscall.SIGTTIN,
40
-	"TTOU":     syscall.SIGTTOU,
41
-	"UNUSED":   syscall.SIGUNUSED,
42
-	"URG":      syscall.SIGURG,
43
-	"USR1":     syscall.SIGUSR1,
44
-	"USR2":     syscall.SIGUSR2,
45
-	"VTALRM":   syscall.SIGVTALRM,
46
-	"WINCH":    syscall.SIGWINCH,
47
-	"XCPU":     syscall.SIGXCPU,
48
-	"XFSZ":     syscall.SIGXFSZ,
49
-	"RTMIN":    sigrtmin,
50
-	"RTMIN+1":  sigrtmin + 1,
51
-	"RTMIN+2":  sigrtmin + 2,
52
-	"RTMIN+3":  sigrtmin + 3,
53
-	"RTMIN+4":  sigrtmin + 4,
54
-	"RTMIN+5":  sigrtmin + 5,
55
-	"RTMIN+6":  sigrtmin + 6,
56
-	"RTMIN+7":  sigrtmin + 7,
57
-	"RTMIN+8":  sigrtmin + 8,
58
-	"RTMIN+9":  sigrtmin + 9,
59
-	"RTMIN+10": sigrtmin + 10,
60
-	"RTMIN+11": sigrtmin + 11,
61
-	"RTMIN+12": sigrtmin + 12,
62
-	"RTMIN+13": sigrtmin + 13,
63
-	"RTMIN+14": sigrtmin + 14,
64
-	"RTMIN+15": sigrtmin + 15,
65
-	"RTMAX-14": sigrtmax - 14,
66
-	"RTMAX-13": sigrtmax - 13,
67
-	"RTMAX-12": sigrtmax - 12,
68
-	"RTMAX-11": sigrtmax - 11,
69
-	"RTMAX-10": sigrtmax - 10,
70
-	"RTMAX-9":  sigrtmax - 9,
71
-	"RTMAX-8":  sigrtmax - 8,
72
-	"RTMAX-7":  sigrtmax - 7,
73
-	"RTMAX-6":  sigrtmax - 6,
74
-	"RTMAX-5":  sigrtmax - 5,
75
-	"RTMAX-4":  sigrtmax - 4,
76
-	"RTMAX-3":  sigrtmax - 3,
77
-	"RTMAX-2":  sigrtmax - 2,
78
-	"RTMAX-1":  sigrtmax - 1,
79
-	"RTMAX":    sigrtmax,
80
-}
81 1
deleted file mode 100644
... ...
@@ -1,19 +0,0 @@
1
-// +build !windows
2
-
3
-package signal
4
-
5
-import (
6
-	"syscall"
7
-)
8
-
9
-// Signals used in api/client (no windows equivalent, use
10
-// invalid signals so they don't get handled)
11
-
12
-const (
13
-	// SIGCHLD is a signal sent to a process when a child process terminates, is interrupted, or resumes after being interrupted.
14
-	SIGCHLD = syscall.SIGCHLD
15
-	// SIGWINCH is a signal sent to a process when its controlling terminal changes its size
16
-	SIGWINCH = syscall.SIGWINCH
17
-	// DefaultStopSignal is the syscall signal used to stop a container in unix systems.
18
-	DefaultStopSignal = "SIGTERM"
19
-)
20 1
deleted file mode 100644
... ...
@@ -1,10 +0,0 @@
1
-// +build !linux,!darwin,!freebsd,!windows
2
-
3
-package signal
4
-
5
-import (
6
-	"syscall"
7
-)
8
-
9
-// SignalMap is an empty map of signals for unsupported platform.
10
-var SignalMap = map[string]syscall.Signal{}
11 1
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-// +build windows
2
-
3
-package signal
4
-
5
-import (
6
-	"syscall"
7
-)
8
-
9
-// Signals used in api/client (no windows equivalent, use
10
-// invalid signals so they don't get handled)
11
-const (
12
-	SIGCHLD  = syscall.Signal(0xff)
13
-	SIGWINCH = syscall.Signal(0xff)
14
-	// DefaultStopSignal is the syscall signal used to stop a container in windows systems.
15
-	DefaultStopSignal = "15"
16
-)
17
-
18
-// SignalMap is a map of "supported" signals. As per the comment in GOLang's
19
-// ztypes_windows.go: "More invented values for signals". Windows doesn't
20
-// really support signals in any way, shape or form that Unix does.
21
-//
22
-// We have these so that docker kill can be used to gracefully (TERM) and
23
-// forcibly (KILL) terminate a container on Windows.
24
-var SignalMap = map[string]syscall.Signal{
25
-	"KILL": syscall.SIGKILL,
26
-	"TERM": syscall.SIGTERM,
27
-}
28 1
deleted file mode 100644
... ...
@@ -1,30 +0,0 @@
1
-package strslice
2
-
3
-import "encoding/json"
4
-
5
-// StrSlice represents a string or an array of strings.
6
-// We need to override the json decoder to accept both options.
7
-type StrSlice []string
8
-
9
-// UnmarshalJSON decodes the byte slice whether it's a string or an array of
10
-// strings. This method is needed to implement json.Unmarshaler.
11
-func (e *StrSlice) UnmarshalJSON(b []byte) error {
12
-	if len(b) == 0 {
13
-		// With no input, we preserve the existing value by returning nil and
14
-		// leaving the target alone. This allows defining default values for
15
-		// the type.
16
-		return nil
17
-	}
18
-
19
-	p := make([]string, 0, 1)
20
-	if err := json.Unmarshal(b, &p); err != nil {
21
-		var s string
22
-		if err := json.Unmarshal(b, &s); err != nil {
23
-			return err
24
-		}
25
-		p = append(p, s)
26
-	}
27
-
28
-	*e = p
29
-	return nil
30
-}
31 1
deleted file mode 100644
... ...
@@ -1,11 +0,0 @@
1
-FROM busybox
2
-ADD https://github.com/openshift/origin/raw/master/README.md README.md
3
-USER 1001
4
-ADD https://github.com/openshift/origin/raw/master/LICENSE .
5
-ADD https://github.com/openshift/origin/raw/master/LICENSE A
6
-ADD https://github.com/openshift/origin/raw/master/LICENSE ./a
7
-USER root
8
-RUN mkdir ./b
9
-ADD https://github.com/openshift/origin/raw/master/LICENSE ./b/a
10
-ADD https://github.com/openshift/origin/raw/master/LICENSE ./b/.
11
-ADD https://github.com/openshift/ruby-hello-world/archive/master.zip /tmp/
12 1
deleted file mode 100644
... ...
@@ -1,48 +0,0 @@
1
-FROM busybox
2
-
3
-MAINTAINER docker <docker@docker.io>
4
-
5
-ONBUILD RUN ["echo", "test"]
6
-ONBUILD RUN echo test
7
-ONBUILD COPY . /
8
-
9
-
10
-# RUN Commands \
11
-# linebreak in comment \
12
-RUN ["ls", "-la"]
13
-RUN ["echo", "'1234'"]
14
-RUN echo "1234"
15
-RUN echo 1234
16
-RUN echo '1234' && \
17
-    echo "456" && \
18
-    echo 789
19
-RUN    sh -c 'echo root:testpass \
20
-        > /tmp/passwd'
21
-RUN mkdir -p /test /test2 /test3/test
22
-
23
-# ENV \
24
-ENV SCUBA 1 DUBA 3
25
-ENV SCUBA "1 DUBA 3"
26
-
27
-# CMD \
28
-CMD ["echo", "test"]
29
-CMD echo test
30
-CMD echo "test"
31
-CMD echo 'test'
32
-CMD echo 'test' | wc -
33
-
34
-#EXPOSE\
35
-EXPOSE 3000
36
-EXPOSE 9000 5000 6000
37
-
38
-USER docker
39
-USER docker:root
40
-
41
-VOLUME ["/test"]
42
-VOLUME ["/test", "/test2"]
43
-VOLUME /test3
44
-
45
-WORKDIR /test
46
-
47
-ADD . /
48
-COPY . copy
49 1
\ No newline at end of file
50 2
deleted file mode 100644
... ...
@@ -1,23 +0,0 @@
1
-FROM busybox
2
-ENV name value
3
-ENV name=value
4
-ENV name=value name2=value2
5
-ENV name="value value1"
6
-ENV name=value\ value2
7
-ENV name="value'quote space'value2"
8
-ENV name='value"double quote"value2'
9
-ENV name=value\ value2 name2=value2\ value3
10
-ENV name="a\"b"
11
-ENV name="a\'b"
12
-ENV name='a\'b'
13
-ENV name='a\'b''
14
-ENV name='a\"b'
15
-ENV name="''"
16
-# don't put anything after the next line - it must be the last line of the
17
-# Dockerfile and it must end with \
18
-ENV name=value \
19
-    name1=value1 \
20
-    name2="value2a \
21
-           value2b" \
22
-    name3="value3a\n\"value3b\"" \
23
-	name4="value4a\\nvalue4b" \
24 1
\ No newline at end of file
25 2
deleted file mode 100644
... ...
@@ -1,2 +0,0 @@
1
-FROM busybox
2
-EXPOSE 3469
3 1
\ No newline at end of file
4 2
deleted file mode 100644
... ...
@@ -1,4 +0,0 @@
1
-FROM busybox
2
-COPY . /
3
-COPY . dir
4
-COPY subdir/ test/
5 1
\ No newline at end of file
6 2
deleted file mode 100644
7 3
deleted file mode 100644
8 4
deleted file mode 100644
... ...
@@ -1,2 +0,0 @@
1
-file
2
-file2
3 1
\ No newline at end of file
4 2
deleted file mode 100644
... ...
@@ -1,2 +0,0 @@
1
-FROM busybox
2
-COPY . /
3 1
deleted file mode 100644
4 2
deleted file mode 100644
5 3
deleted file mode 100644