[builder] cleanup env dispatcher and Builder.build()
| ... | ... |
@@ -100,8 +100,7 @@ type ContainerCommitConfig struct {
|
| 100 | 100 |
Changes []string |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 |
-// ProgressWriter is an interface |
|
| 104 |
-// to transport progress streams. |
|
| 103 |
+// ProgressWriter is a data object to transport progress streams to the client |
|
| 105 | 104 |
type ProgressWriter struct {
|
| 106 | 105 |
Output io.Writer |
| 107 | 106 |
StdoutFormatter *streamformatter.StdoutFormatter |
| ... | ... |
@@ -2,7 +2,6 @@ package dockerfile |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 |
- "errors" |
|
| 6 | 5 |
"fmt" |
| 7 | 6 |
"io" |
| 8 | 7 |
"io/ioutil" |
| ... | ... |
@@ -20,7 +19,7 @@ import ( |
| 20 | 20 |
"github.com/docker/docker/builder/dockerfile/parser" |
| 21 | 21 |
"github.com/docker/docker/image" |
| 22 | 22 |
"github.com/docker/docker/pkg/stringid" |
| 23 |
- perrors "github.com/pkg/errors" |
|
| 23 |
+ "github.com/pkg/errors" |
|
| 24 | 24 |
"golang.org/x/net/context" |
| 25 | 25 |
) |
| 26 | 26 |
|
| ... | ... |
@@ -51,7 +50,6 @@ type Builder struct {
|
| 51 | 51 |
docker builder.Backend |
| 52 | 52 |
context builder.Context |
| 53 | 53 |
clientCtx context.Context |
| 54 |
- cancel context.CancelFunc |
|
| 55 | 54 |
|
| 56 | 55 |
runConfig *container.Config // runconfig for cmd, run, entrypoint etc. |
| 57 | 56 |
flags *BFlags |
| ... | ... |
@@ -66,9 +64,6 @@ type Builder struct {
|
| 66 | 66 |
buildArgs *buildArgs |
| 67 | 67 |
directive parser.Directive |
| 68 | 68 |
|
| 69 |
- // TODO: remove once docker.Commit can receive a tag |
|
| 70 |
- id string |
|
| 71 |
- |
|
| 72 | 69 |
imageCache builder.ImageCache |
| 73 | 70 |
from builder.Image |
| 74 | 71 |
} |
| ... | ... |
@@ -117,10 +112,8 @@ func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, back |
| 117 | 117 |
if config == nil {
|
| 118 | 118 |
config = new(types.ImageBuildOptions) |
| 119 | 119 |
} |
| 120 |
- ctx, cancel := context.WithCancel(clientCtx) |
|
| 121 | 120 |
b = &Builder{
|
| 122 |
- clientCtx: ctx, |
|
| 123 |
- cancel: cancel, |
|
| 121 |
+ clientCtx: clientCtx, |
|
| 124 | 122 |
options: config, |
| 125 | 123 |
Stdout: os.Stdout, |
| 126 | 124 |
Stderr: os.Stderr, |
| ... | ... |
@@ -128,7 +121,6 @@ func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, back |
| 128 | 128 |
context: buildContext, |
| 129 | 129 |
runConfig: new(container.Config), |
| 130 | 130 |
tmpContainers: map[string]struct{}{},
|
| 131 |
- id: stringid.GenerateNonCryptoID(), |
|
| 132 | 131 |
buildArgs: newBuildArgs(config.BuildArgs), |
| 133 | 132 |
directive: parser.Directive{
|
| 134 | 133 |
EscapeSeen: false, |
| ... | ... |
@@ -186,17 +178,6 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
|
| 186 | 186 |
|
| 187 | 187 |
// build runs the Dockerfile builder from a context and a docker object that allows to make calls |
| 188 | 188 |
// to Docker. |
| 189 |
-// |
|
| 190 |
-// This will (barring errors): |
|
| 191 |
-// |
|
| 192 |
-// * read the dockerfile from context |
|
| 193 |
-// * parse the dockerfile if not already parsed |
|
| 194 |
-// * walk the AST and execute it by dispatching to handlers. If Remove |
|
| 195 |
-// or ForceRemove is set, additional cleanup around containers happens after |
|
| 196 |
-// processing. |
|
| 197 |
-// * Tag image, if applicable. |
|
| 198 |
-// * Print a happy message and return the image ID. |
|
| 199 |
-// |
|
| 200 | 189 |
func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (string, error) {
|
| 201 | 190 |
defer b.imageContexts.unmount() |
| 202 | 191 |
|
| ... | ... |
@@ -204,7 +185,7 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri |
| 204 | 204 |
b.Stderr = stderr |
| 205 | 205 |
b.Output = out |
| 206 | 206 |
|
| 207 |
- dockerfile, err := b.readDockerfile() |
|
| 207 |
+ dockerfile, err := b.readAndParseDockerfile() |
|
| 208 | 208 |
if err != nil {
|
| 209 | 209 |
return "", err |
| 210 | 210 |
} |
| ... | ... |
@@ -216,14 +197,37 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri |
| 216 | 216 |
|
| 217 | 217 |
addNodesForLabelOption(dockerfile, b.options.Labels) |
| 218 | 218 |
|
| 219 |
- var shortImgID string |
|
| 220 |
- total := len(dockerfile.Children) |
|
| 221 |
- for _, n := range dockerfile.Children {
|
|
| 222 |
- if err := b.checkDispatch(n, false); err != nil {
|
|
| 223 |
- return "", perrors.Wrapf(err, "Dockerfile parse error line %d", n.StartLine) |
|
| 219 |
+ if err := checkDispatchDockerfile(dockerfile); err != nil {
|
|
| 220 |
+ return "", err |
|
| 221 |
+ } |
|
| 222 |
+ |
|
| 223 |
+ shortImageID, err := b.dispatchDockerfileWithCancellation(dockerfile) |
|
| 224 |
+ if err != nil {
|
|
| 225 |
+ return "", err |
|
| 226 |
+ } |
|
| 227 |
+ |
|
| 228 |
+ b.warnOnUnusedBuildArgs() |
|
| 229 |
+ |
|
| 230 |
+ if b.image == "" {
|
|
| 231 |
+ return "", errors.New("No image was generated. Is your Dockerfile empty?")
|
|
| 232 |
+ } |
|
| 233 |
+ |
|
| 234 |
+ if b.options.Squash {
|
|
| 235 |
+ if err := b.squashBuild(); err != nil {
|
|
| 236 |
+ return "", err |
|
| 224 | 237 |
} |
| 225 | 238 |
} |
| 226 | 239 |
|
| 240 |
+ fmt.Fprintf(b.Stdout, "Successfully built %s\n", shortImageID) |
|
| 241 |
+ if err := b.tagImages(repoAndTags); err != nil {
|
|
| 242 |
+ return "", err |
|
| 243 |
+ } |
|
| 244 |
+ return b.image, nil |
|
| 245 |
+} |
|
| 246 |
+ |
|
| 247 |
+func (b *Builder) dispatchDockerfileWithCancellation(dockerfile *parser.Node) (string, error) {
|
|
| 248 |
+ total := len(dockerfile.Children) |
|
| 249 |
+ var shortImgID string |
|
| 227 | 250 |
for i, n := range dockerfile.Children {
|
| 228 | 251 |
select {
|
| 229 | 252 |
case <-b.clientCtx.Done(): |
| ... | ... |
@@ -253,37 +257,23 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri |
| 253 | 253 |
} |
| 254 | 254 |
|
| 255 | 255 |
if b.options.Target != "" && !b.imageContexts.isCurrentTarget(b.options.Target) {
|
| 256 |
- return "", perrors.Errorf("failed to reach build target %s in Dockerfile", b.options.Target)
|
|
| 256 |
+ return "", errors.Errorf("failed to reach build target %s in Dockerfile", b.options.Target)
|
|
| 257 | 257 |
} |
| 258 | 258 |
|
| 259 |
- b.warnOnUnusedBuildArgs() |
|
| 260 |
- |
|
| 261 |
- if b.image == "" {
|
|
| 262 |
- return "", errors.New("No image was generated. Is your Dockerfile empty?")
|
|
| 263 |
- } |
|
| 259 |
+ return shortImgID, nil |
|
| 260 |
+} |
|
| 264 | 261 |
|
| 265 |
- if b.options.Squash {
|
|
| 266 |
- var fromID string |
|
| 267 |
- if b.from != nil {
|
|
| 268 |
- fromID = b.from.ImageID() |
|
| 269 |
- } |
|
| 270 |
- b.image, err = b.docker.SquashImage(b.image, fromID) |
|
| 271 |
- if err != nil {
|
|
| 272 |
- return "", perrors.Wrap(err, "error squashing image") |
|
| 273 |
- } |
|
| 262 |
+func (b *Builder) squashBuild() error {
|
|
| 263 |
+ var fromID string |
|
| 264 |
+ var err error |
|
| 265 |
+ if b.from != nil {
|
|
| 266 |
+ fromID = b.from.ImageID() |
|
| 274 | 267 |
} |
| 275 |
- |
|
| 276 |
- fmt.Fprintf(b.Stdout, "Successfully built %s\n", shortImgID) |
|
| 277 |
- |
|
| 278 |
- imageID := image.ID(b.image) |
|
| 279 |
- for _, rt := range repoAndTags {
|
|
| 280 |
- if err := b.docker.TagImageWithReference(imageID, rt); err != nil {
|
|
| 281 |
- return "", err |
|
| 282 |
- } |
|
| 283 |
- fmt.Fprintf(b.Stdout, "Successfully tagged %s\n", reference.FamiliarString(rt)) |
|
| 268 |
+ b.image, err = b.docker.SquashImage(b.image, fromID) |
|
| 269 |
+ if err != nil {
|
|
| 270 |
+ return errors.Wrap(err, "error squashing image") |
|
| 284 | 271 |
} |
| 285 |
- |
|
| 286 |
- return b.image, nil |
|
| 272 |
+ return nil |
|
| 287 | 273 |
} |
| 288 | 274 |
|
| 289 | 275 |
func addNodesForLabelOption(dockerfile *parser.Node, labels map[string]string) {
|
| ... | ... |
@@ -304,16 +294,22 @@ func (b *Builder) warnOnUnusedBuildArgs() {
|
| 304 | 304 |
} |
| 305 | 305 |
} |
| 306 | 306 |
|
| 307 |
+func (b *Builder) tagImages(repoAndTags []reference.Named) error {
|
|
| 308 |
+ imageID := image.ID(b.image) |
|
| 309 |
+ for _, rt := range repoAndTags {
|
|
| 310 |
+ if err := b.docker.TagImageWithReference(imageID, rt); err != nil {
|
|
| 311 |
+ return err |
|
| 312 |
+ } |
|
| 313 |
+ fmt.Fprintf(b.Stdout, "Successfully tagged %s\n", reference.FamiliarString(rt)) |
|
| 314 |
+ } |
|
| 315 |
+ return nil |
|
| 316 |
+} |
|
| 317 |
+ |
|
| 307 | 318 |
// hasFromImage returns true if the builder has processed a `FROM <image>` line |
| 308 | 319 |
func (b *Builder) hasFromImage() bool {
|
| 309 | 320 |
return b.image != "" || b.noBaseImage |
| 310 | 321 |
} |
| 311 | 322 |
|
| 312 |
-// Cancel cancels an ongoing Dockerfile build. |
|
| 313 |
-func (b *Builder) Cancel() {
|
|
| 314 |
- b.cancel() |
|
| 315 |
-} |
|
| 316 |
- |
|
| 317 | 323 |
// BuildFromConfig builds directly from `changes`, treating it as if it were the contents of a Dockerfile |
| 318 | 324 |
// It will: |
| 319 | 325 |
// - Call parse.Parse() to get an AST root for the concatenated Dockerfile entries. |
| ... | ... |
@@ -346,18 +342,31 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con |
| 346 | 346 |
b.Stderr = ioutil.Discard |
| 347 | 347 |
b.disableCommit = true |
| 348 | 348 |
|
| 349 |
- total := len(ast.Children) |
|
| 350 |
- for _, n := range ast.Children {
|
|
| 351 |
- if err := b.checkDispatch(n, false); err != nil {
|
|
| 352 |
- return nil, err |
|
| 349 |
+ if err := checkDispatchDockerfile(ast); err != nil {
|
|
| 350 |
+ return nil, err |
|
| 351 |
+ } |
|
| 352 |
+ |
|
| 353 |
+ if err := dispatchFromDockerfile(b, ast); err != nil {
|
|
| 354 |
+ return nil, err |
|
| 355 |
+ } |
|
| 356 |
+ return b.runConfig, nil |
|
| 357 |
+} |
|
| 358 |
+ |
|
| 359 |
+func checkDispatchDockerfile(dockerfile *parser.Node) error {
|
|
| 360 |
+ for _, n := range dockerfile.Children {
|
|
| 361 |
+ if err := checkDispatch(n); err != nil {
|
|
| 362 |
+ return errors.Wrapf(err, "Dockerfile parse error line %d", n.StartLine) |
|
| 353 | 363 |
} |
| 354 | 364 |
} |
| 365 |
+ return nil |
|
| 366 |
+} |
|
| 355 | 367 |
|
| 368 |
+func dispatchFromDockerfile(b *Builder, ast *parser.Node) error {
|
|
| 369 |
+ total := len(ast.Children) |
|
| 356 | 370 |
for i, n := range ast.Children {
|
| 357 | 371 |
if err := b.dispatch(i, total, n); err != nil {
|
| 358 |
- return nil, err |
|
| 372 |
+ return err |
|
| 359 | 373 |
} |
| 360 | 374 |
} |
| 361 |
- |
|
| 362 |
- return b.runConfig, nil |
|
| 375 |
+ return nil |
|
| 363 | 376 |
} |
| ... | ... |
@@ -16,6 +16,7 @@ import ( |
| 16 | 16 |
"strings" |
| 17 | 17 |
"time" |
| 18 | 18 |
|
| 19 |
+ "bytes" |
|
| 19 | 20 |
"github.com/Sirupsen/logrus" |
| 20 | 21 |
"github.com/docker/docker/api" |
| 21 | 22 |
"github.com/docker/docker/api/types" |
| ... | ... |
@@ -46,45 +47,22 @@ func env(b *Builder, args []string, attributes map[string]bool, original string) |
| 46 | 46 |
return err |
| 47 | 47 |
} |
| 48 | 48 |
|
| 49 |
- // TODO/FIXME/NOT USED |
|
| 50 |
- // Just here to show how to use the builder flags stuff within the |
|
| 51 |
- // context of a builder command. Will remove once we actually add |
|
| 52 |
- // a builder command to something! |
|
| 53 |
- /* |
|
| 54 |
- flBool1 := b.flags.AddBool("bool1", false)
|
|
| 55 |
- flStr1 := b.flags.AddString("str1", "HI")
|
|
| 56 |
- |
|
| 57 |
- if err := b.flags.Parse(); err != nil {
|
|
| 58 |
- return err |
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- fmt.Printf("Bool1:%v\n", flBool1)
|
|
| 62 |
- fmt.Printf("Str1:%v\n", flStr1)
|
|
| 63 |
- */ |
|
| 64 |
- |
|
| 65 |
- commitStr := "ENV" |
|
| 66 |
- |
|
| 67 |
- for j := 0; j < len(args); j++ {
|
|
| 68 |
- // name ==> args[j] |
|
| 69 |
- // value ==> args[j+1] |
|
| 49 |
+ commitMessage := bytes.NewBufferString("ENV")
|
|
| 70 | 50 |
|
| 51 |
+ for j := 0; j < len(args); j += 2 {
|
|
| 71 | 52 |
if len(args[j]) == 0 {
|
| 72 | 53 |
return errBlankCommandNames("ENV")
|
| 73 | 54 |
} |
| 74 |
- newVar := args[j] + "=" + args[j+1] + "" |
|
| 75 |
- commitStr += " " + newVar |
|
| 55 |
+ name := args[j] |
|
| 56 |
+ value := args[j+1] |
|
| 57 |
+ newVar := name + "=" + value |
|
| 58 |
+ commitMessage.WriteString(" " + newVar)
|
|
| 76 | 59 |
|
| 77 | 60 |
gotOne := false |
| 78 | 61 |
for i, envVar := range b.runConfig.Env {
|
| 79 | 62 |
envParts := strings.SplitN(envVar, "=", 2) |
| 80 | 63 |
compareFrom := envParts[0] |
| 81 |
- compareTo := args[j] |
|
| 82 |
- if runtime.GOOS == "windows" {
|
|
| 83 |
- // Case insensitive environment variables on Windows |
|
| 84 |
- compareFrom = strings.ToUpper(compareFrom) |
|
| 85 |
- compareTo = strings.ToUpper(compareTo) |
|
| 86 |
- } |
|
| 87 |
- if compareFrom == compareTo {
|
|
| 64 |
+ if equalEnvKeys(compareFrom, name) {
|
|
| 88 | 65 |
b.runConfig.Env[i] = newVar |
| 89 | 66 |
gotOne = true |
| 90 | 67 |
break |
| ... | ... |
@@ -93,10 +71,9 @@ func env(b *Builder, args []string, attributes map[string]bool, original string) |
| 93 | 93 |
if !gotOne {
|
| 94 | 94 |
b.runConfig.Env = append(b.runConfig.Env, newVar) |
| 95 | 95 |
} |
| 96 |
- j++ |
|
| 97 | 96 |
} |
| 98 | 97 |
|
| 99 |
- return b.commit("", b.runConfig.Cmd, commitStr)
|
|
| 98 |
+ return b.commit("", b.runConfig.Cmd, commitMessage.String())
|
|
| 100 | 99 |
} |
| 101 | 100 |
|
| 102 | 101 |
// MAINTAINER some text <maybe@an.email.address> |
| ... | ... |
@@ -440,6 +417,7 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) |
| 440 | 440 |
return err |
| 441 | 441 |
} |
| 442 | 442 |
|
| 443 |
+ // FIXME: this is duplicated with the defer above in this function (i think?) |
|
| 443 | 444 |
// revert to original config environment and set the command string to |
| 444 | 445 |
// have the build-time env vars in it (if any) so that future cache look-ups |
| 445 | 446 |
// properly match it. |
| ... | ... |
@@ -132,27 +132,34 @@ func TestCommandseBlankNames(t *testing.T) {
|
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 | 134 |
func TestEnv2Variables(t *testing.T) {
|
| 135 |
- variables := []string{"var1", "val1", "var2", "val2"}
|
|
| 136 |
- |
|
| 137 |
- bflags := &BFlags{}
|
|
| 138 |
- config := &container.Config{}
|
|
| 135 |
+ b := newBuilderWithMockBackend() |
|
| 136 |
+ b.disableCommit = true |
|
| 139 | 137 |
|
| 140 |
- b := &Builder{flags: bflags, runConfig: config, disableCommit: true}
|
|
| 138 |
+ args := []string{"var1", "val1", "var2", "val2"}
|
|
| 139 |
+ err := env(b, args, nil, "") |
|
| 140 |
+ assert.NilError(t, err) |
|
| 141 | 141 |
|
| 142 |
- if err := env(b, variables, nil, ""); err != nil {
|
|
| 143 |
- t.Fatalf("Error when executing env: %s", err.Error())
|
|
| 142 |
+ expected := []string{
|
|
| 143 |
+ fmt.Sprintf("%s=%s", args[0], args[1]),
|
|
| 144 |
+ fmt.Sprintf("%s=%s", args[2], args[3]),
|
|
| 144 | 145 |
} |
| 146 |
+ assert.DeepEqual(t, b.runConfig.Env, expected) |
|
| 147 |
+} |
|
| 145 | 148 |
|
| 146 |
- expectedVar1 := fmt.Sprintf("%s=%s", variables[0], variables[1])
|
|
| 147 |
- expectedVar2 := fmt.Sprintf("%s=%s", variables[2], variables[3])
|
|
| 149 |
+func TestEnvValueWithExistingRunConfigEnv(t *testing.T) {
|
|
| 150 |
+ b := newBuilderWithMockBackend() |
|
| 151 |
+ b.disableCommit = true |
|
| 152 |
+ b.runConfig.Env = []string{"var1=old", "var2=fromenv"}
|
|
| 148 | 153 |
|
| 149 |
- if b.runConfig.Env[0] != expectedVar1 {
|
|
| 150 |
- t.Fatalf("Wrong env output for first variable. Got: %s. Should be: %s", b.runConfig.Env[0], expectedVar1)
|
|
| 151 |
- } |
|
| 154 |
+ args := []string{"var1", "val1"}
|
|
| 155 |
+ err := env(b, args, nil, "") |
|
| 156 |
+ assert.NilError(t, err) |
|
| 152 | 157 |
|
| 153 |
- if b.runConfig.Env[1] != expectedVar2 {
|
|
| 154 |
- t.Fatalf("Wrong env output for second variable. Got: %s, Should be: %s", b.runConfig.Env[1], expectedVar2)
|
|
| 158 |
+ expected := []string{
|
|
| 159 |
+ fmt.Sprintf("%s=%s", args[0], args[1]),
|
|
| 160 |
+ "var2=fromenv", |
|
| 155 | 161 |
} |
| 162 |
+ assert.DeepEqual(t, b.runConfig.Env, expected) |
|
| 156 | 163 |
} |
| 157 | 164 |
|
| 158 | 165 |
func TestMaintainer(t *testing.T) {
|
| ... | ... |
@@ -26,3 +26,9 @@ func normaliseWorkdir(current string, requested string) (string, error) {
|
| 26 | 26 |
func errNotJSON(command, _ string) error {
|
| 27 | 27 |
return fmt.Errorf("%s requires the arguments to be in JSON form", command)
|
| 28 | 28 |
} |
| 29 |
+ |
|
| 30 |
+// equalEnvKeys compare two strings and returns true if they are equal. On |
|
| 31 |
+// Windows this comparison is case insensitive. |
|
| 32 |
+func equalEnvKeys(from, to string) bool {
|
|
| 33 |
+ return from == to |
|
| 34 |
+} |
| ... | ... |
@@ -85,3 +85,9 @@ func errNotJSON(command, original string) error {
|
| 85 | 85 |
} |
| 86 | 86 |
return fmt.Errorf("%s requires the arguments to be in JSON form%s", command, extra)
|
| 87 | 87 |
} |
| 88 |
+ |
|
| 89 |
+// equalEnvKeys compare two strings and returns true if they are equal. On |
|
| 90 |
+// Windows this comparison is case insensitive. |
|
| 91 |
+func equalEnvKeys(from, to string) bool {
|
|
| 92 |
+ return strings.ToUpper(from) == strings.ToUpper(to) |
|
| 93 |
+} |
| ... | ... |
@@ -20,13 +20,13 @@ |
| 20 | 20 |
package dockerfile |
| 21 | 21 |
|
| 22 | 22 |
import ( |
| 23 |
- "errors" |
|
| 24 | 23 |
"fmt" |
| 25 | 24 |
"strings" |
| 26 | 25 |
|
| 27 | 26 |
"github.com/docker/docker/builder/dockerfile/command" |
| 28 | 27 |
"github.com/docker/docker/builder/dockerfile/parser" |
| 29 |
- runconfigopts "github.com/docker/docker/runconfig/opts" |
|
| 28 |
+ "github.com/docker/docker/runconfig/opts" |
|
| 29 |
+ "github.com/pkg/errors" |
|
| 30 | 30 |
) |
| 31 | 31 |
|
| 32 | 32 |
// Environment variable interpolation will happen on these statements only. |
| ... | ... |
@@ -187,7 +187,7 @@ func (b *Builder) evaluateEnv(cmd string, str string, envs []string) ([]string, |
| 187 | 187 |
// args that are not overriden by runConfig environment variables. |
| 188 | 188 |
func (b *Builder) buildArgsWithoutConfigEnv() []string {
|
| 189 | 189 |
envs := []string{}
|
| 190 |
- configEnv := runconfigopts.ConvertKVStringsToMap(b.runConfig.Env) |
|
| 190 |
+ configEnv := b.runConfigEnvMapping() |
|
| 191 | 191 |
|
| 192 | 192 |
for key, val := range b.buildArgs.GetAllAllowed() {
|
| 193 | 193 |
if _, ok := configEnv[key]; !ok {
|
| ... | ... |
@@ -197,13 +197,16 @@ func (b *Builder) buildArgsWithoutConfigEnv() []string {
|
| 197 | 197 |
return envs |
| 198 | 198 |
} |
| 199 | 199 |
|
| 200 |
+func (b *Builder) runConfigEnvMapping() map[string]string {
|
|
| 201 |
+ return opts.ConvertKVStringsToMap(b.runConfig.Env) |
|
| 202 |
+} |
|
| 203 |
+ |
|
| 200 | 204 |
// checkDispatch does a simple check for syntax errors of the Dockerfile. |
| 201 | 205 |
// Because some of the instructions can only be validated through runtime, |
| 202 | 206 |
// arg, env, etc., this syntax check will not be complete and could not replace |
| 203 | 207 |
// the runtime check. Instead, this function is only a helper that allows |
| 204 | 208 |
// user to find out the obvious error in Dockerfile earlier on. |
| 205 |
-// onbuild bool: indicate if instruction XXX is part of `ONBUILD XXX` trigger |
|
| 206 |
-func (b *Builder) checkDispatch(ast *parser.Node, onbuild bool) error {
|
|
| 209 |
+func checkDispatch(ast *parser.Node) error {
|
|
| 207 | 210 |
cmd := ast.Value |
| 208 | 211 |
upperCasedCmd := strings.ToUpper(cmd) |
| 209 | 212 |
|
| ... | ... |
@@ -221,19 +224,9 @@ func (b *Builder) checkDispatch(ast *parser.Node, onbuild bool) error {
|
| 221 | 221 |
} |
| 222 | 222 |
} |
| 223 | 223 |
|
| 224 |
- // The instruction is part of ONBUILD trigger (not the instruction itself) |
|
| 225 |
- if onbuild {
|
|
| 226 |
- switch upperCasedCmd {
|
|
| 227 |
- case "ONBUILD": |
|
| 228 |
- return errors.New("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
|
| 229 |
- case "MAINTAINER", "FROM": |
|
| 230 |
- return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", upperCasedCmd)
|
|
| 231 |
- } |
|
| 232 |
- } |
|
| 233 |
- |
|
| 234 | 224 |
if _, ok := evaluateTable[cmd]; ok {
|
| 235 | 225 |
return nil |
| 236 | 226 |
} |
| 237 | 227 |
|
| 238 |
- return fmt.Errorf("Unknown instruction: %s", upperCasedCmd)
|
|
| 228 |
+ return errors.Errorf("unknown instruction: %s", upperCasedCmd)
|
|
| 239 | 229 |
} |
| ... | ... |
@@ -35,7 +35,6 @@ import ( |
| 35 | 35 |
"github.com/docker/docker/pkg/system" |
| 36 | 36 |
"github.com/docker/docker/pkg/tarsum" |
| 37 | 37 |
"github.com/docker/docker/pkg/urlutil" |
| 38 |
- "github.com/docker/docker/runconfig/opts" |
|
| 39 | 38 |
"github.com/pkg/errors" |
| 40 | 39 |
) |
| 41 | 40 |
|
| ... | ... |
@@ -433,11 +432,7 @@ func (b *Builder) processImageFrom(img builder.Image) error {
|
| 433 | 433 |
// Check to see if we have a default PATH, note that windows won't |
| 434 | 434 |
// have one as it's set by HCS |
| 435 | 435 |
if system.DefaultPathEnv != "" {
|
| 436 |
- // Convert the slice of strings that represent the current list |
|
| 437 |
- // of env vars into a map so we can see if PATH is already set. |
|
| 438 |
- // If it's not set then go ahead and give it our default value |
|
| 439 |
- configEnv := opts.ConvertKVStringsToMap(b.runConfig.Env) |
|
| 440 |
- if _, ok := configEnv["PATH"]; !ok {
|
|
| 436 |
+ if _, ok := b.runConfigEnvMapping()["PATH"]; !ok {
|
|
| 441 | 437 |
b.runConfig.Env = append(b.runConfig.Env, |
| 442 | 438 |
"PATH="+system.DefaultPathEnv) |
| 443 | 439 |
} |
| ... | ... |
@@ -472,19 +467,24 @@ func (b *Builder) processImageFrom(img builder.Image) error {
|
| 472 | 472 |
return err |
| 473 | 473 |
} |
| 474 | 474 |
|
| 475 |
- total := len(ast.Children) |
|
| 476 | 475 |
for _, n := range ast.Children {
|
| 477 |
- if err := b.checkDispatch(n, true); err != nil {
|
|
| 476 |
+ if err := checkDispatch(n); err != nil {
|
|
| 478 | 477 |
return err |
| 479 | 478 |
} |
| 480 |
- } |
|
| 481 |
- for i, n := range ast.Children {
|
|
| 482 |
- if err := b.dispatch(i, total, n); err != nil {
|
|
| 483 |
- return err |
|
| 479 |
+ |
|
| 480 |
+ upperCasedCmd := strings.ToUpper(n.Value) |
|
| 481 |
+ switch upperCasedCmd {
|
|
| 482 |
+ case "ONBUILD": |
|
| 483 |
+ return errors.New("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
|
| 484 |
+ case "MAINTAINER", "FROM": |
|
| 485 |
+ return errors.Errorf("%s isn't allowed as an ONBUILD trigger", upperCasedCmd)
|
|
| 484 | 486 |
} |
| 485 | 487 |
} |
| 486 |
- } |
|
| 487 | 488 |
|
| 489 |
+ if err := dispatchFromDockerfile(b, ast); err != nil {
|
|
| 490 |
+ return err |
|
| 491 |
+ } |
|
| 492 |
+ } |
|
| 488 | 493 |
return nil |
| 489 | 494 |
} |
| 490 | 495 |
|
| ... | ... |
@@ -649,8 +649,8 @@ func (b *Builder) clearTmp() {
|
| 649 | 649 |
} |
| 650 | 650 |
} |
| 651 | 651 |
|
| 652 |
-// readDockerfile reads a Dockerfile from the current context. |
|
| 653 |
-func (b *Builder) readDockerfile() (*parser.Node, error) {
|
|
| 652 |
+// readAndParseDockerfile reads a Dockerfile from the current context. |
|
| 653 |
+func (b *Builder) readAndParseDockerfile() (*parser.Node, error) {
|
|
| 654 | 654 |
// If no -f was specified then look for 'Dockerfile'. If we can't find |
| 655 | 655 |
// that then look for 'dockerfile'. If neither are found then default |
| 656 | 656 |
// back to 'Dockerfile' and use that in the error message. |
| ... | ... |
@@ -8,7 +8,6 @@ package dockerfile |
| 8 | 8 |
|
| 9 | 9 |
import ( |
| 10 | 10 |
"fmt" |
| 11 |
- "runtime" |
|
| 12 | 11 |
"strings" |
| 13 | 12 |
"text/scanner" |
| 14 | 13 |
"unicode" |
| ... | ... |
@@ -296,17 +295,10 @@ func (sw *shellWord) processName() string {
|
| 296 | 296 |
} |
| 297 | 297 |
|
| 298 | 298 |
func (sw *shellWord) getEnv(name string) string {
|
| 299 |
- if runtime.GOOS == "windows" {
|
|
| 300 |
- // Case-insensitive environment variables on Windows |
|
| 301 |
- name = strings.ToUpper(name) |
|
| 302 |
- } |
|
| 303 | 299 |
for _, env := range sw.envs {
|
| 304 | 300 |
i := strings.Index(env, "=") |
| 305 | 301 |
if i < 0 {
|
| 306 |
- if runtime.GOOS == "windows" {
|
|
| 307 |
- env = strings.ToUpper(env) |
|
| 308 |
- } |
|
| 309 |
- if name == env {
|
|
| 302 |
+ if equalEnvKeys(name, env) {
|
|
| 310 | 303 |
// Should probably never get here, but just in case treat |
| 311 | 304 |
// it like "var" and "var=" are the same |
| 312 | 305 |
return "" |
| ... | ... |
@@ -314,10 +306,7 @@ func (sw *shellWord) getEnv(name string) string {
|
| 314 | 314 |
continue |
| 315 | 315 |
} |
| 316 | 316 |
compareName := env[:i] |
| 317 |
- if runtime.GOOS == "windows" {
|
|
| 318 |
- compareName = strings.ToUpper(compareName) |
|
| 319 |
- } |
|
| 320 |
- if name != compareName {
|
|
| 317 |
+ if !equalEnvKeys(name, compareName) {
|
|
| 321 | 318 |
continue |
| 322 | 319 |
} |
| 323 | 320 |
return env[i+1:] |
| ... | ... |
@@ -6133,7 +6133,7 @@ func (s *DockerSuite) TestBuildCopyFromPreviousFromWindows(c *check.C) {
|
| 6133 | 6133 |
func (s *DockerSuite) TestBuildCopyFromForbidWindowsSystemPaths(c *check.C) {
|
| 6134 | 6134 |
testRequires(c, DaemonIsWindows) |
| 6135 | 6135 |
dockerfile := ` |
| 6136 |
- FROM ` + testEnv.MinimalBaseImage() + ` |
|
| 6136 |
+ FROM ` + testEnv.MinimalBaseImage() + ` |
|
| 6137 | 6137 |
FROM ` + testEnv.MinimalBaseImage() + ` |
| 6138 | 6138 |
COPY --from=0 %s c:\\oscopy |
| 6139 | 6139 |
` |
| ... | ... |
@@ -6150,7 +6150,7 @@ func (s *DockerSuite) TestBuildCopyFromForbidWindowsSystemPaths(c *check.C) {
|
| 6150 | 6150 |
func (s *DockerSuite) TestBuildCopyFromForbidWindowsRelativePaths(c *check.C) {
|
| 6151 | 6151 |
testRequires(c, DaemonIsWindows) |
| 6152 | 6152 |
dockerfile := ` |
| 6153 |
- FROM ` + testEnv.MinimalBaseImage() + ` |
|
| 6153 |
+ FROM ` + testEnv.MinimalBaseImage() + ` |
|
| 6154 | 6154 |
FROM ` + testEnv.MinimalBaseImage() + ` |
| 6155 | 6155 |
COPY --from=0 %s c:\\oscopy |
| 6156 | 6156 |
` |
| ... | ... |
@@ -6169,7 +6169,7 @@ func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) {
|
| 6169 | 6169 |
testRequires(c, DaemonIsWindows) |
| 6170 | 6170 |
dockerfile := ` |
| 6171 | 6171 |
FROM ` + testEnv.MinimalBaseImage() + ` |
| 6172 |
- COPY foo / |
|
| 6172 |
+ COPY foo / |
|
| 6173 | 6173 |
FROM ` + testEnv.MinimalBaseImage() + ` |
| 6174 | 6174 |
COPY --from=0 c:\\fOo c:\\copied |
| 6175 | 6175 |
RUN type c:\\copied |
| ... | ... |
@@ -6322,23 +6322,23 @@ func (s *DockerSuite) TestBuildLineErrorOnBuild(c *check.C) {
|
| 6322 | 6322 |
} |
| 6323 | 6323 |
|
| 6324 | 6324 |
// FIXME(vdemeester) should be a unit test |
| 6325 |
-func (s *DockerSuite) TestBuildLineErrorUknownInstruction(c *check.C) {
|
|
| 6325 |
+func (s *DockerSuite) TestBuildLineErrorUnknownInstruction(c *check.C) {
|
|
| 6326 | 6326 |
name := "test_build_line_error_unknown_instruction" |
| 6327 |
- buildImage(name, build.WithDockerfile(`FROM busybox |
|
| 6327 |
+ cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox |
|
| 6328 | 6328 |
RUN echo hello world |
| 6329 | 6329 |
NOINSTRUCTION echo ba |
| 6330 | 6330 |
RUN echo hello |
| 6331 | 6331 |
ERROR |
| 6332 | 6332 |
`)).Assert(c, icmd.Expected{
|
| 6333 | 6333 |
ExitCode: 1, |
| 6334 |
- Err: "Dockerfile parse error line 3: Unknown instruction: NOINSTRUCTION", |
|
| 6334 |
+ Err: "Dockerfile parse error line 3: unknown instruction: NOINSTRUCTION", |
|
| 6335 | 6335 |
}) |
| 6336 | 6336 |
} |
| 6337 | 6337 |
|
| 6338 | 6338 |
// FIXME(vdemeester) should be a unit test |
| 6339 | 6339 |
func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *check.C) {
|
| 6340 | 6340 |
name := "test_build_line_error_with_empty_lines" |
| 6341 |
- buildImage(name, build.WithDockerfile(` |
|
| 6341 |
+ cli.Docker(cli.Build(name), build.WithDockerfile(` |
|
| 6342 | 6342 |
FROM busybox |
| 6343 | 6343 |
|
| 6344 | 6344 |
RUN echo hello world |
| ... | ... |
@@ -6348,21 +6348,21 @@ func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *check.C) {
|
| 6348 | 6348 |
CMD ["/bin/init"] |
| 6349 | 6349 |
`)).Assert(c, icmd.Expected{
|
| 6350 | 6350 |
ExitCode: 1, |
| 6351 |
- Err: "Dockerfile parse error line 6: Unknown instruction: NOINSTRUCTION", |
|
| 6351 |
+ Err: "Dockerfile parse error line 6: unknown instruction: NOINSTRUCTION", |
|
| 6352 | 6352 |
}) |
| 6353 | 6353 |
} |
| 6354 | 6354 |
|
| 6355 | 6355 |
// FIXME(vdemeester) should be a unit test |
| 6356 | 6356 |
func (s *DockerSuite) TestBuildLineErrorWithComments(c *check.C) {
|
| 6357 | 6357 |
name := "test_build_line_error_with_comments" |
| 6358 |
- buildImage(name, build.WithDockerfile(`FROM busybox |
|
| 6358 |
+ cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox |
|
| 6359 | 6359 |
# This will print hello world |
| 6360 | 6360 |
# and then ba |
| 6361 | 6361 |
RUN echo hello world |
| 6362 | 6362 |
NOINSTRUCTION echo ba |
| 6363 | 6363 |
`)).Assert(c, icmd.Expected{
|
| 6364 | 6364 |
ExitCode: 1, |
| 6365 |
- Err: "Dockerfile parse error line 5: Unknown instruction: NOINSTRUCTION", |
|
| 6365 |
+ Err: "Dockerfile parse error line 5: unknown instruction: NOINSTRUCTION", |
|
| 6366 | 6366 |
}) |
| 6367 | 6367 |
} |
| 6368 | 6368 |
|