Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions api/v1/runtimecomponent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ type RuntimeComponentStatus struct {

// The reconciliation interval in seconds.
ReconcileInterval *int32 `json:"reconcileInterval,omitempty"`

TrackedAnnotations map[common.StatusTrackedAnnotationType][]string `json:"trackedAnnotations,omitempty"`
}

// Defines possible status conditions.
Expand Down Expand Up @@ -801,6 +803,28 @@ func (s *RuntimeComponentStatus) SetBinding(r *corev1.LocalObjectReference) {
s.Binding = r
}

func (s *RuntimeComponentStatus) GetTrackedAnnotations() common.StatusTrackedAnnotations {
return s.TrackedAnnotations
}

func (s *RuntimeComponentStatus) SetTrackedAnnotations(trackedAnnotations common.StatusTrackedAnnotations) {
s.TrackedAnnotations = trackedAnnotations
}

func (s *RuntimeComponentStatus) GetTrackedAnnotation(annotationType common.StatusTrackedAnnotationType) []string {
if s.TrackedAnnotations == nil {
s.TrackedAnnotations = make(common.StatusTrackedAnnotations)
}
return s.TrackedAnnotations[annotationType]
}

func (s *RuntimeComponentStatus) SetTrackedAnnotation(annotationType common.StatusTrackedAnnotationType, annotationKeys []string) {
if s.TrackedAnnotations == nil {
s.TrackedAnnotations = make(common.StatusTrackedAnnotations)
}
s.TrackedAnnotations[annotationType] = annotationKeys
}

