github.com/redhat-appstudio/release-service@v0.0.0-20240507143925-083712697924/controllers/release/adapter.go (about) 1 /* 2 Copyright 2022. 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 release 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "strings" 24 "time" 25 26 "github.com/go-logr/logr" 27 "github.com/konflux-ci/operator-toolkit/controller" 28 integrationgitops "github.com/redhat-appstudio/integration-service/gitops" 29 "github.com/redhat-appstudio/release-service/api/v1alpha1" 30 "github.com/redhat-appstudio/release-service/loader" 31 "github.com/redhat-appstudio/release-service/metadata" 32 "github.com/redhat-appstudio/release-service/syncer" 33 "github.com/redhat-appstudio/release-service/tekton/utils" 34 tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" 35 rbac "k8s.io/api/rbac/v1" 36 "k8s.io/apimachinery/pkg/api/errors" 37 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 "k8s.io/apimachinery/pkg/types" 39 "knative.dev/pkg/apis" 40 ctrl "sigs.k8s.io/controller-runtime" 41 "sigs.k8s.io/controller-runtime/pkg/client" 42 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 43 ) 44 45 // adapter holds the objects needed to reconcile a Release. 46 type adapter struct { 47 client client.Client 48 ctx context.Context 49 loader loader.ObjectLoader 50 logger *logr.Logger 51 release *v1alpha1.Release 52 releaseServiceConfig *v1alpha1.ReleaseServiceConfig 53 syncer *syncer.Syncer 54 validations []controller.ValidationFunction 55 } 56 57 // newAdapter creates and returns an adapter instance. 58 func newAdapter(ctx context.Context, client client.Client, release *v1alpha1.Release, loader loader.ObjectLoader, logger *logr.Logger) *adapter { 59 releaseAdapter := &adapter{ 60 client: client, 61 ctx: ctx, 62 loader: loader, 63 logger: logger, 64 release: release, 65 syncer: syncer.NewSyncerWithContext(client, logger, ctx), 66 } 67 68 releaseAdapter.validations = []controller.ValidationFunction{ 69 releaseAdapter.validateProcessingResources, 70 releaseAdapter.validateAuthor, 71 releaseAdapter.validatePipelineRef, 72 } 73 74 return releaseAdapter 75 } 76 77 // EnsureConfigIsLoaded is an operation that will load the service ReleaseServiceConfig from the manager namespace. If not found, 78 // an empty ReleaseServiceConfig resource will be generated and attached to the adapter. 79 func (a *adapter) EnsureConfigIsLoaded() (controller.OperationResult, error) { 80 namespace := os.Getenv("SERVICE_NAMESPACE") 81 if namespace == "" { 82 patch := client.MergeFrom(a.release.DeepCopy()) 83 a.release.MarkValidationFailed("SERVICE_NAMESPACE env var not set") 84 a.release.MarkReleaseFailed("Release validation failed") 85 return controller.RequeueOnErrorOrStop(a.client.Status().Patch(a.ctx, a.release, patch)) 86 } 87 88 var err error 89 a.releaseServiceConfig, err = a.loader.GetReleaseServiceConfig(a.ctx, a.client, v1alpha1.ReleaseServiceConfigResourceName, namespace) 90 if err != nil && !errors.IsNotFound(err) { 91 return controller.RequeueWithError(err) 92 } 93 94 if err != nil { 95 a.releaseServiceConfig = a.getEmptyReleaseServiceConfig(namespace) 96 } 97 98 return controller.ContinueProcessing() 99 } 100 101 // EnsureFinalizersAreCalled is an operation that will ensure that finalizers are called whenever the Release being 102 // processed is marked for deletion. Once finalizers get called, the finalizer will be removed and the Release will go 103 // back to the queue, so it gets deleted. If a finalizer function fails its execution or a finalizer fails to be removed, 104 // the Release will be requeued with the error attached. 105 func (a *adapter) EnsureFinalizersAreCalled() (controller.OperationResult, error) { 106 // Check if the Release is marked for deletion and continue processing other operations otherwise 107 if a.release.GetDeletionTimestamp() == nil { 108 return controller.ContinueProcessing() 109 } 110 111 if controllerutil.ContainsFinalizer(a.release, metadata.ReleaseFinalizer) { 112 if err := a.finalizeRelease(); err != nil { 113 return controller.RequeueWithError(err) 114 } 115 116 patch := client.MergeFrom(a.release.DeepCopy()) 117 controllerutil.RemoveFinalizer(a.release, metadata.ReleaseFinalizer) 118 err := a.client.Patch(a.ctx, a.release, patch) 119 if err != nil { 120 return controller.RequeueWithError(err) 121 } 122 } 123 124 // Requeue the release again so it gets deleted and other operations are not executed 125 return controller.Requeue() 126 } 127 128 // EnsureFinalizerIsAdded is an operation that will ensure that the Release being processed contains a finalizer. 129 func (a *adapter) EnsureFinalizerIsAdded() (controller.OperationResult, error) { 130 var finalizerFound bool 131 for _, finalizer := range a.release.GetFinalizers() { 132 if finalizer == metadata.ReleaseFinalizer { 133 finalizerFound = true 134 } 135 } 136 137 if !finalizerFound { 138 a.logger.Info("Adding Finalizer to the Release") 139 patch := client.MergeFrom(a.release.DeepCopy()) 140 controllerutil.AddFinalizer(a.release, metadata.ReleaseFinalizer) 141 err := a.client.Patch(a.ctx, a.release, patch) 142 143 return controller.RequeueOnErrorOrContinue(err) 144 } 145 146 return controller.ContinueProcessing() 147 } 148 149 // EnsureReleaseIsCompleted is an operation that will ensure that a Release is completed (marked as released) when 150 // all required phases (e.g. deployment or processing) have been completed. 151 func (a *adapter) EnsureReleaseIsCompleted() (controller.OperationResult, error) { 152 // Do nothing if the release status has been already added 153 if a.release.HasReleaseFinished() { 154 return controller.ContinueProcessing() 155 } 156 157 // The processing has to complete for a Release to be completed 158 if !a.release.HasProcessingFinished() { 159 return controller.ContinueProcessing() 160 } 161 162 patch := client.MergeFrom(a.release.DeepCopy()) 163 a.release.MarkReleased() 164 return controller.RequeueOnErrorOrContinue(a.client.Status().Patch(a.ctx, a.release, patch)) 165 } 166 167 // EnsureReleaseIsRunning is an operation that will ensure that a Release has not finished already and that 168 // it is marked as releasing. If the Release has finished, no other operation after this one will be executed. 169 func (a *adapter) EnsureReleaseIsRunning() (controller.OperationResult, error) { 170 if a.release.HasReleaseFinished() { 171 return controller.StopProcessing() 172 } 173 174 if !a.release.IsReleasing() { 175 patch := client.MergeFrom(a.release.DeepCopy()) 176 a.release.MarkReleasing("") 177 return controller.RequeueOnErrorOrContinue(a.client.Status().Patch(a.ctx, a.release, patch)) 178 } 179 180 return controller.ContinueProcessing() 181 } 182 183 // EnsureReleaseIsProcessed is an operation that will ensure that a managed Release PipelineRun associated to the Release 184 // being processed and a RoleBinding to grant its serviceAccount permissions exist. Otherwise, it will create them. 185 func (a *adapter) EnsureReleaseIsProcessed() (controller.OperationResult, error) { 186 if a.release.HasProcessingFinished() { 187 return controller.ContinueProcessing() 188 } 189 190 pipelineRun, err := a.loader.GetManagedReleasePipelineRun(a.ctx, a.client, a.release) 191 if err != nil && !errors.IsNotFound(err) { 192 return controller.RequeueWithError(err) 193 } 194 195 roleBinding, _ := a.loader.GetRoleBindingFromReleaseStatus(a.ctx, a.client, a.release) 196 if err != nil && !errors.IsNotFound(err) && !strings.Contains(err.Error(), "valid reference to a RoleBinding") { 197 return controller.RequeueWithError(err) 198 } 199 200 if pipelineRun == nil || !a.release.IsProcessing() { 201 resources, err := a.loader.GetProcessingResources(a.ctx, a.client, a.release) 202 if err != nil { 203 return controller.RequeueWithError(err) 204 } 205 206 if pipelineRun == nil { 207 // Only create a RoleBinding if a ServiceAccount is specified 208 if roleBinding == nil && resources.ReleasePlanAdmission.Spec.Pipeline.ServiceAccount != "" { 209 // This string should probably be a constant somewhere 210 roleBinding, err = a.createRoleBindingForClusterRole("release-pipeline-resource-role", resources.ReleasePlanAdmission) 211 if err != nil { 212 return controller.RequeueWithError(err) 213 } 214 } 215 216 pipelineRun, err = a.createManagedPipelineRun(resources) 217 if err != nil { 218 return controller.RequeueWithError(err) 219 } 220 221 a.logger.Info("Created managed Release PipelineRun", 222 "PipelineRun.Name", pipelineRun.Name, "PipelineRun.Namespace", pipelineRun.Namespace) 223 } 224 225 return controller.RequeueOnErrorOrContinue(a.registerProcessingData(pipelineRun, roleBinding)) 226 } 227 228 return controller.ContinueProcessing() 229 } 230 231 // EnsureReleaseExpirationTimeIsAdded is an operation that ensures that a Release has the ExpirationTime set. 232 func (a *adapter) EnsureReleaseExpirationTimeIsAdded() (controller.OperationResult, error) { 233 if a.release.Status.ExpirationTime == nil { 234 releasePlan, err := a.loader.GetReleasePlan(a.ctx, a.client, a.release) 235 if err != nil && !errors.IsNotFound(err) { 236 return controller.RequeueWithError(err) 237 } 238 239 patch := client.MergeFrom(a.release.DeepCopy()) 240 if a.release.Spec.GracePeriodDays == 0 { 241 a.release.Spec.GracePeriodDays = releasePlan.Spec.ReleaseGracePeriodDays 242 } 243 a.release.SetExpirationTime(time.Duration(a.release.Spec.GracePeriodDays)) 244 245 return controller.RequeueOnErrorOrContinue(a.client.Status().Patch(a.ctx, a.release, patch)) 246 } 247 248 return controller.ContinueProcessing() 249 } 250 251 // EnsureReleaseIsValid is an operation that will ensure that a Release is valid by performing all 252 // validation checks. 253 func (a *adapter) EnsureReleaseIsValid() (controller.OperationResult, error) { 254 patch := client.MergeFrom(a.release.DeepCopy()) 255 256 result := controller.Validate(a.validations...) 257 if !result.Valid { 258 if result.Err != nil { 259 return controller.RequeueWithError(result.Err) 260 } 261 a.release.MarkReleaseFailed("Release validation failed") 262 } 263 264 // IsReleasing will be false if MarkReleaseFailed was called 265 if a.release.IsReleasing() { 266 a.release.MarkValidated() 267 return controller.RequeueOnErrorOrContinue(a.client.Status().Patch(a.ctx, a.release, patch)) 268 } 269 270 return controller.RequeueOnErrorOrStop(a.client.Status().Patch(a.ctx, a.release, patch)) 271 } 272 273 // EnsureReleaseProcessingIsTracked is an operation that will ensure that the managed Release PipelineRun status is tracked 274 // in the Release being processed. 275 func (a *adapter) EnsureReleaseProcessingIsTracked() (controller.OperationResult, error) { 276 if !a.release.IsProcessing() || a.release.HasProcessingFinished() { 277 return controller.ContinueProcessing() 278 } 279 280 pipelineRun, err := a.loader.GetManagedReleasePipelineRun(a.ctx, a.client, a.release) 281 if err != nil { 282 return controller.RequeueWithError(err) 283 } 284 if pipelineRun != nil { 285 err = a.registerProcessingStatus(pipelineRun) 286 if err != nil { 287 return controller.RequeueWithError(err) 288 } 289 } 290 291 return controller.ContinueProcessing() 292 } 293 294 // EnsureReleaseProcessingResourcesAreCleanedUp is an operation that will ensure that the resources created for the Release 295 // Processing step are cleaned up once processing is finished. 296 func (a *adapter) EnsureReleaseProcessingResourcesAreCleanedUp() (controller.OperationResult, error) { 297 if !a.release.HasProcessingFinished() { 298 return controller.ContinueProcessing() 299 } 300 301 pipelineRun, err := a.loader.GetManagedReleasePipelineRun(a.ctx, a.client, a.release) 302 if err != nil && !errors.IsNotFound(err) { 303 return controller.RequeueWithError(err) 304 } 305 306 roleBinding, err := a.loader.GetRoleBindingFromReleaseStatus(a.ctx, a.client, a.release) 307 if err != nil && !errors.IsNotFound(err) && !strings.Contains(err.Error(), "valid reference to a RoleBinding") { 308 return controller.RequeueWithError(err) 309 } 310 311 return controller.RequeueOnErrorOrContinue(a.cleanupProcessingResources(pipelineRun, roleBinding)) 312 } 313 314 // cleanupProcessingResources cleans up the PipelineRun created for the Release Processing 315 // and all resources that were created in order for the PipelineRun to succeed. 316 func (a *adapter) cleanupProcessingResources(pipelineRun *tektonv1.PipelineRun, roleBinding *rbac.RoleBinding) error { 317 if roleBinding != nil { 318 err := a.client.Delete(a.ctx, roleBinding) 319 if err != nil { 320 return err 321 } 322 } 323 324 if pipelineRun != nil { 325 if controllerutil.ContainsFinalizer(pipelineRun, metadata.ReleaseFinalizer) { 326 patch := client.MergeFrom(pipelineRun.DeepCopy()) 327 controllerutil.RemoveFinalizer(pipelineRun, metadata.ReleaseFinalizer) 328 return a.client.Patch(a.ctx, pipelineRun, patch) 329 } 330 } 331 332 return nil 333 } 334 335 // createManagedPipelineRun creates and returns a new managed Release PipelineRun. The new PipelineRun will include owner 336 // annotations, so it triggers Release reconciles whenever it changes. The Pipeline information and the parameters to it 337 // will be extracted from the given ReleaseStrategy. The Release's Snapshot will also be passed to the release 338 // PipelineRun. 339 func (a *adapter) createManagedPipelineRun(resources *loader.ProcessingResources) (*tektonv1.PipelineRun, error) { 340 pipelineRun, err := utils.NewPipelineRunBuilder("managed", resources.ReleasePlanAdmission.Namespace). 341 WithAnnotations(metadata.GetAnnotationsWithPrefix(a.release, integrationgitops.PipelinesAsCodePrefix)). 342 WithFinalizer(metadata.ReleaseFinalizer). 343 WithLabels(map[string]string{ 344 metadata.ApplicationNameLabel: resources.ReleasePlan.Spec.Application, 345 metadata.PipelinesTypeLabel: metadata.ManagedPipelineType, 346 metadata.ReleaseNameLabel: a.release.Name, 347 metadata.ReleaseNamespaceLabel: a.release.Namespace, 348 metadata.ReleaseSnapshotLabel: a.release.Spec.Snapshot, 349 }). 350 WithObjectReferences(a.release, resources.ReleasePlan, resources.ReleasePlanAdmission, a.releaseServiceConfig, 351 resources.Snapshot). 352 WithObjectSpecsAsJson(resources.EnterpriseContractPolicy). 353 WithOwner(a.release). 354 WithParamsFromConfigMap(resources.EnterpriseContractConfigMap, []string{"verify_ec_task_bundle"}). 355 WithPipelineRef(resources.ReleasePlanAdmission.Spec.Pipeline.PipelineRef.ToTektonPipelineRef()). 356 WithServiceAccount(resources.ReleasePlanAdmission.Spec.Pipeline.ServiceAccount). 357 WithTimeouts(&resources.ReleasePlanAdmission.Spec.Pipeline.Timeouts, &a.releaseServiceConfig.Spec.DefaultTimeouts). 358 WithWorkspaceFromVolumeTemplate( 359 os.Getenv("DEFAULT_RELEASE_WORKSPACE_NAME"), 360 os.Getenv("DEFAULT_RELEASE_WORKSPACE_SIZE"), 361 ). 362 Build() 363 364 if err != nil { 365 return nil, err 366 } 367 368 err = a.client.Create(a.ctx, pipelineRun) 369 if err != nil { 370 return nil, err 371 } 372 373 return pipelineRun, nil 374 } 375 376 // createRoleBindingForClusterRole creates a RoleBinding that binds the serviceAccount from the passed 377 // ReleasePlanAdmission to the passed ClusterRole. If the creation fails, the error is returned. If the creation 378 // is successful, the RoleBinding is returned. 379 func (a *adapter) createRoleBindingForClusterRole(clusterRole string, releasePlanAdmission *v1alpha1.ReleasePlanAdmission) (*rbac.RoleBinding, error) { 380 roleBinding := &rbac.RoleBinding{ 381 ObjectMeta: metav1.ObjectMeta{ 382 GenerateName: fmt.Sprintf("%s-rolebinding-for-%s-", a.release.Name, clusterRole), 383 Namespace: releasePlanAdmission.Spec.Origin, 384 }, 385 RoleRef: rbac.RoleRef{ 386 APIGroup: rbac.GroupName, 387 Kind: "ClusterRole", 388 Name: clusterRole, 389 }, 390 Subjects: []rbac.Subject{ 391 { 392 Kind: "ServiceAccount", 393 Name: releasePlanAdmission.Spec.Pipeline.ServiceAccount, 394 Namespace: releasePlanAdmission.Namespace, 395 }, 396 }, 397 } 398 399 // Set ownerRef so it is deleted if the Release is deleted 400 err := ctrl.SetControllerReference(a.release, roleBinding, a.client.Scheme()) 401 if err != nil { 402 return nil, err 403 } 404 405 err = a.client.Create(a.ctx, roleBinding) 406 if err != nil { 407 return nil, err 408 } 409 410 return roleBinding, nil 411 } 412 413 // finalizeRelease will finalize the Release being processed, removing the associated resources. 414 func (a *adapter) finalizeRelease() error { 415 pipelineRun, err := a.loader.GetManagedReleasePipelineRun(a.ctx, a.client, a.release) 416 if err != nil { 417 return err 418 } 419 420 if pipelineRun != nil { 421 // The finalizer could still exist at this point in the case of the PipelineRun not having succeeded at the time 422 // of finalizing the Release. 423 if controllerutil.ContainsFinalizer(pipelineRun, metadata.ReleaseFinalizer) { 424 patch := client.MergeFrom(pipelineRun.DeepCopy()) 425 removedFinalizer := controllerutil.RemoveFinalizer(pipelineRun, metadata.ReleaseFinalizer) 426 if !removedFinalizer { 427 return fmt.Errorf("finalizer not removed") 428 } 429 err := a.client.Patch(a.ctx, pipelineRun, patch) 430 if err != nil { 431 return err 432 } 433 } 434 435 err = a.client.Delete(a.ctx, pipelineRun) 436 if err != nil && !errors.IsNotFound(err) { 437 return err 438 } 439 } 440 441 a.logger.Info("Successfully finalized Release") 442 443 return nil 444 } 445 446 // getEmptyReleaseServiceConfig creates and returns an empty ReleaseServiceConfig resource. 447 func (a *adapter) getEmptyReleaseServiceConfig(namespace string) *v1alpha1.ReleaseServiceConfig { 448 releaseServiceConfig := &v1alpha1.ReleaseServiceConfig{ 449 ObjectMeta: metav1.ObjectMeta{ 450 Name: v1alpha1.ReleaseServiceConfigResourceName, 451 Namespace: namespace, 452 }, 453 } 454 releaseServiceConfig.Kind = "ReleaseServiceConfig" 455 return releaseServiceConfig 456 } 457 458 // registerProcessingData adds all the Release processing information to its Status and marks it as processing. 459 func (a *adapter) registerProcessingData(releasePipelineRun *tektonv1.PipelineRun, roleBinding *rbac.RoleBinding) error { 460 if releasePipelineRun == nil { 461 return nil 462 } 463 464 patch := client.MergeFrom(a.release.DeepCopy()) 465 466 a.release.Status.Processing.PipelineRun = fmt.Sprintf("%s%c%s", 467 releasePipelineRun.Namespace, types.Separator, releasePipelineRun.Name) 468 if roleBinding != nil { 469 a.release.Status.Processing.RoleBinding = fmt.Sprintf("%s%c%s", 470 roleBinding.Namespace, types.Separator, roleBinding.Name) 471 } 472 a.release.Status.Target = releasePipelineRun.Namespace 473 474 a.release.MarkProcessing("") 475 476 return a.client.Status().Patch(a.ctx, a.release, patch) 477 } 478 479 // registerProcessingStatus updates the status of the Release being processed by monitoring the status of the 480 // associated managed Release PipelineRun and setting the appropriate state in the Release. If the PipelineRun hasn't 481 // started/succeeded, no action will be taken. 482 func (a *adapter) registerProcessingStatus(pipelineRun *tektonv1.PipelineRun) error { 483 if pipelineRun != nil && pipelineRun.IsDone() { 484 patch := client.MergeFrom(a.release.DeepCopy()) 485 486 condition := pipelineRun.Status.GetCondition(apis.ConditionSucceeded) 487 if condition.IsTrue() { 488 a.release.MarkProcessed() 489 } else { 490 a.release.MarkProcessingFailed(condition.Message) 491 a.release.MarkReleaseFailed("Release processing failed") 492 } 493 494 return a.client.Status().Patch(a.ctx, a.release, patch) 495 } 496 497 return nil 498 } 499 500 // syncResources sync all the resources needed to trigger the deployment of the Release being processed. 501 func (a *adapter) syncResources() error { 502 releasePlanAdmission, err := a.loader.GetActiveReleasePlanAdmissionFromRelease(a.ctx, a.client, a.release) 503 if err != nil { 504 return err 505 } 506 507 snapshot, err := a.loader.GetSnapshot(a.ctx, a.client, a.release) 508 if err != nil { 509 return err 510 } 511 512 return a.syncer.SyncSnapshot(snapshot, releasePlanAdmission.Namespace) 513 } 514 515 // validateAuthor will ensure that a valid author exists for the Release and add it to its status. If the Release 516 // has the automated label but doesn't have automated set in its status, this function will return an error so the 517 // operation knows to requeue the Release. 518 func (a *adapter) validateAuthor() *controller.ValidationResult { 519 if a.release.IsAttributed() { 520 return &controller.ValidationResult{Valid: true} 521 } 522 523 if a.release.Labels[metadata.AutomatedLabel] == "true" && !a.release.IsAutomated() { 524 err := fmt.Errorf("automated not set in status for automated release") 525 a.release.MarkValidationFailed(err.Error()) 526 if a.release.CreationTimestamp.Add(5 * time.Minute).Before(time.Now()) { 527 return &controller.ValidationResult{Valid: false} 528 } 529 return &controller.ValidationResult{Err: err} 530 } 531 532 releasePlan, err := a.loader.GetReleasePlan(a.ctx, a.client, a.release) 533 if err != nil { 534 if errors.IsNotFound(err) { 535 a.release.MarkValidationFailed(err.Error()) 536 return &controller.ValidationResult{Valid: false} 537 } 538 return &controller.ValidationResult{Err: err} 539 } 540 541 var author string 542 543 if a.release.Labels[metadata.AutomatedLabel] == "true" { 544 author = releasePlan.Labels[metadata.AuthorLabel] 545 if author == "" { 546 a.release.MarkValidationFailed("no author in the ReleasePlan found for automated release") 547 return &controller.ValidationResult{Valid: false} 548 } 549 a.release.Status.Attribution.StandingAuthorization = true 550 } else { 551 author = a.release.Labels[metadata.AuthorLabel] 552 if author == "" { // webhooks prevent this from happening but they could be disabled in some scenarios 553 a.release.MarkValidationFailed("no author found for manual release") 554 return &controller.ValidationResult{Valid: false} 555 } 556 } 557 558 a.release.Status.Attribution.Author = author 559 return &controller.ValidationResult{Valid: true} 560 } 561 562 // validateProcessingResources will ensure that all the resources needed to process the Release exist. 563 func (a *adapter) validateProcessingResources() *controller.ValidationResult { 564 resources, err := a.loader.GetProcessingResources(a.ctx, a.client, a.release) 565 if err != nil { 566 if resources == nil || resources.ReleasePlanAdmission == nil || errors.IsNotFound(err) { 567 a.release.MarkValidationFailed(err.Error()) 568 return &controller.ValidationResult{Valid: false} 569 } 570 571 return &controller.ValidationResult{Err: err} 572 } 573 return &controller.ValidationResult{Valid: true} 574 } 575 576 // validatePipelineRef checks that the managed Release PipelineRun ref passes the checks from the ReleaseServiceConfig. 577 func (a *adapter) validatePipelineRef() *controller.ValidationResult { 578 releasePlanAdmission, err := a.loader.GetActiveReleasePlanAdmissionFromRelease(a.ctx, a.client, a.release) 579 if err != nil { 580 if errors.IsNotFound(err) { 581 a.release.MarkValidationFailed(err.Error()) 582 return &controller.ValidationResult{Valid: false} 583 } 584 return &controller.ValidationResult{Err: err} 585 } 586 587 if !a.releaseServiceConfig.Spec.Debug && releasePlanAdmission.Spec.Pipeline.PipelineRef.IsClusterScoped() { 588 a.release.MarkValidationFailed("tried using debug only options while debug mode is disabled in the ReleaseServiceConfig") 589 return &controller.ValidationResult{Valid: false} 590 } 591 592 return &controller.ValidationResult{Valid: true} 593 }