in pkg/controllers/clusterresourceplacement/controller.go [428:536]
func (r *Reconciler) getOrCreateClusterResourceSnapshot(ctx context.Context, crp *fleetv1beta1.ClusterResourcePlacement, envelopeObjCount int, resourceSnapshotSpec *fleetv1beta1.ResourceSnapshotSpec, revisionHistoryLimit int) (*fleetv1beta1.ClusterResourceSnapshot, error) {
resourceHash, err := resource.HashOf(resourceSnapshotSpec)
crpKObj := klog.KObj(crp)
if err != nil {
klog.ErrorS(err, "Failed to generate resource hash of crp", "clusterResourcePlacement", crpKObj)
return nil, controller.NewUnexpectedBehaviorError(err)
}
// latestResourceSnapshotIndex should be -1 when there is no snapshot.
latestResourceSnapshot, latestResourceSnapshotIndex, err := r.lookupLatestResourceSnapshot(ctx, crp)
if err != nil {
return nil, err
}
latestResourceSnapshotHash := ""
numberOfSnapshots := -1
if latestResourceSnapshot != nil {
latestResourceSnapshotHash, err = parseResourceGroupHashFromAnnotation(latestResourceSnapshot)
if err != nil {
klog.ErrorS(err, "Failed to get the ResourceGroupHashAnnotation", "clusterResourceSnapshot", klog.KObj(latestResourceSnapshot))
return nil, controller.NewUnexpectedBehaviorError(err)
}
numberOfSnapshots, err = annotations.ExtractNumberOfResourceSnapshotsFromResourceSnapshot(latestResourceSnapshot)
if err != nil {
klog.ErrorS(err, "Failed to get the NumberOfResourceSnapshotsAnnotation", "clusterResourceSnapshot", klog.KObj(latestResourceSnapshot))
return nil, controller.NewUnexpectedBehaviorError(err)
}
}
shouldCreateNewMasterClusterSnapshot := true
// This index indicates the selected resource in the split selectedResourceList, if this index is zero we start
// from creating the master clusterResourceSnapshot if it's greater than zero it means that the master clusterResourceSnapshot
// got created but not all sub-indexed clusterResourceSnapshots have been created yet. It covers the corner case where the
// controller crashes in the middle.
resourceSnapshotStartIndex := 0
if latestResourceSnapshot != nil && latestResourceSnapshotHash == resourceHash {
if err := r.ensureLatestResourceSnapshot(ctx, latestResourceSnapshot); err != nil {
return nil, err
}
// check to see all that the master cluster resource snapshot and sub-indexed snapshots belonging to the same group index exists.
latestGroupResourceLabelMatcher := client.MatchingLabels{
fleetv1beta1.ResourceIndexLabel: latestResourceSnapshot.Labels[fleetv1beta1.ResourceIndexLabel],
fleetv1beta1.CRPTrackingLabel: crp.Name,
}
resourceSnapshotList := &fleetv1beta1.ClusterResourceSnapshotList{}
if err := r.Client.List(ctx, resourceSnapshotList, latestGroupResourceLabelMatcher); err != nil {
klog.ErrorS(err, "Failed to list the latest group clusterResourceSnapshots associated with the clusterResourcePlacement",
"clusterResourcePlacement", crp.Name)
return nil, controller.NewAPIServerError(true, err)
}
if len(resourceSnapshotList.Items) == numberOfSnapshots {
klog.V(2).InfoS("ClusterResourceSnapshots have not changed", "clusterResourcePlacement", crpKObj, "clusterResourceSnapshot", klog.KObj(latestResourceSnapshot))
return latestResourceSnapshot, nil
}
// we should not create a new master cluster resource snapshot.
shouldCreateNewMasterClusterSnapshot = false
// set resourceSnapshotStartIndex to start from this index, so we don't try to recreate existing sub-indexed cluster resource snapshots.
resourceSnapshotStartIndex = len(resourceSnapshotList.Items)
}
// Need to create new snapshot when 1) there is no snapshots or 2) the latest snapshot hash != current one.
// mark the last resource snapshot as inactive if it is different from what we have now or 3) when some
// sub-indexed cluster resource snapshots belonging to the same group have not been created, the master
// cluster resource snapshot should exist and be latest.
if latestResourceSnapshot != nil &&
latestResourceSnapshotHash != resourceHash &&
latestResourceSnapshot.Labels[fleetv1beta1.IsLatestSnapshotLabel] == strconv.FormatBool(true) {
// set the latest label to false first to make sure there is only one or none active resource snapshot
latestResourceSnapshot.Labels[fleetv1beta1.IsLatestSnapshotLabel] = strconv.FormatBool(false)
if err := r.Client.Update(ctx, latestResourceSnapshot); err != nil {
klog.ErrorS(err, "Failed to set the isLatestSnapshot label to false", "clusterResourceSnapshot", klog.KObj(latestResourceSnapshot))
return nil, controller.NewUpdateIgnoreConflictError(err)
}
klog.V(2).InfoS("Marked the existing clusterResourceSnapshot as inactive", "clusterResourcePlacement", crpKObj, "clusterResourceSnapshot", klog.KObj(latestResourceSnapshot))
}
// only delete redundant resource snapshots and increment the latest resource snapshot index if new master cluster resource snapshot is to be created.
if shouldCreateNewMasterClusterSnapshot {
// delete redundant snapshot revisions before creating a new master cluster resource snapshot to guarantee that the number of snapshots
// won't exceed the limit.
if err := r.deleteRedundantResourceSnapshots(ctx, crp, revisionHistoryLimit); err != nil {
return nil, err
}
latestResourceSnapshotIndex++
}
// split selected resources as list of lists.
selectedResourcesList := splitSelectedResources(resourceSnapshotSpec.SelectedResources)
var resourceSnapshot *fleetv1beta1.ClusterResourceSnapshot
for i := resourceSnapshotStartIndex; i < len(selectedResourcesList); i++ {
if i == 0 {
resourceSnapshot = buildMasterClusterResourceSnapshot(latestResourceSnapshotIndex, len(selectedResourcesList), envelopeObjCount, crp.Name, resourceHash, selectedResourcesList[i])
latestResourceSnapshot = resourceSnapshot
} else {
resourceSnapshot = buildSubIndexResourceSnapshot(latestResourceSnapshotIndex, i-1, crp.Name, selectedResourcesList[i])
}
if err = r.createResourceSnapshot(ctx, crp, resourceSnapshot); err != nil {
return nil, err
}
}
// shouldCreateNewMasterClusterSnapshot is used here to be defensive in case of the regression.
if shouldCreateNewMasterClusterSnapshot && len(selectedResourcesList) == 0 {
resourceSnapshot = buildMasterClusterResourceSnapshot(latestResourceSnapshotIndex, 1, envelopeObjCount, crp.Name, resourceHash, []fleetv1beta1.ResourceContent{})
latestResourceSnapshot = resourceSnapshot
if err = r.createResourceSnapshot(ctx, crp, resourceSnapshot); err != nil {
return nil, err
}
}
return latestResourceSnapshot, nil
}