// GetMinReplicas returns minimum replicas
func (a *RuntimeComponentAutoScaling) GetMinReplicas() *int32 {
return a.MinReplicas
Expand Down
16 changes: 16 additions & 0 deletions api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions api/v1beta2/runtimecomponent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,20 @@ func (s *RuntimeComponentStatus) SetBinding(r *corev1.LocalObjectReference) {
s.Binding = r
}

func (s *RuntimeComponentStatus) GetTrackedAnnotations() common.StatusTrackedAnnotations {
return nil
}

func (s *RuntimeComponentStatus) SetTrackedAnnotations(trackedAnnotations common.StatusTrackedAnnotations) {
}

func (s *RuntimeComponentStatus) GetTrackedAnnotation(annotationType common.StatusTrackedAnnotationType) []string {
return nil
}

func (s *RuntimeComponentStatus) SetTrackedAnnotation(annotationType common.StatusTrackedAnnotationType, annotationKeys []string) {
}

// GetMinReplicas returns minimum replicas
func (a *RuntimeComponentAutoScaling) GetMinReplicas() *int32 {
return a.MinReplicas
Expand Down
95 changes: 95 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package common

import (
"slices"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
Expand Down Expand Up @@ -59,3 +61,96 @@ func GetDefaultMicroProfileLivenessProbe(ba BaseComponent) *corev1.Probe {
func GetComponentNameLabel(ba BaseComponent) string {
return ba.GetGroupName() + "/name"
}

// GetComponentAnnotationsGetter returns the getter for annotationType annotations configured in the BaseComponent instance.
func GetComponentAnnotationsGetter(ba BaseComponent, annotationType StatusTrackedAnnotationType) func() map[string]string {
var annotationGetter func() map[string]string
annotationGetter = nil
switch annotationType {
case StatusTrackedAnnotationTypeGlobal:
annotationGetter = ba.GetAnnotations
case StatusTrackedAnnotationTypeDeployment:
if ba.GetDeployment() != nil {
annotationGetter = ba.GetDeployment().GetAnnotations
}
case StatusTrackedAnnotationTypeStatefulSet:
if ba.GetStatefulSet() != nil {
annotationGetter = ba.GetStatefulSet().GetAnnotations
}
case StatusTrackedAnnotationTypeService:
if ba.GetService() != nil {
annotationGetter = ba.GetService().GetAnnotations
}
case StatusTrackedAnnotationTypeRoute:
if ba.GetRoute() != nil {
annotationGetter = ba.GetRoute().GetAnnotations
}
}
return annotationGetter
}

// SaveTrackedAnnotations stores current annotations configured in the BaseComponent into the BaseComponentStatus.
func SaveTrackedAnnotations(ba BaseComponent, annotationTypes ...StatusTrackedAnnotationType) {
for _, annotationType := range annotationTypes {
if annotationGetter := GetComponentAnnotationsGetter(ba, annotationType); annotationGetter != nil {
currentAnnotationKeys := []string{}
for key := range annotationGetter() {
currentAnnotationKeys = append(currentAnnotationKeys, key)
}
if len(currentAnnotationKeys) > 0 {
slices.Sort(currentAnnotationKeys)
ba.GetStatus().SetTrackedAnnotation(annotationType, currentAnnotationKeys)
} else {
ba.GetStatus().SetTrackedAnnotation(annotationType, nil)
}
}
}
}

// DeleteMissingTrackedAnnotations returns the map of currentAnnotations after removing annotationType annotations
// that are no longer configured in BaseComponent instance but still found within the tracked annotations in BaseComponentStatus.
func DeleteMissingTrackedAnnotations(currentAnnotations map[string]string, ba BaseComponent, annotationTypes ...StatusTrackedAnnotationType) map[string]string {
missingTrackedAnnotations := FilterMissingTrackedAnnotations(ba, StatusTrackedAnnotationTypeGlobal, ba.GetAnnotations())
for _, missingTrackedAnnotation := range missingTrackedAnnotations {
delete(currentAnnotations, missingTrackedAnnotation)
}
for _, annotationType := range annotationTypes {
if annotationType != StatusTrackedAnnotationTypeGlobal {
if annotationGetter := GetComponentAnnotationsGetter(ba, annotationType); annotationGetter != nil {
missingTrackedAnnotations := FilterMissingTrackedAnnotations(ba, annotationType, annotationGetter())
for _, missingTrackedAnnotation := range missingTrackedAnnotations {
delete(currentAnnotations, missingTrackedAnnotation)
}
}
}
}
return currentAnnotations
}

// FilterMissingTrackedAnnotations returns an array of tracked annotations that are no longer configured in the BaseComponent instance
// but still exist in the tracked annotations of BaseComponentStatus.
func FilterMissingTrackedAnnotations(ba BaseComponent, annotationType StatusTrackedAnnotationType, annotations map[string]string) []string {
if ba.GetStatus() == nil {
return []string{}
}
trackedAnnotations := ba.GetStatus().GetTrackedAnnotation(annotationType)
if len(trackedAnnotations) == 0 || annotations == nil {
return []string{}
}
for annotationKey := range annotations {
if slices.Contains(trackedAnnotations, annotationKey) {
// remove annotationKey from trackedAnnotations
deleteIndex := -1
for i := range trackedAnnotations {
if trackedAnnotations[i] == annotationKey {
deleteIndex = i
break
}
}
if deleteIndex != -1 {
trackedAnnotations = append(trackedAnnotations[:deleteIndex], trackedAnnotations[deleteIndex+1:]...)
}
}
}
return trackedAnnotations
}
16 changes: 16 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type StatusEndpointScope string

type StatusReferences map[string]string

type StatusTrackedAnnotationType string

type StatusTrackedAnnotations map[StatusTrackedAnnotationType][]string

const (
StatusReferenceCertSecretName = "svcCertSecretName"
StatusReferencePullSecretName = "saPullSecretName"
Expand Down Expand Up @@ -90,6 +94,11 @@ type BaseComponentStatus interface {
UnsetReconcileInterval()

GetLatestTransitionTime() *metav1.Time

GetTrackedAnnotations() StatusTrackedAnnotations
SetTrackedAnnotations(StatusTrackedAnnotations)
GetTrackedAnnotation(StatusTrackedAnnotationType) []string
SetTrackedAnnotation(StatusTrackedAnnotationType, []string)
}

const (
Expand All @@ -105,6 +114,13 @@ const (
// Status Endpoint Scopes
StatusEndpointScopeExternal StatusEndpointScope = "External"
StatusEndpointScopeInternal StatusEndpointScope = "Internal"

// Status Tracked Annotation Types
StatusTrackedAnnotationTypeGlobal StatusTrackedAnnotationType = "global"
StatusTrackedAnnotationTypeDeployment StatusTrackedAnnotationType = "deployment"
StatusTrackedAnnotationTypeStatefulSet StatusTrackedAnnotationType = "statefulset"
StatusTrackedAnnotationTypeService StatusTrackedAnnotationType = "service"
StatusTrackedAnnotationTypeRoute StatusTrackedAnnotationType = "route"
)

// BaseComponentAutoscaling represents basic HPA configuration
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/rc.app.stacks_runtimecomponents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8865,6 +8865,12 @@ spec:
additionalProperties:
type: string
type: object
trackedAnnotations:
additionalProperties:
items:
type: string
type: array
type: object
versions:
properties:
reconciled:
Expand Down
3 changes: 2 additions & 1 deletion internal/controller/runtimecomponent_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,8 @@ func (r *RuntimeComponentReconciler) SetupWithManager(mgr ctrl.Manager) error {
pred := predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
// Ignore updates to CR status in which case metadata.Generation does not change
return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() && (isClusterWide || watchNamespacesMap[e.ObjectNew.GetNamespace()])
return (e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() && (isClusterWide || watchNamespacesMap[e.ObjectNew.GetNamespace()])) ||
(e.ObjectOld.GetGeneration() == e.ObjectNew.GetGeneration() && !appstacksutils.AnnotationsEqual(e.ObjectOld.GetAnnotations(), e.ObjectNew.GetAnnotations()))
},
CreateFunc: func(e event.CreateEvent) bool {
return isClusterWide || watchNamespacesMap[e.Object.GetNamespace()]
Expand Down
8 changes: 8 additions & 0 deletions utils/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,14 @@ func (r *ReconcilerBase) ManageSuccess(conditionType common.StatusConditionType,
}
}

// Track annotations that were defined in the CR into status
common.SaveTrackedAnnotations(ba,
common.StatusTrackedAnnotationTypeGlobal,
common.StatusTrackedAnnotationTypeDeployment,
common.StatusTrackedAnnotationTypeStatefulSet,
common.StatusTrackedAnnotationTypeService,
common.StatusTrackedAnnotationTypeRoute)

err := r.UpdateStatus(ba.(client.Object))
if err != nil {
log.Error(err, "Unable to update status")
Expand Down
37 changes: 37 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"os"
"slices"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -45,10 +46,41 @@ const RCOOperandVersion = "1.4.4"

var APIVersionNotFoundError = errors.New("APIVersion is not available")

func AnnotationsEqual(a1, a2 map[string]string) bool {
if a1 == nil && a2 == nil {
return true
}
if a1 == nil {
return false
}
if a2 == nil {
return false
}
if len(a1) != len(a2) {
return false
}
for k1, v1 := range a1 {
if a2[k1] != v1 {
return false
}
}
return true
}

func FilterMapByKeys(values map[string]string, keys []string) map[string]string {
for k := range values {
if !slices.Contains(keys, k) {
delete(values, k)
}
}
return values
}

// CustomizeDeployment ...
func CustomizeDeployment(deploy *appsv1.Deployment, ba common.BaseComponent) {
obj := ba.(metav1.Object)
deploy.Labels = ba.GetLabels()
deploy.Annotations = common.DeleteMissingTrackedAnnotations(deploy.Annotations, ba, common.StatusTrackedAnnotationTypeDeployment)
deploy.Annotations = MergeMaps(deploy.Annotations, ba.GetAnnotations())

if ba.GetAutoscaling() == nil {
Expand Down Expand Up @@ -79,6 +111,7 @@ func CustomizeDeployment(deploy *appsv1.Deployment, ba common.BaseComponent) {
func CustomizeStatefulSet(statefulSet *appsv1.StatefulSet, ba common.BaseComponent) {
obj := ba.(metav1.Object)
statefulSet.Labels = ba.GetLabels()
statefulSet.Annotations = common.DeleteMissingTrackedAnnotations(statefulSet.Annotations, ba, common.StatusTrackedAnnotationTypeStatefulSet)
statefulSet.Annotations = MergeMaps(statefulSet.Annotations, ba.GetAnnotations())

if ba.GetAutoscaling() == nil {
Expand Down Expand Up @@ -110,6 +143,7 @@ func CustomizeStatefulSet(statefulSet *appsv1.StatefulSet, ba common.BaseCompone
func CustomizeRoute(route *routev1.Route, ba common.BaseComponent, key string, crt string, ca string, destCACert string) {
obj := ba.(metav1.Object)
route.Labels = ba.GetLabels()
route.Annotations = common.DeleteMissingTrackedAnnotations(route.Annotations, ba, common.StatusTrackedAnnotationTypeRoute)
route.Annotations = MergeMaps(route.Annotations, ba.GetAnnotations())

if ba.GetRoute() != nil {
Expand Down Expand Up @@ -198,6 +232,7 @@ func ErrorIsNoMatchesForKind(err error, kind string, version string) bool {
func CustomizeService(svc *corev1.Service, ba common.BaseComponent) {
obj := ba.(metav1.Object)
svc.Labels = ba.GetLabels()
svc.Annotations = common.DeleteMissingTrackedAnnotations(svc.Annotations, ba, common.StatusTrackedAnnotationTypeService)
CustomizeServiceAnnotations(svc)
svc.Annotations = MergeMaps(svc.Annotations, ba.GetAnnotations())

Expand Down Expand Up @@ -344,6 +379,7 @@ func customizeProbeDefaults(config *corev1.Probe, defaultProbe *corev1.Probe) *c
func CustomizeNetworkPolicy(networkPolicy *networkingv1.NetworkPolicy, isOpenShift bool, ba common.BaseComponent) {
obj := ba.(metav1.Object)
networkPolicy.Labels = ba.GetLabels()
networkPolicy.Annotations = common.DeleteMissingTrackedAnnotations(networkPolicy.Annotations, ba, common.StatusTrackedAnnotationTypeGlobal)
networkPolicy.Annotations = MergeMaps(networkPolicy.Annotations, ba.GetAnnotations())

networkPolicy.Spec.PolicyTypes = []networkingv1.PolicyType{networkingv1.PolicyTypeIngress}
Expand Down Expand Up @@ -649,6 +685,7 @@ func customizeAffinityArchitectures(affinity *corev1.Affinity, affinityConfig co
func CustomizePodSpec(pts *corev1.PodTemplateSpec, ba common.BaseComponent) {
obj := ba.(metav1.Object)
pts.Labels = ba.GetLabels()
pts.Annotations = common.DeleteMissingTrackedAnnotations(pts.Annotations, ba, common.StatusTrackedAnnotationTypeDeployment, common.StatusTrackedAnnotationTypeStatefulSet)
pts.Annotations = MergeMaps(pts.Annotations, ba.GetAnnotations())

// If they exist, add annotations from the StatefulSet or Deployment to the pods
Expand Down