Add MetaArgs for ARG that occur before the first FROM
Integration test for these cases.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,124 @@ |
| 0 |
+package dockerfile |
|
| 1 |
+ |
|
| 2 |
+// builtinAllowedBuildArgs is list of built-in allowed build args |
|
| 3 |
+// these args are considered transparent and are excluded from the image history. |
|
| 4 |
+// Filtering from history is implemented in dispatchers.go |
|
| 5 |
+var builtinAllowedBuildArgs = map[string]bool{
|
|
| 6 |
+ "HTTP_PROXY": true, |
|
| 7 |
+ "http_proxy": true, |
|
| 8 |
+ "HTTPS_PROXY": true, |
|
| 9 |
+ "https_proxy": true, |
|
| 10 |
+ "FTP_PROXY": true, |
|
| 11 |
+ "ftp_proxy": true, |
|
| 12 |
+ "NO_PROXY": true, |
|
| 13 |
+ "no_proxy": true, |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// buildArgs manages arguments used by the builder |
|
| 17 |
+type buildArgs struct {
|
|
| 18 |
+ // args that are allowed for expansion/substitution and passing to commands in 'run'. |
|
| 19 |
+ allowedBuildArgs map[string]*string |
|
| 20 |
+ // args defined before the first `FROM` in a Dockerfile |
|
| 21 |
+ allowedMetaArgs map[string]*string |
|
| 22 |
+ // args referenced by the Dockerfile |
|
| 23 |
+ referencedArgs map[string]struct{}
|
|
| 24 |
+ // args provided by the user on the command line |
|
| 25 |
+ argsFromOptions map[string]*string |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func newBuildArgs(argsFromOptions map[string]*string) *buildArgs {
|
|
| 29 |
+ return &buildArgs{
|
|
| 30 |
+ allowedBuildArgs: make(map[string]*string), |
|
| 31 |
+ allowedMetaArgs: make(map[string]*string), |
|
| 32 |
+ referencedArgs: make(map[string]struct{}),
|
|
| 33 |
+ argsFromOptions: argsFromOptions, |
|
| 34 |
+ } |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// UnreferencedOptionArgs returns the list of args that were set from options but |
|
| 38 |
+// were never referenced from the Dockerfile |
|
| 39 |
+func (b *buildArgs) UnreferencedOptionArgs() []string {
|
|
| 40 |
+ leftoverArgs := []string{}
|
|
| 41 |
+ for arg := range b.argsFromOptions {
|
|
| 42 |
+ if _, ok := b.referencedArgs[arg]; !ok {
|
|
| 43 |
+ leftoverArgs = append(leftoverArgs, arg) |
|
| 44 |
+ } |
|
| 45 |
+ } |
|
| 46 |
+ return leftoverArgs |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+// ResetAllowed clears the list of args that are allowed to be used by a |
|
| 50 |
+// directive |
|
| 51 |
+func (b *buildArgs) ResetAllowed() {
|
|
| 52 |
+ b.allowedBuildArgs = make(map[string]*string) |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+// AddMetaArg adds a new meta arg that can be used by FROM directives |
|
| 56 |
+func (b *buildArgs) AddMetaArg(key string, value *string) {
|
|
| 57 |
+ b.allowedMetaArgs[key] = value |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+// AddArg adds a new arg that can be used by directives |
|
| 61 |
+func (b *buildArgs) AddArg(key string, value *string) {
|
|
| 62 |
+ b.allowedBuildArgs[key] = value |
|
| 63 |
+ b.referencedArgs[key] = struct{}{}
|
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+// IsUnreferencedBuiltin checks if the key is a built-in arg, or if it has been |
|
| 67 |
+// referenced by the Dockerfile. Returns true if the arg is a builtin that has |
|
| 68 |
+// not been referenced in the Dockerfile. |
|
| 69 |
+func (b *buildArgs) IsUnreferencedBuiltin(key string) bool {
|
|
| 70 |
+ _, isBuiltin := builtinAllowedBuildArgs[key] |
|
| 71 |
+ _, isAllowed := b.allowedBuildArgs[key] |
|
| 72 |
+ return isBuiltin && !isAllowed |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// GetAllAllowed returns a mapping with all the allowed args |
|
| 76 |
+func (b *buildArgs) GetAllAllowed() map[string]string {
|
|
| 77 |
+ return b.getAllFromMapping(b.allowedBuildArgs) |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+// GetAllMeta returns a mapping with all the meta meta args |
|
| 81 |
+func (b *buildArgs) GetAllMeta() map[string]string {
|
|
| 82 |
+ return b.getAllFromMapping(b.allowedMetaArgs) |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func (b *buildArgs) getAllFromMapping(source map[string]*string) map[string]string {
|
|
| 86 |
+ m := make(map[string]string) |
|
| 87 |
+ |
|
| 88 |
+ keys := keysFromMaps(source, builtinAllowedBuildArgs) |
|
| 89 |
+ for _, key := range keys {
|
|
| 90 |
+ v, ok := b.getBuildArg(key, source) |
|
| 91 |
+ if ok {
|
|
| 92 |
+ m[key] = v |
|
| 93 |
+ } |
|
| 94 |
+ } |
|
| 95 |
+ return m |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+func (b *buildArgs) getBuildArg(key string, mapping map[string]*string) (string, bool) {
|
|
| 99 |
+ defaultValue, exists := mapping[key] |
|
| 100 |
+ // Return override from options if one is defined |
|
| 101 |
+ if v, ok := b.argsFromOptions[key]; ok && v != nil {
|
|
| 102 |
+ return *v, ok |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ if defaultValue == nil {
|
|
| 106 |
+ if v, ok := b.allowedMetaArgs[key]; ok && v != nil {
|
|
| 107 |
+ return *v, ok |
|
| 108 |
+ } |
|
| 109 |
+ return "", false |
|
| 110 |
+ } |
|
| 111 |
+ return *defaultValue, exists |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+func keysFromMaps(source map[string]*string, builtin map[string]bool) []string {
|
|
| 115 |
+ keys := []string{}
|
|
| 116 |
+ for key := range source {
|
|
| 117 |
+ keys = append(keys, key) |
|
| 118 |
+ } |
|
| 119 |
+ for key := range builtin {
|
|
| 120 |
+ keys = append(keys, key) |
|
| 121 |
+ } |
|
| 122 |
+ return keys |
|
| 123 |
+} |
| 0 | 124 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,63 @@ |
| 0 |
+package dockerfile |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/docker/docker/pkg/testutil/assert" |
|
| 4 |
+ "testing" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+func strPtr(source string) *string {
|
|
| 8 |
+ return &source |
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+func TestGetAllAllowed(t *testing.T) {
|
|
| 12 |
+ buildArgs := newBuildArgs(map[string]*string{
|
|
| 13 |
+ "ArgNotUsedInDockerfile": strPtr("fromopt1"),
|
|
| 14 |
+ "ArgOverriddenByOptions": strPtr("fromopt2"),
|
|
| 15 |
+ "ArgNoDefaultInDockerfileFromOptions": strPtr("fromopt3"),
|
|
| 16 |
+ "HTTP_PROXY": strPtr("theproxy"),
|
|
| 17 |
+ }) |
|
| 18 |
+ |
|
| 19 |
+ buildArgs.AddMetaArg("ArgFromMeta", strPtr("frommeta1"))
|
|
| 20 |
+ buildArgs.AddMetaArg("ArgFromMetaOverriden", strPtr("frommeta2"))
|
|
| 21 |
+ buildArgs.AddMetaArg("ArgFromMetaNotUsed", strPtr("frommeta3"))
|
|
| 22 |
+ |
|
| 23 |
+ buildArgs.AddArg("ArgOverriddenByOptions", strPtr("fromdockerfile2"))
|
|
| 24 |
+ buildArgs.AddArg("ArgWithDefaultInDockerfile", strPtr("fromdockerfile1"))
|
|
| 25 |
+ buildArgs.AddArg("ArgNoDefaultInDockerfile", nil)
|
|
| 26 |
+ buildArgs.AddArg("ArgNoDefaultInDockerfileFromOptions", nil)
|
|
| 27 |
+ buildArgs.AddArg("ArgFromMeta", nil)
|
|
| 28 |
+ buildArgs.AddArg("ArgFromMetaOverriden", strPtr("fromdockerfile3"))
|
|
| 29 |
+ |
|
| 30 |
+ all := buildArgs.GetAllAllowed() |
|
| 31 |
+ expected := map[string]string{
|
|
| 32 |
+ "HTTP_PROXY": "theproxy", |
|
| 33 |
+ "ArgOverriddenByOptions": "fromopt2", |
|
| 34 |
+ "ArgWithDefaultInDockerfile": "fromdockerfile1", |
|
| 35 |
+ "ArgNoDefaultInDockerfileFromOptions": "fromopt3", |
|
| 36 |
+ "ArgFromMeta": "frommeta1", |
|
| 37 |
+ "ArgFromMetaOverriden": "fromdockerfile3", |
|
| 38 |
+ } |
|
| 39 |
+ assert.DeepEqual(t, all, expected) |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func TestGetAllMeta(t *testing.T) {
|
|
| 43 |
+ buildArgs := newBuildArgs(map[string]*string{
|
|
| 44 |
+ "ArgNotUsedInDockerfile": strPtr("fromopt1"),
|
|
| 45 |
+ "ArgOverriddenByOptions": strPtr("fromopt2"),
|
|
| 46 |
+ "ArgNoDefaultInMetaFromOptions": strPtr("fromopt3"),
|
|
| 47 |
+ "HTTP_PROXY": strPtr("theproxy"),
|
|
| 48 |
+ }) |
|
| 49 |
+ |
|
| 50 |
+ buildArgs.AddMetaArg("ArgFromMeta", strPtr("frommeta1"))
|
|
| 51 |
+ buildArgs.AddMetaArg("ArgOverriddenByOptions", strPtr("frommeta2"))
|
|
| 52 |
+ buildArgs.AddMetaArg("ArgNoDefaultInMetaFromOptions", nil)
|
|
| 53 |
+ |
|
| 54 |
+ all := buildArgs.GetAllMeta() |
|
| 55 |
+ expected := map[string]string{
|
|
| 56 |
+ "HTTP_PROXY": "theproxy", |
|
| 57 |
+ "ArgFromMeta": "frommeta1", |
|
| 58 |
+ "ArgOverriddenByOptions": "fromopt2", |
|
| 59 |
+ "ArgNoDefaultInMetaFromOptions": "fromopt3", |
|
| 60 |
+ } |
|
| 61 |
+ assert.DeepEqual(t, all, expected) |
|
| 62 |
+} |
| ... | ... |
@@ -36,20 +36,6 @@ var validCommitCommands = map[string]bool{
|
| 36 | 36 |
"workdir": true, |
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 |
-// BuiltinAllowedBuildArgs is list of built-in allowed build args |
|
| 40 |
-// these args are considered transparent and are excluded from the image history. |
|
| 41 |
-// Filtering from history is implemented in dispatchers.go |
|
| 42 |
-var BuiltinAllowedBuildArgs = map[string]bool{
|
|
| 43 |
- "HTTP_PROXY": true, |
|
| 44 |
- "http_proxy": true, |
|
| 45 |
- "HTTPS_PROXY": true, |
|
| 46 |
- "https_proxy": true, |
|
| 47 |
- "FTP_PROXY": true, |
|
| 48 |
- "ftp_proxy": true, |
|
| 49 |
- "NO_PROXY": true, |
|
| 50 |
- "no_proxy": true, |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 | 39 |
var defaultLogConfig = container.LogConfig{Type: "none"}
|
| 54 | 40 |
|
| 55 | 41 |
// Builder is a Dockerfile builder |
| ... | ... |
@@ -66,20 +52,19 @@ type Builder struct {
|
| 66 | 66 |
clientCtx context.Context |
| 67 | 67 |
cancel context.CancelFunc |
| 68 | 68 |
|
| 69 |
- dockerfile *parser.Node |
|
| 70 |
- runConfig *container.Config // runconfig for cmd, run, entrypoint etc. |
|
| 71 |
- flags *BFlags |
|
| 72 |
- tmpContainers map[string]struct{}
|
|
| 73 |
- image string // imageID |
|
| 74 |
- imageContexts *imageContexts // helper for storing contexts from builds |
|
| 75 |
- noBaseImage bool // A flag to track the use of `scratch` as the base image |
|
| 76 |
- maintainer string |
|
| 77 |
- cmdSet bool |
|
| 78 |
- disableCommit bool |
|
| 79 |
- cacheBusted bool |
|
| 80 |
- allowedBuildArgs map[string]*string // list of build-time args that are allowed for expansion/substitution and passing to commands in 'run'. |
|
| 81 |
- allBuildArgs map[string]struct{} // list of all build-time args found during parsing of the Dockerfile
|
|
| 82 |
- directive parser.Directive |
|
| 69 |
+ dockerfile *parser.Node |
|
| 70 |
+ runConfig *container.Config // runconfig for cmd, run, entrypoint etc. |
|
| 71 |
+ flags *BFlags |
|
| 72 |
+ tmpContainers map[string]struct{}
|
|
| 73 |
+ image string // imageID |
|
| 74 |
+ imageContexts *imageContexts // helper for storing contexts from builds |
|
| 75 |
+ noBaseImage bool // A flag to track the use of `scratch` as the base image |
|
| 76 |
+ maintainer string |
|
| 77 |
+ cmdSet bool |
|
| 78 |
+ disableCommit bool |
|
| 79 |
+ cacheBusted bool |
|
| 80 |
+ buildArgs *buildArgs |
|
| 81 |
+ directive parser.Directive |
|
| 83 | 82 |
|
| 84 | 83 |
// TODO: remove once docker.Commit can receive a tag |
| 85 | 84 |
id string |
| ... | ... |
@@ -134,18 +119,17 @@ func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, back |
| 134 | 134 |
} |
| 135 | 135 |
ctx, cancel := context.WithCancel(clientCtx) |
| 136 | 136 |
b = &Builder{
|
| 137 |
- clientCtx: ctx, |
|
| 138 |
- cancel: cancel, |
|
| 139 |
- options: config, |
|
| 140 |
- Stdout: os.Stdout, |
|
| 141 |
- Stderr: os.Stderr, |
|
| 142 |
- docker: backend, |
|
| 143 |
- context: buildContext, |
|
| 144 |
- runConfig: new(container.Config), |
|
| 145 |
- tmpContainers: map[string]struct{}{},
|
|
| 146 |
- id: stringid.GenerateNonCryptoID(), |
|
| 147 |
- allowedBuildArgs: make(map[string]*string), |
|
| 148 |
- allBuildArgs: make(map[string]struct{}),
|
|
| 137 |
+ clientCtx: ctx, |
|
| 138 |
+ cancel: cancel, |
|
| 139 |
+ options: config, |
|
| 140 |
+ Stdout: os.Stdout, |
|
| 141 |
+ Stderr: os.Stderr, |
|
| 142 |
+ docker: backend, |
|
| 143 |
+ context: buildContext, |
|
| 144 |
+ runConfig: new(container.Config), |
|
| 145 |
+ tmpContainers: map[string]struct{}{},
|
|
| 146 |
+ id: stringid.GenerateNonCryptoID(), |
|
| 147 |
+ buildArgs: newBuildArgs(config.BuildArgs), |
|
| 149 | 148 |
directive: parser.Directive{
|
| 150 | 149 |
EscapeSeen: false, |
| 151 | 150 |
LookingForDirectives: true, |
| ... | ... |
@@ -316,13 +300,7 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri |
| 316 | 316 |
// check if there are any leftover build-args that were passed but not |
| 317 | 317 |
// consumed during build. Print a warning, if there are any. |
| 318 | 318 |
func (b *Builder) warnOnUnusedBuildArgs() {
|
| 319 |
- leftoverArgs := []string{}
|
|
| 320 |
- for arg := range b.options.BuildArgs {
|
|
| 321 |
- if _, ok := b.allBuildArgs[arg]; !ok {
|
|
| 322 |
- leftoverArgs = append(leftoverArgs, arg) |
|
| 323 |
- } |
|
| 324 |
- } |
|
| 325 |
- |
|
| 319 |
+ leftoverArgs := b.buildArgs.UnreferencedOptionArgs() |
|
| 326 | 320 |
if len(leftoverArgs) > 0 {
|
| 327 | 321 |
fmt.Fprintf(b.Stderr, "[Warning] One or more build-args %v were not consumed\n", leftoverArgs) |
| 328 | 322 |
} |
| ... | ... |
@@ -218,7 +218,11 @@ func from(b *Builder, args []string, attributes map[string]bool, original string |
| 218 | 218 |
return err |
| 219 | 219 |
} |
| 220 | 220 |
|
| 221 |
- substituionArgs := b.buildArgsWithoutConfigEnv() |
|
| 221 |
+ substituionArgs := []string{}
|
|
| 222 |
+ for key, value := range b.buildArgs.GetAllMeta() {
|
|
| 223 |
+ substituionArgs = append(substituionArgs, key+"="+value) |
|
| 224 |
+ } |
|
| 225 |
+ |
|
| 222 | 226 |
name, err := ProcessWord(args[0], substituionArgs, b.directive.EscapeToken) |
| 223 | 227 |
if err != nil {
|
| 224 | 228 |
return err |
| ... | ... |
@@ -256,8 +260,7 @@ func from(b *Builder, args []string, attributes map[string]bool, original string |
| 256 | 256 |
} |
| 257 | 257 |
b.from = image |
| 258 | 258 |
|
| 259 |
- b.allowedBuildArgs = make(map[string]*string) |
|
| 260 |
- |
|
| 259 |
+ b.buildArgs.ResetAllowed() |
|
| 261 | 260 |
return b.processImageFrom(image) |
| 262 | 261 |
} |
| 263 | 262 |
|
| ... | ... |
@@ -442,7 +445,7 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) |
| 442 | 442 |
// properly match it. |
| 443 | 443 |
b.runConfig.Env = env |
| 444 | 444 |
|
| 445 |
- // remove BuiltinAllowedBuildArgs (see: builder.go) from the saveCmd |
|
| 445 |
+ // remove builtinAllowedBuildArgs (see: builder.go) from the saveCmd |
|
| 446 | 446 |
// these args are transparent so resulting image should be the same regardless of the value |
| 447 | 447 |
if len(cmdBuildEnv) > 0 {
|
| 448 | 448 |
saveCmd = config.Cmd |
| ... | ... |
@@ -450,11 +453,8 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) |
| 450 | 450 |
copy(tmpBuildEnv, cmdBuildEnv) |
| 451 | 451 |
for i, env := range tmpBuildEnv {
|
| 452 | 452 |
key := strings.SplitN(env, "=", 2)[0] |
| 453 |
- if _, ok := BuiltinAllowedBuildArgs[key]; ok {
|
|
| 454 |
- // If an built-in arg is explicitly added in the Dockerfile, don't prune it |
|
| 455 |
- if _, ok := b.allowedBuildArgs[key]; !ok {
|
|
| 456 |
- tmpBuildEnv = append(tmpBuildEnv[:i], tmpBuildEnv[i+1:]...) |
|
| 457 |
- } |
|
| 453 |
+ if b.buildArgs.IsUnreferencedBuiltin(key) {
|
|
| 454 |
+ tmpBuildEnv = append(tmpBuildEnv[:i], tmpBuildEnv[i+1:]...) |
|
| 458 | 455 |
} |
| 459 | 456 |
} |
| 460 | 457 |
sort.Strings(tmpBuildEnv) |
| ... | ... |
@@ -785,17 +785,16 @@ func arg(b *Builder, args []string, attributes map[string]bool, original string) |
| 785 | 785 |
name = arg |
| 786 | 786 |
hasDefault = false |
| 787 | 787 |
} |
| 788 |
- // add the arg to allowed list of build-time args from this step on. |
|
| 789 |
- b.allBuildArgs[name] = struct{}{}
|
|
| 790 | 788 |
|
| 791 | 789 |
var value *string |
| 792 | 790 |
if hasDefault {
|
| 793 | 791 |
value = &newValue |
| 794 | 792 |
} |
| 795 |
- b.allowedBuildArgs[name] = value |
|
| 793 |
+ b.buildArgs.AddArg(name, value) |
|
| 796 | 794 |
|
| 797 | 795 |
// Arg before FROM doesn't add a layer |
| 798 | 796 |
if !b.hasFromImage() {
|
| 797 |
+ b.buildArgs.AddMetaArg(name, value) |
|
| 799 | 798 |
return nil |
| 800 | 799 |
} |
| 801 | 800 |
return b.commit("", b.runConfig.Cmd, fmt.Sprintf("ARG %s", arg))
|
| ... | ... |
@@ -193,12 +193,11 @@ func TestLabel(t *testing.T) {
|
| 193 | 193 |
|
| 194 | 194 |
func newBuilderWithMockBackend() *Builder {
|
| 195 | 195 |
b := &Builder{
|
| 196 |
- flags: &BFlags{},
|
|
| 197 |
- runConfig: &container.Config{},
|
|
| 198 |
- options: &types.ImageBuildOptions{},
|
|
| 199 |
- docker: &MockBackend{},
|
|
| 200 |
- allowedBuildArgs: make(map[string]*string), |
|
| 201 |
- allBuildArgs: make(map[string]struct{}),
|
|
| 196 |
+ flags: &BFlags{},
|
|
| 197 |
+ runConfig: &container.Config{},
|
|
| 198 |
+ options: &types.ImageBuildOptions{},
|
|
| 199 |
+ docker: &MockBackend{},
|
|
| 200 |
+ buildArgs: newBuildArgs(make(map[string]*string)), |
|
| 202 | 201 |
} |
| 203 | 202 |
b.imageContexts = &imageContexts{b: b}
|
| 204 | 203 |
return b |
| ... | ... |
@@ -235,8 +234,8 @@ func TestFromWithArg(t *testing.T) {
|
| 235 | 235 |
assert.NilError(t, err) |
| 236 | 236 |
assert.Equal(t, b.image, expected) |
| 237 | 237 |
assert.Equal(t, b.from.ImageID(), expected) |
| 238 |
- assert.NotNil(t, b.allowedBuildArgs) |
|
| 239 |
- assert.Equal(t, len(b.allowedBuildArgs), 0) |
|
| 238 |
+ assert.Equal(t, len(b.buildArgs.GetAllAllowed()), 0) |
|
| 239 |
+ assert.Equal(t, len(b.buildArgs.GetAllMeta()), 1) |
|
| 240 | 240 |
} |
| 241 | 241 |
|
| 242 | 242 |
func TestFromWithUndefinedArg(t *testing.T) {
|
| ... | ... |
@@ -496,29 +495,18 @@ func TestStopSignal(t *testing.T) {
|
| 496 | 496 |
} |
| 497 | 497 |
|
| 498 | 498 |
func TestArg(t *testing.T) {
|
| 499 |
- // This is a bad test that tests implementation details and not at |
|
| 500 |
- // any features of the builder. Replace or remove. |
|
| 501 |
- buildOptions := &types.ImageBuildOptions{BuildArgs: make(map[string]*string)}
|
|
| 502 |
- |
|
| 503 |
- b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true, allowedBuildArgs: make(map[string]*string), allBuildArgs: make(map[string]struct{}), options: buildOptions}
|
|
| 499 |
+ b := newBuilderWithMockBackend() |
|
| 504 | 500 |
|
| 505 | 501 |
argName := "foo" |
| 506 | 502 |
argVal := "bar" |
| 507 | 503 |
argDef := fmt.Sprintf("%s=%s", argName, argVal)
|
| 508 | 504 |
|
| 509 |
- if err := arg(b, []string{argDef}, nil, ""); err != nil {
|
|
| 510 |
- t.Fatalf("Error should be empty, got: %s", err.Error())
|
|
| 511 |
- } |
|
| 512 |
- |
|
| 513 |
- value, ok := b.getBuildArg(argName) |
|
| 514 |
- |
|
| 515 |
- if !ok {
|
|
| 516 |
- t.Fatalf("%s argument should be a build arg", argName)
|
|
| 517 |
- } |
|
| 505 |
+ err := arg(b, []string{argDef}, nil, "")
|
|
| 506 |
+ assert.NilError(t, err) |
|
| 518 | 507 |
|
| 519 |
- if value != "bar" {
|
|
| 520 |
- t.Fatalf("%s argument should have default value 'bar', got %s", argName, value)
|
|
| 521 |
- } |
|
| 508 |
+ expected := map[string]string{argName: argVal}
|
|
| 509 |
+ allowed := b.buildArgs.GetAllAllowed() |
|
| 510 |
+ assert.DeepEqual(t, allowed, expected) |
|
| 522 | 511 |
} |
| 523 | 512 |
|
| 524 | 513 |
func TestShell(t *testing.T) {
|
| ... | ... |
@@ -189,7 +189,7 @@ func (b *Builder) buildArgsWithoutConfigEnv() []string {
|
| 189 | 189 |
envs := []string{}
|
| 190 | 190 |
configEnv := runconfigopts.ConvertKVStringsToMap(b.runConfig.Env) |
| 191 | 191 |
|
| 192 |
- for key, val := range b.getBuildArgs() {
|
|
| 192 |
+ for key, val := range b.buildArgs.GetAllAllowed() {
|
|
| 193 | 193 |
if _, ok := configEnv[key]; !ok {
|
| 194 | 194 |
envs = append(envs, fmt.Sprintf("%s=%s", key, val))
|
| 195 | 195 |
} |
| ... | ... |
@@ -180,9 +180,17 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
|
| 180 | 180 |
} |
| 181 | 181 |
|
| 182 | 182 |
config := &container.Config{}
|
| 183 |
- options := &types.ImageBuildOptions{}
|
|
| 183 |
+ options := &types.ImageBuildOptions{
|
|
| 184 |
+ BuildArgs: make(map[string]*string), |
|
| 185 |
+ } |
|
| 184 | 186 |
|
| 185 |
- b := &Builder{runConfig: config, options: options, Stdout: ioutil.Discard, context: context}
|
|
| 187 |
+ b := &Builder{
|
|
| 188 |
+ runConfig: config, |
|
| 189 |
+ options: options, |
|
| 190 |
+ Stdout: ioutil.Discard, |
|
| 191 |
+ context: context, |
|
| 192 |
+ buildArgs: newBuildArgs(options.BuildArgs), |
|
| 193 |
+ } |
|
| 186 | 194 |
|
| 187 | 195 |
err = b.dispatch(0, len(n.Children), n.Children[0]) |
| 188 | 196 |
|
| ... | ... |
@@ -697,36 +697,3 @@ func (b *Builder) parseDockerfile() error {
|
| 697 | 697 |
|
| 698 | 698 |
return nil |
| 699 | 699 |
} |
| 700 |
- |
|
| 701 |
-func (b *Builder) getBuildArg(arg string) (string, bool) {
|
|
| 702 |
- defaultValue, defined := b.allowedBuildArgs[arg] |
|
| 703 |
- _, builtin := BuiltinAllowedBuildArgs[arg] |
|
| 704 |
- if defined || builtin {
|
|
| 705 |
- if v, ok := b.options.BuildArgs[arg]; ok && v != nil {
|
|
| 706 |
- return *v, ok |
|
| 707 |
- } |
|
| 708 |
- } |
|
| 709 |
- if defaultValue == nil {
|
|
| 710 |
- return "", false |
|
| 711 |
- } |
|
| 712 |
- return *defaultValue, defined |
|
| 713 |
-} |
|
| 714 |
- |
|
| 715 |
-func (b *Builder) getBuildArgs() map[string]string {
|
|
| 716 |
- m := make(map[string]string) |
|
| 717 |
- for arg := range b.options.BuildArgs {
|
|
| 718 |
- v, ok := b.getBuildArg(arg) |
|
| 719 |
- if ok {
|
|
| 720 |
- m[arg] = v |
|
| 721 |
- } |
|
| 722 |
- } |
|
| 723 |
- for arg := range b.allowedBuildArgs {
|
|
| 724 |
- if _, ok := m[arg]; !ok {
|
|
| 725 |
- v, ok := b.getBuildArg(arg) |
|
| 726 |
- if ok {
|
|
| 727 |
- m[arg] = v |
|
| 728 |
- } |
|
| 729 |
- } |
|
| 730 |
- } |
|
| 731 |
- return m |
|
| 732 |
-} |
| ... | ... |
@@ -4728,7 +4728,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgDefintionWithNoEnvInjection(c *check. |
| 4728 | 4728 |
ARG %s |
| 4729 | 4729 |
RUN env`, envKey) |
| 4730 | 4730 |
|
| 4731 |
- result := buildImage(imgName, build.WithDockerfile(dockerfile)) |
|
| 4731 |
+ result := cli.BuildCmd(c, imgName, build.WithDockerfile(dockerfile)) |
|
| 4732 | 4732 |
result.Assert(c, icmd.Success) |
| 4733 | 4733 |
if strings.Count(result.Combined(), envKey) != 1 {
|
| 4734 | 4734 |
c.Fatalf("unexpected number of occurrences of the arg in output: %q expected: 1", result.Combined())
|
| ... | ... |
@@ -4745,29 +4745,45 @@ func (s *DockerSuite) TestBuildBuildTimeArgMultipleFrom(c *check.C) {
|
| 4745 | 4745 |
ARG bar=def |
| 4746 | 4746 |
RUN env > /out` |
| 4747 | 4747 |
|
| 4748 |
- result := buildImage(imgName, build.WithDockerfile(dockerfile)) |
|
| 4748 |
+ result := cli.BuildCmd(c, imgName, build.WithDockerfile(dockerfile)) |
|
| 4749 | 4749 |
result.Assert(c, icmd.Success) |
| 4750 | 4750 |
|
| 4751 |
- result = icmd.RunCmd(icmd.Cmd{
|
|
| 4752 |
- Command: []string{dockerBinary, "images", "-q", "-f", "label=multifromtest=1"},
|
|
| 4753 |
- }) |
|
| 4754 |
- result.Assert(c, icmd.Success) |
|
| 4751 |
+ result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1") |
|
| 4755 | 4752 |
parentID := strings.TrimSpace(result.Stdout()) |
| 4756 | 4753 |
|
| 4757 |
- result = icmd.RunCmd(icmd.Cmd{
|
|
| 4758 |
- Command: []string{dockerBinary, "run", "--rm", parentID, "cat", "/out"},
|
|
| 4759 |
- }) |
|
| 4760 |
- result.Assert(c, icmd.Success) |
|
| 4754 |
+ result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out") |
|
| 4761 | 4755 |
c.Assert(result.Stdout(), checker.Contains, "foo=abc") |
| 4762 | 4756 |
|
| 4763 |
- result = icmd.RunCmd(icmd.Cmd{
|
|
| 4764 |
- Command: []string{dockerBinary, "run", "--rm", imgName, "cat", "/out"},
|
|
| 4765 |
- }) |
|
| 4766 |
- result.Assert(c, icmd.Success) |
|
| 4757 |
+ result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") |
|
| 4767 | 4758 |
c.Assert(result.Stdout(), checker.Not(checker.Contains), "foo") |
| 4768 | 4759 |
c.Assert(result.Stdout(), checker.Contains, "bar=def") |
| 4769 | 4760 |
} |
| 4770 | 4761 |
|
| 4762 |
+func (s *DockerSuite) TestBuildBuildTimeFromArgMultipleFrom(c *check.C) {
|
|
| 4763 |
+ imgName := "multifrombldargtest" |
|
| 4764 |
+ dockerfile := `ARG tag=nosuchtag |
|
| 4765 |
+ FROM busybox:${tag}
|
|
| 4766 |
+ LABEL multifromtest=1 |
|
| 4767 |
+ RUN env > /out |
|
| 4768 |
+ FROM busybox:${tag}
|
|
| 4769 |
+ ARG tag |
|
| 4770 |
+ RUN env > /out` |
|
| 4771 |
+ |
|
| 4772 |
+ result := cli.BuildCmd(c, imgName, |
|
| 4773 |
+ build.WithDockerfile(dockerfile), |
|
| 4774 |
+ cli.WithFlags("--build-arg", fmt.Sprintf("tag=latest")))
|
|
| 4775 |
+ result.Assert(c, icmd.Success) |
|
| 4776 |
+ |
|
| 4777 |
+ result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1") |
|
| 4778 |
+ parentID := strings.TrimSpace(result.Stdout()) |
|
| 4779 |
+ |
|
| 4780 |
+ result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out") |
|
| 4781 |
+ c.Assert(result.Stdout(), checker.Not(checker.Contains), "tag") |
|
| 4782 |
+ |
|
| 4783 |
+ result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") |
|
| 4784 |
+ c.Assert(result.Stdout(), checker.Contains, "tag=latest") |
|
| 4785 |
+} |
|
| 4786 |
+ |
|
| 4771 | 4787 |
func (s *DockerSuite) TestBuildBuildTimeUnusedArgMultipleFrom(c *check.C) {
|
| 4772 | 4788 |
imgName := "multifromunusedarg" |
| 4773 | 4789 |
dockerfile := `FROM busybox |
| ... | ... |
@@ -4776,16 +4792,14 @@ func (s *DockerSuite) TestBuildBuildTimeUnusedArgMultipleFrom(c *check.C) {
|
| 4776 | 4776 |
ARG bar |
| 4777 | 4777 |
RUN env > /out` |
| 4778 | 4778 |
|
| 4779 |
- result := buildImage(imgName, build.WithDockerfile(dockerfile), cli.WithFlags( |
|
| 4780 |
- "--build-arg", fmt.Sprintf("baz=abc")))
|
|
| 4779 |
+ result := cli.BuildCmd(c, imgName, |
|
| 4780 |
+ build.WithDockerfile(dockerfile), |
|
| 4781 |
+ cli.WithFlags("--build-arg", fmt.Sprintf("baz=abc")))
|
|
| 4781 | 4782 |
result.Assert(c, icmd.Success) |
| 4782 | 4783 |
c.Assert(result.Combined(), checker.Contains, "[Warning]") |
| 4783 | 4784 |
c.Assert(result.Combined(), checker.Contains, "[baz] were not consumed") |
| 4784 | 4785 |
|
| 4785 |
- result = icmd.RunCmd(icmd.Cmd{
|
|
| 4786 |
- Command: []string{dockerBinary, "run", "--rm", imgName, "cat", "/out"},
|
|
| 4787 |
- }) |
|
| 4788 |
- result.Assert(c, icmd.Success) |
|
| 4786 |
+ result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") |
|
| 4789 | 4787 |
c.Assert(result.Stdout(), checker.Not(checker.Contains), "bar") |
| 4790 | 4788 |
c.Assert(result.Stdout(), checker.Not(checker.Contains), "baz") |
| 4791 | 4789 |
} |