Skip to content

Commit

Permalink
Merge pull request konflux-ci#656 from johnbieren/release1227
Browse files Browse the repository at this point in the history
feat(RELEASE-1227): set application metadata in release controller
  • Loading branch information
johnbieren authored Jan 28, 2025
2 parents c497639 + f1cc9bd commit 6edf445
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 10 deletions.
48 changes: 48 additions & 0 deletions controllers/release/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/go-logr/logr"
"github.com/konflux-ci/operator-toolkit/controller"
toolkitmetadata "github.com/konflux-ci/operator-toolkit/metadata"
"github.com/konflux-ci/release-service/api/v1alpha1"
"github.com/konflux-ci/release-service/loader"
"github.com/konflux-ci/release-service/metadata"
Expand Down Expand Up @@ -337,6 +338,53 @@ func (a *adapter) EnsureFinalPipelineIsProcessed() (controller.OperationResult,
return controller.ContinueProcessing()
}

// EnsureApplicationMetadataIsSet is an operation that will ensure that the owner reference is set
// to be the application the Release was created for and that all annotations and labels from the
// Snapshot pertaining to Pipelines as Code or the RhtapDomain prefix are copied to the Release.
func (a *adapter) EnsureApplicationMetadataIsSet() (controller.OperationResult, error) {
if len(a.release.OwnerReferences) > 0 {
return controller.ContinueProcessing()
}

releasePlan, err := a.loader.GetReleasePlan(a.ctx, a.client, a.release)
if err != nil {
return controller.RequeueWithError(err)
}

snapshot, err := a.loader.GetSnapshot(a.ctx, a.client, a.release)
if err != nil {
return controller.RequeueWithError(err)
}

patch := client.MergeFrom(a.release.DeepCopy())

application, err := a.loader.GetApplication(a.ctx, a.client, releasePlan)
if err != nil {
a.release.MarkReleaseFailed("This Release is for a nonexistent Application")
return controller.RequeueOnErrorOrStop(a.client.Status().Patch(a.ctx, a.release, patch))
}

err = ctrl.SetControllerReference(application, a.release, a.client.Scheme())
if err != nil {
return controller.RequeueWithError(err)
}

// Propagate PaC annotations and labels
_ = toolkitmetadata.CopyAnnotationsByPrefix(&snapshot.ObjectMeta, &a.release.ObjectMeta, metadata.PipelinesAsCodePrefix)
_ = toolkitmetadata.CopyLabelsByPrefix(&snapshot.ObjectMeta, &a.release.ObjectMeta, metadata.PipelinesAsCodePrefix)

// Propagate annotations and labels prefixed with the RhtapDomain prefix
_ = toolkitmetadata.CopyAnnotationsByPrefix(&snapshot.ObjectMeta, &a.release.ObjectMeta, metadata.RhtapDomain)
_ = toolkitmetadata.CopyLabelsByPrefix(&snapshot.ObjectMeta, &a.release.ObjectMeta, metadata.RhtapDomain)

err = a.client.Patch(a.ctx, a.release, patch)
if err != nil && !errors.IsNotFound(err) {
return controller.RequeueWithError(err)
}

return controller.ContinueProcessing()
}

