Skip to content

Commit

Permalink
ClusterSync: Warn on resource conflicts
Browse files Browse the repository at this point in the history
Add a new ClusterSync.Status field, ResourceConflicts, and a
corresponding printColumn visible when `-o wide` is used.

When a single resource (GVK + name [+ namespace if ns-scoped]) is
mentioned more than once in the `resources` sections (not `patches` or
`secretMappings`) of the [Selector]SyncSets matching a
ClusterDeployment, we will add a human-readable message to this status
field indicating the resource and the [Selector]SyncSets in which it is
mentioned.

Example message:

```
"Resource {v1 Namespace openshift-logging } is mentioned by 2 [Selector]SyncSets: mygroup, osd-logging1"
```

HIVE-2634
  • Loading branch information
2uasimojo committed Nov 15, 2024
1 parent 3ccd44d commit a8e68ad
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 5 deletions.
7 changes: 7 additions & 0 deletions apis/hiveinternal/v1alpha1/clustersync_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[0].reason`
// +kubebuilder:printcolumn:name="ControllerReplica",type=string,JSONPath=`.status.controlledByReplica`
// +kubebuilder:printcolumn:name="Message",type=string,priority=1,JSONPath=`.status.conditions[?(@.type=="Failed")].message`
// +kubebuilder:printcolumn:name="ResourceConflicts",type=string,priority=1,JSONPath=`.status.resourceConflicts`
type ClusterSync struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand Down Expand Up @@ -50,6 +51,12 @@ type ClusterSyncStatus struct {
// recently handled the ClusterSync. If the hive-clustersync statefulset is scaled up or down, the
// controlling replica can change, potentially causing logs to be spread across multiple pods.
ControlledByReplica *int64 `json:"controlledByReplica,omitempty"`

// ResourceConflicts is a list of human-readable messages warning when a resource is mentioned
// more than once across all [Selector]SyncSets affecting this ClusterSync. Note that this only
// counts Spec.Resources, not Patches or SecretMappings.
// +optional
ResourceConflicts []string `json:"resourceConflicts,omitempty"`
}

// SyncStatus is the status of applying a specific SyncSet or SelectorSyncSet to the cluster.
Expand Down
5 changes: 5 additions & 0 deletions apis/hiveinternal/v1alpha1/zz_generated.deepcopy.go

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

12 changes: 12 additions & 0 deletions config/crds/hiveinternal.openshift.io_clustersyncs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ spec:
name: Message
priority: 1
type: string
- jsonPath: .status.resourceConflicts
name: ResourceConflicts
priority: 1
type: string
name: v1alpha1
schema:
openAPIV3Schema:
Expand Down Expand Up @@ -103,6 +107,14 @@ spec:
all (selector)syncsets to a cluster.
format: date-time
type: string
resourceConflicts:
description: |-
ResourceConflicts is a list of human-readable messages warning when a resource is mentioned
more than once across all [Selector]SyncSets affecting this ClusterSync. Note that this only
counts Spec.Resources, not Patches or SecretMappings.
items:
type: string
type: array
selectorSyncSets:
description: SelectorSyncSets is the sync status of all of the SelectorSyncSets
for the cluster.
Expand Down
15 changes: 15 additions & 0 deletions hack/app-sre/saas-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4547,6 +4547,10 @@ objects:
name: Message
priority: 1
type: string
- jsonPath: .status.resourceConflicts
name: ResourceConflicts
priority: 1
type: string
name: v1alpha1
schema:
openAPIV3Schema:
Expand Down Expand Up @@ -4640,6 +4644,17 @@ objects:
applied all (selector)syncsets to a cluster.
format: date-time
type: string
resourceConflicts:
description: 'ResourceConflicts is a list of human-readable messages
warning when a resource is mentioned
more than once across all [Selector]SyncSets affecting this ClusterSync.
Note that this only
counts Spec.Resources, not Patches or SecretMappings.'
items:
type: string
type: array
selectorSyncSets:
description: SelectorSyncSets is the sync status of all of the SelectorSyncSets
for the cluster.
Expand Down
34 changes: 29 additions & 5 deletions pkg/controller/clustersync/clustersync_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import (
"github.com/openshift/hive/pkg/resource"
)

type ResourceToSyncSetTracker map[hiveintv1alpha1.SyncResourceReference][]string

const (
ControllerName = hivev1.ClustersyncControllerName
defaultReapplyInterval = 2 * time.Hour
Expand Down Expand Up @@ -391,12 +393,15 @@ func (r *ReconcileClusterSync) Reconcile(ctx context.Context, request reconcile.
needToDoFullReapply := needToCreateLease || needToRenew
recobsrv.SetOutcome(hivemetrics.ReconcileOutcomeFullSync)

r2sTracker := ResourceToSyncSetTracker{}

// Apply SyncSets
syncStatusesForSyncSets, syncSetsNeedRequeue := r.applySyncSets(
cd,
"SyncSet",
syncSets,
clusterSync.Status.SyncSets,
r2sTracker,
needToDoFullReapply,
false, // no need to report SelectorSyncSet metrics if we're reconciling non-selector SyncSets
resourceHelper,
Expand All @@ -410,6 +415,7 @@ func (r *ReconcileClusterSync) Reconcile(ctx context.Context, request reconcile.
"SelectorSyncSet",
selectorSyncSets,
clusterSync.Status.SelectorSyncSets,
r2sTracker,
needToDoFullReapply,
clusterSync.Status.FirstSuccessTime == nil, // only report SelectorSyncSet metrics if we haven't reached first success
resourceHelper,
Expand All @@ -418,6 +424,7 @@ func (r *ReconcileClusterSync) Reconcile(ctx context.Context, request reconcile.
clusterSync.Status.SelectorSyncSets = syncStatusesForSelectorSyncSets

setFailedCondition(clusterSync)
setResourceConflicts(clusterSync, r2sTracker)

// Set clusterSync.Status.FirstSyncSetsSuccessTime
syncStatuses := append(syncStatusesForSyncSets, syncStatusesForSelectorSyncSets...)
Expand Down Expand Up @@ -469,6 +476,7 @@ func (r *ReconcileClusterSync) applySyncSets(
syncSetType string,
syncSets []CommonSyncSet,
syncStatuses []hiveintv1alpha1.SyncStatus,
r2sTracker ResourceToSyncSetTracker,
needToDoFullReapply bool,
reportSelectorSyncSetMetrics bool,
resourceHelper resource.Helper,
Expand Down Expand Up @@ -538,7 +546,7 @@ func (r *ReconcileClusterSync) applySyncSets(
}

// Apply the syncset
resourcesApplied, resourcesInSyncSet, syncSetNeedsRequeue, err := r.applySyncSet(syncSet, cd, resourceHelper, logger)
resourcesApplied, resourcesInSyncSet, syncSetNeedsRequeue, err := r.applySyncSet(syncSet, cd, r2sTracker, resourceHelper, logger)
newSyncStatus := hiveintv1alpha1.SyncStatus{
Name: syncSet.AsMetaObject().GetName(),
ObservedGeneration: syncSet.AsMetaObject().GetGeneration(),
Expand Down Expand Up @@ -649,6 +657,7 @@ func getOldSyncStatus(syncSet CommonSyncSet, syncSetStatuses []hiveintv1alpha1.S
func (r *ReconcileClusterSync) applySyncSet(
syncSet CommonSyncSet,
cd *hivev1.ClusterDeployment,
r2sTracker ResourceToSyncSetTracker,
resourceHelper resource.Helper,
logger log.FieldLogger,
) (
Expand All @@ -657,7 +666,7 @@ func (r *ReconcileClusterSync) applySyncSet(
requeue bool,
returnErr error,
) {
resources, referencesToResources, decodeErr := decodeResources(syncSet, cd, logger)
resources, referencesToResources, decodeErr := decodeResources(syncSet, cd, r2sTracker, logger)
referencesToSecrets := referencesToSecrets(syncSet)
resourcesInSyncSet = append(referencesToResources, referencesToSecrets...)
if decodeErr != nil {
Expand Down Expand Up @@ -708,7 +717,7 @@ func (r *ReconcileClusterSync) applySyncSet(
return
}

func decodeResources(syncSet CommonSyncSet, cd *hivev1.ClusterDeployment, logger log.FieldLogger) (
func decodeResources(syncSet CommonSyncSet, cd *hivev1.ClusterDeployment, r2sTracker ResourceToSyncSetTracker, logger log.FieldLogger) (
resources []*unstructured.Unstructured, references []hiveintv1alpha1.SyncResourceReference, returnErr error,
) {
var decodeErrors []error
Expand All @@ -728,12 +737,15 @@ func decodeResources(syncSet CommonSyncSet, cd *hivev1.ClusterDeployment, logger
}
}
resources = append(resources, u)
references = append(references, hiveintv1alpha1.SyncResourceReference{
ref := hiveintv1alpha1.SyncResourceReference{
APIVersion: u.GetAPIVersion(),
Kind: u.GetKind(),
Namespace: u.GetNamespace(),
Name: u.GetName(),
})
}
references = append(references, ref)
r2sTracker[ref] = append(r2sTracker[ref], syncSet.AsMetaObject().GetName())
logger.Debugf("Tracking resource %v from syncset %s", ref, syncSet.AsMetaObject().GetName())
}
returnErr = utilerrors.NewAggregate(decodeErrors)
return
Expand Down Expand Up @@ -997,6 +1009,18 @@ func setFailedCondition(clusterSync *hiveintv1alpha1.ClusterSync) {
}}
}

func setResourceConflicts(clusterSync *hiveintv1alpha1.ClusterSync, r2sTracker ResourceToSyncSetTracker) {
clusterSync.Status.ResourceConflicts = []string{}
tpl := "Resource %v is mentioned by %d [Selector]SyncSets: %v"
for resource, syncsets := range r2sTracker {
if count := len(syncsets); count > 1 {
clusterSync.Status.ResourceConflicts = append(
clusterSync.Status.ResourceConflicts,
fmt.Sprintf(tpl, resource, count, strings.Join(syncsets, ", ")))
}
}
}

func getFailingSyncSets(syncStatuses []hiveintv1alpha1.SyncStatus) []string {
var failures []string
for _, status := range syncStatuses {
Expand Down

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

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

0 comments on commit a8e68ad

Please sign in to comment.