Skip to content
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

PB-4958: RT for restore flow for non-nfs BLs #1596

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
17 changes: 9 additions & 8 deletions pkg/apis/stork/v1alpha1/applicationrestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ type ApplicationRestore struct {

// ApplicationRestoreSpec is the spec used to restore applications
type ApplicationRestoreSpec struct {
BackupName string `json:"backupName"`
BackupLocation string `json:"backupLocation"`
NamespaceMapping map[string]string `json:"namespaceMapping"`
ReplacePolicy ApplicationRestoreReplacePolicyType `json:"replacePolicy"`
IncludeOptionalResourceTypes []string `json:"includeOptionalResourceTypes"`
IncludeResources []ObjectInfo `json:"includeResources"`
StorageClassMapping map[string]string `json:"storageClassMapping"`
RancherProjectMapping map[string]string `json:"rancherProjectMapping"`
BackupName string `json:"backupName"`
BackupLocation string `json:"backupLocation"`
NamespaceMapping map[string]string `json:"namespaceMapping"`
ReplacePolicy ApplicationRestoreReplacePolicyType `json:"replacePolicy"`
IncludeOptionalResourceTypes []string `json:"includeOptionalResourceTypes"`
IncludeResources []ObjectInfo `json:"includeResources"`
StorageClassMapping map[string]string `json:"storageClassMapping"`
RancherProjectMapping map[string]string `json:"rancherProjectMapping"`
ResourceTransformationTemplate string `json:"resourceTransformationTemplate"`
}

// ApplicationRestoreReplacePolicyType is the replace policy for the application restore
Expand Down
2 changes: 1 addition & 1 deletion pkg/applicationmanager/controllers/applicationclone.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ func (a *ApplicationCloneController) prepareResources(
clone.Spec.IncludeOptionalResourceTypes,
nil,
&opts,
"", "",
"", "", nil,
)
if err != nil {
return nil, err
Expand Down
82 changes: 82 additions & 0 deletions pkg/applicationmanager/controllers/applicationrestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,81 @@ func (a *ApplicationRestoreController) verifyNamespaces(restore *storkapi.Applic
func (a *ApplicationRestoreController) createNamespaces(backup *storkapi.ApplicationBackup,
backupLocation string,
restore *storkapi.ApplicationRestore) error {
logrus.Info("Starting createNamespaces")
var namespaces []*v1.Namespace

// rtTemplate := &storkapi.ResourceTransformation{}
// a.client.Get(context.Background(), types.NamespacedName{Name: restore.Spec.ResourceTransformationTemplate, Namespace: kdmputils.AdminNamespace}, rtTemplate)

var gvkResourceTransform map[schema.GroupVersionKind][]storkapi.TransformSpecs
Copy link
Contributor

Choose a reason for hiding this comment

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

@sgajawada-px The array nature of TransformSpecs will be effectively utilised only in future wherein we have multiple set of rules for a single type of resource and it is via a two different selector. As of current UX we don't support this via UI.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it's just good to have support from our end.

if restore.Spec.ResourceTransformationTemplate != "" {
var err error
gvkResourceTransform, err = resourcecollector.GetGVKToTransformSpecMapper(restore.Spec.ResourceTransformationTemplate, kdmputils.AdminNamespace)
if err != nil {
log.ApplicationRestoreLog(restore).
Warnf("Unable to get transformation spec from :%s, skipping transformation for this restore, err: %v", restore.Spec.ResourceTransformationTemplate, err)
return err
}
}

logrus.Info("gvkResourceTransform: ", gvkResourceTransform)

nsData, err := a.downloadObject(backup, backupLocation, restore.Namespace, nsObjectName, true)
if err != nil {
return err
}

logrus.Info("Before nsdata: ", string(nsData))

if gvkResourceTransform != nil {
objects := make([]*unstructured.Unstructured, 0)
if err = json.Unmarshal(nsData, &namespaces); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

This sound like a hack because of the way we store namespace resources., We should have fixed while we are backing up namespaces with GVK info in namespace.json. While doing actual implementation lets see if we have to chnage in backup path and how big that challenge would be

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO for the actual implementation

return err
}

for _, ns := range namespaces {
var object unstructured.Unstructured
content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns)
if err != nil {
logrus.Errorf("Error while DefaultUnstructuredConverter.ToUnstructured: %v", err)
}
object.SetUnstructuredContent(content)
objects = append(objects, &object)
}

logrus.Info("one: objects: ", objects)
// gvk, err := resourcecollector.GetGVK(nsData)
// if err != nil {
// return err
// }
gvk := schema.GroupVersionKind{
Group: "",
Version: "v1",
Kind: "Namespace",
}
logrus.Info("two: gvk: ", gvk)

if transforms, ok := gvkResourceTransform[gvk]; ok {
for _, object := range objects {
for _, transform := range transforms {
if resourcecollector.IsObjectLabelsMatched(object.UnstructuredContent(), transform.Selectors) {
resourcecollector.TransformObject(object, transform)
}
}
}
}
logrus.Info("three: transform done ")

//v1.SchemeGroupVersion.WithKind()
nsData, err = json.Marshal(objects)
if err != nil {
logrus.Error("error while marshaling the unstructed objects to []byte: err", err)
return err
}
}

logrus.Info("After nsdata: ", string(nsData))

rancherProjectMapping := getRancherProjectMapping(restore)
if nsData != nil {
if err = json.Unmarshal(nsData, &namespaces); err != nil {
Expand Down Expand Up @@ -683,6 +751,7 @@ func (a *ApplicationRestoreController) restoreVolumes(restore *storkapi.Applicat
&opts,
restore.Spec.BackupLocation,
restore.Namespace,
nil,
)
if err != nil {
return err
Expand Down Expand Up @@ -1593,6 +1662,18 @@ func (a *ApplicationRestoreController) applyResources(
break
}

var gvkResourceTransform map[schema.GroupVersionKind][]storkapi.TransformSpecs
log.ApplicationRestoreLog(restore).Info("restore.Spec.ResourceTransformationTemplate value:", restore.Spec.ResourceTransformationTemplate)
if restore.Spec.ResourceTransformationTemplate != "" {
var err error
gvkResourceTransform, err = resourcecollector.GetGVKToTransformSpecMapper(restore.Spec.ResourceTransformationTemplate, kdmputils.AdminNamespace)
if err != nil {
log.ApplicationRestoreLog(restore).
Warnf("Unable to get transformation spec from :%s, skipping transformation for this restore, err: %v", restore.Spec.ResourceTransformationTemplate, err)
return err
}
}

// This channel listens on two values if the CR's time stamp to be updated or to quit the go routine
startTime := time.Now()
for _, o := range objects {
Expand All @@ -1613,6 +1694,7 @@ func (a *ApplicationRestoreController) applyResources(
&opts,
restore.Spec.BackupLocation,
restore.Namespace,
gvkResourceTransform,
)
if err != nil {
return err
Expand Down
12 changes: 9 additions & 3 deletions pkg/migration/controllers/resourcetransformation.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ func (r *ResourceTransformationController) Init(mgr manager.Manager) error {
return controllers.RegisterTo(mgr, ResourceTransformationControllerName, r, &stork_api.ResourceTransformation{})
}

const (
rtCreatedForAnnotationKey = "portworx.io/CreatedFor"
rtCreatedForRestoreAnnotationValue = "restore"
)

// Reconcile manages ResourceTransformation resources.
func (r *ResourceTransformationController) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
resourceTransformation := &stork_api.ResourceTransformation{}
Expand Down Expand Up @@ -121,6 +126,9 @@ func (r *ResourceTransformationController) handle(ctx context.Context, transform
return nil
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: During r.validateSpecPath(transform) need to remove the filter getsupportsk8stypes to allow cr and crds in rt only for the restore flow

}
transform.Status.Status = stork_api.ResourceTransformationStatusInProgress
if value, ok := transform.GetAnnotations()[rtCreatedForAnnotationKey]; ok && value == rtCreatedForRestoreAnnotationValue {
transform.Status.Status = stork_api.ResourceTransformationStatusReady
}
if err = r.client.Update(ctx, transform); err != nil {
return err
}
Expand Down Expand Up @@ -154,9 +162,7 @@ func (r *ResourceTransformationController) validateSpecPath(transform *stork_api
if err != nil {
return err
}
if !resourcecollector.GetSupportedK8SResources(kind, []string{}) {
return fmt.Errorf("unsupported resource kind for transformation: %s", kind)
}

for _, path := range spec.Paths {
// TODO: this can be validated via CRDs as well, when we have defined schema
// for stork crds
Expand Down
27 changes: 27 additions & 0 deletions pkg/resourcecollector/resourcecollector.go
Original file line number Diff line number Diff line change
Expand Up @@ -1131,7 +1131,9 @@ func (r *ResourceCollector) PrepareResourceForApply(
opts *Options,
backuplocationName string,
backuplocationNamespace string,
gvkResourceTransform map[schema.GroupVersionKind][]stork_api.TransformSpecs,
) (bool, error) {
logrus.Info("Starting PrepareResourceForApply")
objectType, err := meta.TypeAccessor(object)
if err != nil {
return false, err
Expand All @@ -1158,6 +1160,31 @@ func (r *ResourceCollector) PrepareResourceForApply(
// Update the namespace of the object, will be no-op for clustered resources
metadata.SetNamespace(val)
}

logrus.Infof("gvkResourceTransform: %v", gvkResourceTransform)
if gvkResourceTransform != nil {
gv, err := schema.ParseGroupVersion(objectType.GetAPIVersion())
logrus.Infof("Parse groupVersion %v", gv)
if err != nil {
logrus.Error("Error while parsing the group version for the object: ", object)
}
gvk := gv.WithKind(objectType.GetKind())
logrus.Infof("groupVersion %v with kind %v is gvk: %v", gv, objectType.GetKind(), gvk)

if transforms, ok := gvkResourceTransform[gvk]; ok {
logrus.Infof("[]stork_api.TransformSpecs %v for gvk %v", transforms, gvk)
for _, transform := range transforms {
logrus.Infof("TransformSpec %v for gvk %v", transform, gvk)
if IsObjectLabelsMatched(object.UnstructuredContent(), transform.Selectors) {
err := TransformObject(object, transform)
if err != nil {
logrus.Error("Error Transforming the Object")
}
}
}
}
}

switch objectType.GetKind() {
case "Job":
if slice.ContainsString(optionalResourceTypes, "job", strings.ToLower) ||
Expand Down
Loading