// EnsureReleaseExpirationTimeIsAdded is an operation that ensures that a Release has the ExpirationTime set.
func (a *adapter) EnsureReleaseExpirationTimeIsAdded() (controller.OperationResult, error) {
if a.release.Status.ExpirationTime == nil {
Expand Down
121 changes: 121 additions & 0 deletions controllers/release/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,127 @@ var _ = Describe("Release adapter", Ordered, func() {

})

Context("When EnsureApplicationMetadataIsSet is called", func() {
var adapter *adapter

AfterEach(func() {
_ = adapter.client.Delete(ctx, adapter.release)
})

BeforeEach(func() {
adapter = createReleaseAndAdapter()
})

It("should do nothing if the Release already has an owner reference", func() {
adapter.release.OwnerReferences = []metav1.OwnerReference{
{Kind: "Application", Name: "foo"},
}

Expect(adapter.release.OwnerReferences).To(HaveLen(1))
result, err := adapter.EnsureApplicationMetadataIsSet()
Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue())
Expect(err).NotTo(HaveOccurred())
Expect(adapter.release.OwnerReferences).To(HaveLen(1))
})

It("should fail if the ReleasePlan does not exist", func() {
adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
ContextKey: loader.ReleasePlanContextKey,
Err: errors.NewNotFound(schema.GroupResource{}, ""),
},
})

result, err := adapter.EnsureApplicationMetadataIsSet()
Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue())
Expect(err).To(HaveOccurred())
Expect(adapter.release.OwnerReferences).To(HaveLen(0))
})

It("should fail if the Snapshot does not exist", func() {
adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
ContextKey: loader.SnapshotContextKey,
Err: errors.NewNotFound(schema.GroupResource{}, ""),
},
})

result, err := adapter.EnsureApplicationMetadataIsSet()
Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue())
Expect(err).To(HaveOccurred())
Expect(adapter.release.OwnerReferences).To(HaveLen(0))
})

It("should fail if the Application does not exist", func() {
adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
ContextKey: loader.ApplicationContextKey,
Err: errors.NewNotFound(schema.GroupResource{}, ""),
},
})

result, err := adapter.EnsureApplicationMetadataIsSet()
Expect(!result.RequeueRequest && result.CancelRequest).To(BeTrue())
Expect(err).NotTo(HaveOccurred())
Expect(adapter.release.IsValid()).To(BeFalse())
Expect(adapter.release.OwnerReferences).To(HaveLen(0))
})

It("should set the owner reference", func() {
Expect(adapter.release.OwnerReferences).To(HaveLen(0))
result, err := adapter.EnsureApplicationMetadataIsSet()
Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue())
Expect(err).NotTo(HaveOccurred())

boolTrue := true
expectedOwnerReference := metav1.OwnerReference{
Kind: "Application",
APIVersion: "appstudio.redhat.com/v1alpha1",
UID: application.UID,
Name: application.Name,
Controller: &boolTrue,
BlockOwnerDeletion: &boolTrue,
}
Expect(adapter.release.ObjectMeta.OwnerReferences).To(ContainElement(expectedOwnerReference))
})

It("should add the annotations and labels that have the proper prefix from the snapshot", func() {
metadataSnapshot := &applicationapiv1alpha1.Snapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "metadata-snapshot",
Namespace: "default",
Labels: map[string]string{
metadata.PipelinesAsCodePrefix + "/foo": "value",
metadata.RhtapDomain + "/bar": "value2",
"something-else": "value3",
},
Annotations: map[string]string{
metadata.PipelinesAsCodePrefix + "/test": "value",
metadata.RhtapDomain + "/baz": "value2",
"something-else-else": "value3",
},
},
Spec: applicationapiv1alpha1.SnapshotSpec{
Application: application.Name,
},
}
adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
ContextKey: loader.SnapshotContextKey,
Resource: metadataSnapshot,
},
})
Expect(adapter.release.Labels).To(HaveLen(0))
Expect(adapter.release.Annotations).To(HaveLen(0))

result, err := adapter.EnsureApplicationMetadataIsSet()
Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue())
Expect(err).NotTo(HaveOccurred())
Expect(adapter.release.Labels).To(HaveLen(2))
Expect(adapter.release.Annotations).To(HaveLen(2))
})
})

