github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/cache/hash_test.go (about)

     1  /*
     2  Copyright 2019 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cache
    18  
    19  import (
    20  	"context"
    21  	"os"
    22  	"testing"
    23  
    24  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
    25  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
    26  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
    27  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
    29  	"github.com/GoogleContainerTools/skaffold/testutil"
    30  )
    31  
    32  func stubDependencyLister(dependencies []string) DependencyLister {
    33  	return func(context.Context, *latest.Artifact) ([]string, error) {
    34  		return dependencies, nil
    35  	}
    36  }
    37  
    38  var mockCacheHasher = func(s string) (string, error) {
    39  	if s == "not-found" {
    40  		return "", os.ErrNotExist
    41  	}
    42  	return s, nil
    43  }
    44  
    45  var fakeArtifactConfig = func(a *latest.Artifact) (string, error) {
    46  	if a.ArtifactType.DockerArtifact != nil {
    47  		return "docker/target=" + a.ArtifactType.DockerArtifact.Target, nil
    48  	}
    49  	return "", nil
    50  }
    51  
    52  const Dockerfile = "Dockerfile"
    53  
    54  func TestGetHashForArtifact(t *testing.T) {
    55  	tests := []struct {
    56  		description  string
    57  		dependencies []string
    58  		artifact     *latest.Artifact
    59  		mode         config.RunMode
    60  		platforms    platform.Resolver
    61  		expected     string
    62  	}{
    63  		{
    64  			description:  "hash for artifact",
    65  			dependencies: []string{"a", "b"},
    66  			artifact:     &latest.Artifact{},
    67  			mode:         config.RunModes.Dev,
    68  			expected:     "d99ab295a682897269b4db0fe7c136ea1ecd542150fa224ee03155b4e3e995d9",
    69  		},
    70  		{
    71  			description:  "ignore file not found",
    72  			dependencies: []string{"a", "b", "not-found"},
    73  			artifact:     &latest.Artifact{},
    74  			mode:         config.RunModes.Dev,
    75  			expected:     "d99ab295a682897269b4db0fe7c136ea1ecd542150fa224ee03155b4e3e995d9",
    76  		},
    77  		{
    78  			description:  "dependencies in different orders",
    79  			dependencies: []string{"b", "a"},
    80  			artifact:     &latest.Artifact{},
    81  			mode:         config.RunModes.Dev,
    82  			expected:     "d99ab295a682897269b4db0fe7c136ea1ecd542150fa224ee03155b4e3e995d9",
    83  		},
    84  		{
    85  			description: "no dependencies",
    86  			artifact:    &latest.Artifact{},
    87  			mode:        config.RunModes.Dev,
    88  			expected:    "7c077ca2308714493d07163e1033c4282bd869ff6d477b3e77408587f95e2930",
    89  		},
    90  		{
    91  			description: "docker target",
    92  			artifact: &latest.Artifact{
    93  				ArtifactType: latest.ArtifactType{
    94  					DockerArtifact: &latest.DockerArtifact{
    95  						Target: "target",
    96  					},
    97  				},
    98  			},
    99  			mode:     config.RunModes.Dev,
   100  			expected: "f947b5aad32734914aa2dea0ec95bceff257037e6c2a529007183c3f21547eae",
   101  		},
   102  		{
   103  			description: "different docker target",
   104  			artifact: &latest.Artifact{
   105  				ArtifactType: latest.ArtifactType{
   106  					DockerArtifact: &latest.DockerArtifact{
   107  						Target: "other",
   108  					},
   109  				},
   110  			},
   111  			mode:     config.RunModes.Dev,
   112  			expected: "09b366c764d0e39f942283cc081d5522b9dde52e725376661808054e3ed0177f",
   113  		},
   114  		{
   115  			description:  "build args",
   116  			dependencies: []string{"a", "b"},
   117  			artifact: &latest.Artifact{
   118  				ArtifactType: latest.ArtifactType{
   119  					DockerArtifact: &latest.DockerArtifact{
   120  						BuildArgs: map[string]*string{
   121  							"key": util.StringPtr("value"),
   122  						},
   123  					},
   124  				},
   125  			},
   126  			mode:     config.RunModes.Dev,
   127  			expected: "f3f710a4ec1d1bfb2a9b8ef2b4b7cc5f254102d17095a71872821b396953a4ce",
   128  		},
   129  		{
   130  			description:  "buildpack in dev mode",
   131  			dependencies: []string{"a", "b"},
   132  			artifact: &latest.Artifact{
   133  				ArtifactType: latest.ArtifactType{
   134  					BuildpackArtifact: &latest.BuildpackArtifact{},
   135  				},
   136  			},
   137  			mode:     config.RunModes.Dev,
   138  			expected: "d99ab295a682897269b4db0fe7c136ea1ecd542150fa224ee03155b4e3e995d9",
   139  		},
   140  		{
   141  			description:  "buildpack in debug mode",
   142  			dependencies: []string{"a", "b"},
   143  			artifact: &latest.Artifact{
   144  				ArtifactType: latest.ArtifactType{
   145  					BuildpackArtifact: &latest.BuildpackArtifact{},
   146  				},
   147  			},
   148  			mode:     config.RunModes.Debug,
   149  			expected: "c3a878f799b2a6532db71683a09771af4f9d20ef5884c57642a272934e5c93ea",
   150  		},
   151  	}
   152  	for _, test := range tests {
   153  		testutil.Run(t, test.description, func(t *testutil.T) {
   154  			t.Override(&fileHasherFunc, mockCacheHasher)
   155  			t.Override(&artifactConfigFunc, fakeArtifactConfig)
   156  			if test.artifact.DockerArtifact != nil {
   157  				tmpDir := t.NewTempDir()
   158  				tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo")
   159  				test.artifact.Workspace = tmpDir.Path(".")
   160  				test.artifact.DockerArtifact.DockerfilePath = Dockerfile
   161  			}
   162  
   163  			depLister := stubDependencyLister(test.dependencies)
   164  			actual, err := newArtifactHasher(nil, depLister, test.mode).hash(context.Background(), test.artifact, test.platforms)
   165  
   166  			t.CheckNoError(err)
   167  			t.CheckDeepEqual(test.expected, actual)
   168  		})
   169  	}
   170  }
   171  
   172  func TestGetHashForArtifactWithDependencies(t *testing.T) {
   173  	tests := []struct {
   174  		description string
   175  		artifacts   []*latest.Artifact
   176  		fileDeps    map[string][]string // keyed on artifact ImageName, returns a list of mock file dependencies.
   177  		mode        config.RunMode
   178  		expected    string
   179  	}{
   180  		{
   181  			description: "hash for artifact with two dependencies",
   182  			artifacts: []*latest.Artifact{
   183  				{ImageName: "img1", Dependencies: []*latest.ArtifactDependency{{ImageName: "img2"}, {ImageName: "img3"}}},
   184  				{ImageName: "img2", Dependencies: []*latest.ArtifactDependency{{ImageName: "img4"}}, ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target2"}}},
   185  				{ImageName: "img3", ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target3"}}},
   186  				{ImageName: "img4", ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target4"}}},
   187  			},
   188  			fileDeps: map[string][]string{"img1": {"a"}, "img2": {"b"}, "img3": {"c"}, "img4": {"d"}},
   189  			mode:     config.RunModes.Dev,
   190  			expected: "ccd159a9a50853f89ab6784530b58d658a0b349c92828eba335f1074f9a63bb3",
   191  		},
   192  		{
   193  			description: "hash for artifact with two dependencies in different order",
   194  			artifacts: []*latest.Artifact{
   195  				{ImageName: "img1", Dependencies: []*latest.ArtifactDependency{{ImageName: "img3"}, {ImageName: "img2"}}},
   196  				{ImageName: "img2", Dependencies: []*latest.ArtifactDependency{{ImageName: "img4"}}, ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target2"}}},
   197  				{ImageName: "img3", ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target3"}}},
   198  				{ImageName: "img4", ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target4"}}},
   199  			},
   200  			fileDeps: map[string][]string{"img1": {"a"}, "img2": {"b"}, "img3": {"c"}, "img4": {"d"}},
   201  			mode:     config.RunModes.Dev,
   202  			expected: "ccd159a9a50853f89ab6784530b58d658a0b349c92828eba335f1074f9a63bb3",
   203  		},
   204  		{
   205  			description: "hash for artifact with different dependencies (img4 builder changed)",
   206  			artifacts: []*latest.Artifact{
   207  				{ImageName: "img1", Dependencies: []*latest.ArtifactDependency{{ImageName: "img2"}, {ImageName: "img3"}}},
   208  				{ImageName: "img2", Dependencies: []*latest.ArtifactDependency{{ImageName: "img4"}}, ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target2"}}},
   209  				{ImageName: "img3", ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target3"}}},
   210  				{ImageName: "img4", ArtifactType: latest.ArtifactType{BuildpackArtifact: &latest.BuildpackArtifact{Builder: "builder"}}},
   211  			},
   212  			fileDeps: map[string][]string{"img1": {"a"}, "img2": {"b"}, "img3": {"c"}, "img4": {"d"}},
   213  			mode:     config.RunModes.Dev,
   214  			expected: "26defaa1291289f40b756b83824f0549a3a9c03cca5471bd268f0ac6e499aba6",
   215  		},
   216  		{
   217  			description: "hash for artifact with different dependencies (img4 files changed)",
   218  			artifacts: []*latest.Artifact{
   219  				{ImageName: "img1", Dependencies: []*latest.ArtifactDependency{{ImageName: "img2"}, {ImageName: "img3"}}},
   220  				{ImageName: "img2", Dependencies: []*latest.ArtifactDependency{{ImageName: "img4"}}, ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target2"}}},
   221  				{ImageName: "img3", ArtifactType: latest.ArtifactType{DockerArtifact: &latest.DockerArtifact{Target: "target3"}}},
   222  				{ImageName: "img4", ArtifactType: latest.ArtifactType{BuildpackArtifact: &latest.BuildpackArtifact{}}},
   223  			},
   224  			fileDeps: map[string][]string{"img1": {"a"}, "img2": {"b"}, "img3": {"c"}, "img4": {"e"}},
   225  			mode:     config.RunModes.Dev,
   226  			expected: "bab56a88d483fa97ae072b027a46681177628156839b7e390842e6243b1ac6aa",
   227  		},
   228  	}
   229  	for _, test := range tests {
   230  		testutil.Run(t, test.description, func(t *testutil.T) {
   231  			t.Override(&fileHasherFunc, mockCacheHasher)
   232  			t.Override(&artifactConfigFunc, fakeArtifactConfig)
   233  			g := graph.ToArtifactGraph(test.artifacts)
   234  
   235  			for _, a := range test.artifacts {
   236  				if a.DockerArtifact != nil {
   237  					tmpDir := t.NewTempDir()
   238  					tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo")
   239  					a.Workspace = tmpDir.Path(".")
   240  					a.DockerArtifact.DockerfilePath = Dockerfile
   241  				}
   242  			}
   243  
   244  			depLister := func(_ context.Context, a *latest.Artifact) ([]string, error) {
   245  				return test.fileDeps[a.ImageName], nil
   246  			}
   247  
   248  			actual, err := newArtifactHasher(g, depLister, test.mode).hash(context.Background(), test.artifacts[0], platform.Resolver{})
   249  
   250  			t.CheckNoError(err)
   251  			t.CheckDeepEqual(test.expected, actual)
   252  		})
   253  	}
   254  }
   255  
   256  func TestArtifactConfig(t *testing.T) {
   257  	testutil.Run(t, "", func(t *testutil.T) {
   258  		config1, err := artifactConfig(&latest.Artifact{
   259  			ArtifactType: latest.ArtifactType{
   260  				DockerArtifact: &latest.DockerArtifact{
   261  					Target: "target",
   262  				},
   263  			},
   264  		})
   265  		t.CheckNoError(err)
   266  
   267  		config2, err := artifactConfig(&latest.Artifact{
   268  			ArtifactType: latest.ArtifactType{
   269  				DockerArtifact: &latest.DockerArtifact{
   270  					Target: "other",
   271  				},
   272  			},
   273  		})
   274  		t.CheckNoError(err)
   275  
   276  		if config1 == config2 {
   277  			t.Errorf("configs should be different: [%s] [%s]", config1, config2)
   278  		}
   279  	})
   280  }
   281  
   282  func TestBuildArgs(t *testing.T) {
   283  	tests := []struct {
   284  		mode     config.RunMode
   285  		expected string
   286  	}{
   287  		{
   288  			mode:     config.RunModes.Debug,
   289  			expected: "a8544410acafce64325abfffcb21e75efdcd575bd9f8d3be2a516125ec547651",
   290  		},
   291  		{
   292  			mode:     config.RunModes.Dev,
   293  			expected: "f5b610f4fea07461411b2ea0e2cddfd2ffc28d1baed49180f5d3acee5a18f9e7",
   294  		},
   295  	}
   296  	for _, test := range tests {
   297  		testutil.Run(t, "", func(t *testutil.T) {
   298  			tmpDir := t.NewTempDir()
   299  			tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo")
   300  			artifact := &latest.Artifact{
   301  				Workspace: tmpDir.Path("."),
   302  				ArtifactType: latest.ArtifactType{
   303  					DockerArtifact: &latest.DockerArtifact{
   304  						DockerfilePath: Dockerfile,
   305  						BuildArgs:      map[string]*string{"one": util.StringPtr("1"), "two": util.StringPtr("2")},
   306  					},
   307  				},
   308  			}
   309  			t.Override(&fileHasherFunc, mockCacheHasher)
   310  			t.Override(&artifactConfigFunc, fakeArtifactConfig)
   311  			actual, err := newArtifactHasher(nil, stubDependencyLister(nil), test.mode).hash(context.Background(), artifact, platform.Resolver{})
   312  
   313  			t.CheckNoError(err)
   314  			t.CheckDeepEqual(test.expected, actual)
   315  
   316  			// Change order of buildargs
   317  			artifact.ArtifactType.DockerArtifact.BuildArgs = map[string]*string{"two": util.StringPtr("2"), "one": util.StringPtr("1")}
   318  			actual, err = newArtifactHasher(nil, stubDependencyLister(nil), test.mode).hash(context.Background(), artifact, platform.Resolver{})
   319  
   320  			t.CheckNoError(err)
   321  			t.CheckDeepEqual(test.expected, actual)
   322  
   323  			// Change build args, get different hash
   324  			artifact.ArtifactType.DockerArtifact.BuildArgs = map[string]*string{"one": util.StringPtr("1")}
   325  			actual, err = newArtifactHasher(nil, stubDependencyLister(nil), test.mode).hash(context.Background(), artifact, platform.Resolver{})
   326  
   327  			t.CheckNoError(err)
   328  			if actual == test.expected {
   329  				t.Fatal("got same hash as different artifact; expected different hashes.")
   330  			}
   331  		})
   332  	}
   333  }
   334  
   335  func TestBuildArgsEnvSubstitution(t *testing.T) {
   336  	testutil.Run(t, "", func(t *testutil.T) {
   337  		original := util.OSEnviron
   338  		defer func() { util.OSEnviron = original }()
   339  		util.OSEnviron = func() []string {
   340  			return []string{"FOO=bar"}
   341  		}
   342  		tmpDir := t.NewTempDir()
   343  		tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo")
   344  		artifact := &latest.Artifact{
   345  			Workspace: tmpDir.Path("."),
   346  			ArtifactType: latest.ArtifactType{
   347  				DockerArtifact: &latest.DockerArtifact{
   348  					BuildArgs:      map[string]*string{"env": util.StringPtr("${{.FOO}}")},
   349  					DockerfilePath: Dockerfile,
   350  				},
   351  			},
   352  		}
   353  
   354  		t.Override(&fileHasherFunc, mockCacheHasher)
   355  		t.Override(&artifactConfigFunc, fakeArtifactConfig)
   356  
   357  		depLister := stubDependencyLister([]string{"graph"})
   358  		hash1, err := newArtifactHasher(nil, depLister, config.RunModes.Build).hash(context.Background(), artifact, platform.Resolver{})
   359  
   360  		t.CheckNoError(err)
   361  
   362  		// Make sure hash is different with a new env
   363  
   364  		util.OSEnviron = func() []string {
   365  			return []string{"FOO=baz"}
   366  		}
   367  
   368  		hash2, err := newArtifactHasher(nil, depLister, config.RunModes.Build).hash(context.Background(), artifact, platform.Resolver{})
   369  
   370  		t.CheckNoError(err)
   371  		if hash1 == hash2 {
   372  			t.Fatal("hashes are the same even though build arg changed")
   373  		}
   374  	})
   375  }
   376  
   377  func TestCacheHasher(t *testing.T) {
   378  	tests := []struct {
   379  		description   string
   380  		differentHash bool
   381  		newFilename   string
   382  		update        func(oldFile string, folder *testutil.TempDir)
   383  	}{
   384  		{
   385  			description:   "change filename",
   386  			differentHash: true,
   387  			newFilename:   "newfoo",
   388  			update: func(oldFile string, folder *testutil.TempDir) {
   389  				folder.Rename(oldFile, "newfoo")
   390  			},
   391  		},
   392  		{
   393  			description:   "change file contents",
   394  			differentHash: true,
   395  			update: func(oldFile string, folder *testutil.TempDir) {
   396  				folder.Write(oldFile, "newcontents")
   397  			},
   398  		},
   399  		{
   400  			description:   "change both",
   401  			differentHash: true,
   402  			newFilename:   "newfoo",
   403  			update: func(oldFile string, folder *testutil.TempDir) {
   404  				folder.Rename(oldFile, "newfoo")
   405  				folder.Write(oldFile, "newcontents")
   406  			},
   407  		},
   408  		{
   409  			description:   "change nothing",
   410  			differentHash: false,
   411  			update:        func(oldFile string, folder *testutil.TempDir) {},
   412  		},
   413  	}
   414  	for _, test := range tests {
   415  		testutil.Run(t, test.description, func(t *testutil.T) {
   416  			originalFile := "foo"
   417  			originalContents := "contents"
   418  
   419  			tmpDir := t.NewTempDir().
   420  				Write(originalFile, originalContents)
   421  
   422  			path := originalFile
   423  			depLister := stubDependencyLister([]string{tmpDir.Path(originalFile)})
   424  
   425  			oldHash, err := newArtifactHasher(nil, depLister, config.RunModes.Build).hash(context.Background(), &latest.Artifact{}, platform.Resolver{})
   426  			t.CheckNoError(err)
   427  
   428  			test.update(originalFile, tmpDir)
   429  			if test.newFilename != "" {
   430  				path = test.newFilename
   431  			}
   432  
   433  			depLister = stubDependencyLister([]string{tmpDir.Path(path)})
   434  			newHash, err := newArtifactHasher(nil, depLister, config.RunModes.Build).hash(context.Background(), &latest.Artifact{}, platform.Resolver{})
   435  
   436  			t.CheckNoError(err)
   437  			t.CheckFalse(test.differentHash && oldHash == newHash)
   438  			t.CheckFalse(!test.differentHash && oldHash != newHash)
   439  		})
   440  	}
   441  }
   442  
   443  func TestHashBuildArgs(t *testing.T) {
   444  	tests := []struct {
   445  		description  string
   446  		artifactType latest.ArtifactType
   447  		expected     []string
   448  		mode         config.RunMode
   449  	}{
   450  		{
   451  			description: "docker artifact with build args for dev",
   452  			artifactType: latest.ArtifactType{
   453  				DockerArtifact: &latest.DockerArtifact{
   454  					BuildArgs: map[string]*string{
   455  						"foo": util.StringPtr("bar"),
   456  					},
   457  				},
   458  			},
   459  			mode:     config.RunModes.Dev,
   460  			expected: []string{"foo=bar"},
   461  		}, {
   462  			description: "docker artifact with build args for debug",
   463  			artifactType: latest.ArtifactType{
   464  				DockerArtifact: &latest.DockerArtifact{
   465  					BuildArgs: map[string]*string{
   466  						"foo": util.StringPtr("bar"),
   467  					},
   468  				},
   469  			},
   470  			mode:     config.RunModes.Debug,
   471  			expected: []string{"SKAFFOLD_GO_GCFLAGS=all=-N -l", "foo=bar"},
   472  		}, {
   473  			description: "docker artifact without build args for debug",
   474  			artifactType: latest.ArtifactType{
   475  				DockerArtifact: &latest.DockerArtifact{},
   476  			},
   477  			mode:     config.RunModes.Debug,
   478  			expected: []string{"SKAFFOLD_GO_GCFLAGS=all=-N -l"},
   479  		}, {
   480  			description: "docker artifact without build args for dev",
   481  			artifactType: latest.ArtifactType{
   482  				DockerArtifact: &latest.DockerArtifact{},
   483  			},
   484  			mode: config.RunModes.Dev,
   485  		}, {
   486  			description: "kaniko artifact with build args",
   487  			artifactType: latest.ArtifactType{
   488  				KanikoArtifact: &latest.KanikoArtifact{
   489  					BuildArgs: map[string]*string{},
   490  				},
   491  			},
   492  			expected: nil,
   493  		}, {
   494  			description: "kaniko artifact without build args",
   495  			artifactType: latest.ArtifactType{
   496  				KanikoArtifact: &latest.KanikoArtifact{},
   497  			},
   498  		}, {
   499  			description: "buildpacks artifact with env for dev",
   500  			artifactType: latest.ArtifactType{
   501  				BuildpackArtifact: &latest.BuildpackArtifact{
   502  					Env: []string{"foo=bar"},
   503  				},
   504  			},
   505  			mode:     config.RunModes.Dev,
   506  			expected: []string{"foo=bar"},
   507  		}, {
   508  			description: "buildpacks artifact without env for dev",
   509  			artifactType: latest.ArtifactType{
   510  				BuildpackArtifact: &latest.BuildpackArtifact{},
   511  			},
   512  			mode: config.RunModes.Dev,
   513  		}, {
   514  			description: "buildpacks artifact with env for debug",
   515  			artifactType: latest.ArtifactType{
   516  				BuildpackArtifact: &latest.BuildpackArtifact{
   517  					Env: []string{"foo=bar"},
   518  				},
   519  			},
   520  			mode:     config.RunModes.Debug,
   521  			expected: []string{"GOOGLE_GOGCFLAGS=all=-N -l", "foo=bar"},
   522  		}, {
   523  			description: "buildpacks artifact without env for debug",
   524  			artifactType: latest.ArtifactType{
   525  				BuildpackArtifact: &latest.BuildpackArtifact{},
   526  			},
   527  			mode:     config.RunModes.Debug,
   528  			expected: []string{"GOOGLE_GOGCFLAGS=all=-N -l"},
   529  		}, {
   530  			description: "custom artifact, dockerfile dependency, with build args",
   531  			artifactType: latest.ArtifactType{
   532  				CustomArtifact: &latest.CustomArtifact{
   533  					Dependencies: &latest.CustomDependencies{
   534  						Dockerfile: &latest.DockerfileDependency{
   535  							BuildArgs: map[string]*string{},
   536  						},
   537  					},
   538  				},
   539  			},
   540  			expected: nil,
   541  		}, {
   542  			description: "custom artifact, no dockerfile dependency",
   543  			artifactType: latest.ArtifactType{
   544  				CustomArtifact: &latest.CustomArtifact{
   545  					Dependencies: &latest.CustomDependencies{},
   546  				},
   547  			},
   548  		},
   549  	}
   550  
   551  	for _, test := range tests {
   552  		testutil.Run(t, test.description, func(t *testutil.T) {
   553  			a := &latest.Artifact{
   554  				ArtifactType: test.artifactType,
   555  			}
   556  			if test.artifactType.DockerArtifact != nil {
   557  				tmpDir := t.NewTempDir()
   558  				tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo")
   559  				a.Workspace = tmpDir.Path(".")
   560  				a.ArtifactType.DockerArtifact.DockerfilePath = Dockerfile
   561  			}
   562  			if test.artifactType.KanikoArtifact != nil {
   563  				tmpDir := t.NewTempDir()
   564  				tmpDir.Write("./Dockerfile", "ARG SKAFFOLD_GO_GCFLAGS\nFROM foo")
   565  				a.Workspace = tmpDir.Path(".")
   566  				a.ArtifactType.KanikoArtifact.DockerfilePath = Dockerfile
   567  			}
   568  			actual, err := hashBuildArgs(a, test.mode)
   569  			t.CheckNoError(err)
   570  			t.CheckDeepEqual(test.expected, actual)
   571  		})
   572  	}
   573  }