github.com/SamarSidharth/kpt@v0.0.0-20231122062228-c7d747ae3ace/pkg/live/rgstream_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 live
    16  
    17  import (
    18  	"sort"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    24  	"k8s.io/apimachinery/pkg/runtime/schema"
    25  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    26  	"k8s.io/kubectl/pkg/scheme"
    27  	"sigs.k8s.io/cli-utils/pkg/manifestreader"
    28  	"sigs.k8s.io/cli-utils/pkg/object"
    29  )
    30  
    31  func TestResourceStreamManifestReader_Read(t *testing.T) {
    32  	_ = apiextv1.AddToScheme(scheme.Scheme)
    33  	testCases := map[string]struct {
    34  		manifests      map[string]string
    35  		namespace      string
    36  		expectedObjs   object.ObjMetadataSet
    37  		expectedErrMsg string
    38  	}{
    39  		"Kptfile is excluded": {
    40  			manifests: map[string]string{
    41  				"Kptfile": kptFile,
    42  			},
    43  			namespace:    "test-namespace",
    44  			expectedObjs: []object.ObjMetadata{},
    45  		},
    46  		"Only a pod is valid": {
    47  			manifests: map[string]string{
    48  				"pod-a.yaml": podA,
    49  			},
    50  			namespace: "test-namespace",
    51  			expectedObjs: []object.ObjMetadata{
    52  				{
    53  					GroupKind: schema.GroupKind{
    54  						Kind: "Pod",
    55  					},
    56  					Name:      "pod-a",
    57  					Namespace: "test-namespace",
    58  				},
    59  			},
    60  		},
    61  		"Multiple resources are valid": {
    62  			manifests: map[string]string{
    63  				"pod-a.yaml":        podA,
    64  				"deployment-a.yaml": deploymentA,
    65  			},
    66  			namespace: "test-namespace",
    67  			expectedObjs: []object.ObjMetadata{
    68  				{
    69  					GroupKind: schema.GroupKind{
    70  						Kind: "Pod",
    71  					},
    72  					Name:      "pod-a",
    73  					Namespace: "test-namespace",
    74  				},
    75  				{
    76  					GroupKind: schema.GroupKind{
    77  						Group: "apps",
    78  						Kind:  "Deployment",
    79  					},
    80  					Name:      "test-deployment",
    81  					Namespace: "test-namespace",
    82  				},
    83  			},
    84  		},
    85  		"CR and CRD in the same set is ok": {
    86  			manifests: map[string]string{
    87  				"crd.yaml": crd,
    88  				"cr.yaml":  cr,
    89  			},
    90  			namespace: "test-namespace",
    91  			expectedObjs: []object.ObjMetadata{
    92  				{
    93  					GroupKind: schema.GroupKind{
    94  						Group: "custom.io",
    95  						Kind:  "Custom",
    96  					},
    97  					Name: "cr",
    98  				},
    99  				{
   100  					GroupKind: schema.GroupKind{
   101  						Group: "apiextensions.k8s.io",
   102  						Kind:  "CustomResourceDefinition",
   103  					},
   104  					Name: "custom.io",
   105  				},
   106  			},
   107  		},
   108  		"CR with unknown type is not allowed": {
   109  			manifests: map[string]string{
   110  				"cr.yaml": cr,
   111  			},
   112  			namespace:      "test-namespace",
   113  			expectedErrMsg: "unknown resource types: custom.io/v1/Custom",
   114  		},
   115  	}
   116  
   117  	for tn, tc := range testCases {
   118  		t.Run(tn, func(t *testing.T) {
   119  			tf := cmdtesting.NewTestFactory().WithNamespace("test-ns")
   120  			defer tf.Cleanup()
   121  
   122  			mapper, err := tf.ToRESTMapper()
   123  			if !assert.NoError(t, err) {
   124  				t.FailNow()
   125  			}
   126  
   127  			streamStr := ""
   128  			for _, manifestStr := range tc.manifests {
   129  				streamStr = streamStr + "\n---\n" + manifestStr
   130  			}
   131  			streamStr += "\n---\n"
   132  			rgStreamReader := &ResourceGroupStreamManifestReader{
   133  				ReaderName: "rgstream",
   134  				Reader:     strings.NewReader(streamStr),
   135  				ReaderOptions: manifestreader.ReaderOptions{
   136  					Mapper:           mapper,
   137  					Namespace:        tc.namespace,
   138  					EnforceNamespace: false,
   139  				},
   140  			}
   141  			readObjs, err := rgStreamReader.Read()
   142  			if tc.expectedErrMsg != "" {
   143  				if !assert.Error(t, err) {
   144  					t.FailNow()
   145  				}
   146  				assert.Contains(t, err.Error(), tc.expectedErrMsg)
   147  				return
   148  			}
   149  			assert.NoError(t, err)
   150  
   151  			readObjMetas := object.UnstructuredSetToObjMetadataSet(readObjs)
   152  
   153  			sort.Slice(readObjMetas, func(i, j int) bool {
   154  				return readObjMetas[i].String() < readObjMetas[j].String()
   155  			})
   156  			assert.Equal(t, tc.expectedObjs, readObjMetas)
   157  		})
   158  	}
   159  }
   160  
   161  func TestResourceStreamManifestReader_isKptfile(t *testing.T) {
   162  	testCases := map[string]struct {
   163  		kptfile  string
   164  		expected bool
   165  	}{
   166  		"Empty kptfile is invalid": {
   167  			kptfile:  "",
   168  			expected: false,
   169  		},
   170  		"Kptfile with foo/bar GVK is invalid": {
   171  			kptfile: `
   172  apiVersion: foo/v1
   173  kind: FooBar
   174  metadata:
   175    name: test1
   176  `,
   177  			expected: false,
   178  		},
   179  		"Kptfile with bad apiVersion is invalid": {
   180  			kptfile: `
   181  apiVersion: foo/v1
   182  kind: Kptfile
   183  metadata:
   184    name: test1
   185  `,
   186  			expected: false,
   187  		},
   188  		"Kptfile with wrong kind is invalid": {
   189  			kptfile: `
   190  apiVersion: kpt.dev/v1
   191  kind: foo
   192  metadata:
   193    name: test1
   194  `,
   195  			expected: false,
   196  		},
   197  		"Kptfile with different GVK is invalid": {
   198  			kptfile: `
   199  kind: Deployment
   200  apiVersion: apps/v1
   201  metadata:
   202    name: test-deployment
   203  spec:
   204    replicas: 1
   205  `,
   206  			expected: false,
   207  		},
   208  		"Wrong fields (foo/bar) in kptfile is invalid": {
   209  			kptfile: `
   210  apiVersion: kpt.dev/v1
   211  kind: Kptfile
   212  foo: bar
   213  `,
   214  			expected: false,
   215  		},
   216  		"Kptfile with deployment/replicas fields is invalid": {
   217  			kptfile: `
   218  apiVersion: kpt.dev/v1
   219  kind: Kptfile
   220  metadata:
   221    name: test-deployment
   222  spec:
   223    replicas: 1
   224  `,
   225  			expected: false,
   226  		},
   227  		"Wrong fields (foo/bar) in kptfile inventory is invalid": {
   228  			kptfile: `
   229  apiVersion: kpt.dev/v1
   230  kind: Kptfile
   231  metadata:
   232    name: test1
   233  inventory:
   234    namespace: test-namespace
   235    name: inventory-obj-name
   236    foo: bar
   237  `,
   238  			expected: false,
   239  		},
   240  		"Full, regular kptfile is valid": {
   241  			kptfile:  kptFile,
   242  			expected: true,
   243  		},
   244  		"Kptfile with only GVK is valid": {
   245  			kptfile: `
   246  apiVersion: kpt.dev/v1
   247  kind: Kptfile
   248  `,
   249  			expected: true,
   250  		},
   251  		"Kptfile missing optional inventory is still valid": {
   252  			kptfile: `
   253  apiVersion: kpt.dev/v1
   254  kind: Kptfile
   255  metadata:
   256    name: test1
   257  `,
   258  			expected: true,
   259  		},
   260  	}
   261  
   262  	for tn, tc := range testCases {
   263  		t.Run(tn, func(t *testing.T) {
   264  			actual := isKptfile([]byte(tc.kptfile))
   265  			if tc.expected != actual {
   266  				t.Errorf("expected isKptfile (%t), got (%t)", tc.expected, actual)
   267  			}
   268  		})
   269  	}
   270  }