github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/internal/util/get/get_test.go (about)

     1  // Copyright 2019 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package get_test
    16  
    17  import (
    18  	"bytes"
    19  	"os"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/GoogleContainerTools/kpt/internal/printer/fake"
    24  	"github.com/GoogleContainerTools/kpt/internal/testutil"
    25  	"github.com/GoogleContainerTools/kpt/internal/testutil/pkgbuilder"
    26  	. "github.com/GoogleContainerTools/kpt/internal/util/get"
    27  	kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
    28  	"github.com/stretchr/testify/assert"
    29  	"sigs.k8s.io/kustomize/kyaml/kio"
    30  	"sigs.k8s.io/kustomize/kyaml/kio/filters"
    31  	"sigs.k8s.io/kustomize/kyaml/yaml"
    32  )
    33  
    34  const JavaSubdir = "java"
    35  
    36  func TestMain(m *testing.M) {
    37  	os.Exit(testutil.ConfigureTestKptCache(m))
    38  }
    39  
    40  // TestCommand_Run_failEmptyRepo verifies that Command fail if not repo is provided.
    41  func TestCommand_Run_failEmptyRepo(t *testing.T) {
    42  	_, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{})
    43  	defer clean()
    44  
    45  	err := Command{
    46  		Destination: w.WorkspaceDirectory,
    47  	}.Run(fake.CtxWithDefaultPrinter())
    48  	if !assert.Error(t, err) {
    49  		t.FailNow()
    50  	}
    51  	assert.Contains(t, err.Error(), "must specify git repo information")
    52  }
    53  
    54  // TestCommand_Run_failEmptyRepo verifies that Command fail if not repo is provided.
    55  func TestCommand_Run_failNoRevision(t *testing.T) {
    56  	_, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{})
    57  	defer clean()
    58  
    59  	err := Command{
    60  		Git: &kptfilev1.Git{
    61  			Repo: "foo",
    62  		},
    63  		Destination: w.WorkspaceDirectory,
    64  	}.Run(fake.CtxWithDefaultPrinter())
    65  	if !assert.Error(t, err) {
    66  		t.FailNow()
    67  	}
    68  	assert.Contains(t, err.Error(), "must specify ref")
    69  }
    70  
    71  // TestCommand_Run verifies that Command will clone the HEAD of the master branch.
    72  //
    73  // - destination directory should match the base name of the repo
    74  // - KptFile should be populated with values pointing to the origin
    75  func TestCommand_Run(t *testing.T) {
    76  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
    77  		Data:   testutil.Dataset1,
    78  		Branch: "master",
    79  	})
    80  	defer clean()
    81  
    82  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
    83  
    84  	absPath := filepath.Join(w.WorkspaceDirectory, g.RepoName)
    85  	err := Command{Git: &kptfilev1.Git{
    86  		Repo:      "file://" + g.RepoDirectory,
    87  		Ref:       "master",
    88  		Directory: "/",
    89  	},
    90  		Destination: absPath}.Run(fake.CtxWithDefaultPrinter())
    91  	assert.NoError(t, err)
    92  
    93  	// verify the cloned contents matches the repository
    94  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset1), absPath, true)
    95  
    96  	// verify the KptFile contains the expected values
    97  	commit, err := g.GetCommit()
    98  	assert.NoError(t, err)
    99  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   100  		ResourceMeta: yaml.ResourceMeta{
   101  			ObjectMeta: yaml.ObjectMeta{
   102  				NameMeta: yaml.NameMeta{
   103  					Name: g.RepoName,
   104  				},
   105  			},
   106  			TypeMeta: yaml.TypeMeta{
   107  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   108  				Kind:       kptfilev1.TypeMeta.Kind},
   109  		},
   110  		UpstreamLock: &kptfilev1.UpstreamLock{
   111  			Type: kptfilev1.GitOrigin,
   112  			Git: &kptfilev1.GitLock{
   113  				Directory: "/",
   114  				Repo:      "file://" + g.RepoDirectory,
   115  				Ref:       "master",
   116  				Commit:    commit, // verify the commit matches the repo
   117  			},
   118  		},
   119  		Upstream: &kptfilev1.Upstream{
   120  			Type: kptfilev1.GitOrigin,
   121  			Git: &kptfilev1.Git{
   122  				Directory: "/",
   123  				Repo:      "file://" + g.RepoDirectory,
   124  				Ref:       "master",
   125  			},
   126  			UpdateStrategy: kptfilev1.ResourceMerge,
   127  		},
   128  	})
   129  }
   130  
   131  // TestCommand_Run_subdir verifies that Command will clone a subdirectory of a repo.
   132  //
   133  // - destination dir should match the name of the subdirectory
   134  // - KptFile should have the subdir listed
   135  func TestCommand_Run_subdir(t *testing.T) {
   136  	subdir := JavaSubdir
   137  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   138  		Data:   testutil.Dataset1,
   139  		Branch: "master",
   140  	})
   141  	defer clean()
   142  
   143  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
   144  
   145  	absPath := filepath.Join(w.WorkspaceDirectory, subdir)
   146  	err := Command{Git: &kptfilev1.Git{
   147  		Repo: g.RepoDirectory, Ref: "refs/heads/master", Directory: subdir},
   148  		Destination: absPath,
   149  	}.Run(fake.CtxWithDefaultPrinter())
   150  	assert.NoError(t, err)
   151  
   152  	// verify the cloned contents matches the repository
   153  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset1, subdir), absPath, true)
   154  
   155  	// verify the KptFile contains the expected values
   156  	commit, err := g.GetCommit()
   157  	assert.NoError(t, err)
   158  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   159  		ResourceMeta: yaml.ResourceMeta{
   160  			ObjectMeta: yaml.ObjectMeta{
   161  				NameMeta: yaml.NameMeta{
   162  					Name: subdir,
   163  				},
   164  			},
   165  			TypeMeta: yaml.TypeMeta{
   166  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   167  				Kind:       kptfilev1.TypeMeta.Kind},
   168  		},
   169  		UpstreamLock: &kptfilev1.UpstreamLock{
   170  			Type: "git",
   171  			Git: &kptfilev1.GitLock{
   172  				Commit:    commit,
   173  				Directory: subdir,
   174  				Ref:       "refs/heads/master",
   175  				Repo:      g.RepoDirectory,
   176  			},
   177  		},
   178  		Upstream: &kptfilev1.Upstream{
   179  			Type: "git",
   180  			Git: &kptfilev1.Git{
   181  				Directory: subdir,
   182  				Ref:       "refs/heads/master",
   183  				Repo:      g.RepoDirectory,
   184  			},
   185  			UpdateStrategy: kptfilev1.ResourceMerge,
   186  		},
   187  	})
   188  }
   189  
   190  // TestCommand_Run_subdir_symlinks verifies Command will
   191  // clone a subdirectory of a repo inside the subdirectory.
   192  //
   193  // - destination dir should match the name of the subdirectory
   194  // - KptFile should have the subdir listed
   195  // - Content outside the subdirectory should be ignored
   196  // - symlinks inside the subdirectory should be ignored
   197  func TestCommand_Run_subdir_symlinks(t *testing.T) {
   198  	subdir := JavaSubdir
   199  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   200  		Data:   testutil.Dataset6,
   201  		Branch: "master",
   202  	})
   203  	defer clean()
   204  
   205  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
   206  
   207  	cliOutput := &bytes.Buffer{}
   208  
   209  	absPath := filepath.Join(w.WorkspaceDirectory, subdir)
   210  	err := Command{Git: &kptfilev1.Git{
   211  		Repo: g.RepoDirectory, Ref: "refs/heads/master", Directory: subdir},
   212  		Destination: absPath,
   213  	}.Run(fake.CtxWithPrinter(cliOutput, cliOutput))
   214  	assert.NoError(t, err)
   215  
   216  	// ensure warning for symlink is printed on the CLI
   217  	assert.Contains(t, cliOutput.String(), `[Warn] Ignoring symlink "config-symlink"`)
   218  
   219  	// verify the cloned contents do not contains symlinks
   220  	diff, err := testutil.Diff(filepath.Join(g.DatasetDirectory, testutil.Dataset6, subdir), absPath, true)
   221  	assert.NoError(t, err)
   222  	diff = diff.Difference(testutil.KptfileSet)
   223  	// original repo contains symlink and cloned doesn't, so the difference
   224  	assert.Contains(t, diff.List(), "config-symlink")
   225  
   226  	// verify the KptFile contains the expected values
   227  	commit, err := g.GetCommit()
   228  	assert.NoError(t, err)
   229  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   230  		ResourceMeta: yaml.ResourceMeta{
   231  			ObjectMeta: yaml.ObjectMeta{
   232  				NameMeta: yaml.NameMeta{
   233  					Name: subdir,
   234  				},
   235  			},
   236  			TypeMeta: yaml.TypeMeta{
   237  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   238  				Kind:       kptfilev1.TypeMeta.Kind},
   239  		},
   240  		UpstreamLock: &kptfilev1.UpstreamLock{
   241  			Type: "git",
   242  			Git: &kptfilev1.GitLock{
   243  				Commit:    commit,
   244  				Directory: subdir,
   245  				Ref:       "refs/heads/master",
   246  				Repo:      g.RepoDirectory,
   247  			},
   248  		},
   249  		Upstream: &kptfilev1.Upstream{
   250  			Type: "git",
   251  			Git: &kptfilev1.Git{
   252  				Directory: subdir,
   253  				Ref:       "refs/heads/master",
   254  				Repo:      g.RepoDirectory,
   255  			},
   256  			UpdateStrategy: kptfilev1.ResourceMerge,
   257  		},
   258  	})
   259  }
   260  
   261  // TestCommand_Run_destination verifies Command clones the repo to a destination with a specific name rather
   262  // than using the name of the source repo.
   263  func TestCommand_Run_destination(t *testing.T) {
   264  	dest := "my-dataset"
   265  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   266  		Data:   testutil.Dataset1,
   267  		Branch: "master",
   268  	})
   269  	defer clean()
   270  
   271  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
   272  
   273  	absPath := filepath.Join(w.WorkspaceDirectory, dest)
   274  	err := Command{
   275  		Git: &kptfilev1.Git{
   276  			Repo:      g.RepoDirectory,
   277  			Ref:       "master",
   278  			Directory: "/",
   279  		},
   280  		Destination: absPath,
   281  	}.Run(fake.CtxWithDefaultPrinter())
   282  	assert.NoError(t, err)
   283  
   284  	// verify the cloned contents matches the repository
   285  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset1), absPath, true)
   286  
   287  	// verify the KptFile contains the expected values
   288  	commit, err := g.GetCommit()
   289  	assert.NoError(t, err)
   290  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   291  		ResourceMeta: yaml.ResourceMeta{
   292  			ObjectMeta: yaml.ObjectMeta{
   293  				NameMeta: yaml.NameMeta{
   294  					Name: dest,
   295  				},
   296  			},
   297  			TypeMeta: yaml.TypeMeta{
   298  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   299  				Kind:       kptfilev1.TypeMeta.Kind},
   300  		},
   301  		UpstreamLock: &kptfilev1.UpstreamLock{
   302  			Type: kptfilev1.GitOrigin,
   303  			Git: &kptfilev1.GitLock{
   304  				Directory: "/",
   305  				Repo:      g.RepoDirectory,
   306  				Ref:       "master",
   307  				Commit:    commit,
   308  			},
   309  		},
   310  		Upstream: &kptfilev1.Upstream{
   311  			Type: kptfilev1.GitOrigin,
   312  			Git: &kptfilev1.Git{
   313  				Directory: "/",
   314  				Repo:      g.RepoDirectory,
   315  				Ref:       "master",
   316  			},
   317  			UpdateStrategy: kptfilev1.ResourceMerge,
   318  		},
   319  	})
   320  }
   321  
   322  // TestCommand_Run_subdirAndDestination verifies that Command will copy a subdirectory of a repo to a
   323  // specific destination.
   324  //
   325  // - name of the destination is used over the name of the subdir in the KptFile
   326  func TestCommand_Run_subdirAndDestination(t *testing.T) {
   327  	subdir := JavaSubdir
   328  	dest := "new-java"
   329  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   330  		Data:   testutil.Dataset1,
   331  		Branch: "master",
   332  	})
   333  	defer clean()
   334  
   335  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
   336  
   337  	absPath := filepath.Join(w.WorkspaceDirectory, dest)
   338  	err := Command{
   339  		Git: &kptfilev1.Git{
   340  			Repo:      g.RepoDirectory,
   341  			Ref:       "master",
   342  			Directory: subdir,
   343  		},
   344  		Destination: absPath,
   345  	}.Run(fake.CtxWithDefaultPrinter())
   346  	assert.NoError(t, err)
   347  
   348  	// verify the cloned contents matches the repository
   349  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset1, subdir), absPath, true)
   350  
   351  	// verify the KptFile contains the expected values
   352  	commit, err := g.GetCommit()
   353  	assert.NoError(t, err)
   354  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   355  		ResourceMeta: yaml.ResourceMeta{
   356  			ObjectMeta: yaml.ObjectMeta{
   357  				NameMeta: yaml.NameMeta{
   358  					Name: dest,
   359  				},
   360  			},
   361  			TypeMeta: yaml.TypeMeta{
   362  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   363  				Kind:       kptfilev1.TypeMeta.Kind},
   364  		},
   365  		UpstreamLock: &kptfilev1.UpstreamLock{
   366  			Type: kptfilev1.GitOrigin,
   367  			Git: &kptfilev1.GitLock{
   368  				Commit:    commit,
   369  				Directory: subdir,
   370  				Ref:       "master",
   371  				Repo:      g.RepoDirectory,
   372  			},
   373  		},
   374  		Upstream: &kptfilev1.Upstream{
   375  			Type: kptfilev1.GitOrigin,
   376  			Git: &kptfilev1.Git{
   377  				Directory: subdir,
   378  				Ref:       "master",
   379  				Repo:      g.RepoDirectory,
   380  			},
   381  			UpdateStrategy: kptfilev1.ResourceMerge,
   382  		},
   383  	})
   384  }
   385  
   386  // TestCommand_Run_branch verifies Command can clone a git branch
   387  //
   388  // 1. create a new branch
   389  // 2. add data to the branch
   390  // 3. checkout the master branch again
   391  // 4. clone the new branch
   392  // 5. verify contents match the new branch
   393  func TestCommand_Run_branch(t *testing.T) {
   394  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   395  		Data:   testutil.Dataset1,
   396  		Branch: "master",
   397  	})
   398  	defer clean()
   399  
   400  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
   401  
   402  	// add commits to the exp branch
   403  	err := g.CheckoutBranch("exp", true)
   404  	assert.NoError(t, err)
   405  	err = g.ReplaceData(testutil.Dataset2)
   406  	assert.NoError(t, err)
   407  	_, err = g.Commit("new dataset")
   408  	assert.NoError(t, err)
   409  	commit, err := g.GetCommit()
   410  	assert.NoError(t, err)
   411  	err = g.CheckoutBranch("master", false)
   412  	assert.NoError(t, err)
   413  	commit2, err := g.GetCommit()
   414  	assert.NoError(t, err)
   415  	assert.NotEqual(t, commit, commit2)
   416  
   417  	absPath := filepath.Join(w.WorkspaceDirectory, g.RepoName)
   418  	err = Command{
   419  		Git: &kptfilev1.Git{
   420  			Repo:      g.RepoDirectory,
   421  			Ref:       "refs/heads/exp",
   422  			Directory: "/",
   423  		},
   424  		Destination: absPath,
   425  	}.Run(fake.CtxWithDefaultPrinter())
   426  	assert.NoError(t, err)
   427  
   428  	// verify the cloned contents matches the repository
   429  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset2), absPath, true)
   430  
   431  	// verify the KptFile contains the expected values
   432  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   433  		ResourceMeta: yaml.ResourceMeta{
   434  			ObjectMeta: yaml.ObjectMeta{
   435  				NameMeta: yaml.NameMeta{
   436  					Name: g.RepoName,
   437  				},
   438  			},
   439  			TypeMeta: yaml.TypeMeta{
   440  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   441  				Kind:       kptfilev1.TypeMeta.Kind},
   442  		},
   443  		UpstreamLock: &kptfilev1.UpstreamLock{
   444  			Type: kptfilev1.GitOrigin,
   445  			Git: &kptfilev1.GitLock{
   446  				Directory: "/",
   447  				Repo:      g.RepoDirectory,
   448  				Ref:       "refs/heads/exp",
   449  				Commit:    commit,
   450  			},
   451  		},
   452  		Upstream: &kptfilev1.Upstream{
   453  			Type: kptfilev1.GitOrigin,
   454  			Git: &kptfilev1.Git{
   455  				Directory: "/",
   456  				Repo:      g.RepoDirectory,
   457  				Ref:       "refs/heads/exp",
   458  			},
   459  			UpdateStrategy: kptfilev1.ResourceMerge,
   460  		},
   461  	})
   462  }
   463  
   464  // TestCommand_Run_tag verifies Command can clone from a git tag
   465  //
   466  // 1. add data to the master branch
   467  // 2. commit and tag the master branch
   468  // 3. add more data to the master branch, commit it
   469  // 4. clone at the tag
   470  // 5. verify the clone has the data from the tagged version
   471  func TestCommand_Run_tag(t *testing.T) {
   472  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   473  		Data:   testutil.Dataset1,
   474  		Branch: "master",
   475  	})
   476  	defer clean()
   477  
   478  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
   479  
   480  	// create a commit with dataset2 and tag it v2, then add another commit on top with dataset3
   481  	commit0, err := g.GetCommit()
   482  	assert.NoError(t, err)
   483  	err = g.ReplaceData(testutil.Dataset2)
   484  	assert.NoError(t, err)
   485  	_, err = g.Commit("new-data for v2")
   486  	assert.NoError(t, err)
   487  	commit, err := g.GetCommit()
   488  	assert.NoError(t, err)
   489  	err = g.Tag("v2")
   490  	assert.NoError(t, err)
   491  	err = g.ReplaceData(testutil.Dataset3)
   492  	assert.NoError(t, err)
   493  	_, err = g.Commit("new-data post-v2")
   494  	assert.NoError(t, err)
   495  	commit2, err := g.GetCommit()
   496  	assert.NoError(t, err)
   497  	assert.NotEqual(t, commit, commit0)
   498  	assert.NotEqual(t, commit, commit2)
   499  
   500  	absPath := filepath.Join(w.WorkspaceDirectory, g.RepoName)
   501  	err = Command{
   502  		Git: &kptfilev1.Git{
   503  			Repo:      g.RepoDirectory,
   504  			Ref:       "refs/tags/v2",
   505  			Directory: "/",
   506  		},
   507  		Destination: absPath,
   508  	}.Run(fake.CtxWithDefaultPrinter())
   509  	assert.NoError(t, err)
   510  
   511  	// verify the cloned contents matches the repository
   512  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset2), absPath, true)
   513  
   514  	// verify the KptFile contains the expected values
   515  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   516  		ResourceMeta: yaml.ResourceMeta{
   517  			ObjectMeta: yaml.ObjectMeta{
   518  				NameMeta: yaml.NameMeta{
   519  					Name: g.RepoName,
   520  				},
   521  			},
   522  			TypeMeta: yaml.TypeMeta{
   523  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   524  				Kind:       kptfilev1.TypeMeta.Kind},
   525  		},
   526  		UpstreamLock: &kptfilev1.UpstreamLock{
   527  			Type: kptfilev1.GitOrigin,
   528  			Git: &kptfilev1.GitLock{
   529  				Directory: "/",
   530  				Repo:      g.RepoDirectory,
   531  				Ref:       "refs/tags/v2",
   532  				Commit:    commit,
   533  			},
   534  		},
   535  		Upstream: &kptfilev1.Upstream{
   536  			Type: kptfilev1.GitOrigin,
   537  			Git: &kptfilev1.Git{
   538  				Directory: "/",
   539  				Repo:      g.RepoDirectory,
   540  				Ref:       "refs/tags/v2",
   541  			},
   542  			UpdateStrategy: kptfilev1.ResourceMerge,
   543  		},
   544  	})
   545  }
   546  
   547  func TestCommand_Run_ref(t *testing.T) {
   548  	testCases := map[string]struct {
   549  		reposContent map[string][]testutil.Content
   550  		directory    string
   551  		ref          func(repos map[string]*testutil.TestGitRepo) string
   552  		expected     *pkgbuilder.RootPkg
   553  	}{
   554  		"package tag": {
   555  			reposContent: map[string][]testutil.Content{
   556  				testutil.Upstream: {
   557  					{
   558  						Pkg: pkgbuilder.NewRootPkg().
   559  							WithSubPackages(
   560  								pkgbuilder.NewSubPkg("kafka").
   561  									WithResource(pkgbuilder.DeploymentResource).
   562  									WithResource(pkgbuilder.SecretResource),
   563  							),
   564  						Branch: "master",
   565  						Tag:    "kafka/v2",
   566  					},
   567  					{
   568  						Pkg: pkgbuilder.NewRootPkg().
   569  							WithSubPackages(
   570  								pkgbuilder.NewSubPkg("kafka").
   571  									WithResource(pkgbuilder.DeploymentResource).
   572  									WithResource(pkgbuilder.ConfigMapResource),
   573  							),
   574  						Tag: "v2",
   575  					},
   576  				},
   577  			},
   578  			directory: "kafka",
   579  			ref: func(_ map[string]*testutil.TestGitRepo) string {
   580  				return "v2"
   581  			},
   582  			expected: pkgbuilder.NewRootPkg().
   583  				WithKptfile(
   584  					pkgbuilder.NewKptfile().
   585  						WithUpstreamRef(testutil.Upstream, "kafka", "v2", "resource-merge").
   586  						WithUpstreamLockRef(testutil.Upstream, "kafka", "kafka/v2", 0),
   587  				).
   588  				WithResource(pkgbuilder.DeploymentResource).
   589  				WithResource(pkgbuilder.SecretResource),
   590  		},
   591  		"commit sha": {
   592  			reposContent: map[string][]testutil.Content{
   593  				testutil.Upstream: {
   594  					{
   595  						Pkg: pkgbuilder.NewRootPkg().
   596  							WithSubPackages(
   597  								pkgbuilder.NewSubPkg("kafka").
   598  									WithResource(pkgbuilder.DeploymentResource).
   599  									WithResource(pkgbuilder.SecretResource),
   600  							),
   601  						Branch: "master",
   602  						Tag:    "kafka/v2",
   603  					},
   604  					{
   605  						Pkg: pkgbuilder.NewRootPkg().
   606  							WithSubPackages(
   607  								pkgbuilder.NewSubPkg("kafka").
   608  									WithResource(pkgbuilder.DeploymentResource).
   609  									WithResource(pkgbuilder.ConfigMapResource),
   610  							),
   611  						Tag: "v2",
   612  					},
   613  				},
   614  			},
   615  			directory: "kafka",
   616  			ref: func(repos map[string]*testutil.TestGitRepo) string {
   617  				return repos[testutil.Upstream].Commits[0]
   618  			},
   619  			expected: pkgbuilder.NewRootPkg().
   620  				WithKptfile(
   621  					pkgbuilder.NewKptfile().
   622  						WithUpstreamRef(testutil.Upstream, "kafka", "COMMIT-INDEX:0", "resource-merge").
   623  						WithUpstreamLockRef(testutil.Upstream, "kafka", "COMMIT-INDEX:0", 0),
   624  				).
   625  				WithResource(pkgbuilder.DeploymentResource).
   626  				WithResource(pkgbuilder.SecretResource),
   627  		},
   628  	}
   629  
   630  	for tn, tc := range testCases {
   631  		t.Run(tn, func(t *testing.T) {
   632  			repos, w, clean := testutil.SetupReposAndWorkspace(t, tc.reposContent)
   633  			defer clean()
   634  			err := testutil.UpdateRepos(t, repos, tc.reposContent)
   635  			if !assert.NoError(t, err) {
   636  				t.FailNow()
   637  			}
   638  
   639  			ref := tc.ref(repos)
   640  
   641  			absPath := filepath.Join(w.WorkspaceDirectory, repos[testutil.Upstream].RepoName)
   642  			err = Command{
   643  				Git: &kptfilev1.Git{
   644  					Repo:      repos[testutil.Upstream].RepoDirectory,
   645  					Ref:       ref,
   646  					Directory: tc.directory,
   647  				},
   648  				Destination: absPath,
   649  			}.Run(fake.CtxWithDefaultPrinter())
   650  			assert.NoError(t, err)
   651  
   652  			expectedPath := tc.expected.ExpandPkgWithName(t, repos[testutil.Upstream].RepoName, testutil.ToReposInfo(repos))
   653  
   654  			testutil.KptfileAwarePkgEqual(t, expectedPath, absPath, true)
   655  		})
   656  	}
   657  }
   658  
   659  // TestCommand_Run_failExistingDir verifies that command will fail without changing anything if the
   660  // directory already exists
   661  func TestCommand_Run_failExistingDir(t *testing.T) {
   662  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   663  		Data:   testutil.Dataset1,
   664  		Branch: "master",
   665  	})
   666  	defer clean()
   667  
   668  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
   669  
   670  	absPath := filepath.Join(w.WorkspaceDirectory, g.RepoName)
   671  	err := Command{
   672  		Git: &kptfilev1.Git{
   673  			Repo:      g.RepoDirectory,
   674  			Ref:       "master",
   675  			Directory: "/",
   676  		},
   677  		Destination: absPath,
   678  	}.Run(fake.CtxWithDefaultPrinter())
   679  	assert.NoError(t, err)
   680  
   681  	// verify the KptFile contains the expected values
   682  	commit, err := g.GetCommit()
   683  	assert.NoError(t, err)
   684  
   685  	// verify the cloned contents matches the repository
   686  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset1), absPath, true)
   687  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   688  		ResourceMeta: yaml.ResourceMeta{
   689  			ObjectMeta: yaml.ObjectMeta{
   690  				NameMeta: yaml.NameMeta{
   691  					Name: g.RepoName,
   692  				},
   693  			},
   694  			TypeMeta: yaml.TypeMeta{
   695  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   696  				Kind:       kptfilev1.TypeMeta.Kind},
   697  		},
   698  		UpstreamLock: &kptfilev1.UpstreamLock{
   699  			Type: kptfilev1.GitOrigin,
   700  			Git: &kptfilev1.GitLock{
   701  				Directory: "/",
   702  				Repo:      g.RepoDirectory,
   703  				Ref:       "master",
   704  				Commit:    commit, // verify the commit matches the repo
   705  			},
   706  		},
   707  		Upstream: &kptfilev1.Upstream{
   708  			Type: kptfilev1.GitOrigin,
   709  			Git: &kptfilev1.Git{
   710  				Directory: "/",
   711  				Repo:      g.RepoDirectory,
   712  				Ref:       "master",
   713  			},
   714  			UpdateStrategy: kptfilev1.ResourceMerge,
   715  		},
   716  	})
   717  
   718  	// update the data that would be cloned
   719  	err = g.ReplaceData(testutil.Dataset2)
   720  	assert.NoError(t, err)
   721  	_, err = g.Commit("new-data")
   722  	assert.NoError(t, err)
   723  
   724  	// try to clone and expect a failure
   725  	err = Command{
   726  		Git: &kptfilev1.Git{
   727  			Repo:      g.RepoDirectory,
   728  			Ref:       "master",
   729  			Directory: "/",
   730  		},
   731  		Destination: absPath,
   732  	}.Run(fake.CtxWithDefaultPrinter())
   733  	if !assert.Error(t, err) {
   734  		t.FailNow()
   735  	}
   736  	assert.Contains(t, err.Error(), "destination directory already exists")
   737  
   738  	// verify files are unchanged
   739  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset1), absPath, true)
   740  	g.AssertKptfile(t, absPath, kptfilev1.KptFile{
   741  		ResourceMeta: yaml.ResourceMeta{
   742  			ObjectMeta: yaml.ObjectMeta{
   743  				NameMeta: yaml.NameMeta{
   744  					Name: g.RepoName,
   745  				},
   746  			},
   747  			TypeMeta: yaml.TypeMeta{
   748  				APIVersion: kptfilev1.TypeMeta.APIVersion,
   749  				Kind:       kptfilev1.TypeMeta.Kind},
   750  		},
   751  		UpstreamLock: &kptfilev1.UpstreamLock{
   752  			Type: kptfilev1.GitOrigin,
   753  			Git: &kptfilev1.GitLock{
   754  				Directory: "/",
   755  				Repo:      g.RepoDirectory,
   756  				Ref:       "master",
   757  				Commit:    commit, // verify the commit matches the repo
   758  			},
   759  		},
   760  		Upstream: &kptfilev1.Upstream{
   761  			Type: kptfilev1.GitOrigin,
   762  			Git: &kptfilev1.Git{
   763  				Directory: "/",
   764  				Repo:      g.RepoDirectory,
   765  				Ref:       "master",
   766  			},
   767  			UpdateStrategy: kptfilev1.ResourceMerge,
   768  		},
   769  	})
   770  }
   771  
   772  func TestCommand_Run_nonexistingParentDir(t *testing.T) {
   773  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   774  		Data:   testutil.Dataset1,
   775  		Branch: "master",
   776  	})
   777  	defer clean()
   778  
   779  	defer testutil.Chdir(t, w.WorkspaceDirectory)()
   780  
   781  	absPath := filepath.Join(w.WorkspaceDirectory, "more", "dirs", g.RepoName)
   782  	err := Command{
   783  		Git: &kptfilev1.Git{
   784  			Repo:      g.RepoDirectory,
   785  			Ref:       "master",
   786  			Directory: "/",
   787  		},
   788  		Destination: absPath,
   789  	}.Run(fake.CtxWithDefaultPrinter())
   790  	assert.NoError(t, err)
   791  	g.AssertEqual(t, filepath.Join(g.DatasetDirectory, testutil.Dataset1), absPath, true)
   792  }
   793  
   794  func TestCommand_Run_failInvalidRepo(t *testing.T) {
   795  	_, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   796  		Data:   testutil.Dataset1,
   797  		Branch: "master",
   798  	})
   799  	defer clean()
   800  
   801  	absPath := filepath.Join(w.WorkspaceDirectory, "foo")
   802  	err := Command{
   803  		Git: &kptfilev1.Git{
   804  			Repo:      "foo",
   805  			Directory: "/",
   806  			Ref:       "refs/heads/master",
   807  		},
   808  		Destination: absPath,
   809  	}.Run(fake.CtxWithDefaultPrinter())
   810  	if !assert.Error(t, err) {
   811  		t.FailNow()
   812  	}
   813  	if !assert.Contains(t, err.Error(), "'foo' does not appear to be a git repository") {
   814  		t.FailNow()
   815  	}
   816  
   817  	// Confirm destination directory no longer exists.
   818  	_, err = os.Stat(absPath)
   819  	if !assert.Error(t, err) {
   820  		t.FailNow()
   821  	}
   822  }
   823  
   824  func TestCommand_Run_failInvalidBranch(t *testing.T) {
   825  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   826  		Data:   testutil.Dataset1,
   827  		Branch: "master",
   828  	})
   829  	defer clean()
   830  
   831  	absPath := filepath.Join(w.WorkspaceDirectory, g.RepoDirectory)
   832  	err := Command{
   833  		Git: &kptfilev1.Git{
   834  			Repo:      g.RepoDirectory,
   835  			Directory: "/",
   836  			Ref:       "refs/heads/foo",
   837  		},
   838  		Destination: absPath,
   839  	}.Run(fake.CtxWithDefaultPrinter())
   840  	if !assert.Error(t, err) {
   841  		t.FailNow()
   842  	}
   843  	if !assert.Contains(t, err.Error(), "refs/heads/foo") {
   844  		t.FailNow()
   845  	}
   846  	if !assert.Contains(t, err.Error(), "exit status 128") {
   847  		t.FailNow()
   848  	}
   849  
   850  	// Confirm destination directory no longer exists.
   851  	_, err = os.Stat(absPath)
   852  	if !assert.Error(t, err) {
   853  		t.FailNow()
   854  	}
   855  }
   856  
   857  func TestCommand_Run_failInvalidTag(t *testing.T) {
   858  	g, w, clean := testutil.SetupRepoAndWorkspace(t, testutil.Content{
   859  		Data:   testutil.Dataset1,
   860  		Branch: "master",
   861  	})
   862  	defer clean()
   863  
   864  	absPath := filepath.Join(w.WorkspaceDirectory, g.RepoDirectory)
   865  	err := Command{
   866  		Git: &kptfilev1.Git{
   867  			Repo:      g.RepoDirectory,
   868  			Directory: "/",
   869  			Ref:       "refs/tags/foo",
   870  		},
   871  		Destination: absPath,
   872  	}.Run(fake.CtxWithDefaultPrinter())
   873  	if !assert.Error(t, err) {
   874  		t.FailNow()
   875  	}
   876  	if !assert.Contains(t, err.Error(), "refs/tags/foo") {
   877  		t.FailNow()
   878  	}
   879  	if !assert.Contains(t, err.Error(), "exit status 128") {
   880  		t.FailNow()
   881  	}
   882  
   883  	// Confirm destination directory no longer exists.
   884  	_, err = os.Stat(absPath)
   885  	if !assert.Error(t, err) {
   886  		t.FailNow()
   887  	}
   888  }
   889  
   890  func TestCommand_Run_subpackages(t *testing.T) {
   891  	testCases := map[string]struct {
   892  		directory      string
   893  		ref            string
   894  		updateStrategy kptfilev1.UpdateStrategyType
   895  		reposContent   map[string][]testutil.Content
   896  		expectedResult *pkgbuilder.RootPkg
   897  		expectedErrMsg string
   898  	}{
   899  		"basic package without pipeline": {
   900  			directory: "/",
   901  			ref:       "master",
   902  			reposContent: map[string][]testutil.Content{
   903  				testutil.Upstream: {
   904  					{
   905  						Branch: "master",
   906  						Pkg: pkgbuilder.NewRootPkg().
   907  							WithKptfile().
   908  							WithResource(pkgbuilder.DeploymentResource),
   909  					},
   910  				},
   911  			},
   912  			expectedResult: pkgbuilder.NewRootPkg().
   913  				WithKptfile(
   914  					pkgbuilder.NewKptfile().
   915  						WithUpstreamRef("upstream", "/", "master", "resource-merge").
   916  						WithUpstreamLockRef("upstream", "/", "master", 0),
   917  				).
   918  				WithResource(pkgbuilder.DeploymentResource),
   919  		},
   920  
   921  		"basic package with non-KRM files": {
   922  			directory: "/",
   923  			ref:       "master",
   924  			reposContent: map[string][]testutil.Content{
   925  				testutil.Upstream: {
   926  					{
   927  						Branch: "master",
   928  						Pkg: pkgbuilder.NewRootPkg().
   929  							WithKptfile().
   930  							WithFile("foo.txt", `this is a test`),
   931  					},
   932  				},
   933  			},
   934  			expectedResult: pkgbuilder.NewRootPkg().
   935  				WithKptfile(
   936  					pkgbuilder.NewKptfile().
   937  						WithUpstreamRef("upstream", "/", "master", "resource-merge").
   938  						WithUpstreamLockRef("upstream", "/", "master", 0),
   939  				).
   940  				WithFile("foo.txt", `this is a test`),
   941  		},
   942  		"basic package with pipeline": {
   943  			directory: "/",
   944  			ref:       "master",
   945  			reposContent: map[string][]testutil.Content{
   946  				testutil.Upstream: {
   947  					{
   948  						Branch: "master",
   949  						Pkg: pkgbuilder.NewRootPkg().
   950  							WithKptfile(
   951  								pkgbuilder.NewKptfile().
   952  									WithPipeline(
   953  										pkgbuilder.NewFunction("gcr.io/kpt-dev/foo:latest"),
   954  									),
   955  							).
   956  							WithResource(pkgbuilder.DeploymentResource),
   957  					},
   958  				},
   959  			},
   960  			expectedResult: pkgbuilder.NewRootPkg().
   961  				WithKptfile(
   962  					pkgbuilder.NewKptfile().
   963  						WithUpstreamRef("upstream", "/", "master", "resource-merge").
   964  						WithUpstreamLockRef("upstream", "/", "master", 0).
   965  						WithPipeline(
   966  							pkgbuilder.NewFunction("gcr.io/kpt-dev/foo:latest"),
   967  						),
   968  				).
   969  				WithResource(pkgbuilder.DeploymentResource),
   970  		},
   971  		"basic package with no Kptfile in upstream": {
   972  			directory: "/",
   973  			ref:       "master",
   974  			reposContent: map[string][]testutil.Content{
   975  				testutil.Upstream: {
   976  					{
   977  						Branch: "master",
   978  						Pkg: pkgbuilder.NewRootPkg().
   979  							WithResource(pkgbuilder.DeploymentResource),
   980  					},
   981  				},
   982  			},
   983  			expectedResult: pkgbuilder.NewRootPkg().
   984  				WithKptfile(
   985  					pkgbuilder.NewKptfile().
   986  						WithUpstreamRef("upstream", "/", "master", "resource-merge").
   987  						WithUpstreamLockRef("upstream", "/", "master", 0),
   988  				).
   989  				WithResource(pkgbuilder.DeploymentResource),
   990  		},
   991  		"basic package with explicit update strategy": {
   992  			directory:      "/",
   993  			ref:            "master",
   994  			updateStrategy: kptfilev1.FastForward,
   995  			reposContent: map[string][]testutil.Content{
   996  				testutil.Upstream: {
   997  					{
   998  						Branch: "master",
   999  						Pkg: pkgbuilder.NewRootPkg().
  1000  							WithResource(pkgbuilder.DeploymentResource),
  1001  					},
  1002  				},
  1003  			},
  1004  			expectedResult: pkgbuilder.NewRootPkg().
  1005  				WithKptfile(
  1006  					pkgbuilder.NewKptfile().
  1007  						WithUpstreamRef("upstream", "/", "master", "fast-forward").
  1008  						WithUpstreamLockRef("upstream", "/", "master", 0),
  1009  				).
  1010  				WithResource(pkgbuilder.DeploymentResource),
  1011  		},
  1012  		"package with subpackages": {
  1013  			directory: "/",
  1014  			ref:       "master",
  1015  			reposContent: map[string][]testutil.Content{
  1016  				testutil.Upstream: {
  1017  					{
  1018  						Branch: "master",
  1019  						Pkg: pkgbuilder.NewRootPkg().
  1020  							WithKptfile().
  1021  							WithResource(pkgbuilder.DeploymentResource).
  1022  							WithSubPackages(
  1023  								pkgbuilder.NewSubPkg("subpkg").
  1024  									WithKptfile().
  1025  									WithResource(pkgbuilder.ConfigMapResource),
  1026  							),
  1027  					},
  1028  				},
  1029  			},
  1030  			expectedResult: pkgbuilder.NewRootPkg().
  1031  				WithKptfile(
  1032  					pkgbuilder.NewKptfile().
  1033  						WithUpstreamRef("upstream", "/", "master", "resource-merge").
  1034  						WithUpstreamLockRef("upstream", "/", "master", 0),
  1035  				).
  1036  				WithResource(pkgbuilder.DeploymentResource).
  1037  				WithSubPackages(
  1038  					pkgbuilder.NewSubPkg("subpkg").
  1039  						WithKptfile().
  1040  						WithResource(pkgbuilder.ConfigMapResource),
  1041  				),
  1042  		},
  1043  		"package with deeply nested subpackages": {
  1044  			directory: "/",
  1045  			ref:       "master",
  1046  			reposContent: map[string][]testutil.Content{
  1047  				testutil.Upstream: {
  1048  					{
  1049  						Branch: "master",
  1050  						Pkg: pkgbuilder.NewRootPkg().
  1051  							WithKptfile().
  1052  							WithResource(pkgbuilder.DeploymentResource).
  1053  							WithSubPackages(
  1054  								pkgbuilder.NewSubPkg("subpkg").
  1055  									WithResource(pkgbuilder.ConfigMapResource).
  1056  									WithSubPackages(
  1057  										pkgbuilder.NewSubPkg("deepsubpkg").
  1058  											WithKptfile(
  1059  												pkgbuilder.NewKptfile().
  1060  													WithUpstreamRef("foo", "/", "main", "fast-forward"),
  1061  											),
  1062  									),
  1063  							),
  1064  					},
  1065  				},
  1066  				"foo": {
  1067  					{
  1068  						Branch: "main",
  1069  						Pkg: pkgbuilder.NewRootPkg().
  1070  							WithKptfile().
  1071  							WithResource(pkgbuilder.SecretResource),
  1072  					},
  1073  				},
  1074  			},
  1075  			expectedResult: pkgbuilder.NewRootPkg().
  1076  				WithKptfile(
  1077  					pkgbuilder.NewKptfile().
  1078  						WithUpstreamRef("upstream", "/", "master", "resource-merge").
  1079  						WithUpstreamLockRef("upstream", "/", "master", 0),
  1080  				).
  1081  				WithResource(pkgbuilder.DeploymentResource).
  1082  				WithSubPackages(
  1083  					pkgbuilder.NewSubPkg("subpkg").
  1084  						WithResource(pkgbuilder.ConfigMapResource).
  1085  						WithSubPackages(
  1086  							pkgbuilder.NewSubPkg("deepsubpkg").
  1087  								WithKptfile(
  1088  									pkgbuilder.NewKptfile().
  1089  										WithUpstreamRef("foo", "/", "main", "fast-forward").
  1090  										WithUpstreamLockRef("foo", "/", "main", 0),
  1091  								).
  1092  								WithResource(pkgbuilder.SecretResource),
  1093  						),
  1094  				),
  1095  		},
  1096  		"package with local and remote subpackages": {
  1097  			directory: "/",
  1098  			ref:       "master",
  1099  			reposContent: map[string][]testutil.Content{
  1100  				testutil.Upstream: {
  1101  					{
  1102  						Branch: "master",
  1103  						Pkg: pkgbuilder.NewRootPkg().
  1104  							WithResource(pkgbuilder.DeploymentResource).
  1105  							WithSubPackages(
  1106  								pkgbuilder.NewSubPkg("subpkg").
  1107  									WithResource(pkgbuilder.ConfigMapResource),
  1108  								pkgbuilder.NewSubPkg("foo").
  1109  									WithKptfile(
  1110  										pkgbuilder.NewKptfile().
  1111  											WithUpstreamRef("foo", "/", "main", "fast-forward").
  1112  											WithUpstreamLockRef("foo", "/", "main", 0),
  1113  									).
  1114  									WithResource(pkgbuilder.DeploymentResource).
  1115  									WithSubPackages(
  1116  										pkgbuilder.NewSubPkg("subpkg").
  1117  											WithKptfile(
  1118  												pkgbuilder.NewKptfile(),
  1119  											).
  1120  											WithResource(pkgbuilder.ConfigMapResource).
  1121  											WithSubPackages(
  1122  												pkgbuilder.NewSubPkg("bar").
  1123  													WithKptfile(
  1124  														pkgbuilder.NewKptfile().
  1125  															WithUpstreamRef("bar", "/", "main", "fast-forward").
  1126  															WithUpstreamLockRef("bar", "/", "main", 0),
  1127  													).WithResource(pkgbuilder.DeploymentResource),
  1128  											),
  1129  									),
  1130  							),
  1131  					},
  1132  				},
  1133  				"foo": {
  1134  					{
  1135  						Pkg: pkgbuilder.NewRootPkg().
  1136  							WithKptfile().
  1137  							WithResource(pkgbuilder.DeploymentResource).
  1138  							WithSubPackages(
  1139  								pkgbuilder.NewSubPkg("subpkg").
  1140  									WithKptfile(
  1141  										pkgbuilder.NewKptfile(),
  1142  									).
  1143  									WithResource(pkgbuilder.ConfigMapResource).
  1144  									WithSubPackages(
  1145  										pkgbuilder.NewSubPkg("bar").
  1146  											WithKptfile(
  1147  												pkgbuilder.NewKptfile().
  1148  													WithUpstreamRef("bar", "/", "main", "fast-forward").
  1149  													WithUpstreamLockRef("bar", "/", "main", 0),
  1150  											).
  1151  											WithResource(pkgbuilder.DeploymentResource),
  1152  									),
  1153  							),
  1154  					},
  1155  				},
  1156  				"bar": {
  1157  					{
  1158  						Pkg: pkgbuilder.NewRootPkg().
  1159  							WithKptfile().
  1160  							WithResource(pkgbuilder.DeploymentResource),
  1161  					},
  1162  				},
  1163  			},
  1164  			expectedResult: pkgbuilder.NewRootPkg().
  1165  				WithKptfile(
  1166  					pkgbuilder.NewKptfile().
  1167  						WithUpstreamRef("upstream", "/", "master", "resource-merge").
  1168  						WithUpstreamLockRef("upstream", "/", "master", 0),
  1169  				).
  1170  				WithResource(pkgbuilder.DeploymentResource).
  1171  				WithSubPackages(
  1172  					pkgbuilder.NewSubPkg("foo").
  1173  						WithKptfile(
  1174  							pkgbuilder.NewKptfile().
  1175  								WithUpstreamRef("foo", "/", "main", "fast-forward").
  1176  								WithUpstreamLockRef("foo", "/", "main", 0),
  1177  						).
  1178  						WithResource(pkgbuilder.DeploymentResource).
  1179  						WithSubPackages(
  1180  							pkgbuilder.NewSubPkg("subpkg").
  1181  								WithKptfile(
  1182  									pkgbuilder.NewKptfile(),
  1183  								).
  1184  								WithResource(pkgbuilder.ConfigMapResource).
  1185  								WithSubPackages(
  1186  									pkgbuilder.NewSubPkg("bar").
  1187  										WithKptfile(
  1188  											pkgbuilder.NewKptfile().
  1189  												WithUpstreamRef("bar", "/", "main", "fast-forward").
  1190  												WithUpstreamLockRef("bar", "/", "main", 0),
  1191  										).
  1192  										WithResource(pkgbuilder.DeploymentResource),
  1193  								),
  1194  						),
  1195  					pkgbuilder.NewSubPkg("subpkg").
  1196  						WithResource(pkgbuilder.ConfigMapResource),
  1197  				),
  1198  		},
  1199  		"fetch subpackage on a different branch than master": {
  1200  			directory: "/bar",
  1201  			ref:       "main",
  1202  			reposContent: map[string][]testutil.Content{
  1203  				testutil.Upstream: {
  1204  					{
  1205  						Branch: "main",
  1206  						Pkg: pkgbuilder.NewRootPkg().
  1207  							WithKptfile().
  1208  							WithResource(pkgbuilder.DeploymentResource).
  1209  							WithSubPackages(
  1210  								pkgbuilder.NewSubPkg("bar").
  1211  									WithKptfile().
  1212  									WithResource(pkgbuilder.ConfigMapResource),
  1213  								pkgbuilder.NewSubPkg("foo").
  1214  									WithKptfile(
  1215  										pkgbuilder.NewKptfile().
  1216  											WithUpstreamRef("foo", "/subpkg", "v1.2", "fast-forward"),
  1217  									),
  1218  							),
  1219  					},
  1220  				},
  1221  				"foo": {
  1222  					{
  1223  						Pkg: pkgbuilder.NewRootPkg().
  1224  							WithKptfile().
  1225  							WithResource(pkgbuilder.DeploymentResource),
  1226  					},
  1227  				},
  1228  			},
  1229  			expectedResult: pkgbuilder.NewRootPkg().
  1230  				WithKptfile(
  1231  					pkgbuilder.NewKptfile().
  1232  						WithUpstreamRef("upstream", "/bar", "main", "resource-merge").
  1233  						WithUpstreamLockRef("upstream", "/bar", "main", 0),
  1234  				).
  1235  				WithResource(pkgbuilder.ConfigMapResource),
  1236  		},
  1237  		"package with unfetched remote subpackage with a tag reference": {
  1238  			directory: "/",
  1239  			ref:       "main",
  1240  			reposContent: map[string][]testutil.Content{
  1241  				testutil.Upstream: {
  1242  					{
  1243  						Branch: "main",
  1244  						Pkg: pkgbuilder.NewRootPkg().
  1245  							WithResource(pkgbuilder.DeploymentResource).
  1246  							WithSubPackages(
  1247  								pkgbuilder.NewSubPkg("bar").
  1248  									WithKptfile().
  1249  									WithResource(pkgbuilder.ConfigMapResource),
  1250  								pkgbuilder.NewSubPkg("foo").
  1251  									WithKptfile(
  1252  										pkgbuilder.NewKptfile().
  1253  											WithUpstreamRef("foo", "/subpkg", "v1.2", "fast-forward"),
  1254  									),
  1255  							),
  1256  					},
  1257  				},
  1258  				"foo": {
  1259  					{
  1260  						Branch: "master",
  1261  						Pkg: pkgbuilder.NewRootPkg().
  1262  							WithKptfile().
  1263  							WithResource(pkgbuilder.ConfigMapResource),
  1264  					},
  1265  					{
  1266  						Pkg: pkgbuilder.NewRootPkg().
  1267  							WithKptfile().
  1268  							WithResource(pkgbuilder.ConfigMapResource).
  1269  							WithSubPackages(
  1270  								pkgbuilder.NewSubPkg("subpkg").
  1271  									WithKptfile().
  1272  									WithResource(pkgbuilder.DeploymentResource),
  1273  							),
  1274  						Tag:    "v1.2",
  1275  						Branch: "master",
  1276  					},
  1277  				},
  1278  			},
  1279  			expectedResult: pkgbuilder.NewRootPkg().
  1280  				WithKptfile(
  1281  					pkgbuilder.NewKptfile().
  1282  						WithUpstreamRef("upstream", "/", "main", "resource-merge").
  1283  						WithUpstreamLockRef("upstream", "/", "main", 0),
  1284  				).
  1285  				WithResource(pkgbuilder.DeploymentResource).
  1286  				WithSubPackages(
  1287  					pkgbuilder.NewSubPkg("bar").
  1288  						WithKptfile().
  1289  						WithResource(pkgbuilder.ConfigMapResource),
  1290  					pkgbuilder.NewSubPkg("foo").
  1291  						WithKptfile(
  1292  							pkgbuilder.NewKptfile().
  1293  								WithUpstreamRef("foo", "/subpkg", "v1.2", "fast-forward").
  1294  								WithUpstreamLockRef("foo", "/subpkg", "v1.2", 1),
  1295  						).
  1296  						WithResource(pkgbuilder.DeploymentResource),
  1297  				),
  1298  		},
  1299  		"same unfetched remote subpackage referenced multiple times": {
  1300  			directory: "/",
  1301  			ref:       "master",
  1302  			reposContent: map[string][]testutil.Content{
  1303  				testutil.Upstream: {
  1304  					{
  1305  						Branch: "master",
  1306  						Pkg: pkgbuilder.NewRootPkg().
  1307  							WithSubPackages(
  1308  								pkgbuilder.NewSubPkg("foo-sub").
  1309  									WithKptfile(
  1310  										pkgbuilder.NewKptfile().
  1311  											WithUpstreamRef("foo", "/subpkg", "subpkg/v1.2", "fast-forward"),
  1312  									),
  1313  								pkgbuilder.NewSubPkg("foo-root").
  1314  									WithKptfile(
  1315  										pkgbuilder.NewKptfile().
  1316  											WithUpstreamRef("foo", "/", "master", "fast-forward"),
  1317  									),
  1318  							),
  1319  					},
  1320  				},
  1321  				"foo": {
  1322  					{
  1323  						Branch: "master",
  1324  						Pkg: pkgbuilder.NewRootPkg().
  1325  							WithKptfile().
  1326  							WithResource(pkgbuilder.ConfigMapResource),
  1327  					},
  1328  					{
  1329  						Pkg: pkgbuilder.NewRootPkg().
  1330  							WithKptfile().
  1331  							WithResource(pkgbuilder.ConfigMapResource).
  1332  							WithSubPackages(
  1333  								pkgbuilder.NewSubPkg("subpkg").
  1334  									WithKptfile().
  1335  									WithResource(pkgbuilder.DeploymentResource),
  1336  							),
  1337  						Tag: "subpkg/v1.2",
  1338  					},
  1339  				},
  1340  			},
  1341  			expectedResult: pkgbuilder.NewRootPkg().
  1342  				WithKptfile(
  1343  					pkgbuilder.NewKptfile().
  1344  						WithUpstreamRef("upstream", "/", "master", "resource-merge").
  1345  						WithUpstreamLockRef("upstream", "/", "master", 0),
  1346  				).
  1347  				WithSubPackages(
  1348  					pkgbuilder.NewSubPkg("foo-sub").
  1349  						WithKptfile(
  1350  							pkgbuilder.NewKptfile().
  1351  								WithUpstreamRef("foo", "/subpkg", "subpkg/v1.2", "fast-forward").
  1352  								WithUpstreamLockRef("foo", "/subpkg", "subpkg/v1.2", 1),
  1353  						).
  1354  						WithResource(pkgbuilder.DeploymentResource),
  1355  					pkgbuilder.NewSubPkg("foo-root").
  1356  						WithKptfile(
  1357  							pkgbuilder.NewKptfile().
  1358  								WithUpstreamRef("foo", "/", "master", "fast-forward").
  1359  								WithUpstreamLockRef("foo", "/", "master", 1),
  1360  						).
  1361  						WithResource(pkgbuilder.ConfigMapResource).
  1362  						WithSubPackages(
  1363  							pkgbuilder.NewSubPkg("subpkg").
  1364  								WithKptfile().
  1365  								WithResource(pkgbuilder.DeploymentResource),
  1366  						),
  1367  				),
  1368  		},
  1369  	}
  1370  
  1371  	for tn, tc := range testCases {
  1372  		t.Run(tn, func(t *testing.T) {
  1373  			repos, w, clean := testutil.SetupReposAndWorkspace(t, tc.reposContent)
  1374  			defer clean()
  1375  			upstreamRepo := repos[testutil.Upstream]
  1376  			err := testutil.UpdateRepos(t, repos, tc.reposContent)
  1377  			if !assert.NoError(t, err) {
  1378  				t.FailNow()
  1379  			}
  1380  
  1381  			var targetDir string
  1382  			if tc.directory == "/" {
  1383  				targetDir = filepath.Base(upstreamRepo.RepoName)
  1384  			} else {
  1385  				targetDir = filepath.Base(tc.directory)
  1386  			}
  1387  			w.PackageDir = targetDir
  1388  			destinationDir := filepath.Join(w.WorkspaceDirectory, targetDir)
  1389  
  1390  			err = Command{
  1391  				Git: &kptfilev1.Git{
  1392  					Repo:      upstreamRepo.RepoDirectory,
  1393  					Directory: tc.directory,
  1394  					Ref:       tc.ref,
  1395  				},
  1396  				Destination:    destinationDir,
  1397  				UpdateStrategy: tc.updateStrategy,
  1398  			}.Run(fake.CtxWithDefaultPrinter())
  1399  
  1400  			if tc.expectedErrMsg != "" {
  1401  				if !assert.Error(t, err) {
  1402  					t.FailNow()
  1403  				}
  1404  				assert.Contains(t, err.Error(), tc.expectedErrMsg)
  1405  				return
  1406  			}
  1407  
  1408  			if !assert.NoError(t, err) {
  1409  				t.FailNow()
  1410  			}
  1411  
  1412  			// Format the Kptfiles so we can diff the output without
  1413  			// formatting issues.
  1414  			rw := &kio.LocalPackageReadWriter{
  1415  				NoDeleteFiles:     true,
  1416  				PackagePath:       w.FullPackagePath(),
  1417  				MatchFilesGlob:    []string{kptfilev1.KptFileName},
  1418  				PreserveSeqIndent: true,
  1419  				WrapBareSeqNode:   true,
  1420  			}
  1421  			err = kio.Pipeline{
  1422  				Inputs:  []kio.Reader{rw},
  1423  				Filters: []kio.Filter{filters.FormatFilter{}},
  1424  				Outputs: []kio.Writer{rw},
  1425  			}.Execute()
  1426  			if !assert.NoError(t, err) {
  1427  				t.FailNow()
  1428  			}
  1429  
  1430  			expectedPath := tc.expectedResult.ExpandPkgWithName(t, targetDir, testutil.ToReposInfo(repos))
  1431  			testutil.KptfileAwarePkgEqual(t, expectedPath, w.FullPackagePath(), true)
  1432  		})
  1433  	}
  1434  }
  1435  
  1436  func TestCommand_Run_symlinks(t *testing.T) {
  1437  	repos, w, clean := testutil.SetupReposAndWorkspace(t, map[string][]testutil.Content{
  1438  		testutil.Upstream: {
  1439  			{
  1440  				Branch: "master",
  1441  				Pkg: pkgbuilder.NewRootPkg().
  1442  					WithKptfile().
  1443  					WithResource(pkgbuilder.DeploymentResource).
  1444  					WithSubPackages(
  1445  						pkgbuilder.NewSubPkg("subpkg").
  1446  							WithKptfile().
  1447  							WithResource(pkgbuilder.ConfigMapResource),
  1448  					),
  1449  				UpdateFunc: func(path string) error {
  1450  					// Create symlink in the upstream repo.
  1451  					return os.Symlink(filepath.Join(path, "subpkg"),
  1452  						filepath.Join(path, "subpkg-sym"))
  1453  				},
  1454  			},
  1455  		},
  1456  	})
  1457  	defer clean()
  1458  	upstreamRepo := repos[testutil.Upstream]
  1459  
  1460  	destinationDir := filepath.Join(w.WorkspaceDirectory, upstreamRepo.RepoName)
  1461  	err := Command{
  1462  		Git: &kptfilev1.Git{
  1463  			Repo:      upstreamRepo.RepoDirectory,
  1464  			Directory: "/",
  1465  			Ref:       "master",
  1466  		},
  1467  		Destination: destinationDir,
  1468  	}.Run(fake.CtxWithDefaultPrinter())
  1469  	if !assert.NoError(t, err) {
  1470  		t.FailNow()
  1471  	}
  1472  	w.PackageDir = upstreamRepo.RepoName
  1473  
  1474  	expectedPkg := pkgbuilder.NewRootPkg().
  1475  		WithKptfile(
  1476  			pkgbuilder.NewKptfile().
  1477  				WithUpstreamRef(testutil.Upstream, "/", "master", "resource-merge").
  1478  				WithUpstreamLockRef(testutil.Upstream, "/", "master", 0),
  1479  		).
  1480  		WithResource(pkgbuilder.DeploymentResource).
  1481  		WithSubPackages(
  1482  			pkgbuilder.NewSubPkg("subpkg").
  1483  				WithKptfile().
  1484  				WithResource(pkgbuilder.ConfigMapResource),
  1485  		)
  1486  	expectedPath := expectedPkg.ExpandPkgWithName(t, upstreamRepo.RepoName, testutil.ToReposInfo(repos))
  1487  
  1488  	testutil.KptfileAwarePkgEqual(t, expectedPath, w.FullPackagePath(), true)
  1489  }