Skip to content

PWX-31470: Clusterwide operator CR migration support.#1451

Merged
diptiranjanpx merged 1 commit intomasterfrom
PWX-31470
Aug 29, 2023
Merged

PWX-31470: Clusterwide operator CR migration support.#1451
diptiranjanpx merged 1 commit intomasterfrom
PWX-31470

Conversation

@diptiranjanpx
Copy link
Contributor

@diptiranjanpx diptiranjanpx commented Jul 13, 2023

What type of PR is this?
improvement

What this PR does / why we need it:
Stashing strategy for the CR content to a configmap so that after migration with startApplication: False the application automatically does not get started when it is managed by a clusterwide operator.

Avoid recreating the resources during migration if there is no change to the resource content.

Does this PR change a user-facing CRD or CLI?:

Is a release note needed?:

Issue: Applications controlled by CR automatically gets started after migration if the operator is already up and running in a different namespace.
User Impact: Apps in destination cluster gets started which is not as per expectation with startApplication: false
Resolution: Stashing strategy for the CR content to a configmap has been added so that only after activate migration the CR spec gets applied and app will start then only.

Does this change need to be cherry-picked to a release branch?:

Test
Have been updated in PWX-31470 & PWX-32807.

@cnbu-jenkins
Copy link
Collaborator

Can one of the admins verify this patch?

Copy link
Contributor

@pp511 pp511 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok. Some nits and one concern related to newOwnerUIDMapping fetch.

