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 }