github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/commands/live/init/cmdliveinit_test.go (about) 1 // Copyright 2020 Google LLC 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 init 16 17 import ( 18 "os" 19 "path/filepath" 20 "regexp" 21 "testing" 22 "time" 23 24 "github.com/GoogleContainerTools/kpt/internal/pkg" 25 "github.com/GoogleContainerTools/kpt/internal/printer/fake" 26 "github.com/GoogleContainerTools/kpt/internal/testutil" 27 kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" 28 rgfilev1alpha1 "github.com/GoogleContainerTools/kpt/pkg/api/resourcegroup/v1alpha1" 29 "github.com/stretchr/testify/assert" 30 "k8s.io/cli-runtime/pkg/genericclioptions" 31 cmdtesting "k8s.io/kubectl/pkg/cmd/testing" 32 "sigs.k8s.io/kustomize/kyaml/filesys" 33 ) 34 35 var ( 36 inventoryName = "inventory-obj-name" 37 inventoryNamespace = "test-namespace" 38 inventoryID = "XXXXXXX-OOOOOOOOOO-XXXX" 39 ) 40 41 var kptFile = ` 42 apiVersion: kpt.dev/v1 43 kind: Kptfile 44 metadata: 45 name: test1 46 upstreamLock: 47 type: git 48 git: 49 repo: git@github.com:seans3/blueprint-helloworld 50 directory: / 51 ref: master 52 ` 53 54 const testInventoryID = "SSSSSSSSSS-RRRRR" 55 56 var kptFileWithInventory = ` 57 apiVersion: kpt.dev/v1 58 kind: Kptfile 59 metadata: 60 name: test1 61 upstreamLock: 62 type: git 63 git: 64 repo: git@github.com:seans3/blueprint-helloworld 65 directory: / 66 ref: master 67 inventory: 68 name: foo 69 namespace: test-namespace 70 inventoryID: ` + testInventoryID + "\n" 71 72 var testTime = time.Unix(5555555, 66666666) 73 74 var resourceGroupInventory = ` 75 apiVersion: kpt.dev/v1alpha1 76 kind: ResourceGroup 77 metadata: 78 name: foo 79 namespace: test-namespace 80 ` 81 82 func TestCmd_generateID(t *testing.T) { 83 testCases := map[string]struct { 84 namespace string 85 name string 86 t time.Time 87 expected string 88 isError bool 89 }{ 90 "Empty inventory namespace is an error": { 91 name: inventoryName, 92 namespace: "", 93 t: testTime, 94 isError: true, 95 }, 96 "Empty inventory name is an error": { 97 name: "", 98 namespace: inventoryNamespace, 99 t: testTime, 100 isError: true, 101 }, 102 "Namespace/name hash is valid": { 103 name: inventoryName, 104 namespace: inventoryNamespace, 105 t: testTime, 106 expected: "fa6dc0d39b0465b90f101c2ad50d50e9b4022f23-5555555066666666", 107 isError: false, 108 }, 109 } 110 111 for tn, tc := range testCases { 112 t.Run(tn, func(t *testing.T) { 113 actual, err := generateID(tc.namespace, tc.name, tc.t) 114 // Check if there should be an error 115 if tc.isError { 116 if err == nil { 117 t.Fatalf("expected error but received none") 118 } 119 return 120 } 121 assert.NoError(t, err) 122 if tc.expected != actual { 123 t.Errorf("expecting generated id (%s), got (%s)", tc.expected, actual) 124 } 125 }) 126 } 127 } 128 129 func TestCmd_Run(t *testing.T) { 130 testCases := map[string]struct { 131 kptfile string 132 resourcegroup string 133 rgfilename string 134 name string 135 namespace string 136 inventoryID string 137 force bool 138 expectedErrorMsg string 139 expectAutoGenID bool 140 expectedInventory kptfilev1.Inventory 141 }{ 142 "Fields are defaulted if not provided": { 143 kptfile: kptFile, 144 name: "", 145 rgfilename: "resourcegroup.yaml", 146 namespace: "testns", 147 inventoryID: "", 148 expectAutoGenID: true, 149 expectedInventory: kptfilev1.Inventory{ 150 Namespace: "testns", 151 Name: "inventory-*", 152 }, 153 }, 154 "Provided values are used": { 155 kptfile: kptFile, 156 rgfilename: "custom-rg.yaml", 157 name: "my-pkg", 158 namespace: "my-ns", 159 inventoryID: "my-inv-id", 160 expectedInventory: kptfilev1.Inventory{ 161 Namespace: "my-ns", 162 Name: "my-pkg", 163 InventoryID: "my-inv-id", 164 }, 165 }, 166 "Provided values are used with custom resourcegroup filename": { 167 kptfile: kptFile, 168 rgfilename: "custom-rg.yaml", 169 name: "my-pkg", 170 namespace: "my-ns", 171 inventoryID: "my-inv-id", 172 expectedInventory: kptfilev1.Inventory{ 173 Namespace: "my-ns", 174 Name: "my-pkg", 175 InventoryID: "my-inv-id", 176 }, 177 }, 178 "Kptfile with inventory already set is error": { 179 kptfile: kptFileWithInventory, 180 name: inventoryName, 181 rgfilename: "custom-rg.yaml", 182 namespace: inventoryNamespace, 183 inventoryID: inventoryID, 184 force: false, 185 expectedErrorMsg: "inventory information already set", 186 }, 187 "ResourceGroup with inventory already set is error": { 188 kptfile: kptFile, 189 resourcegroup: resourceGroupInventory, 190 rgfilename: "resourcegroup.yaml", 191 name: inventoryName, 192 namespace: inventoryNamespace, 193 inventoryID: inventoryID, 194 force: false, 195 expectedErrorMsg: "inventory information already set for package", 196 }, 197 "ResourceGroup with inventory and Kptfile with inventory already set is error": { 198 kptfile: kptFileWithInventory, 199 resourcegroup: resourceGroupInventory, 200 rgfilename: "resourcegroup.yaml", 201 name: inventoryName, 202 namespace: inventoryNamespace, 203 inventoryID: inventoryID, 204 force: false, 205 expectedErrorMsg: "inventory information already set", 206 }, 207 "The force flag allows changing inventory information even if already set in Kptfile": { 208 kptfile: kptFileWithInventory, 209 name: inventoryName, 210 rgfilename: "resourcegroup.yaml", 211 namespace: inventoryNamespace, 212 inventoryID: inventoryID, 213 force: true, 214 expectedInventory: kptfilev1.Inventory{ 215 Namespace: inventoryNamespace, 216 Name: inventoryName, 217 InventoryID: inventoryID, 218 }, 219 }, 220 "The force flag allows changing inventory information even if already set in ResourceGroup": { 221 kptfile: kptFile, 222 resourcegroup: resourceGroupInventory, 223 rgfilename: "resourcegroup.yaml", 224 name: inventoryName, 225 namespace: inventoryNamespace, 226 inventoryID: inventoryID, 227 force: true, 228 expectedInventory: kptfilev1.Inventory{ 229 Namespace: inventoryNamespace, 230 Name: inventoryName, 231 InventoryID: inventoryID, 232 }, 233 }, 234 } 235 236 for tn, tc := range testCases { 237 t.Run(tn, func(t *testing.T) { 238 // Set up fake test factory 239 tf := cmdtesting.NewTestFactory().WithNamespace(tc.namespace) 240 defer tf.Cleanup() 241 ioStreams, _, _, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled 242 243 w, clean := testutil.SetupWorkspace(t) 244 defer clean() 245 err := os.WriteFile(filepath.Join(w.WorkspaceDirectory, kptfilev1.KptFileName), 246 []byte(tc.kptfile), 0600) 247 if !assert.NoError(t, err) { 248 t.FailNow() 249 } 250 251 // Create ResourceGroup file if testing the STDIN feature. 252 if tc.resourcegroup != "" && tc.rgfilename != "" { 253 err := os.WriteFile(filepath.Join(w.WorkspaceDirectory, tc.rgfilename), 254 []byte(tc.resourcegroup), 0600) 255 if !assert.NoError(t, err) { 256 t.FailNow() 257 } 258 } 259 260 revert := testutil.Chdir(t, w.WorkspaceDirectory) 261 defer revert() 262 263 runner := NewRunner(fake.CtxWithDefaultPrinter(), tf, ioStreams) 264 runner.RGFileName = tc.rgfilename 265 args := []string{ 266 "--name", tc.name, 267 "--inventory-id", tc.inventoryID, 268 } 269 if tc.force { 270 args = append(args, "--force") 271 } 272 runner.Command.SetArgs(args) 273 274 err = runner.Command.Execute() 275 276 // Check if there should be an error 277 if tc.expectedErrorMsg != "" { 278 if !assert.Error(t, err) { 279 t.FailNow() 280 } 281 assert.Contains(t, err.Error(), tc.expectedErrorMsg) 282 return 283 } 284 285 // Otherwise, validate the kptfile values and/or resourcegroup values. 286 var actualInv kptfilev1.Inventory 287 assert.NoError(t, err) 288 kf, err := pkg.ReadKptfile(filesys.FileSystemOrOnDisk{}, w.WorkspaceDirectory) 289 assert.NoError(t, err) 290 291 switch tc.rgfilename { 292 case "": 293 if !assert.NotNil(t, kf.Inventory) { 294 t.FailNow() 295 } 296 actualInv = *kf.Inventory 297 default: 298 // Check resourcegroup file if testing the STDIN feature. 299 rg, err := pkg.ReadRGFile(w.WorkspaceDirectory, tc.rgfilename) 300 assert.NoError(t, err) 301 if !assert.NotNil(t, rg) { 302 t.FailNow() 303 } 304 305 // Convert resourcegroup inventory back to Kptfile structure so we can share assertion 306 // logic for Kptfile inventory and ResourceGroup inventory structure. 307 actualInv = kptfilev1.Inventory{ 308 Name: rg.Name, 309 Namespace: rg.Namespace, 310 InventoryID: rg.Labels[rgfilev1alpha1.RGInventoryIDLabel], 311 } 312 } 313 314 expectedInv := tc.expectedInventory 315 assertInventoryName(t, expectedInv.Name, actualInv.Name) 316 assert.Equal(t, expectedInv.Namespace, actualInv.Namespace) 317 if tc.expectAutoGenID { 318 assertGenInvID(t, actualInv.Name, actualInv.Namespace, actualInv.InventoryID) 319 } else { 320 assert.Equal(t, expectedInv.InventoryID, actualInv.InventoryID) 321 } 322 }) 323 } 324 } 325 326 func assertInventoryName(t *testing.T, expected, actual string) bool { 327 re := regexp.MustCompile(`^inventory-[0-9]+$`) 328 if expected == "inventory-*" { 329 if re.MatchString(actual) { 330 return true 331 } 332 t.Errorf("expected value on the format 'inventory-[0-9]+', but found %q", actual) 333 } 334 return assert.Equal(t, expected, actual) 335 } 336 337 func assertGenInvID(t *testing.T, name, namespace, actual string) bool { 338 re := regexp.MustCompile(`^([a-z0-9]+)-[0-9]+$`) 339 match := re.FindStringSubmatch(actual) 340 if len(match) != 2 { 341 t.Errorf("unexpected format for autogenerated inventoryID") 342 return false 343 } 344 prefix, err := generateHash(namespace, name) 345 if err != nil { 346 panic(err) 347 } 348 if got, want := match[1], prefix; got != want { 349 t.Errorf("expected prefix %q, but found %q", want, got) 350 return false 351 } 352 return true 353 }