// Clusterwide Operators will control the CRs in all namespaces
// StashStratergy option will make sure as part of migration the CR does not get applied
// if StashCR is enabled. The CR will be applied as part of app activation
StashStratergy StashStratergy `json:"stashStratergy"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit..typo. Strategy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +53 to +54
// StashStratergy to restrict CR applying during migration
type StashStratergy struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit..typo. Strategy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

log.MigrationLog(migration).Infof("getting configmap details for stashing resource %s/%s", unstructured.GetKind(), unstructured.GetName())
unstructured, err = getStashedConfigMap(unstructured)
if err != nil {
log.MigrationLog(migration).Warnf("unable to get stashed configmap content for object %s/%s, error: %v", unstructured.GetKind(), unstructured.GetName(), err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to move ahead here despite the error?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That function returns an error only in case of json marshal which is unlikely to fail. But we should continue in the for loop and avoid going ahead in this function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided we will continue in the error case. This has not been addressed yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of continue , addressed with a goto such that we can update the migration CR with error message.

return pvToPVCMapping
}

func getStashCREnabledAppRegs(crdList stork_api.ApplicationRegistrationList) map[string]bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit..Since the function is named getStashCREnabledAppRegs. So maybe we can return a list of AppReg for which StashCR is true. Also we won't have to validate true or false on the caller side. Or we should rename this to avoid confusion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed it to "getAppRegsStashMap".
Returning a list of AppRegs and again checking whether for a crd if stash strategy is enabled is costly.

Comment on lines +2628 to +2632
configMap.SetGroupVersionKind(schema.GroupVersionKind{
Group: "",
Version: "v1",
Kind: "ConfigMap",
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit..I think it would be cleaner to not use unstructured cm here(as used "core v1 . ConfigMap{}) so we won't have to explicitly set gvk. We can convert it to unstructured when we are returning from the function

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on this - would be easier to just create a corev1.ConfigMap objet and then convert it to unstructered.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return
}
ruleset := resourcecollector.GetDefaultRuleSet()
// Create the CRs in the same namespace if those dont exist
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit..nit.. don't or do not :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

util.CheckErr(err)
continue
}
//var client k8sdynamic.ResourceInterface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit.. remove the commended line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines +388 to +389
//resourceInterface := configClient.Resource()
//client = resourceInterface.Namespace(namespace)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit..remove comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

// Get the CR
newParentResourceUnstructured, err := resourceClient.Get(context.TODO(), unstructuredObj.GetName(), metav1.GetOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit.. I think we should rename it and remove "parent". While reading the code it seemed like we are getting parentresource for the CR but it is not the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, renamed it to "newResourceUnstructured"

destPvcOwnersList := make([]metav1.OwnerReference, 0)
needUpdate := false
for _, pvcOwner := range pvc.GetOwnerReferences() {
if newOwnerUID, ok := newOwnerUIDMapping[fmt.Sprintf("%s-%s", pvcOwner.Name, pvcOwner.Kind)]; ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are putting(name,namespace) as the key in newOwnerUIDMapping. But while fetching we are using (name, kind)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, fixed it now. thanks.

excludeSelectors := make(map[string]string)
if migration.Spec.ExcludeSelectors != nil {
excludeSelectors = migration.Spec.ExcludeSelectors
excludeSelectors["stash-cr"] = "true"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: lets define stash-cr as a constant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

var dynamicClient dynamic.ResourceInterface
ownerName := ""
for _, o := range objects {
gkv := o.GetObjectKind().GroupVersionKind()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: gvk

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

ownerName := ""
for _, o := range objects {
gkv := o.GetObjectKind().GroupVersionKind()
keyName := fmt.Sprintf("%v-%v-%v", gkv.Group, gkv.Kind, gkv.Version)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: may be write a helper function to get the key name for stashCRMap and use it in both the places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

o.GetObjectKind().GroupVersionKind().GroupVersion().WithResource(resource.Name))
}
} else {
log.MigrationLog(migration).Infof("getting configmap details for stashing resource %s/%s", unstructured.GetKind(), unstructured.GetName())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: start log lines with upper-case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
// obj uid is not available and appending random uid will cause the next migration's get/delete of the configmap fail
// hence adding kind-name for cm name.
cmName := fmt.Sprintf("%s-%s", strings.ToLower(obj.GetKind()), obj.GetName())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this should also be a helper function under pkg/utils/utils.go

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +2628 to +2632
configMap.SetGroupVersionKind(schema.GroupVersionKind{
Group: "",
Version: "v1",
Kind: "ConfigMap",
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on this - would be easier to just create a corev1.ConfigMap objet and then convert it to unstructered.

log.MigrationLog(migration).Infof("getting configmap details for stashing resource %s/%s", unstructured.GetKind(), unstructured.GetName())
unstructured, err = getStashedConfigMap(unstructured)
if err != nil {
log.MigrationLog(migration).Warnf("unable to get stashed configmap content for object %s/%s, error: %v", unstructured.GetKind(), unstructured.GetName(), err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That function returns an error only in case of json marshal which is unlikely to fail. But we should continue in the for loop and avoid going ahead in this function.

configMap.Object["data"] = data
labels := map[string]string{"stash-cr": "true", "resource-kind": obj.GetKind()}
configMap.SetLabels(labels)
annotations := map[string]string{skipResourceAnnotation: "true", storkCreatedAnnotation: "true"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add the name of the object as a label

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name max char allowed is 253 while for label's value it is 63.
Will put a key with name then to keep the full name information. Is it ok , @adityadani ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CR name was added in configmap with a separate key.


// Create the CR
_, err = resourceClient.Create(context.TODO(), unstructuredObj, metav1.CreateOptions{})
if err != nil && !errors.IsAlreadyExists(err) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the error is already exists then should we log it on the CLI output?

Copy link
Contributor Author

@diptiranjanpx diptiranjanpx Jul 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with a message hinting the resource exists.

Copy link
Contributor

@pp511 pp511 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks ok to me. Few comments and clarifications.

Value string `json:"value"`
}

// StashStrategy to restrict CR applying during migration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. nit. CR applying -> applying CR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +41 to +42
// StashStrategy option will make sure as part of migration the CR does not get applied
// if StashCR is enabled. The CR will be applied as part of app activation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. nit...StashStrategy option if enabled will make sure CR does not get applied during migration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +613 to +615
excludeSelectors[StashCRLabel] = "true"
} else {
excludeSelectors[StashCRLabel] = "true"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

excludeSelectors[StashCRLabel] = "true" need not be inside if and else both. It can be outside these conditional statements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

o.GetObjectKind().GroupVersionKind().GroupVersion().WithResource(resource.Name))
}
} else {
log.MigrationLog(migration).Infof("Getting configmap details for stashing resource %s/%s", unstructured.GetKind(), unstructured.GetName())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit.. Why not print the version too?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing in the next statement too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

log.MigrationLog(migration).Infof("getting configmap details for stashing resource %s/%s", unstructured.GetKind(), unstructured.GetName())
unstructured, err = getStashedConfigMap(unstructured)
if err != nil {
log.MigrationLog(migration).Warnf("unable to get stashed configmap content for object %s/%s, error: %v", unstructured.GetKind(), unstructured.GetName(), err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided we will continue in the error case. This has not been addressed yet.

}
// obj uid is not available and appending random uid will cause the next migration's get/delete of the configmap fail
// hence adding kind-name for cm name.
cmName := utils.GetStashedConfigMapName(strings.ToLower(obj.GetKind()), obj.GetName())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should include version too in the cmName. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted this as version will contain "/" which is not allowed for configmap name.

Namespace: obj.GetNamespace(),
Labels: map[string]string{
StashCRLabel: "true",
"resource-kind": obj.GetKind(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we setting the resource-kind label here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been kept for debugging purpose.

Data: map[string]string{
StashedCMCRKey: string(jsonData),
StashedCMOwnedPVCKey: string(ownedPVCsEncoded),
"name": obj.GetName(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit..Why not have a constant for "name" too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return fmt.Sprintf("%v-%v-%v", group, kind, version)
}

func getStashedConfigMap(obj *unstructured.Unstructured, inputObjectHash uint64) (*unstructured.Unstructured, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally I would have expected core v1 . ConfigMap{} being returned from this function. But I see the you are trying to use the same worker to work for CM too. So I guess it's fine.

Copy link
Contributor

@adityadani adityadani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you address the comments from the previous iteration?

Comment on lines +1725 to +1726
if _, ok := appRegsStashMap[keyName]; ok {
if appRegsStashMap[keyName] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need both the if conditions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +2310 to +2312
if _, ok := appRegsStashMap[keyName]; ok {
stashCREnabled = appRegsStashMap[keyName]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if _, ok := appRegsStashMap[keyName]; ok {
stashCREnabled = appRegsStashMap[keyName]
}
stashCREnabled, _ = appRegsStashMap[keyName]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

@pp511 pp511 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment on lines +611 to +614
excludeSelectors := make(map[string]string)
excludeSelectors[StashCRLabel] = "true"
if migration.Spec.ExcludeSelectors != nil {
excludeSelectors = migration.Spec.ExcludeSelectors
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You always want to add the StashCRLabel to the exclude selectors right? Then on line 614 you will be overriding it with whatever the user has provided.

Suggested change
excludeSelectors := make(map[string]string)
excludeSelectors[StashCRLabel] = "true"
if migration.Spec.ExcludeSelectors != nil {
excludeSelectors = migration.Spec.ExcludeSelectors
}
if migration.Spec.ExcludeSelectors != nil {
excludeSelectors = migration.Spec.ExcludeSelectors
} else {
excludeSelectors := make(map[string]string)
}
excludeSelectors[StashCRLabel] = "true"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right. now fixed it.

}

func GetStashedConfigMapName(apiVersion string, objKind string, objName string) string {
cmName := fmt.Sprintf("%s-%s-%s", apiVersion, objKind, objName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add group as well here since you are anyway trimming it? Esp the opensource DB operators have conflicting Kinds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

@diptiranjanpx diptiranjanpx force-pushed the PWX-31470 branch 3 times, most recently from 7415850 to 69b9990 Compare August 24, 2023 04:51
@diptiranjanpx diptiranjanpx added enhancement documentation release-note Information about this change needs to be added to the release note labels Aug 25, 2023
@diptiranjanpx diptiranjanpx force-pushed the PWX-31470 branch 2 times, most recently from e2069e1 to 70be3ea Compare August 26, 2023 01:49
PWX-32807: Compare hash objects of resources to avoid deletion and recreation in case of no change during the migration.
@diptiranjanpx diptiranjanpx force-pushed the PWX-31470 branch 2 times, most recently from 70be3ea to 781a3c0 Compare August 29, 2023 02:16
@diptiranjanpx diptiranjanpx merged commit 20c1b0d into master Aug 29, 2023
@diptiranjanpx diptiranjanpx deleted the PWX-31470 branch May 27, 2024 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation enhancement release-note Information about this change needs to be added to the release note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants