github.com/SamarSidharth/kpt@v0.0.0-20231122062228-c7d747ae3ace/internal/pkg/pkg_test.go (about)

     1  // Copyright 2020 The kpt Authors
     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 pkg
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"runtime"
    22  	"sort"
    23  	"testing"
    24  
    25  	"github.com/GoogleContainerTools/kpt/internal/testutil/pkgbuilder"
    26  	"github.com/GoogleContainerTools/kpt/internal/util/pathutil"
    27  	kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
    28  	"github.com/stretchr/testify/assert"
    29  	"sigs.k8s.io/kustomize/kyaml/filesys"
    30  )
    31  
    32  func TestNewPkg(t *testing.T) {
    33  	// this test creates a folders with structure
    34  	// foo
    35  	// └── bar
    36  	//     └── baz
    37  	var tests = []struct {
    38  		name        string
    39  		workingDir  string
    40  		inputPath   string
    41  		displayPath string
    42  	}{
    43  		{
    44  			name:        "invoked from working directory foo on path .",
    45  			workingDir:  "foo",
    46  			inputPath:   ".",
    47  			displayPath: "foo",
    48  		},
    49  		{
    50  			name:        "invoked from working directory foo/bar on path ../",
    51  			workingDir:  "foo/bar",
    52  			inputPath:   "../",
    53  			displayPath: "foo",
    54  		},
    55  		{
    56  			name:        "invoked from working directory foo on nested package baz",
    57  			workingDir:  "foo",
    58  			inputPath:   "./bar/baz",
    59  			displayPath: "baz",
    60  		},
    61  		{
    62  			name:        "invoked from working directory foo/bar on nested package baz",
    63  			workingDir:  "foo/bar",
    64  			inputPath:   "../../foo/bar/baz",
    65  			displayPath: "baz",
    66  		},
    67  		{
    68  			name:        "invoked from working directory baz on ancestor package foo",
    69  			workingDir:  "foo/bar/baz",
    70  			inputPath:   "../../",
    71  			displayPath: "foo",
    72  		},
    73  	}
    74  	for i := range tests {
    75  		test := tests[i]
    76  		t.Run(test.name, func(t *testing.T) {
    77  			dir := t.TempDir()
    78  			err := os.MkdirAll(filepath.Join(dir, "foo", "bar", "baz"), 0700)
    79  			assert.NoError(t, err)
    80  			revert := Chdir(t, filepath.Join(dir, test.workingDir))
    81  			defer revert()
    82  			absInputPath, _, err := pathutil.ResolveAbsAndRelPaths(test.inputPath)
    83  			assert.NoError(t, err)
    84  			p, err := New(filesys.FileSystemOrOnDisk{}, absInputPath)
    85  			assert.NoError(t, err)
    86  			assert.Equal(t, test.displayPath, string(p.DisplayPath))
    87  		})
    88  	}
    89  }
    90  
    91  func TestAdjustDisplayPathForSubpkg(t *testing.T) {
    92  	// this test creates a folders with structure
    93  	// rootPkgParentDir
    94  	// └── rootPkg
    95  	//     └── subPkg
    96  	//         └── nestedPkg
    97  	var tests = []struct {
    98  		name                 string
    99  		workingDir           string
   100  		pkgPath              string
   101  		subPkgPath           string
   102  		rootPkgParentDirPath string
   103  		displayPath          string
   104  	}{
   105  		{
   106  			name:        "display path of subPkg should include rootPkg",
   107  			workingDir:  "rootPkg",
   108  			pkgPath:     ".",
   109  			subPkgPath:  "./subPkg",
   110  			displayPath: "rootPkg/subPkg",
   111  		},
   112  		{
   113  			name:        "display path of nestedPkg should include rootPkg/subPkg",
   114  			workingDir:  "rootPkg",
   115  			pkgPath:     ".",
   116  			subPkgPath:  "./subPkg/nestedPkg",
   117  			displayPath: "rootPkg/subPkg/nestedPkg",
   118  		},
   119  		{
   120  			name:        "display path of subPkg should include rootPkg independent of workingDir",
   121  			workingDir:  "rootPkg/subPkg",
   122  			pkgPath:     "../",
   123  			subPkgPath:  "../subPkg",
   124  			displayPath: "rootPkg/subPkg",
   125  		},
   126  		{
   127  			name:        "display path of nestedPkg should include rootPkg independent of workingDir 1",
   128  			workingDir:  "rootPkg/subPkg/nestedPkg",
   129  			pkgPath:     "../../",
   130  			subPkgPath:  "../../subPkg/nestedPkg",
   131  			displayPath: "rootPkg/subPkg/nestedPkg",
   132  		},
   133  		{
   134  			name:                 "display path of nestedPkg should include rootPkg independent of workingDir 2",
   135  			workingDir:           "rootPkg",
   136  			rootPkgParentDirPath: "../",
   137  			pkgPath:              "./subPkg",
   138  			subPkgPath:           "./subPkg/nestedPkg",
   139  			displayPath:          "rootPkg/subPkg/nestedPkg",
   140  		},
   141  		{
   142  			name:                 "display path of nestedPkg should include rootPkg independent of workingDir 3",
   143  			workingDir:           "rootPkg/subPkg",
   144  			rootPkgParentDirPath: "../../",
   145  			pkgPath:              "../subPkg",
   146  			subPkgPath:           "../subPkg/nestedPkg",
   147  			displayPath:          "rootPkg/subPkg/nestedPkg",
   148  		},
   149  		{
   150  			name:                 "display path of nestedPkg should include rootPkg independent of workingDir 4",
   151  			workingDir:           "rootPkg/subPkg/nestedPkg",
   152  			rootPkgParentDirPath: "../../../",
   153  			pkgPath:              "../../subPkg",
   154  			subPkgPath:           "../../subPkg/nestedPkg",
   155  			displayPath:          "rootPkg/subPkg/nestedPkg",
   156  		},
   157  	}
   158  	for i := range tests {
   159  		test := tests[i]
   160  		t.Run(test.name, func(t *testing.T) {
   161  			dir := t.TempDir()
   162  			err := os.MkdirAll(filepath.Join(dir, "rootPkgParentDir", "rootPkg", "subPkg", "nestedPkg"), 0700)
   163  			assert.NoError(t, err)
   164  			revert := Chdir(t, filepath.Join(dir, "rootPkgParentDir", test.workingDir))
   165  			defer revert()
   166  			absPkgPath, _, err := pathutil.ResolveAbsAndRelPaths(test.pkgPath)
   167  			assert.NoError(t, err)
   168  			parent, err := New(filesys.FileSystemOrOnDisk{}, absPkgPath)
   169  			assert.NoError(t, err)
   170  			if test.rootPkgParentDirPath != "" {
   171  				absRootPkgPath, _, err := pathutil.ResolveAbsAndRelPaths(test.rootPkgParentDirPath)
   172  				assert.NoError(t, err)
   173  				rootPkg, err := New(filesys.FileSystemOrOnDisk{}, absRootPkgPath)
   174  				assert.NoError(t, err)
   175  				parent.rootPkgParentDirPath = string(rootPkg.UniquePath)
   176  			}
   177  			absSubPkgPath, _, err := pathutil.ResolveAbsAndRelPaths(test.subPkgPath)
   178  			assert.NoError(t, err)
   179  			subPkg, err := New(filesys.FileSystemOrOnDisk{}, absSubPkgPath)
   180  			assert.NoError(t, err)
   181  			err = parent.adjustDisplayPathForSubpkg(subPkg)
   182  			assert.NoError(t, err)
   183  			assert.Equal(t, test.displayPath, string(subPkg.DisplayPath))
   184  		})
   185  	}
   186  }
   187  
   188  func TestDirectSubpackages(t *testing.T) {
   189  	testCases := map[string]struct {
   190  		pkg      *pkgbuilder.RootPkg
   191  		expected []string
   192  	}{
   193  		"includes remote subpackages": {
   194  			pkg: pkgbuilder.NewRootPkg().
   195  				WithResource(pkgbuilder.DeploymentResource).
   196  				WithSubPackages(
   197  					pkgbuilder.NewSubPkg("foo").
   198  						WithKptfile(
   199  							pkgbuilder.NewKptfile().
   200  								WithUpstream("github.com/GoogleContainerTools/kpt",
   201  									"/", "main", string(kptfilev1.ResourceMerge)),
   202  						).
   203  						WithResource(pkgbuilder.ConfigMapResource),
   204  				),
   205  			expected: []string{
   206  				"foo",
   207  			},
   208  		},
   209  		"includes local subpackages": {
   210  			pkg: pkgbuilder.NewRootPkg().
   211  				WithResource(pkgbuilder.DeploymentResource).
   212  				WithSubPackages(
   213  					pkgbuilder.NewSubPkg("foo").
   214  						WithKptfile().
   215  						WithResource(pkgbuilder.ConfigMapResource),
   216  				),
   217  			expected: []string{
   218  				"foo",
   219  			},
   220  		},
   221  		"does not include root package": {
   222  			pkg: pkgbuilder.NewRootPkg().
   223  				WithKptfile().
   224  				WithResource(pkgbuilder.DeploymentResource),
   225  			expected: []string{},
   226  		},
   227  		"does not include nested remote subpackages": {
   228  			pkg: pkgbuilder.NewRootPkg().
   229  				WithResource(pkgbuilder.DeploymentResource).
   230  				WithSubPackages(
   231  					pkgbuilder.NewSubPkg("foo").
   232  						WithKptfile(
   233  							pkgbuilder.NewKptfile().
   234  								WithUpstream("github.com/GoogleContainerTools/kpt",
   235  									"/", "main", string(kptfilev1.ResourceMerge)),
   236  						).
   237  						WithResource(pkgbuilder.ConfigMapResource).
   238  						WithSubPackages(
   239  							pkgbuilder.NewSubPkg("bar").
   240  								WithSubPackages(
   241  									pkgbuilder.NewSubPkg("zork").
   242  										WithKptfile(
   243  											pkgbuilder.NewKptfile().
   244  												WithUpstream("github.com/GoogleContainerTools/kpt",
   245  													"/", "main", string(kptfilev1.ResourceMerge)),
   246  										).
   247  										WithResource(pkgbuilder.ConfigMapResource),
   248  								),
   249  						),
   250  				),
   251  			expected: []string{
   252  				"foo",
   253  			},
   254  		},
   255  		"does not include nested local subpackages": {
   256  			pkg: pkgbuilder.NewRootPkg().
   257  				WithResource(pkgbuilder.DeploymentResource).
   258  				WithSubPackages(
   259  					pkgbuilder.NewSubPkg("foo").
   260  						WithKptfile().
   261  						WithResource(pkgbuilder.ConfigMapResource).
   262  						WithSubPackages(
   263  							pkgbuilder.NewSubPkg("zork").
   264  								WithKptfile().
   265  								WithResource(pkgbuilder.ConfigMapResource),
   266  						),
   267  					pkgbuilder.NewSubPkg("subpkg").
   268  						WithKptfile(),
   269  				),
   270  			expected: []string{
   271  				"foo",
   272  				"subpkg",
   273  			},
   274  		},
   275  	}
   276  
   277  	for tn, tc := range testCases {
   278  		t.Run(tn, func(t *testing.T) {
   279  			pkgPath := tc.pkg.ExpandPkg(t, nil)
   280  			defer os.RemoveAll(pkgPath)
   281  			absPkgPath, _, err := pathutil.ResolveAbsAndRelPaths(pkgPath)
   282  			if !assert.NoError(t, err) {
   283  				t.FailNow()
   284  			}
   285  			p, err := New(filesys.FileSystemOrOnDisk{}, absPkgPath)
   286  			if !assert.NoError(t, err) {
   287  				t.FailNow()
   288  			}
   289  			subPkgs, err := p.DirectSubpackages()
   290  			if !assert.NoError(t, err) {
   291  				t.FailNow()
   292  			}
   293  
   294  			relPaths := []string{}
   295  			for _, subPkg := range subPkgs {
   296  				fullPath := subPkg.UniquePath.String()
   297  				relPath, err := filepath.Rel(pkgPath, fullPath)
   298  				if !assert.NoError(t, err) {
   299  					t.FailNow()
   300  				}
   301  				relPaths = append(relPaths, relPath)
   302  			}
   303  			sort.Strings(relPaths)
   304  
   305  			assert.Equal(t, tc.expected, relPaths)
   306  		})
   307  	}
   308  }
   309  
   310  //nolint:scopelint
   311  func TestSubpackages(t *testing.T) {
   312  	type variants struct {
   313  		matcher   []SubpackageMatcher
   314  		recursive []bool
   315  		expected  []string
   316  	}
   317  
   318  	testCases := map[string]struct {
   319  		pkg   *pkgbuilder.RootPkg
   320  		cases []variants
   321  	}{
   322  		"remote and local nested subpackages": {
   323  			// root
   324  			//  ├── remote-sub1 (remote)
   325  			//  │   ├── Kptfile
   326  			//  │   └── directory
   327  			//  │       └── remote-sub3 (remote)
   328  			//  │           └── Kptfile
   329  			//  └── local-sub1 (local)
   330  			//      ├── Kptfile
   331  			//      ├── directory
   332  			//      │   └── remote-sub3 (remote)
   333  			//      │       └── Kptfile
   334  			//      └── local-sub2 (local)
   335  			//          └── Kptfile
   336  			pkg: pkgbuilder.NewRootPkg().
   337  				WithSubPackages(
   338  					pkgbuilder.NewSubPkg("remote-sub1").
   339  						WithKptfile(
   340  							pkgbuilder.NewKptfile().
   341  								WithUpstream("github.com/GoogleContainerTools/kpt",
   342  									"/", "main", string(kptfilev1.ResourceMerge)),
   343  						).
   344  						WithSubPackages(
   345  							pkgbuilder.NewSubPkg("directory").
   346  								WithSubPackages(
   347  									pkgbuilder.NewSubPkg("remote-sub3").
   348  										WithKptfile(
   349  											pkgbuilder.NewKptfile().
   350  												WithUpstream("github.com/GoogleContainerTools/kpt",
   351  													"/", "main", string(kptfilev1.ResourceMerge)),
   352  										),
   353  								),
   354  						),
   355  					pkgbuilder.NewSubPkg("local-sub1").
   356  						WithKptfile().
   357  						WithSubPackages(
   358  							pkgbuilder.NewSubPkg("directory").
   359  								WithSubPackages(
   360  									pkgbuilder.NewSubPkg("remote-sub3").
   361  										WithKptfile(
   362  											pkgbuilder.NewKptfile().
   363  												WithUpstream("github.com/GoogleContainerTools/kpt",
   364  													"/", "main", string(kptfilev1.ResourceMerge)),
   365  										),
   366  								),
   367  							pkgbuilder.NewSubPkg("local-sub2").
   368  								WithKptfile(),
   369  						),
   370  				),
   371  			cases: []variants{
   372  				{
   373  					matcher:   []SubpackageMatcher{All},
   374  					recursive: []bool{true},
   375  					expected: []string{
   376  						"local-sub1",
   377  						"local-sub1/directory/remote-sub3",
   378  						"local-sub1/local-sub2",
   379  						"remote-sub1",
   380  						"remote-sub1/directory/remote-sub3",
   381  					},
   382  				},
   383  				{
   384  					matcher:   []SubpackageMatcher{All},
   385  					recursive: []bool{false},
   386  					expected: []string{
   387  						"local-sub1",
   388  						"remote-sub1",
   389  					},
   390  				},
   391  				{
   392  					matcher:   []SubpackageMatcher{Remote},
   393  					recursive: []bool{true},
   394  					expected: []string{
   395  						"local-sub1/directory/remote-sub3",
   396  						"remote-sub1",
   397  						"remote-sub1/directory/remote-sub3",
   398  					},
   399  				},
   400  				{
   401  					matcher:   []SubpackageMatcher{Remote},
   402  					recursive: []bool{false},
   403  					expected: []string{
   404  						"remote-sub1",
   405  					},
   406  				},
   407  				{
   408  					matcher:   []SubpackageMatcher{Local},
   409  					recursive: []bool{true},
   410  					expected: []string{
   411  						"local-sub1",
   412  						"local-sub1/local-sub2",
   413  					},
   414  				},
   415  				{
   416  					matcher:   []SubpackageMatcher{Local},
   417  					recursive: []bool{false},
   418  					expected: []string{
   419  						"local-sub1",
   420  					},
   421  				},
   422  				{
   423  					matcher:   []SubpackageMatcher{None},
   424  					recursive: []bool{false},
   425  					expected:  []string{},
   426  				},
   427  				{
   428  					matcher:   []SubpackageMatcher{None},
   429  					recursive: []bool{true},
   430  					expected:  []string{},
   431  				},
   432  			},
   433  		},
   434  		"no subpackages": {
   435  			// root
   436  			//  └── Kptfile
   437  			pkg: pkgbuilder.NewRootPkg().
   438  				WithKptfile(),
   439  			cases: []variants{
   440  				{
   441  					matcher:   []SubpackageMatcher{All, Local, Remote, None},
   442  					recursive: []bool{true, false},
   443  					expected:  []string{},
   444  				},
   445  			},
   446  		},
   447  		"no Kptfile in root": {
   448  			// root
   449  			pkg: pkgbuilder.NewRootPkg(),
   450  			cases: []variants{
   451  				{
   452  					matcher:   []SubpackageMatcher{All, Local, Remote, None},
   453  					recursive: []bool{true, false},
   454  					expected:  []string{},
   455  				},
   456  			},
   457  		},
   458  	}
   459  
   460  	for tn, tc := range testCases {
   461  		t.Run(tn, func(t *testing.T) {
   462  			pkgPath := tc.pkg.ExpandPkg(t, nil)
   463  			defer func() {
   464  				_ = os.RemoveAll(pkgPath)
   465  			}()
   466  
   467  			for _, v := range tc.cases {
   468  				for _, matcher := range v.matcher {
   469  					for _, recursive := range v.recursive {
   470  						t.Run(fmt.Sprintf("matcher:%s-recursive:%t", matcher, recursive), func(t *testing.T) {
   471  							paths, err := Subpackages(filesys.FileSystemOrOnDisk{}, pkgPath, matcher, recursive)
   472  							if !assert.NoError(t, err) {
   473  								t.FailNow()
   474  							}
   475  
   476  							sort.Strings(paths)
   477  							sort.Strings(v.expected)
   478  
   479  							assert.Equal(t, v.expected, paths)
   480  						})
   481  					}
   482  				}
   483  			}
   484  		})
   485  	}
   486  }
   487  
   488  func TestSubpackages_symlinks(t *testing.T) {
   489  	if runtime.GOOS == "windows" {
   490  		t.SkipNow()
   491  	}
   492  
   493  	pkg := pkgbuilder.NewRootPkg().
   494  		WithResource(pkgbuilder.DeploymentResource).
   495  		WithSubPackages(
   496  			pkgbuilder.NewSubPkg("subpkg").
   497  				WithKptfile().
   498  				WithResource(pkgbuilder.ConfigMapResource),
   499  		)
   500  
   501  	pkgPath := pkg.ExpandPkg(t, nil)
   502  	defer func() {
   503  		_ = os.RemoveAll(pkgPath)
   504  	}()
   505  
   506  	symLinkOld := filepath.Join(pkgPath, "subpkg")
   507  	symLinkNew := filepath.Join(pkgPath, "symlink-subpkg")
   508  
   509  	err := os.Symlink(symLinkOld, symLinkNew)
   510  	if !assert.NoError(t, err) {
   511  		t.FailNow()
   512  	}
   513  
   514  	paths, err := Subpackages(filesys.FileSystemOrOnDisk{}, pkgPath, All, true)
   515  	if !assert.NoError(t, err) {
   516  		t.FailNow()
   517  	}
   518  	assert.Equal(t, []string{"subpkg"}, paths)
   519  }
   520  
   521  func Chdir(t *testing.T, path string) func() {
   522  	cwd, err := os.Getwd()
   523  	if !assert.NoError(t, err) {
   524  		t.FailNow()
   525  	}
   526  	revertFunc := func() {
   527  		if err := os.Chdir(cwd); err != nil {
   528  			panic(err)
   529  		}
   530  	}
   531  	err = os.Chdir(path)
   532  	if !assert.NoError(t, err) {
   533  		defer revertFunc()
   534  		t.FailNow()
   535  	}
   536  	return revertFunc
   537  }