github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/deploy/util/util_test.go (about)

     1  /*
     2  Copyright 2019 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"testing"
    21  
    22  	v1 "k8s.io/api/core/v1"
    23  	"k8s.io/apimachinery/pkg/runtime/schema"
    24  
    25  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
    26  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
    27  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    30  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml"
    31  	"github.com/GoogleContainerTools/skaffold/testutil"
    32  )
    33  
    34  func TestConsolidateNamespaces(t *testing.T) {
    35  	tests := []struct {
    36  		description   string
    37  		oldNamespaces []string
    38  		newNamespaces []string
    39  		expected      []string
    40  	}{
    41  		{
    42  			description:   "update namespace when not present in runContext",
    43  			oldNamespaces: []string{"test"},
    44  			newNamespaces: []string{"another"},
    45  			expected:      []string{"another", "test"},
    46  		},
    47  		{
    48  			description:   "update namespace with duplicates should not return duplicate",
    49  			oldNamespaces: []string{"test", "foo"},
    50  			newNamespaces: []string{"another", "foo", "another"},
    51  			expected:      []string{"another", "foo", "test"},
    52  		},
    53  		{
    54  			description:   "update namespaces when namespaces is empty",
    55  			oldNamespaces: []string{"test", "foo"},
    56  			newNamespaces: []string{},
    57  			expected:      []string{"test", "foo"},
    58  		},
    59  		{
    60  			description:   "update namespaces when runcontext namespaces is empty",
    61  			oldNamespaces: []string{},
    62  			newNamespaces: []string{"test", "another"},
    63  			expected:      []string{"another", "test"},
    64  		},
    65  		{
    66  			description:   "update namespaces when both namespaces and runcontext namespaces is empty",
    67  			oldNamespaces: []string{},
    68  			newNamespaces: []string{},
    69  			expected:      []string{},
    70  		},
    71  		{
    72  			description:   "update namespace when runcontext namespace has an empty string",
    73  			oldNamespaces: []string{""},
    74  			newNamespaces: []string{"another"},
    75  			expected:      []string{"another"},
    76  		},
    77  		{
    78  			description:   "update namespace when namespace is empty string",
    79  			oldNamespaces: []string{"test"},
    80  			newNamespaces: []string{""},
    81  			expected:      []string{"test"},
    82  		},
    83  		{
    84  			description:   "update namespace when namespace is empty string and runContext is empty",
    85  			oldNamespaces: []string{},
    86  			newNamespaces: []string{""},
    87  		},
    88  	}
    89  	for _, test := range tests {
    90  		testutil.Run(t, test.description, func(t *testutil.T) {
    91  			ns := ConsolidateNamespaces(test.oldNamespaces, test.newNamespaces)
    92  
    93  			t.CheckDeepEqual(test.expected, ns)
    94  		})
    95  	}
    96  }
    97  
    98  func TestAddTagsToPodSelector(t *testing.T) {
    99  	tests := []struct {
   100  		description       string
   101  		artifacts         []graph.Artifact
   102  		deployerArtifacts []graph.Artifact
   103  		expectedImages    []string
   104  	}{
   105  		{
   106  			description: "empty image list",
   107  		},
   108  		{
   109  			description: "non-matching image results in empty list",
   110  			artifacts: []graph.Artifact{
   111  				{
   112  					ImageName: "my-image",
   113  					Tag:       "my-image-tag",
   114  				},
   115  			},
   116  			deployerArtifacts: []graph.Artifact{
   117  				{
   118  					ImageName: "not-my-image",
   119  				},
   120  			},
   121  		},
   122  		{
   123  			description: "matching images appear in list",
   124  			artifacts: []graph.Artifact{
   125  				{
   126  					ImageName: "my-image1",
   127  					Tag:       "registry.example.com/repo/my-image1:tag1",
   128  				},
   129  				{
   130  					ImageName: "my-image2",
   131  					Tag:       "registry.example.com/repo/my-image2:tag2",
   132  				},
   133  				{
   134  					ImageName: "image-not-in-deployer",
   135  					Tag:       "registry.example.com/repo/my-image3:tag3",
   136  				},
   137  			},
   138  			deployerArtifacts: []graph.Artifact{
   139  				{
   140  					ImageName: "my-image1",
   141  				},
   142  				{
   143  					ImageName: "my-image2",
   144  				},
   145  			},
   146  			expectedImages: []string{
   147  				"registry.example.com/repo/my-image1:tag1",
   148  				"registry.example.com/repo/my-image2:tag2",
   149  			},
   150  		},
   151  		{
   152  			description: "images from manifest files with ko:// scheme prefix are sanitized before matching",
   153  			artifacts: []graph.Artifact{
   154  				{
   155  					ImageName: "ko://git.example.com/Foo/bar",
   156  					Tag:       "registry.example.com/repo/git.example.com/foo/bar:tag",
   157  				},
   158  			},
   159  			deployerArtifacts: []graph.Artifact{
   160  				{
   161  					ImageName: "git.example.com/foo/bar",
   162  					Tag:       "ko://git.example.com/Foo/bar",
   163  				},
   164  			},
   165  			expectedImages: []string{
   166  				"registry.example.com/repo/git.example.com/foo/bar:tag",
   167  			},
   168  		},
   169  	}
   170  	for _, test := range tests {
   171  		testutil.Run(t, test.description, func(t *testutil.T) {
   172  			podSelector := kubernetes.NewImageList()
   173  			AddTagsToPodSelector(test.artifacts, test.deployerArtifacts, podSelector)
   174  			for _, expectedImage := range test.expectedImages {
   175  				if exists := podSelector.Select(&v1.Pod{
   176  					Spec: v1.PodSpec{
   177  						Containers: []v1.Container{
   178  							{Image: expectedImage},
   179  						},
   180  					},
   181  				}); !exists {
   182  					t.Errorf("expected image list to contain %s", expectedImage)
   183  				}
   184  			}
   185  		})
   186  	}
   187  }
   188  
   189  func TestConsolidateTransformConfiguration(t *testing.T) {
   190  	tests := []struct {
   191  		description           string
   192  		shouldErr             bool
   193  		allowSchemaTransforms []latest.ResourceFilter
   194  		denySchemaTransforms  []latest.ResourceFilter
   195  		flagTransforms        latest.ResourceSelectorConfig
   196  		expected              func(map[schema.GroupKind]latest.ResourceFilter, map[schema.GroupKind]latest.ResourceFilter) (map[schema.GroupKind]latest.ResourceFilter, map[schema.GroupKind]latest.ResourceFilter)
   197  	}{
   198  		{
   199  			description: "verify schema transform configuration outprioritizes default hardcoded transform configuration",
   200  			denySchemaTransforms: []latest.ResourceFilter{
   201  				{
   202  					GroupKind: "Deployment.apps",
   203  				},
   204  			},
   205  			expected: func(allow map[schema.GroupKind]latest.ResourceFilter, deny map[schema.GroupKind]latest.ResourceFilter) (map[schema.GroupKind]latest.ResourceFilter, map[schema.GroupKind]latest.ResourceFilter) {
   206  				// Deployment.apps removed from hardcoded allowlist
   207  				delete(allow, schema.GroupKind{Group: "apps", Kind: "Deployment"})
   208  				// Deployment.apps added to denylist
   209  				deny[schema.GroupKind{Group: "apps", Kind: "Deployment"}] = latest.ResourceFilter{GroupKind: "Deployment.apps"}
   210  				return allow, deny
   211  			},
   212  		},
   213  		{
   214  			description: "verify flag transform configuration outprioritizes schema transform configuration",
   215  			flagTransforms: latest.ResourceSelectorConfig{
   216  				Allow: []latest.ResourceFilter{
   217  					{
   218  						GroupKind: "Test.skaffold.dev",
   219  					},
   220  				},
   221  			},
   222  			denySchemaTransforms: []latest.ResourceFilter{
   223  				{
   224  					GroupKind: "Test.skaffold.dev",
   225  				},
   226  			},
   227  			expected: func(allow map[schema.GroupKind]latest.ResourceFilter, deny map[schema.GroupKind]latest.ResourceFilter) (map[schema.GroupKind]latest.ResourceFilter, map[schema.GroupKind]latest.ResourceFilter) {
   228  				// Test.skaffold.dev added to allowlist as flag config outprioritizes schema config
   229  				allow[schema.GroupKind{Group: "skaffold.dev", Kind: "Test"}] = latest.ResourceFilter{GroupKind: "Test.skaffold.dev"}
   230  				return allow, deny
   231  			},
   232  		},
   233  		{
   234  			description: "verify denylist outprioritizes allowlist transform configuration (for same config input source)",
   235  			flagTransforms: latest.ResourceSelectorConfig{
   236  				Allow: []latest.ResourceFilter{
   237  					{
   238  						GroupKind: "Test.skaffold.dev",
   239  					},
   240  				},
   241  				Deny: []latest.ResourceFilter{
   242  					{
   243  						GroupKind: "Test.skaffold.dev",
   244  					},
   245  				},
   246  			},
   247  			expected: func(allow map[schema.GroupKind]latest.ResourceFilter, deny map[schema.GroupKind]latest.ResourceFilter) (map[schema.GroupKind]latest.ResourceFilter, map[schema.GroupKind]latest.ResourceFilter) {
   248  				// Test.skaffold.dev added to denylist as deny config outprioritizes allow config for same priority config source (both flag config)
   249  				deny[schema.GroupKind{Group: "skaffold.dev", Kind: "Test"}] = latest.ResourceFilter{GroupKind: "Test.skaffold.dev"}
   250  				return allow, deny
   251  			},
   252  		},
   253  	}
   254  	for _, test := range tests {
   255  		testutil.Run(t, test.description, func(t *testutil.T) {
   256  			// convert flagTransform struct to yaml
   257  			buf, err := yaml.Marshal(test.flagTransforms)
   258  			if err != nil {
   259  				t.Fatalf("error marshalling flagTransforms test inputs: %v", err)
   260  			}
   261  
   262  			// denybuf, err := yaml.Marshal(test.denyFlagTransforms)
   263  			// if err != nil {
   264  			// t.Fatalf("error marshalling denyFlagTransforms test inputs: %v", err)
   265  			// }
   266  
   267  			flagTransformYAMLFile := t.TempFile("TestConsolidateTransformConfiguration", buf)
   268  
   269  			cfg := &mockDeployConfig{
   270  				transformAllowList: test.allowSchemaTransforms,
   271  				transformDenyList:  test.denySchemaTransforms,
   272  				transformRulesFile: flagTransformYAMLFile,
   273  			}
   274  			allowlist, denylist, err := ConsolidateTransformConfiguration(cfg)
   275  			t.CheckError(test.shouldErr, err)
   276  
   277  			copyAllow := map[schema.GroupKind]latest.ResourceFilter{}
   278  			for k, v := range manifest.TransformAllowlist {
   279  				copyAllow[k] = v
   280  			}
   281  
   282  			copyDeny := map[schema.GroupKind]latest.ResourceFilter{}
   283  			for k, v := range manifest.TransformDenylist {
   284  				copyDeny[k] = v
   285  			}
   286  			expectedAllowlist, expectedDenyList := test.expected(copyAllow, copyDeny)
   287  			t.CheckDeepEqual(expectedAllowlist, allowlist)
   288  			t.CheckDeepEqual(expectedDenyList, denylist)
   289  		})
   290  	}
   291  }
   292  
   293  type mockDeployConfig struct {
   294  	runcontext.RunContext // Embedded to provide the default values.
   295  	transformAllowList    []latest.ResourceFilter
   296  	transformDenyList     []latest.ResourceFilter
   297  	transformRulesFile    string
   298  }
   299  
   300  func (c *mockDeployConfig) ForceDeploy() bool                                   { return false }
   301  func (c *mockDeployConfig) GetKubeConfig() string                               { return "" }
   302  func (c *mockDeployConfig) GetKubeContext() string                              { return "" }
   303  func (c *mockDeployConfig) GetKubeNamespace() string                            { return "" }
   304  func (c *mockDeployConfig) ConfigurationFile() string                           { return "" }
   305  func (c *mockDeployConfig) PortForwardResources() []*latest.PortForwardResource { return nil }
   306  func (c *mockDeployConfig) TransformAllowList() []latest.ResourceFilter {
   307  	return c.transformAllowList
   308  }
   309  func (c *mockDeployConfig) TransformDenyList() []latest.ResourceFilter {
   310  	return c.transformDenyList
   311  }
   312  func (c *mockDeployConfig) TransformRulesFile() string { return c.transformRulesFile }