-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generates and labels Volume*SnapshotClass
#168
base: main
Are you sure you want to change the base?
Conversation
d68c5d2
to
7e993b3
Compare
38c58d7
to
b148266
Compare
VolumeGroupSnapshotClass
Volume*SnapshotClass
Volume*SnapshotClass
Volume*SnapshotClass
/cc @rewantsoni |
Volume*SnapshotClass
Volume*SnapshotClass
Volume*SnapshotClass
Volume*SnapshotClass
63d1586
to
0af9ddc
Compare
labels["ramendr.openshift.io/replicationID"] = r.storageClaimHash | ||
labels["ramendr.openshift.io/storageID"] = storageID | ||
if resource.Name == "cephfs" { | ||
volumeSnapshotClass = r.getCephFSVolumeSnapshotClass(data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The labels are not being applied to the cephfs or rbd VolumeSnapshotClass
labels["ramendr.openshift.io/replicationID"] = r.storageClaimHash | |
labels["ramendr.openshift.io/storageID"] = storageID | |
if resource.Name == "cephfs" { | |
volumeSnapshotClass = r.getCephFSVolumeSnapshotClass(data) | |
labels["ramendr.openshift.io/replicationID"] = r.storageClaimHash | |
labels["ramendr.openshift.io/storageID"] = storageID | |
if resource.Name == "cephfs" { | |
volumeSnapshotClass = r.getCephFSVolumeSnapshotClass(data,labels) |
if reflect.DeepEqual(existing.Parameters, volumeGroupSnapshotClass.Parameters) { | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also check for labels?
d3c6028
to
bc8957d
Compare
Makefile
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yq
isn't recognized anymore. (bash: yq: command not found
)
err = json.Unmarshal(resource.Data, &data) | ||
if err != nil { | ||
return reconcile.Result{}, fmt.Errorf("failed to unmarshal StorageClaim configuration response: %v", err) | ||
} | ||
|
||
// SID for RamenDR | ||
storageID := getMD5Hash(string(r.storageClaim.UID)) + r.storageClaimHash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storage ID does not need to be distinct across clusters, only the Replication ID needs to be.
But even if the requirement was true for Storage ID, a UUID is assumed to be globally unique (by definition).
storageID := getMD5Hash(string(r.storageClaim.UID)) + r.storageClaimHash | |
storageID := r.storageClaim.UID |
And please move the definition of the storage ID var outside of the for, there is no need to redefine it on every iteration as it is constant for the entire reconciliation.
labels["ramendr.openshift.io/storageID"] = storageID | ||
volumeSnapshotClass = r.getCephDriverVolumeSnapshotClass(resource.Name, data) | ||
utils.AddLabels(volumeSnapshotClass, labels) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are only concerned with a single-label why do we need the labels map var in the first place
labels["ramendr.openshift.io/storageID"] = storageID | |
volumeSnapshotClass = r.getCephDriverVolumeSnapshotClass(resource.Name, data) | |
utils.AddLabels(volumeSnapshotClass, labels) | |
volumeSnapshotClass = r.getCephDriverVolumeSnapshotClass(resource.Name, data) | |
utils.AddLabel(volumeSnapshotClass, "ramendr.openshift.io/storageID", storageID) |
labels["ramendr.openshift.io/storageID"] = storageID | ||
volumeGroupSnapshotClass = r.getCephDriverVolumeGroupSnapshotClass(resource.Name, data) | ||
utils.AddLabels(volumeGroupSnapshotClass, labels) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as the one above regarding AddLabels
// generate a new clusterID for cephfs subvolumegroup, as | ||
// storageclaim is clusterscoped resources using its | ||
// hash as the clusterID | ||
data["clusterID"] = r.storageClaimHash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raaizik I am confused.
If we can decide on a cluster ID locally why do we need to send it from the provider (as implemented in this PR: red-hat-storage/ocs-operator#2661)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As are we ➡️ comment
Edit: removed from provider
labelsEqual := reflect.DeepEqual(existing.Labels, volumeSnapshotClass.Labels) | ||
if reflect.DeepEqual(existing.Parameters, volumeSnapshotClass.Parameters) && labelsEqual { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make the code more readable by aligning both the params and label equality checks
Also, we are setting annotations so we should also consider checking equality there as well
labelsEqual := reflect.DeepEqual(existing.Labels, volumeSnapshotClass.Labels) | |
if reflect.DeepEqual(existing.Parameters, volumeSnapshotClass.Parameters) && labelsEqual { | |
areLabelsEqual := eflect.DeepEqual(existing.Labels, volumeSnapshotClass.Labels) | |
areAnnotationsEqual := eflect.DeepEqual(existing.Annotations, volumeSnapshotClass.Annotations) | |
areParamsEqual := reflect.DeepEqual(existing.Parameters, volumeSnapshotClass.Parameters) | |
if areLabelsEqual && areAnnotationsEqual && areParamsEqual { |
Edit (leela): Fixed the suggestion rendering
if existing.UID != "" { | ||
// Since we have to update the existing VolumeSnapshotClass, so we will delete the existing VolumeSnapshotClass and create a new one. | ||
r.log.Info("VolumeSnapshotClass needs to be updated, deleting it.", "Name", existing.Name) | ||
if labelsEqual { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check does not make sense.
At this point in the code, it is already established that an update is needed (because of label changes or params changes)
The only concern left is if we need to delete the existing one or not!
Your current approach has a couple of bugs:
- If the VolumeSnapshotClass didn't exist then your labelsEqual will be true and you will jump to the lease which will try to update instead of create
- if both labels and params changed you will jump to the else and try to update instead of deleting and creating
With that in mind, I would prefer to keep the code simple and always delete and recreate, even in cases where we don't have to delete. Trying to do it differently will just lead to many edge cases that we can easily miss and the overhead in deleting and recreating is negligible as we don't assume many label changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nb-ohad i dont agree to delete the resource when its not required, we do have update for that case, these resources have special meaning we should use what is required when instead of having a common case to delete and recreate always if there is a change but it doesn't require to be deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Madhu-1 This is going to be an evergrowing pit of bugs, even the current implementation is missing very important edge cases. I am not predicting too many label, and annotation changes over the lifetime of these resources and want to avoid very common eng mistakes that result in non-so-simple bugs when an Eng touches this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it's pretty straightforward because we already know what will change and what will not change. However, I haven't reviewed this part of the code thoroughly, so I may be missing some edge cases. My suggestion is to update the metadata if it changes, and to use delete/create for anything else. If you believe there are potential issues, we can resort to using delete/create, but I still prefer updating in certain cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed back to no update.
@@ -651,6 +673,70 @@ func (r *StorageClaimReconciler) hasVolumeSnapshotContents() (bool, error) { | |||
return false, nil | |||
} | |||
|
|||
func (r *StorageClaimReconciler) getCephDriverVolumeGroupSnapshotClass(driverType string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we please move this function near the getCephDriverVolumeSnapshotClass
function? both of them have similar concerns and I would prefer to have both of them at a similar location in the file
if err := r.own(volumeGroupSnapshotClass); err != nil { | ||
return fmt.Errorf("failed to own volumegroupsnapshotclass: %v", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't volumeGroupSnapshotClass (and volumeSnapshotClass) a cluster-wide resource? In that case, you cannot own it by a namespace scoped resource (StorageClaim resource). We are already working around that using the storage claim annotation. So let's remove this own
call.
func (r *StorageClaimReconciler) createOrReplaceVolumeGroupSnapshotClass(volumeGroupSnapshotClass *groupsnapapi.VolumeGroupSnapshotClass) error { | ||
existing := &groupsnapapi.VolumeGroupSnapshotClass{} | ||
existing.Name = r.storageClaim.Name | ||
|
||
if err := r.own(volumeGroupSnapshotClass); err != nil { | ||
return fmt.Errorf("failed to own volumegroupsnapshotclass: %v", err) | ||
} | ||
|
||
if err := r.get(existing); err != nil && !errors.IsNotFound(err) { | ||
return fmt.Errorf("failed to get VolumeGroupSnapshotClass: %v", err) | ||
} | ||
|
||
// If present then compare the existing VolumeGroupSnapshotClass parameters and labels with | ||
// the received VolumeGroupSnapshotClass parameters and labels, and only proceed if they differ. | ||
labelsEqual := reflect.DeepEqual(existing.Labels, volumeGroupSnapshotClass.Labels) | ||
if reflect.DeepEqual(existing.Parameters, volumeGroupSnapshotClass.Parameters) && labelsEqual { | ||
return nil | ||
} | ||
|
||
if labelsEqual { | ||
// VolumeGroupSnapshotClass already exists, but parameters have changed. Delete the existing VolumeGroupSnapshotClass and create a new one. | ||
if existing.UID != "" { | ||
// Since we have to update the existing VolumeGroupSnapshotClass, so we will delete the existing VolumeGroupSnapshotClass and create a new one. | ||
r.log.Info("VolumeGroupSnapshotClass needs to be updated, deleting it.", "Name", existing.Name) | ||
|
||
// Delete the VolumeGroupSnapshotClass. | ||
if err := r.delete(existing); err != nil { | ||
r.log.Error(err, "Failed to delete VolumeGroupSnapshotClass.", "Name", existing.Name) | ||
return fmt.Errorf("failed to delete VolumeGroupSnapshotClass: %v", err) | ||
} | ||
} | ||
r.log.Info("Creating VolumeGroupSnapshotClass.", "Name", existing.Name) | ||
if err := r.Client.Create(r.ctx, volumeGroupSnapshotClass); err != nil { | ||
return fmt.Errorf("failed to create VolumeGroupSnapshotClass: %v", err) | ||
} | ||
} else { | ||
r.log.Info("Updating VolumeGroupSnapshotClass", "Name", existing.Name) | ||
if err := r.Client.Update(r.ctx, volumeGroupSnapshotClass); err != nil { | ||
return fmt.Errorf("failed to update VolumeGroupSnapshotClass: %v", err) | ||
} | ||
} | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is almost an identical duplication of the createOrReplaceVolumeSnapshotClass
.
Instead of creating a duplicate where Bugz might be repeating, can we please generalize to a
func (r *StorageClaimReconciler) createOrReplace(client object.Client) error
ff475ee
to
4d46063
Compare
c042c9c
to
fc5325c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some nits, LGTM
@@ -207,6 +219,20 @@ func (r *StorageClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request | |||
return result, nil | |||
} | |||
|
|||
func (r *StorageClaimReconciler) mapCRDAvailability(crdNames ...string) map[string]bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to make crdNames as input? instead of that can we have it as var inside this method only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? We need to be able to send to this function all the CRD names that need to go through this flow path.
var driverName string | ||
if driverType == "cephfs" { | ||
driverName = csi.GetCephFSDriverName() | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please add the case of rbd here?
if err != nil { | ||
return fmt.Errorf("failed to create StorageClass: %v", err) | ||
r.log.Info("Creating "+classKind+".", "Name", types.NamespacedName{ | ||
Namespace: class.GetNamespace(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace is not required as its clusterScoped resouces
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And how about for StoageClass
? Seems like it did have its namespace mentioned in the logs:
r.log.Info("StorageClass needs to be updated, deleting it.", "StorageClass", klog.KRef(storageClass.Namespace, existing.Name))
func (r *StorageClaimReconciler) createOrReplaceStorageClass(storageClass *storagev1.StorageClass) error { | ||
existing := &storagev1.StorageClass{} | ||
existing.Name = r.storageClaim.Name | ||
func (r *StorageClaimReconciler) createOrReplaceClass(class client.Object) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we get the existing before the switch case? existingParameters will always be empty in this case.
We should,
- set the name to existing, get it based on the object kind
- compare the existing parameter/labels/annotations to the desired
- if equal break
- own the desired
- if UID is not nil delete it
- create the desired
Could we also rename class client.Object to a better name? maybe to desired?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like class
since they're all classes
@@ -207,6 +218,20 @@ func (r *StorageClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request | |||
return result, nil | |||
} | |||
|
|||
func (r *StorageClaimReconciler) mapCRDAvailability(crdNames ...string) map[string]bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe move this to a utility package?
Signed-off-by: raaizik <132667934+raaizik@users.noreply.github.com>
Signed-off-by: raaizik <132667934+raaizik@users.noreply.github.com>
Changes
VolumeGroupSnapshotClass
with the required labels for RDR added to both VGSC and VSC.RHSTOR-5795