When("EnsureReleaseExpirationTimeIsAdded is called", func() {
var adapter *adapter
var newReleasePlan *v1alpha1.ReleasePlan
Expand Down
1 change: 1 addition & 0 deletions controllers/release/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func (c *Controller) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
adapter.EnsureConfigIsLoaded, // This operation sets the config in the adapter to be used in other operations.
adapter.EnsureReleaseIsRunning,
adapter.EnsureReleaseIsValid,
adapter.EnsureApplicationMetadataIsSet,
adapter.EnsureFinalizerIsAdded,
adapter.EnsureReleaseExpirationTimeIsAdded,
adapter.EnsureTenantPipelineIsProcessed,
Expand Down
26 changes: 16 additions & 10 deletions metadata/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,49 @@ import "fmt"
// Common constants
const (
// rhtapDomain is the prefix of the application label
rhtapDomain = "appstudio.openshift.io"
RhtapDomain = "appstudio.openshift.io"

// MaxLabelLength is the maximum allowed characters in a label value
MaxLabelLength = 63
)

// Prefixes used by the release controller package
var (
// PipelinesAsCodePrefix contains the prefix applied to labels and annotations copied from Pipelines as Code resources.
PipelinesAsCodePrefix = "pac.test.appstudio.openshift.io"
)

// Labels used by the release api package
var (
// AttributionLabel is the label name for the standing-attribution label
AttributionLabel = fmt.Sprintf("release.%s/standing-attribution", rhtapDomain)
AttributionLabel = fmt.Sprintf("release.%s/standing-attribution", RhtapDomain)

// AutoReleaseLabel is the label name for the auto-release setting
AutoReleaseLabel = fmt.Sprintf("release.%s/auto-release", rhtapDomain)
AutoReleaseLabel = fmt.Sprintf("release.%s/auto-release", RhtapDomain)

// AuthorLabel is the label name for the user who creates a CR
AuthorLabel = fmt.Sprintf("release.%s/author", rhtapDomain)
AuthorLabel = fmt.Sprintf("release.%s/author", RhtapDomain)

// AutomatedLabel is the label name for marking a Release as automated
AutomatedLabel = fmt.Sprintf("release.%s/automated", rhtapDomain)
AutomatedLabel = fmt.Sprintf("release.%s/automated", RhtapDomain)

// ReleasePlanAdmissionLabel is the ReleasePlan label for the name of the ReleasePlanAdmission to use
ReleasePlanAdmissionLabel = fmt.Sprintf("release.%s/releasePlanAdmission", rhtapDomain)
ReleasePlanAdmissionLabel = fmt.Sprintf("release.%s/releasePlanAdmission", RhtapDomain)
)

// Prefixes to be used by Release Pipelines labels
var (
// pipelinesLabelPrefix is the prefix of the pipelines label
pipelinesLabelPrefix = fmt.Sprintf("pipelines.%s", rhtapDomain)
pipelinesLabelPrefix = fmt.Sprintf("pipelines.%s", RhtapDomain)

// releaseLabelPrefix is the prefix of the release labels
releaseLabelPrefix = fmt.Sprintf("release.%s", rhtapDomain)
releaseLabelPrefix = fmt.Sprintf("release.%s", RhtapDomain)
)

// Labels to be used within Release PipelineRuns
var (
// ApplicationNameLabel is the label used to specify the application associated with the PipelineRun
ApplicationNameLabel = fmt.Sprintf("%s/%s", rhtapDomain, "application")
ApplicationNameLabel = fmt.Sprintf("%s/%s", RhtapDomain, "application")

// FinalPipelineType is the value to be used in the PipelinesTypeLabel for final Pipelines
FinalPipelineType = "final"
Expand All @@ -78,5 +84,5 @@ var (
ReleaseNamespaceLabel = fmt.Sprintf("%s/%s", releaseLabelPrefix, "namespace")

// ReleaseSnapshotLabel is the label used to specify the snapshot associated with the PipelineRun
ReleaseSnapshotLabel = fmt.Sprintf("%s/%s", rhtapDomain, "snapshot")
ReleaseSnapshotLabel = fmt.Sprintf("%s/%s", RhtapDomain, "snapshot")
)

0 comments on commit 6edf445

Please sign in to comment.