github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/deploy/kpt/kpt_test.go (about)

     1  /*
     2  Copyright 2020 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 kpt
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"io/ioutil"
    26  	"os"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl"
    31  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label"
    32  	deployutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util"
    33  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
    34  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client"
    35  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
    36  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
    37  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    38  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
    39  	"github.com/GoogleContainerTools/skaffold/testutil"
    40  )
    41  
    42  const (
    43  	testPod = `apiVersion: v1
    44  kind: Pod
    45  metadata:
    46     namespace: default
    47  spec:
    48     containers:
    49     - image: gcr.io/project/image1
    50     name: image1
    51  `
    52  )
    53  
    54  // Test that kpt deployer manipulate manifests in the given order and no intermediate data is
    55  // stored after each step:
    56  //	Step 1. `kpt fn source` (read in the manifest as stdin),
    57  //  Step 2. `kpt fn run` (validate, transform or generate the manifests via kpt functions),
    58  //  Step 3. `kpt fn sink` (to temp dir to run kuustomize build on),
    59  //  Step 4. `kustomize build` (if the temp dir from step 3 has a Kustomization hydrate the manifest),
    60  //  Step 5. `kpt fn sink` (store the stdout in a given dir).
    61  
    62  func TestKpt_Deploy(t *testing.T) {
    63  	sanityCheck = func(ctx context.Context, dir string, buf io.Writer) error { return nil }
    64  	tests := []struct {
    65  		description      string
    66  		builds           []graph.Artifact
    67  		kpt              latest.KptDeploy
    68  		hasKustomization func(string) bool
    69  		commands         util.Command
    70  		expected         []string
    71  		shouldErr        bool
    72  	}{
    73  		{
    74  			description: "no manifest",
    75  			kpt: latest.KptDeploy{
    76  				Dir: ".",
    77  			},
    78  			commands: testutil.
    79  				CmdRunOut("kpt fn source .", ``).
    80  				AndRunOut("kpt fn run", ``).
    81  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``),
    82  		},
    83  		{
    84  			description: "invalid manifest",
    85  			kpt: latest.KptDeploy{
    86  				Dir: ".",
    87  			},
    88  			commands: testutil.
    89  				CmdRunOut("kpt fn source .", ``).
    90  				AndRunOut("kpt fn run", `foo`).
    91  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
    92  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
    93  			shouldErr: true,
    94  		},
    95  		{
    96  			description: "invalid user specified applyDir",
    97  			kpt: latest.KptDeploy{
    98  				Dir: ".",
    99  				Live: latest.KptLive{
   100  					Apply: latest.KptApplyInventory{
   101  						Dir: "invalid_path",
   102  					},
   103  				},
   104  			},
   105  			commands: testutil.
   106  				CmdRunOut("kpt fn source .", ``).
   107  				AndRunOut("kpt fn run", testPod).
   108  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   109  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   110  			shouldErr: true,
   111  		},
   112  
   113  		{
   114  			description: "kustomization and specified kpt fn",
   115  			kpt: latest.KptDeploy{
   116  				Dir: ".",
   117  				Fn:  latest.KptFn{FnPath: "kpt-func.yaml"},
   118  				Live: latest.KptLive{
   119  					Apply: latest.KptApplyInventory{
   120  						Dir: "valid_path",
   121  					},
   122  				},
   123  			},
   124  			hasKustomization: func(dir string) bool { return dir == tmpKustomizeDir },
   125  			commands: testutil.
   126  				CmdRunOut("kpt fn source .", ``).
   127  				AndRunOut("kpt fn run --fn-path kpt-func.yaml", testPod).
   128  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   129  				AndRunOut(fmt.Sprintf("kustomize build %v", tmpKustomizeDir), ``).
   130  				AndRun("kpt live apply valid_path --context kubecontext --namespace testNamespace"),
   131  			expected: []string{"default"},
   132  		},
   133  		{
   134  			description: "kpt live apply fails",
   135  			kpt: latest.KptDeploy{
   136  				Dir: ".",
   137  			},
   138  			commands: testutil.
   139  				CmdRunOut("kpt fn source .", ``).
   140  				AndRunOut("kpt fn run", testPod).
   141  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   142  				AndRunOut("kpt live init .kpt-hydrated --context kubecontext --namespace testNamespace", ``).
   143  				AndRunOut("kpt fn sink .kpt-hydrated", ``).
   144  				AndRunErr("kpt live apply .kpt-hydrated --context kubecontext --namespace testNamespace", errors.New("BUG")),
   145  			shouldErr: true,
   146  		},
   147  		{
   148  			description: "user specifies reconcile timeout and poll period",
   149  			kpt: latest.KptDeploy{
   150  				Dir: ".",
   151  				Live: latest.KptLive{
   152  					Apply: latest.KptApplyInventory{
   153  						Dir: "valid_path",
   154  					},
   155  					Options: latest.KptApplyOptions{
   156  						PollPeriod:       "5s",
   157  						ReconcileTimeout: "2m",
   158  					},
   159  				},
   160  			},
   161  			commands: testutil.
   162  				CmdRunOut("kpt fn source .", ``).
   163  				AndRunOut("kpt fn run", testPod).
   164  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   165  				AndRunOut("kpt fn sink valid_path", ``).
   166  				AndRun("kpt live apply valid_path --poll-period 5s --reconcile-timeout 2m --context kubecontext --namespace testNamespace"),
   167  		},
   168  		{
   169  			description: "user specifies invalid reconcile timeout and poll period",
   170  			kpt: latest.KptDeploy{
   171  				Dir: ".",
   172  				Live: latest.KptLive{
   173  					Apply: latest.KptApplyInventory{
   174  						Dir: "valid_path",
   175  					},
   176  					Options: latest.KptApplyOptions{
   177  						PollPeriod:       "foo",
   178  						ReconcileTimeout: "bar",
   179  					},
   180  				},
   181  			},
   182  			commands: testutil.
   183  				CmdRunOut("kpt fn source .", ``).
   184  				AndRunOut("kpt fn run", testPod).
   185  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   186  				AndRunOut("kpt fn sink valid_path", ``).
   187  				AndRun("kpt live apply valid_path --poll-period foo --reconcile-timeout bar --context kubecontext --namespace testNamespace"),
   188  		},
   189  		{
   190  			description: "user specifies prune propagation policy and prune timeout",
   191  			kpt: latest.KptDeploy{
   192  				Dir: ".",
   193  				Live: latest.KptLive{
   194  					Apply: latest.KptApplyInventory{
   195  						Dir: "valid_path",
   196  					},
   197  					Options: latest.KptApplyOptions{
   198  						PrunePropagationPolicy: "Orphan",
   199  						PruneTimeout:           "2m",
   200  					},
   201  				},
   202  			},
   203  			commands: testutil.
   204  				CmdRunOut("kpt fn source .", ``).
   205  				AndRunOut("kpt fn run", testPod).
   206  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   207  				AndRunOut("kpt fn sink valid_path", ``).
   208  				AndRun("kpt live apply valid_path --prune-propagation-policy Orphan --prune-timeout 2m --context kubecontext --namespace testNamespace"),
   209  		},
   210  		{
   211  			description: "user specifies invalid prune propagation policy and prune timeout",
   212  			kpt: latest.KptDeploy{
   213  				Dir: ".",
   214  				Live: latest.KptLive{
   215  					Apply: latest.KptApplyInventory{
   216  						Dir: "valid_path",
   217  					},
   218  					Options: latest.KptApplyOptions{
   219  						PrunePropagationPolicy: "foo",
   220  						PruneTimeout:           "bar",
   221  					},
   222  				},
   223  			},
   224  			commands: testutil.
   225  				CmdRunOut("kpt fn source .", ``).
   226  				AndRunOut("kpt fn run", testPod).
   227  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   228  				AndRunOut("kpt fn sink valid_path", ``).
   229  				AndRun("kpt live apply valid_path --prune-propagation-policy foo --prune-timeout bar --context kubecontext --namespace testNamespace"),
   230  		},
   231  	}
   232  	for _, test := range tests {
   233  		testutil.Run(t, test.description, func(t *testutil.T) {
   234  			t.Override(&util.DefaultExecCommand, test.commands)
   235  			t.Override(&client.Client, deployutil.MockK8sClient)
   236  			t.NewTempDir().Chdir()
   237  
   238  			var err error
   239  			k, err := NewDeployer(&kptConfig{}, &label.DefaultLabeller{}, &test.kpt)
   240  			if err != nil {
   241  				t.Fatalf("unexpected error occurred attempting to create new kpt deployer")
   242  			}
   243  			if test.hasKustomization != nil {
   244  				k.hasKustomization = test.hasKustomization
   245  			}
   246  
   247  			if k.Live.Apply.Dir == "valid_path" {
   248  				// 0755 is a permission setting where the owner can read, write, and execute.
   249  				// Others can read and execute but not modify the directory.
   250  				t.CheckNoError(os.Mkdir(k.Live.Apply.Dir, 0755))
   251  			}
   252  
   253  			err = k.Deploy(context.Background(), ioutil.Discard, test.builds)
   254  			t.CheckError(test.shouldErr, err)
   255  		})
   256  	}
   257  }
   258  
   259  func TestKpt_Dependencies(t *testing.T) {
   260  	tests := []struct {
   261  		description    string
   262  		kpt            latest.KptDeploy
   263  		createFiles    map[string]string
   264  		kustomizations map[string]string
   265  		expected       []string
   266  		shouldErr      bool
   267  	}{
   268  		{
   269  			description: "bad dir",
   270  			kpt: latest.KptDeploy{
   271  				Dir: "invalid_path",
   272  			},
   273  			shouldErr: true,
   274  		},
   275  		{
   276  			description: "empty dir and unspecified fnPath",
   277  			kpt: latest.KptDeploy{
   278  				Dir: ".",
   279  			},
   280  		},
   281  		{
   282  			description: "dir",
   283  			kpt: latest.KptDeploy{
   284  				Dir: ".",
   285  			},
   286  			createFiles: map[string]string{
   287  				"foo.yaml":  "",
   288  				"README.md": "",
   289  			},
   290  			expected: []string{"foo.yaml"},
   291  		},
   292  		{
   293  			description: "dir with subdirs and file path variants",
   294  			kpt: latest.KptDeploy{
   295  				Dir: ".",
   296  			},
   297  			createFiles: map[string]string{
   298  				"food.yml":           "",
   299  				"foo/bar.yaml":       "",
   300  				"foo/bat//bad.yml":   "",
   301  				"foo/bat\\README.md": "",
   302  			},
   303  			expected: []string{"foo/bar.yaml", "foo/bat/bad.yml", "food.yml"},
   304  		},
   305  		{
   306  			description: "fnpath inside directory",
   307  			kpt: latest.KptDeploy{
   308  				Dir: ".",
   309  				Fn:  latest.KptFn{FnPath: "."},
   310  			},
   311  			createFiles: map[string]string{
   312  				"./kpt-func.yaml": "",
   313  			},
   314  			expected: []string{"kpt-func.yaml"},
   315  		},
   316  		{
   317  			description: "fnpath outside directory",
   318  			kpt: latest.KptDeploy{
   319  				Dir: "./config",
   320  				Fn:  latest.KptFn{FnPath: "./kpt-fn"},
   321  			},
   322  			createFiles: map[string]string{
   323  				"./config/deployment.yaml": "",
   324  				"./kpt-fn/kpt-func.yaml":   "",
   325  			},
   326  			expected: []string{"config/deployment.yaml", "kpt-fn/kpt-func.yaml"},
   327  		},
   328  
   329  		{
   330  			description: "fnpath and dir and kustomization",
   331  			kpt: latest.KptDeploy{
   332  				Dir: ".",
   333  				Fn:  latest.KptFn{FnPath: "./kpt-fn"},
   334  			},
   335  			createFiles: map[string]string{
   336  				"./kpt-fn/func.yaml": "",
   337  			},
   338  			kustomizations: map[string]string{"kustomization.yaml": `configMapGenerator:
   339     - files: [app1.properties]`},
   340  			expected: []string{"app1.properties", "kpt-fn/func.yaml", "kustomization.yaml"},
   341  		},
   342  		{
   343  			description: "dependencies that can only be detected as a kustomization",
   344  			kpt: latest.KptDeploy{
   345  				Dir: ".",
   346  			},
   347  			kustomizations: map[string]string{"kustomization.yaml": `configMapGenerator:
   348     - files: [app1.properties]`},
   349  			expected: []string{"app1.properties", "kustomization.yaml"},
   350  		},
   351  		{
   352  			description: "kustomization.yml variant",
   353  			kpt: latest.KptDeploy{
   354  				Dir: ".",
   355  			},
   356  			kustomizations: map[string]string{"kustomization.yml": `configMapGenerator:
   357     - files: [app1.properties]`},
   358  			expected: []string{"app1.properties", "kustomization.yml"},
   359  		},
   360  		{
   361  			description: "Kustomization variant",
   362  			kpt: latest.KptDeploy{
   363  				Dir: ".",
   364  			},
   365  			kustomizations: map[string]string{"Kustomization": `configMapGenerator:
   366     - files: [app1.properties]`},
   367  			expected: []string{"Kustomization", "app1.properties"},
   368  		},
   369  		{
   370  			description: "incorrectly named kustomization",
   371  			kpt: latest.KptDeploy{
   372  				Dir: ".",
   373  			},
   374  			kustomizations: map[string]string{"customization": `configMapGenerator:
   375     - files: [app1.properties]`},
   376  		},
   377  	}
   378  	for _, test := range tests {
   379  		testutil.Run(t, test.description, func(t *testutil.T) {
   380  			tmpDir := t.NewTempDir().Chdir()
   381  
   382  			tmpDir.WriteFiles(test.createFiles)
   383  			tmpDir.WriteFiles(test.kustomizations)
   384  
   385  			k, err := NewDeployer(&kptConfig{}, &label.DefaultLabeller{}, &test.kpt)
   386  			if err != nil {
   387  				t.Fatalf("unexpected error occurred in NewDeployer: %v", err)
   388  			}
   389  
   390  			res, err := k.Dependencies()
   391  
   392  			t.CheckErrorAndDeepEqual(test.shouldErr, err, tmpDir.Paths(test.expected...), tmpDir.Paths(res...))
   393  		})
   394  	}
   395  }
   396  
   397  func TestKpt_Cleanup(t *testing.T) {
   398  	tests := []struct {
   399  		description string
   400  		applyDir    string
   401  		globalFlags []string
   402  		commands    util.Command
   403  		shouldErr   bool
   404  	}{
   405  		{
   406  			description: "invalid user specified applyDir",
   407  			applyDir:    "invalid_path",
   408  			shouldErr:   true,
   409  		},
   410  		{
   411  			description: "valid user specified applyDir w/o template resource",
   412  			applyDir:    "valid_path",
   413  			commands:    testutil.CmdRunErr("kpt live destroy valid_path --context kubecontext --namespace testNamespace", errors.New("BUG")),
   414  			shouldErr:   true,
   415  		},
   416  		{
   417  			description: "valid user specified applyDir w/ template resource (emulated)",
   418  			applyDir:    "valid_path",
   419  			commands:    testutil.CmdRun("kpt live destroy valid_path --context kubecontext --namespace testNamespace"),
   420  		},
   421  		{
   422  			description: "unspecified applyDir",
   423  			commands: testutil.
   424  				CmdRunOut("kpt live init .kpt-hydrated --context kubecontext --namespace testNamespace", "").
   425  				AndRun("kpt live destroy .kpt-hydrated --context kubecontext --namespace testNamespace"),
   426  		},
   427  	}
   428  	for _, test := range tests {
   429  		testutil.Run(t, test.description, func(t *testutil.T) {
   430  			t.Override(&util.DefaultExecCommand, test.commands)
   431  			t.NewTempDir().Chdir()
   432  
   433  			if test.applyDir == "valid_path" {
   434  				// 0755 is a permission setting where the owner can read, write, and execute.
   435  				// Others can read and execute but not modify the directory.
   436  				t.CheckNoError(os.Mkdir(test.applyDir, 0755))
   437  			}
   438  
   439  			k, err := NewDeployer(&kptConfig{
   440  				workingDir: ".",
   441  			}, &label.DefaultLabeller{}, &latest.KptDeploy{
   442  				Live: latest.KptLive{
   443  					Apply: latest.KptApplyInventory{
   444  						Dir: test.applyDir,
   445  					},
   446  				},
   447  			})
   448  			if err != nil {
   449  				t.Fatalf("unexpected error occurred in NewDeployer: %v", err)
   450  			}
   451  
   452  			err = k.Cleanup(context.Background(), ioutil.Discard, false)
   453  
   454  			t.CheckError(test.shouldErr, err)
   455  		})
   456  	}
   457  }
   458  
   459  func TestKpt_Render(t *testing.T) {
   460  	sanityCheck = func(ctx context.Context, dir string, buf io.Writer) error { return nil }
   461  	// The follow are outputs to `kpt fn run` commands.
   462  	output1 := `apiVersion: v1
   463  kind: Pod
   464  metadata:
   465    namespace: default
   466  spec:
   467    containers:
   468      - image: gcr.io/project/image1
   469        name: image1
   470  `
   471  
   472  	output2 := `apiVersion: v1
   473  kind: Pod
   474  metadata:
   475    namespace: default
   476  spec:
   477    containers:
   478      - image: gcr.io/project/image1
   479        name: image1
   480      - image: gcr.io/project/image2
   481        name: image2
   482  `
   483  
   484  	output3 := `apiVersion: v1
   485  kind: Pod
   486  metadata:
   487    namespace: default
   488  spec:
   489    containers:
   490      - image: gcr.io/project/image1
   491        name: image1
   492  ---
   493  apiVersion: v1
   494  kind: Pod
   495  metadata:
   496    namespace: default
   497  spec:
   498    containers:
   499      - image: gcr.io/project/image2
   500        name: image2
   501  `
   502  
   503  	tests := []struct {
   504  		description      string
   505  		builds           []graph.Artifact
   506  		labels           []string
   507  		kpt              latest.KptDeploy
   508  		commands         util.Command
   509  		hasKustomization func(string) bool
   510  		expected         string
   511  		shouldErr        bool
   512  	}{
   513  		{
   514  			description: "no fnPath or image specified",
   515  			builds: []graph.Artifact{
   516  				{
   517  					ImageName: "gcr.io/project/image1",
   518  					Tag:       "gcr.io/project/image1:tag1",
   519  				},
   520  			},
   521  			kpt: latest.KptDeploy{
   522  				Dir: ".",
   523  			},
   524  			commands: testutil.
   525  				CmdRunOut("kpt fn source .", ``).
   526  				AndRunOut("kpt fn run", output1).
   527  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   528  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   529  			expected: `apiVersion: v1
   530  kind: Pod
   531  metadata:
   532    namespace: default
   533  spec:
   534    containers:
   535      - image: gcr.io/project/image1:tag1
   536        name: image1
   537  `,
   538  		},
   539  		{
   540  			description: "fnPath specified, multiple resources, and labels",
   541  			builds: []graph.Artifact{
   542  				{
   543  					ImageName: "gcr.io/project/image1",
   544  					Tag:       "gcr.io/project/image1:tag1",
   545  				},
   546  				{
   547  					ImageName: "gcr.io/project/image2",
   548  					Tag:       "gcr.io/project/image2:tag2",
   549  				},
   550  			},
   551  			labels: []string{"user/label=test"},
   552  			kpt: latest.KptDeploy{
   553  				Dir: "test",
   554  				Fn:  latest.KptFn{FnPath: "kpt-func.yaml"},
   555  			},
   556  			commands: testutil.
   557  				CmdRunOut("kpt fn source test", ``).
   558  				AndRunOut("kpt fn run --fn-path kpt-func.yaml", output3).
   559  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   560  				AndRunOut("kpt fn sink .tmp-sink-dir/test", ``),
   561  			expected: `apiVersion: v1
   562  kind: Pod
   563  metadata:
   564    labels:
   565      user/label: test
   566    namespace: default
   567  spec:
   568    containers:
   569      - image: gcr.io/project/image1:tag1
   570        name: image1
   571  ---
   572  apiVersion: v1
   573  kind: Pod
   574  metadata:
   575    labels:
   576      user/label: test
   577    namespace: default
   578  spec:
   579    containers:
   580      - image: gcr.io/project/image2:tag2
   581        name: image2
   582  `,
   583  		},
   584  		{
   585  			description: "fn image specified, multiple images in resource",
   586  			builds: []graph.Artifact{
   587  				{
   588  					ImageName: "gcr.io/project/image1",
   589  					Tag:       "gcr.io/project/image1:tag1",
   590  				},
   591  				{
   592  					ImageName: "gcr.io/project/image2",
   593  					Tag:       "gcr.io/project/image2:tag2",
   594  				},
   595  			},
   596  			kpt: latest.KptDeploy{
   597  				Dir: ".",
   598  				Fn:  latest.KptFn{Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar"},
   599  			},
   600  			commands: testutil.
   601  				CmdRunOut("kpt fn source .", ``).
   602  				AndRunOut("kpt fn run --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", output2).
   603  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   604  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   605  			expected: `apiVersion: v1
   606  kind: Pod
   607  metadata:
   608    namespace: default
   609  spec:
   610    containers:
   611      - image: gcr.io/project/image1:tag1
   612        name: image1
   613      - image: gcr.io/project/image2:tag2
   614        name: image2
   615  `,
   616  		},
   617  		{
   618  			description: "empty output from pipeline",
   619  			builds: []graph.Artifact{
   620  				{
   621  					ImageName: "gcr.io/project/image1",
   622  					Tag:       "gcr.io/project/image1:tag1",
   623  				},
   624  			},
   625  			labels: []string{"user/label=test"},
   626  			kpt: latest.KptDeploy{
   627  				Dir: ".",
   628  			},
   629  			commands: testutil.
   630  				CmdRunOut("kpt fn source .", ``).
   631  				AndRunOut("kpt fn run", ``).
   632  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   633  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   634  			expected: "\n",
   635  		},
   636  		{
   637  			description: "both fnPath and image specified",
   638  			kpt: latest.KptDeploy{
   639  				Dir: ".",
   640  				Fn: latest.KptFn{
   641  					FnPath: "kpt-func.yaml",
   642  					Image:  "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar"},
   643  			},
   644  			commands: testutil.
   645  				CmdRunOut("kpt fn source .", ``).
   646  				AndRunOut("kpt fn source kpt-func.yaml", ``).
   647  				AndRunOut("kpt fn run --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``).
   648  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   649  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   650  			shouldErr: true,
   651  		},
   652  		{
   653  			description: "kustomization render",
   654  			builds: []graph.Artifact{
   655  				{
   656  					ImageName: "gcr.io/project/image1",
   657  					Tag:       "gcr.io/project/image1:tag1",
   658  				},
   659  			},
   660  			kpt: latest.KptDeploy{
   661  				Dir: ".",
   662  			},
   663  			commands: testutil.
   664  				CmdRunOut("kpt fn source .", ``).
   665  				AndRunOut("kpt fn run", ``).
   666  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   667  				AndRunOut(fmt.Sprintf("kustomize build %v", tmpKustomizeDir), output1),
   668  			hasKustomization: func(dir string) bool { return dir == tmpKustomizeDir },
   669  			expected: `apiVersion: v1
   670  kind: Pod
   671  metadata:
   672    namespace: default
   673  spec:
   674    containers:
   675      - image: gcr.io/project/image1:tag1
   676        name: image1
   677  `,
   678  		},
   679  		{
   680  			description: "reading configs from sourceDir fails",
   681  			kpt: latest.KptDeploy{
   682  				Dir: ".",
   683  			},
   684  			commands: testutil.
   685  				CmdRunOutErr("kpt fn source .", ``, errors.New("BUG")).
   686  				AndRunOut("kpt fn run", "invalid pipeline").
   687  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   688  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   689  			shouldErr: true,
   690  		},
   691  		{
   692  			description: "outputting configs to sinkDir fails",
   693  			kpt: latest.KptDeploy{
   694  				Dir: ".",
   695  			},
   696  			commands: testutil.
   697  				CmdRunOut("kpt fn source .", ``).
   698  				AndRunOut("kpt fn run", "invalid pipeline").
   699  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   700  				AndRunOutErr("kpt fn sink .tmp-sink-dir", ``, errors.New("BUG")),
   701  			shouldErr: true,
   702  		},
   703  		{
   704  			description: "kustomize build fails (invalid kustomization config)",
   705  			builds: []graph.Artifact{
   706  				{
   707  					ImageName: "gcr.io/project/image1",
   708  					Tag:       "gcr.io/project/image1:tag1",
   709  				},
   710  			},
   711  			kpt: latest.KptDeploy{
   712  				Dir: ".",
   713  			},
   714  			commands: testutil.
   715  				CmdRunOut("kpt fn source .", ``).
   716  				AndRunOut("kpt fn run", output1).
   717  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   718  				AndRunOutErr(fmt.Sprintf("kustomize build %v", tmpKustomizeDir), ``, errors.New("BUG")),
   719  			hasKustomization: func(dir string) bool { return dir == tmpKustomizeDir },
   720  			shouldErr:        true,
   721  		},
   722  		{
   723  			description: "kpt fn run fails",
   724  			kpt: latest.KptDeploy{
   725  				Dir: ".",
   726  			},
   727  			commands: testutil.
   728  				CmdRunOut("kpt fn source .", ``).
   729  				AndRunOutErr("kpt fn run", "invalid pipeline", errors.New("BUG")).
   730  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   731  			shouldErr: true,
   732  		},
   733  		{
   734  			description: "kpt fn run with --global-scope",
   735  			kpt: latest.KptDeploy{
   736  				Dir: ".",
   737  				Fn: latest.KptFn{
   738  					Image:       "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar",
   739  					GlobalScope: true,
   740  				},
   741  			},
   742  			commands: testutil.
   743  				CmdRunOut("kpt fn source .", ``).
   744  				AndRunOut("kpt fn run --global-scope --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``).
   745  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   746  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   747  			expected: "\n",
   748  		},
   749  		{
   750  			description: "kpt fn run with --mount arguments",
   751  			kpt: latest.KptDeploy{
   752  				Dir: ".",
   753  				Fn: latest.KptFn{
   754  					Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar",
   755  					Mount: []string{"type=bind", "src=$(pwd)", "dst=/source"},
   756  				},
   757  			},
   758  			commands: testutil.
   759  				CmdRunOut("kpt fn source .", ``).
   760  				AndRunOut("kpt fn run --mount type=bind,src=$(pwd),dst=/source --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``).
   761  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   762  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   763  			expected: "\n",
   764  		},
   765  		{
   766  			description: "kpt fn run with invalid --mount arguments",
   767  			kpt: latest.KptDeploy{
   768  				Dir: ".",
   769  				Fn: latest.KptFn{
   770  					Image: "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar",
   771  					Mount: []string{"foo", "", "bar"},
   772  				},
   773  			},
   774  			commands: testutil.
   775  				CmdRunOut("kpt fn source .", ``).
   776  				AndRunOut("kpt fn run --mount foo,,bar --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``).
   777  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   778  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   779  			expected: "\n",
   780  		},
   781  		{
   782  			description: "kpt fn run flag with --network and --network-name arguments",
   783  			kpt: latest.KptDeploy{
   784  				Dir: ".",
   785  				Fn: latest.KptFn{
   786  					Image:       "gcr.io/example.com/my-fn:v1.0.0 -- foo=bar",
   787  					Network:     true,
   788  					NetworkName: "foo",
   789  				},
   790  			},
   791  			commands: testutil.
   792  				CmdRunOut("kpt fn source .", ``).
   793  				AndRunOut("kpt fn run --network --network-name foo --image gcr.io/example.com/my-fn:v1.0.0 -- foo=bar", ``).
   794  				AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
   795  				AndRunOut("kpt fn sink .tmp-sink-dir", ``),
   796  			expected: "\n",
   797  		},
   798  	}
   799  	for _, test := range tests {
   800  		testutil.Run(t, test.description, func(t *testutil.T) {
   801  			t.Override(&util.DefaultExecCommand, test.commands)
   802  			t.NewTempDir().Chdir()
   803  
   804  			labeller := label.NewLabeller(false, test.labels, "")
   805  
   806  			k, err := NewDeployer(&kptConfig{workingDir: "."}, labeller, &test.kpt)
   807  			if err != nil {
   808  				t.Fatalf("unexpected error occurred in NewDeployer: %v", err)
   809  			}
   810  
   811  			if test.hasKustomization != nil {
   812  				k.hasKustomization = test.hasKustomization
   813  			}
   814  
   815  			var b bytes.Buffer
   816  			err = k.Render(context.Background(), &b, test.builds, true, "")
   817  
   818  			t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, b.String())
   819  		})
   820  	}
   821  }
   822  
   823  func TestKpt_GetApplyDir(t *testing.T) {
   824  	tests := []struct {
   825  		description string
   826  		live        latest.KptLive
   827  		expected    string
   828  		commands    util.Command
   829  		shouldErr   bool
   830  	}{
   831  		{
   832  			description: "specified an invalid applyDir",
   833  			live: latest.KptLive{
   834  				Apply: latest.KptApplyInventory{
   835  					Dir: "invalid_path",
   836  				},
   837  			},
   838  			shouldErr: true,
   839  		},
   840  		{
   841  			description: "specified a valid applyDir",
   842  			live: latest.KptLive{
   843  				Apply: latest.KptApplyInventory{
   844  					Dir: "valid_path",
   845  				},
   846  			},
   847  			expected: "valid_path",
   848  		},
   849  		{
   850  			description: "unspecified applyDir",
   851  			expected:    ".kpt-hydrated",
   852  			commands:    testutil.CmdRunOut("kpt live init .kpt-hydrated --context kubecontext --namespace testNamespace", ""),
   853  		},
   854  		{
   855  			description: "unspecified applyDir with specified inventory-id and namespace",
   856  			live: latest.KptLive{
   857  				Apply: latest.KptApplyInventory{
   858  					InventoryID:        "1a23bcde-4f56-7891-a2bc-de34fabcde5f6",
   859  					InventoryNamespace: "foo",
   860  				},
   861  			},
   862  			expected: ".kpt-hydrated",
   863  			commands: testutil.CmdRunOut("kpt live init .kpt-hydrated --inventory-id 1a23bcde-4f56-7891-a2bc-de34fabcde5f6 --context kubecontext --namespace foo", ""),
   864  		},
   865  		{
   866  			description: "existing template resource in .kpt-hydrated",
   867  			expected:    ".kpt-hydrated",
   868  		},
   869  	}
   870  	for _, test := range tests {
   871  		testutil.Run(t, test.description, func(t *testutil.T) {
   872  			t.Override(&util.DefaultExecCommand, test.commands)
   873  			tmpDir := t.NewTempDir().Chdir()
   874  
   875  			if test.live.Apply.Dir == test.expected {
   876  				// 0755 is a permission setting where the owner can read, write, and execute.
   877  				// Others can read and execute but not modify the directory.
   878  				t.CheckNoError(os.Mkdir(test.live.Apply.Dir, 0755))
   879  			}
   880  
   881  			if test.description == "existing template resource in .kpt-hydrated" {
   882  				tmpDir.Touch(".kpt-hydrated/inventory-template.yaml")
   883  			}
   884  
   885  			k, err := NewDeployer(&kptConfig{
   886  				workingDir: ".",
   887  			}, &label.DefaultLabeller{}, &latest.KptDeploy{
   888  				Live: test.live,
   889  			})
   890  			if err != nil {
   891  				t.Fatalf("unexpected error occurred in NewDeployer: %v", err)
   892  			}
   893  
   894  			applyDir, err := k.getApplyDir(context.Background())
   895  
   896  			t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, applyDir)
   897  		})
   898  	}
   899  }
   900  
   901  func TestKpt_KptCommandArgs(t *testing.T) {
   902  	tests := []struct {
   903  		description string
   904  		dir         string
   905  		commands    []string
   906  		flags       []string
   907  		globalFlags []string
   908  		expected    []string
   909  	}{
   910  		{
   911  			description: "empty",
   912  		},
   913  		{
   914  			description: "all inputs have len >0",
   915  			dir:         "test",
   916  			commands:    []string{"live", "apply"},
   917  			flags:       []string{"--fn-path", "kpt-func.yaml"},
   918  			globalFlags: []string{"-h"},
   919  			expected:    strings.Split("live apply test --fn-path kpt-func.yaml -h", " "),
   920  		},
   921  		{
   922  			description: "empty dir",
   923  			commands:    []string{"live", "apply"},
   924  			flags:       []string{"--fn-path", "kpt-func.yaml"},
   925  			globalFlags: []string{"-v", "3"},
   926  			expected:    strings.Split("live apply --fn-path kpt-func.yaml -v 3", " "),
   927  		},
   928  		{
   929  			description: "empty commands",
   930  			dir:         "test",
   931  			flags:       []string{"--fn-path", "kpt-func.yaml"},
   932  			globalFlags: []string{"-h"},
   933  			expected:    strings.Split("test --fn-path kpt-func.yaml -h", " "),
   934  		},
   935  		{
   936  			description: "empty flags",
   937  			dir:         "test",
   938  			commands:    []string{"live", "apply"},
   939  			globalFlags: []string{"-h"},
   940  			expected:    strings.Split("live apply test -h", " "),
   941  		},
   942  		{
   943  			description: "empty globalFlags",
   944  			dir:         "test",
   945  			commands:    []string{"live", "apply"},
   946  			flags:       []string{"--fn-path", "kpt-func.yaml"},
   947  			expected:    strings.Split("live apply test --fn-path kpt-func.yaml", " "),
   948  		},
   949  	}
   950  	for _, test := range tests {
   951  		testutil.Run(t, test.description, func(t *testutil.T) {
   952  			res := kptCommandArgs(test.dir, test.commands, test.flags, test.globalFlags)
   953  			t.CheckDeepEqual(test.expected, res)
   954  		})
   955  	}
   956  }
   957  
   958  // TestKpt_ExcludeKptFn checks the declarative kpt fn has expected annotations added.
   959  func TestKpt_ExcludeKptFn(t *testing.T) {
   960  	// A declarative fn.
   961  	testFn1 := []byte(`apiVersion: v1
   962  data:
   963    annotation_name: k1
   964    annotation_value: v1
   965  kind: ConfigMap
   966  metadata:
   967    annotations:
   968      config.kubernetes.io/function: fake`)
   969  	// A declarative fn which has `local-config` annotation specified.
   970  	testFn2 := []byte(`apiVersion: v1
   971  kind: ConfigMap
   972  metadata:
   973    annotations:
   974      config.kubernetes.io/function: fake
   975      config.kubernetes.io/local-config: "false"
   976  data:
   977    annotation_name: k2
   978    annotation_value: v2`)
   979  	testPod := []byte(`apiVersion: v1
   980  kind: Pod
   981  metadata:
   982    namespace: default
   983  spec:
   984    containers:
   985      - image: gcr.io/project/image1
   986        name: image1`)
   987  	tests := []struct {
   988  		description string
   989  		manifests   manifest.ManifestList
   990  		expected    manifest.ManifestList
   991  	}{
   992  		{
   993  			description: "Add `local-config` annotation to kpt fn",
   994  			manifests:   manifest.ManifestList{testFn1},
   995  			expected: manifest.ManifestList{[]byte(`apiVersion: v1
   996  data:
   997    annotation_name: k1
   998    annotation_value: v1
   999  kind: ConfigMap
  1000  metadata:
  1001    annotations:
  1002      config.kubernetes.io/function: fake
  1003      config.kubernetes.io/local-config: "true"`)},
  1004  		},
  1005  		{
  1006  			description: "Skip preset `local-config` annotation",
  1007  			manifests:   manifest.ManifestList{testFn2},
  1008  			expected: manifest.ManifestList{[]byte(`apiVersion: v1
  1009  kind: ConfigMap
  1010  metadata:
  1011    annotations:
  1012      config.kubernetes.io/function: fake
  1013      config.kubernetes.io/local-config: "false"
  1014  data:
  1015    annotation_name: k2
  1016    annotation_value: v2`)},
  1017  		},
  1018  		{
  1019  			description: "Valid in kpt fn pipeline.",
  1020  			manifests:   manifest.ManifestList{testFn1, testFn2, testPod},
  1021  			expected: manifest.ManifestList{[]byte(`apiVersion: v1
  1022  data:
  1023    annotation_name: k1
  1024    annotation_value: v1
  1025  kind: ConfigMap
  1026  metadata:
  1027    annotations:
  1028      config.kubernetes.io/function: fake
  1029      config.kubernetes.io/local-config: "true"`), []byte(`apiVersion: v1
  1030  kind: ConfigMap
  1031  metadata:
  1032    annotations:
  1033      config.kubernetes.io/function: fake
  1034      config.kubernetes.io/local-config: "false"
  1035  data:
  1036    annotation_name: k2
  1037    annotation_value: v2`), []byte(`apiVersion: v1
  1038  kind: Pod
  1039  metadata:
  1040    namespace: default
  1041  spec:
  1042    containers:
  1043      - image: gcr.io/project/image1
  1044        name: image1`)},
  1045  		},
  1046  	}
  1047  	for _, test := range tests {
  1048  		testutil.Run(t, test.description, func(t *testutil.T) {
  1049  			k, err := NewDeployer(&kptConfig{}, &label.DefaultLabeller{}, nil)
  1050  			if err != nil {
  1051  				t.Fatalf("unexpected error occurred in NewDeployer: %v", err)
  1052  			}
  1053  
  1054  			actualManifest, err := k.excludeKptFn(test.manifests)
  1055  			t.CheckErrorAndDeepEqual(false, err, test.expected.String(), actualManifest.String())
  1056  		})
  1057  	}
  1058  }
  1059  
  1060  func TestVersionCheck(t *testing.T) {
  1061  	tests := []struct {
  1062  		description    string
  1063  		commands       util.Command
  1064  		kustomizations map[string]string
  1065  		shouldErr      bool
  1066  		error          error
  1067  		out            string
  1068  	}{
  1069  		{
  1070  			description: "Both kpt and kustomize versions are good",
  1071  			commands: testutil.
  1072  				CmdRunOut("kpt version", `0.38.1`).
  1073  				AndRunOut("kustomize version", `{Version:v3.6.1 GitCommit:a0072a2cf92bf5399565e84c621e1e7c5c1f1094 BuildDate:2020-06-15T20:19:07Z GoOs:darwin GoArch:amd64}`),
  1074  			kustomizations: map[string]string{"Kustomization": `resources:
  1075  				- foo.yaml`},
  1076  			shouldErr: false,
  1077  			error:     nil,
  1078  		},
  1079  		{
  1080  			description: "kpt is not installed",
  1081  			commands:    testutil.CmdRunOutErr("kpt version", "", errors.New("BUG")),
  1082  			shouldErr:   true,
  1083  			error: fmt.Errorf("kpt is not installed yet\nSee kpt installation: %v",
  1084  				kptDownloadLink),
  1085  		},
  1086  		{
  1087  			description: "kustomize is not used, kpt version is good",
  1088  			commands: testutil.
  1089  				CmdRunOut("kpt version", `0.38.1`),
  1090  			shouldErr: false,
  1091  			error:     nil,
  1092  		},
  1093  		{
  1094  			description: "kustomize is used but not installed",
  1095  			commands: testutil.
  1096  				CmdRunOut("kpt version", `0.38.1`).
  1097  				AndRunOutErr("kustomize version", "", errors.New("BUG")),
  1098  			kustomizations: map[string]string{"Kustomization": `resources:
  1099  					- foo.yaml`},
  1100  			shouldErr: true,
  1101  			error: fmt.Errorf("kustomize is not installed yet\nSee kpt installation: %v",
  1102  				kustomizeDownloadLink),
  1103  		},
  1104  		{
  1105  			description: "kpt version is too old (<0.38.1)",
  1106  			commands: testutil.
  1107  				CmdRunOut("kpt version", `0.37.0`),
  1108  			kustomizations: map[string]string{"Kustomization": `resources:
  1109  					- foo.yaml`},
  1110  			shouldErr: true,
  1111  			error: fmt.Errorf("you are using kpt \"v0.37.0\"\nPlease install "+
  1112  				"kpt %v <= version < %v\nSee kpt installation: %v",
  1113  				kptMinVersionInclusive, kptMaxVersionExclusive, kptDownloadLink),
  1114  		},
  1115  		{
  1116  			description: "kpt version is too new (>=1.0.0-alpha)",
  1117  			commands: testutil.
  1118  				CmdRunOut("kpt version", `1.0.0-beta.4`),
  1119  			kustomizations: map[string]string{"Kustomization": `resources:
  1120  					- foo.yaml`},
  1121  			shouldErr: true,
  1122  			error: fmt.Errorf("you are using kpt \"v1.0.0-beta.4\"\nPlease install "+
  1123  				"kpt %v <= version < %v\nSee kpt installation: %v",
  1124  				kptMinVersionInclusive, kptMaxVersionExclusive, kptDownloadLink),
  1125  		},
  1126  		{
  1127  			description: "kpt version is unknown",
  1128  			commands: testutil.
  1129  				CmdRunOut("kpt version", `unknown`),
  1130  			kustomizations: map[string]string{"Kustomization": `resources:
  1131  					- foo.yaml`},
  1132  			shouldErr: true,
  1133  			error: fmt.Errorf("unknown kpt version unknown\nPlease install "+
  1134  				"kpt %v <= version < %v\nSee kpt installation: %v",
  1135  				kptMinVersionInclusive, kptMaxVersionExclusive, kptDownloadLink),
  1136  		},
  1137  		{
  1138  			description: "kustomize versions is too old (< v3.2.3)",
  1139  			commands: testutil.
  1140  				CmdRunOut("kpt version", `0.38.1`).
  1141  				AndRunOut("kustomize version", `{Version:v0.0.1 GitCommit:a0072a2cf92bf5399565e84c621e1e7c5c1f1094 BuildDate:2020-06-15T20:19:07Z GoOs:darwin GoArch:amd64}`),
  1142  			kustomizations: map[string]string{"Kustomization": `resources:
  1143  					- foo.yaml`},
  1144  			shouldErr: false,
  1145  			out: fmt.Sprintf("you are using kustomize version \"v0.0.1\" "+
  1146  				"(recommended >= %v). You can download the official kustomize from %v\n",
  1147  				kustomizeMinVersion, kustomizeDownloadLink),
  1148  		},
  1149  		{
  1150  			description: "kustomize version is unknown",
  1151  			commands: testutil.
  1152  				CmdRunOut("kpt version", `0.38.1`).
  1153  				AndRunOut("kustomize version", `{Version:unknown GitCommit:a0072a2cf92bf5399565e84c621e1e7c5c1f1094 BuildDate:2020-06-15T20:19:07Z GoOs:darwin GoArch:amd64}`),
  1154  			kustomizations: map[string]string{"Kustomization": `resources:
  1155  					- foo.yaml`},
  1156  			shouldErr: false,
  1157  			out: fmt.Sprintf("you are using kustomize version \"unknown\" "+
  1158  				"(recommended >= %v). You can download the official kustomize from %v\n",
  1159  				kustomizeMinVersion, kustomizeDownloadLink),
  1160  		},
  1161  		{
  1162  			description: "kustomize version is non-official",
  1163  			commands: testutil.
  1164  				CmdRunOut("kpt version", `0.38.1`).
  1165  				AndRunOut("kustomize version", `UNKNOWN`),
  1166  			kustomizations: map[string]string{"Kustomization": `resources:
  1167  					- foo.yaml`},
  1168  			shouldErr: false,
  1169  			out: fmt.Sprintf("unable to determine kustomize version from \"UNKNOWN\"\n"+
  1170  				"You can download the official kustomize (recommended >= %v) from %v\n",
  1171  				kustomizeMinVersion, kustomizeDownloadLink),
  1172  		},
  1173  	}
  1174  	for _, test := range tests {
  1175  		var buf bytes.Buffer
  1176  		testutil.Run(t, test.description, func(t *testutil.T) {
  1177  			t.Override(&util.DefaultExecCommand, test.commands)
  1178  			tmpDir := t.NewTempDir().Chdir()
  1179  			tmpDir.WriteFiles(test.kustomizations)
  1180  			err := versionCheck(context.Background(), "", io.Writer(&buf))
  1181  			t.CheckError(test.shouldErr, err)
  1182  			if test.shouldErr {
  1183  				testutil.CheckDeepEqual(t.T, test.error.Error(), err.Error())
  1184  			}
  1185  		})
  1186  		testutil.CheckDeepEqual(t, test.out, buf.String())
  1187  	}
  1188  }
  1189  
  1190  func TestNonEmptyKubeconfig(t *testing.T) {
  1191  	commands := testutil.CmdRunOut("kpt fn source .", ``).
  1192  		AndRunOut("kpt fn run", testPod).
  1193  		AndRunOut(fmt.Sprintf("kpt fn sink %v", tmpKustomizeDir), ``).
  1194  		AndRunOut("kpt fn sink valid_path", ``).
  1195  		AndRun("kpt live apply valid_path --context kubecontext --kubeconfig testConfigPath --namespace testNamespace")
  1196  
  1197  	testutil.Run(t, "", func(t *testutil.T) {
  1198  		t.Override(&util.DefaultExecCommand, commands)
  1199  		t.Override(&client.Client, deployutil.MockK8sClient)
  1200  		k, err := NewDeployer(&kptConfig{config: "testConfigPath"}, &label.DefaultLabeller{}, &latest.KptDeploy{
  1201  			Dir: ".",
  1202  			Live: latest.KptLive{
  1203  				Apply: latest.KptApplyInventory{
  1204  					Dir: "valid_path",
  1205  				},
  1206  			},
  1207  		})
  1208  		if err != nil {
  1209  			t.Fatalf("unexpected error occurred in NewDeployer: %v", err)
  1210  		}
  1211  
  1212  		t.CheckNoError(os.Mkdir(k.Live.Apply.Dir, 0755))
  1213  		defer os.RemoveAll(k.Live.Apply.Dir)
  1214  		err = k.Deploy(context.Background(), ioutil.Discard, []graph.Artifact{})
  1215  		t.CheckNoError(err)
  1216  	})
  1217  }
  1218  
  1219  type kptConfig struct {
  1220  	runcontext.RunContext // Embedded to provide the default values.
  1221  	workingDir            string
  1222  	config                string
  1223  }
  1224  
  1225  func (c *kptConfig) WorkingDir() string                                  { return c.workingDir }
  1226  func (c *kptConfig) GetKubeContext() string                              { return kubectl.TestKubeContext }
  1227  func (c *kptConfig) GetKubeNamespace() string                            { return kubectl.TestNamespace }
  1228  func (c *kptConfig) GetKubeConfig() string                               { return c.config }
  1229  func (c *kptConfig) PortForwardResources() []*latest.PortForwardResource { return nil }