github.com/redhat-appstudio/release-service@v0.0.0-20240507143925-083712697924/loader/loader_test.go (about) 1 package loader 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 tektonutils "github.com/redhat-appstudio/release-service/tekton/utils" 9 10 ecapiv1alpha1 "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1" 11 . "github.com/onsi/ginkgo/v2" 12 . "github.com/onsi/gomega" 13 . "github.com/onsi/gomega/gstruct" 14 applicationapiv1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" 15 "github.com/redhat-appstudio/release-service/api/v1alpha1" 16 "github.com/redhat-appstudio/release-service/metadata" 17 tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" 18 corev1 "k8s.io/api/core/v1" 19 rbac "k8s.io/api/rbac/v1" 20 "k8s.io/apimachinery/pkg/api/errors" 21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 "k8s.io/apimachinery/pkg/types" 23 ) 24 25 var _ = Describe("Release Adapter", Ordered, func() { 26 var ( 27 loader ObjectLoader 28 createResources func() 29 deleteResources func() 30 31 application *applicationapiv1alpha1.Application 32 component *applicationapiv1alpha1.Component 33 enterpriseContractConfigMap *corev1.ConfigMap 34 enterpriseContractPolicy *ecapiv1alpha1.EnterpriseContractPolicy 35 pipelineRun *tektonv1.PipelineRun 36 release *v1alpha1.Release 37 releasePlan *v1alpha1.ReleasePlan 38 releasePlanAdmission *v1alpha1.ReleasePlanAdmission 39 releaseServiceConfig *v1alpha1.ReleaseServiceConfig 40 roleBinding *rbac.RoleBinding 41 snapshot *applicationapiv1alpha1.Snapshot 42 ) 43 44 AfterAll(func() { 45 deleteResources() 46 }) 47 48 BeforeAll(func() { 49 createResources() 50 51 loader = NewLoader() 52 }) 53 54 When("calling GetActiveReleasePlanAdmission", func() { 55 It("returns an active release plan admission", func() { 56 returnedObject, err := loader.GetActiveReleasePlanAdmission(ctx, k8sClient, releasePlan) 57 Expect(err).NotTo(HaveOccurred()) 58 Expect(returnedObject).NotTo(Equal(&v1alpha1.ReleasePlanAdmission{})) 59 Expect(returnedObject.Name).To(Equal(releasePlanAdmission.Name)) 60 }) 61 62 It("fails to return an active release plan admission if the auto release label is set to false", func() { 63 // Use a new application for this test so we don't have timing issues 64 disabledReleasePlanAdmission := releasePlanAdmission.DeepCopy() 65 disabledReleasePlanAdmission.Labels[metadata.AutoReleaseLabel] = "false" 66 disabledReleasePlanAdmission.Name = "disabled-release-plan-admission" 67 disabledReleasePlanAdmission.Spec.Applications = []string{"auto-release-test"} 68 disabledReleasePlanAdmission.ResourceVersion = "" 69 Expect(k8sClient.Create(ctx, disabledReleasePlanAdmission)).To(Succeed()) 70 releasePlan.Spec.Application = "auto-release-test" 71 72 Eventually(func() bool { 73 returnedObject, err := loader.GetActiveReleasePlanAdmission(ctx, k8sClient, releasePlan) 74 return returnedObject == nil && err != nil && strings.Contains(err.Error(), "with auto-release label set to false") 75 }) 76 77 releasePlan.Spec.Application = application.Name 78 Expect(k8sClient.Delete(ctx, disabledReleasePlanAdmission)).To(Succeed()) 79 }) 80 }) 81 82 When("calling GetActiveReleasePlanAdmissionFromRelease", func() { 83 It("returns an active release plan admission", func() { 84 returnedObject, err := loader.GetActiveReleasePlanAdmissionFromRelease(ctx, k8sClient, release) 85 Expect(err).NotTo(HaveOccurred()) 86 Expect(returnedObject).NotTo(Equal(&v1alpha1.ReleasePlanAdmission{})) 87 Expect(returnedObject.Name).To(Equal(releasePlanAdmission.Name)) 88 }) 89 90 It("fails to return an active release plan admission if the release plan does not match", func() { 91 modifiedRelease := release.DeepCopy() 92 modifiedRelease.Spec.ReleasePlan = "non-existent-release-plan" 93 94 returnedObject, err := loader.GetActiveReleasePlanAdmissionFromRelease(ctx, k8sClient, modifiedRelease) 95 Expect(err).To(HaveOccurred()) 96 Expect(errors.IsNotFound(err)).To(BeTrue()) 97 Expect(returnedObject).To(BeNil()) 98 }) 99 }) 100 101 When("calling GetApplication", func() { 102 It("returns the requested application", func() { 103 returnedObject, err := loader.GetApplication(ctx, k8sClient, releasePlan) 104 Expect(err).NotTo(HaveOccurred()) 105 Expect(returnedObject).NotTo(Equal(&applicationapiv1alpha1.Application{})) 106 Expect(returnedObject.Name).To(Equal(application.Name)) 107 }) 108 }) 109 110 When("calling GetEnterpriseContractConfigMap", func() { 111 It("returns nil when the ENTERPRISE_CONTRACT_CONFIG_MAP variable is not set", func() { 112 os.Unsetenv("ENTERPRISE_CONTRACT_CONFIG_MAP") 113 returnedObject, err := loader.GetEnterpriseContractConfigMap(ctx, k8sClient) 114 Expect(err).NotTo(HaveOccurred()) 115 Expect(returnedObject).To(BeNil()) 116 }) 117 118 It("returns the requested enterprise contract configmap", func() { 119 os.Setenv("ENTERPRISE_CONTRACT_CONFIG_MAP", "default/ec-defaults") 120 returnedObject, err := loader.GetEnterpriseContractConfigMap(ctx, k8sClient) 121 Expect(err).NotTo(HaveOccurred()) 122 Expect(returnedObject).NotTo(Equal(&corev1.ConfigMap{})) 123 Expect(returnedObject.Name).To(Equal(enterpriseContractConfigMap.Name)) 124 }) 125 }) 126 127 When("calling GetEnterpriseContractPolicy", func() { 128 It("returns the requested enterprise contract policy", func() { 129 returnedObject, err := loader.GetEnterpriseContractPolicy(ctx, k8sClient, releasePlanAdmission) 130 Expect(err).NotTo(HaveOccurred()) 131 Expect(returnedObject).NotTo(Equal(&ecapiv1alpha1.EnterpriseContractPolicy{})) 132 Expect(returnedObject.Name).To(Equal(enterpriseContractPolicy.Name)) 133 }) 134 }) 135 136 When("calling GetMatchingReleasePlanAdmission", func() { 137 It("returns a release plan admission", func() { 138 returnedObject, err := loader.GetMatchingReleasePlanAdmission(ctx, k8sClient, releasePlan) 139 Expect(err).NotTo(HaveOccurred()) 140 Expect(returnedObject).NotTo(Equal(&v1alpha1.ReleasePlanAdmission{})) 141 Expect(returnedObject.Name).To(Equal(releasePlanAdmission.Name)) 142 }) 143 144 It("returns ReleasePlanAdmission from ReleasePlan label even when multiple matching RPAs exist", func() { 145 modifiedReleasePlan := releasePlan.DeepCopy() 146 modifiedReleasePlan.Labels = map[string]string{ 147 metadata.ReleasePlanAdmissionLabel: "new-release-plan-admission", 148 } 149 150 newReleasePlanAdmission := releasePlanAdmission.DeepCopy() 151 newReleasePlanAdmission.Name = "new-release-plan-admission" 152 newReleasePlanAdmission.ResourceVersion = "" 153 Expect(k8sClient.Create(ctx, newReleasePlanAdmission)).To(Succeed()) 154 155 Eventually(func() bool { 156 returnedObject, err := loader.GetMatchingReleasePlanAdmission(ctx, k8sClient, modifiedReleasePlan) 157 return err == nil && returnedObject.Name == newReleasePlanAdmission.Name 158 }) 159 160 Expect(k8sClient.Delete(ctx, newReleasePlanAdmission)).To(Succeed()) 161 }) 162 163 It("fails to return a ReleasePlanAdmission from ReleasePlan label when targeted RPA doesn't exist", func() { 164 modifiedReleasePlan := releasePlan.DeepCopy() 165 modifiedReleasePlan.Labels = map[string]string{ 166 metadata.ReleasePlanAdmissionLabel: "foo", 167 } 168 169 returnedObject, err := loader.GetMatchingReleasePlanAdmission(ctx, k8sClient, modifiedReleasePlan) 170 Expect(err).To(HaveOccurred()) 171 Expect(errors.IsNotFound(err)).To(BeTrue()) 172 Expect(returnedObject).To(Equal(&v1alpha1.ReleasePlanAdmission{})) 173 }) 174 175 It("fails to return a release plan admission if the target does not match", func() { 176 modifiedReleasePlan := releasePlan.DeepCopy() 177 modifiedReleasePlan.Spec.Target = "non-existent-target" 178 179 returnedObject, err := loader.GetMatchingReleasePlanAdmission(ctx, k8sClient, modifiedReleasePlan) 180 Expect(err).To(HaveOccurred()) 181 Expect(err.Error()).To(ContainSubstring("no ReleasePlanAdmission found in namespace")) 182 Expect(returnedObject).To(BeNil()) 183 }) 184 185 It("fails to return a release plan admission if multiple matches are found", func() { 186 newReleasePlanAdmission := releasePlanAdmission.DeepCopy() 187 newReleasePlanAdmission.Name = "new-release-plan-admission" 188 newReleasePlanAdmission.ResourceVersion = "" 189 Expect(k8sClient.Create(ctx, newReleasePlanAdmission)).To(Succeed()) 190 191 Eventually(func() bool { 192 returnedObject, err := loader.GetMatchingReleasePlanAdmission(ctx, k8sClient, releasePlan) 193 return returnedObject == nil && err != nil && strings.Contains(err.Error(), "multiple ReleasePlanAdmissions") 194 }) 195 196 Expect(k8sClient.Delete(ctx, newReleasePlanAdmission)).To(Succeed()) 197 }) 198 }) 199 200 When("calling GetMatchingReleasePlans", func() { 201 var releasePlanTwo, releasePlanDiffApp *v1alpha1.ReleasePlan 202 203 BeforeEach(func() { 204 releasePlanTwo = releasePlan.DeepCopy() 205 releasePlanTwo.Name = "rp-two" 206 releasePlanTwo.ResourceVersion = "" 207 releasePlanDiffApp = releasePlan.DeepCopy() 208 releasePlanDiffApp.Name = "rp-diff" 209 releasePlanDiffApp.Spec.Application = "some-other-app" 210 releasePlanDiffApp.ResourceVersion = "" 211 Expect(k8sClient.Create(ctx, releasePlanTwo)).To(Succeed()) 212 Expect(k8sClient.Create(ctx, releasePlanDiffApp)).To(Succeed()) 213 }) 214 215 AfterEach(func() { 216 Expect(k8sClient.Delete(ctx, releasePlanTwo)).To(Succeed()) 217 Expect(k8sClient.Delete(ctx, releasePlanDiffApp)).To(Succeed()) 218 }) 219 220 It("returns the requested list of release plans", func() { 221 Eventually(func() bool { 222 returnedObject, err := loader.GetMatchingReleasePlans(ctx, k8sClient, releasePlanAdmission) 223 return returnedObject != &v1alpha1.ReleasePlanList{} && err == nil && len(returnedObject.Items) == 2 224 }) 225 }) 226 227 It("does not return a ReleasePlan with a different application", func() { 228 Eventually(func() bool { 229 returnedObject, err := loader.GetMatchingReleasePlans(ctx, k8sClient, releasePlanAdmission) 230 contains := false 231 for _, releasePlan := range returnedObject.Items { 232 if releasePlan.Spec.Application == "some-other-app" { 233 contains = true 234 } 235 } 236 return returnedObject != &v1alpha1.ReleasePlanList{} && err == nil && contains == false 237 }) 238 }) 239 }) 240 241 When("calling GetRelease", func() { 242 It("returns the requested release", func() { 243 returnedObject, err := loader.GetRelease(ctx, k8sClient, release.Name, release.Namespace) 244 Expect(err).NotTo(HaveOccurred()) 245 Expect(returnedObject).NotTo(Equal(&v1alpha1.Release{})) 246 Expect(returnedObject.Name).To(Equal(release.Name)) 247 }) 248 }) 249 250 When("calling GetRoleBindingFromReleaseStatus", func() { 251 It("fails to return a RoleBinding if the reference is not in the release", func() { 252 returnedObject, err := loader.GetRoleBindingFromReleaseStatus(ctx, k8sClient, release) 253 Expect(returnedObject).To(BeNil()) 254 Expect(err.Error()).To(ContainSubstring("release doesn't contain a valid reference to a RoleBinding")) 255 }) 256 257 It("fails to return a RoleBinding if the roleBinding does not exist", func() { 258 modifiedRelease := release.DeepCopy() 259 modifiedRelease.Status.Processing.RoleBinding = "foo/bar" 260 261 returnedObject, err := loader.GetRoleBindingFromReleaseStatus(ctx, k8sClient, modifiedRelease) 262 Expect(returnedObject).To(BeNil()) 263 Expect(errors.IsNotFound(err)).To(BeTrue()) 264 }) 265 266 It("returns the requested resource", func() { 267 modifiedRelease := release.DeepCopy() 268 modifiedRelease.Status.Processing.RoleBinding = fmt.Sprintf("%s%c%s", roleBinding.Namespace, 269 types.Separator, roleBinding.Name) 270 271 returnedObject, err := loader.GetRoleBindingFromReleaseStatus(ctx, k8sClient, modifiedRelease) 272 Expect(err).NotTo(HaveOccurred()) 273 Expect(returnedObject).NotTo(Equal(&rbac.RoleBinding{})) 274 Expect(returnedObject.Name).To(Equal(roleBinding.Name)) 275 }) 276 }) 277 278 When("calling GetManagedReleasePipelineRun", func() { 279 It("returns a PipelineRun if the labels match with the release data", func() { 280 returnedObject, err := loader.GetManagedReleasePipelineRun(ctx, k8sClient, release) 281 Expect(err).NotTo(HaveOccurred()) 282 Expect(returnedObject).NotTo(Equal(&tektonv1.PipelineRun{})) 283 Expect(returnedObject.Name).To(Equal(pipelineRun.Name)) 284 }) 285 286 It("fails to return a PipelineRun if the labels don't match with the release data", func() { 287 modifiedRelease := release.DeepCopy() 288 modifiedRelease.Name = "non-existing-release" 289 290 returnedObject, err := loader.GetManagedReleasePipelineRun(ctx, k8sClient, modifiedRelease) 291 Expect(err).NotTo(HaveOccurred()) 292 Expect(returnedObject).To(BeNil()) 293 }) 294 }) 295 296 When("calling GetReleasePlan", func() { 297 It("returns the requested release plan", func() { 298 returnedObject, err := loader.GetReleasePlan(ctx, k8sClient, release) 299 Expect(err).NotTo(HaveOccurred()) 300 Expect(returnedObject).NotTo(Equal(&v1alpha1.ReleasePlan{})) 301 Expect(returnedObject.Name).To(Equal(releasePlan.Name)) 302 }) 303 }) 304 305 When("calling GetReleaseServiceConfig", func() { 306 It("returns the requested ReleaseServiceConfig", func() { 307 returnedObject, err := loader.GetReleaseServiceConfig(ctx, k8sClient, releaseServiceConfig.Name, releaseServiceConfig.Namespace) 308 Expect(err).NotTo(HaveOccurred()) 309 Expect(returnedObject).NotTo(Equal(&v1alpha1.ReleaseServiceConfig{})) 310 Expect(returnedObject.Name).To(Equal(releaseServiceConfig.Name)) 311 }) 312 }) 313 314 When("calling GetSnapshot", func() { 315 It("returns the requested snapshot", func() { 316 returnedObject, err := loader.GetSnapshot(ctx, k8sClient, release) 317 Expect(err).NotTo(HaveOccurred()) 318 Expect(returnedObject).NotTo(Equal(&applicationapiv1alpha1.Snapshot{})) 319 Expect(returnedObject.Name).To(Equal(snapshot.Name)) 320 }) 321 }) 322 323 // Composite functions 324 325 When("calling GetProcessingResources", func() { 326 It("returns all the relevant resources", func() { 327 os.Setenv("ENTERPRISE_CONTRACT_CONFIG_MAP", "default/ec-defaults") 328 resources, err := loader.GetProcessingResources(ctx, k8sClient, release) 329 Expect(err).NotTo(HaveOccurred()) 330 Expect(*resources).To(MatchFields(IgnoreExtras, Fields{ 331 "EnterpriseContractConfigMap": Not(BeNil()), 332 "EnterpriseContractPolicy": Not(BeNil()), 333 "ReleasePlan": Not(BeNil()), 334 "ReleasePlanAdmission": Not(BeNil()), 335 "Snapshot": Not(BeNil()), 336 })) 337 }) 338 339 It("fails if any resource fails to be fetched", func() { 340 modifiedRelease := release.DeepCopy() 341 modifiedRelease.Spec.Snapshot = "non-existent-snapshot" 342 343 _, err := loader.GetProcessingResources(ctx, k8sClient, modifiedRelease) 344 Expect(err).To(HaveOccurred()) 345 }) 346 }) 347 348 createResources = func() { 349 application = &applicationapiv1alpha1.Application{ 350 ObjectMeta: metav1.ObjectMeta{ 351 Name: "application", 352 Namespace: "default", 353 }, 354 Spec: applicationapiv1alpha1.ApplicationSpec{ 355 DisplayName: "application", 356 }, 357 } 358 Expect(k8sClient.Create(ctx, application)).To(Succeed()) 359 360 component = &applicationapiv1alpha1.Component{ 361 ObjectMeta: metav1.ObjectMeta{ 362 Name: "component", 363 Namespace: "default", 364 }, 365 Spec: applicationapiv1alpha1.ComponentSpec{ 366 Application: application.Name, 367 ComponentName: "component", 368 }, 369 } 370 Expect(k8sClient.Create(ctx, component)).Should(Succeed()) 371 372 enterpriseContractConfigMap = &corev1.ConfigMap{ 373 ObjectMeta: metav1.ObjectMeta{ 374 Name: "ec-defaults", 375 Namespace: "default", 376 }, 377 } 378 Expect(k8sClient.Create(ctx, enterpriseContractConfigMap)).Should(Succeed()) 379 380 enterpriseContractPolicy = &ecapiv1alpha1.EnterpriseContractPolicy{ 381 ObjectMeta: metav1.ObjectMeta{ 382 Name: "enterprise-contract-policy", 383 Namespace: "default", 384 }, 385 Spec: ecapiv1alpha1.EnterpriseContractPolicySpec{ 386 Sources: []ecapiv1alpha1.Source{ 387 {Name: "foo"}, 388 }, 389 }, 390 } 391 Expect(k8sClient.Create(ctx, enterpriseContractPolicy)).Should(Succeed()) 392 393 releasePlan = &v1alpha1.ReleasePlan{ 394 ObjectMeta: metav1.ObjectMeta{ 395 Name: "release-plan", 396 Namespace: "default", 397 }, 398 Spec: v1alpha1.ReleasePlanSpec{ 399 Application: application.Name, 400 Target: "default", 401 }, 402 } 403 Expect(k8sClient.Create(ctx, releasePlan)).To(Succeed()) 404 405 releaseServiceConfig = &v1alpha1.ReleaseServiceConfig{ 406 ObjectMeta: metav1.ObjectMeta{ 407 Name: v1alpha1.ReleaseServiceConfigResourceName, 408 Namespace: "default", 409 }, 410 } 411 Expect(k8sClient.Create(ctx, releaseServiceConfig)).To(Succeed()) 412 413 releasePlanAdmission = &v1alpha1.ReleasePlanAdmission{ 414 ObjectMeta: metav1.ObjectMeta{ 415 Name: "release-plan-admission", 416 Namespace: "default", 417 Labels: map[string]string{ 418 metadata.AutoReleaseLabel: "true", 419 }, 420 }, 421 Spec: v1alpha1.ReleasePlanAdmissionSpec{ 422 Applications: []string{application.Name}, 423 Origin: "default", 424 Pipeline: &tektonutils.Pipeline{ 425 PipelineRef: tektonutils.PipelineRef{ 426 Resolver: "bundles", 427 Params: []tektonutils.Param{ 428 {Name: "bundle", Value: "testbundle"}, 429 {Name: "name", Value: "release-pipeline"}, 430 {Name: "kind", Value: "pipeline"}, 431 }, 432 }, 433 }, 434 Policy: enterpriseContractPolicy.Name, 435 }, 436 } 437 Expect(k8sClient.Create(ctx, releasePlanAdmission)).Should(Succeed()) 438 439 roleBinding = &rbac.RoleBinding{ 440 ObjectMeta: metav1.ObjectMeta{ 441 Name: "rolebinding", 442 Namespace: "default", 443 }, 444 RoleRef: rbac.RoleRef{ 445 APIGroup: rbac.GroupName, 446 Kind: "ClusterRole", 447 Name: "clusterrole", 448 }, 449 } 450 Expect(k8sClient.Create(ctx, roleBinding)).To(Succeed()) 451 452 snapshot = &applicationapiv1alpha1.Snapshot{ 453 ObjectMeta: metav1.ObjectMeta{ 454 Name: "snapshot", 455 Namespace: "default", 456 }, 457 Spec: applicationapiv1alpha1.SnapshotSpec{ 458 Application: application.Name, 459 }, 460 } 461 Expect(k8sClient.Create(ctx, snapshot)).To(Succeed()) 462 463 release = &v1alpha1.Release{ 464 ObjectMeta: metav1.ObjectMeta{ 465 Name: "release", 466 Namespace: "default", 467 }, 468 Spec: v1alpha1.ReleaseSpec{ 469 Snapshot: snapshot.Name, 470 ReleasePlan: releasePlan.Name, 471 }, 472 } 473 Expect(k8sClient.Create(ctx, release)).To(Succeed()) 474 475 pipelineRun = &tektonv1.PipelineRun{ 476 ObjectMeta: metav1.ObjectMeta{ 477 Labels: map[string]string{ 478 metadata.ReleaseNameLabel: release.Name, 479 metadata.ReleaseNamespaceLabel: release.Namespace, 480 }, 481 Name: "pipeline-run", 482 Namespace: "default", 483 }, 484 } 485 Expect(k8sClient.Create(ctx, pipelineRun)).To(Succeed()) 486 } 487 488 deleteResources = func() { 489 Expect(k8sClient.Delete(ctx, application)).To(Succeed()) 490 Expect(k8sClient.Delete(ctx, component)).To(Succeed()) 491 Expect(k8sClient.Delete(ctx, enterpriseContractPolicy)).To(Succeed()) 492 Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) 493 Expect(k8sClient.Delete(ctx, release)).To(Succeed()) 494 Expect(k8sClient.Delete(ctx, releasePlan)).To(Succeed()) 495 Expect(k8sClient.Delete(ctx, releasePlanAdmission)).To(Succeed()) 496 Expect(k8sClient.Delete(ctx, roleBinding)).To(Succeed()) 497 Expect(k8sClient.Delete(ctx, snapshot)).To(Succeed()) 498 } 499 500 })