Skip to content

Commit

Permalink
feat: implement the first version of the plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
tuusberg committed Dec 27, 2023
1 parent fe3c56f commit 1791783
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 92 deletions.
115 changes: 23 additions & 92 deletions internal/plugin/restorepluginv2.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,14 @@
/*
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package plugin

import (
"strings"
"time"

"github.com/sirupsen/logrus"
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
"k8s.io/apimachinery/pkg/api/meta"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/apimachinery/pkg/runtime"
)

const (
// If this annotation is found on the Velero Restore CR, then create an operation
// that is considered done at backup start time + example RIA operation duration
// If this annotation is not present, then operationID returned from Execute() will
// be empty.
// This annotation can also be set on the item, which overrides the restore CR value,
// to allow for testing multiple action lengths
AsyncRIADurationAnnotation = "velero.io/example-ria-operation-duration"
)

// RestorePlugin is a restore item action plugin for Velero
// RestorePluginV2 is a restore item action plugin for Velero
type RestorePluginV2 struct {
log logrus.FieldLogger
}
Expand All @@ -51,7 +22,7 @@ func NewRestorePluginV2(log logrus.FieldLogger) *RestorePluginV2 {
// method -- it's used to tell velero what name it was registered under. The plugin implementation
// must define it, but it will never actually be called.
func (p *RestorePluginV2) Name() string {
return "exampleRestorePlugin"
return "velero-plugin-suspend-cronjobs"
}

// AppliesTo returns information about which resources this action should be invoked for.
Expand All @@ -60,77 +31,37 @@ func (p *RestorePluginV2) Name() string {
// A RestoreItemAction's Execute function will only be invoked on items that match the returned
// selector. A zero-valued ResourceSelector matches all resources.
func (p *RestorePluginV2) AppliesTo() (velero.ResourceSelector, error) {
return velero.ResourceSelector{}, nil
return velero.ResourceSelector{
IncludedResources: []string{"cronjobs"},
}, nil
}

// Execute allows the RestorePlugin to perform arbitrary logic with the item being restored,
// in this case, setting a custom annotation on the item being restored.
func (p *RestorePluginV2) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
p.log.Info("Hello from my RestorePlugin(v2)!")

metadata, err := meta.Accessor(input.Item)
// Since the resource selector we defined in AppliesTo() only matches
// CronJob resources, we can safely cast the input.Item to a *v1.CronJob.
var cronJob batchv1.CronJob
err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), &cronJob)
if err != nil {
return &velero.RestoreItemActionExecuteOutput{}, err
return nil, err
}

annotations := metadata.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}

annotations["velero.io/my-restore-pluginv2"] = "1"

metadata.SetAnnotations(annotations)
p.log.Infof("CronJob %s/%s will be suspended", cronJob.Namespace, cronJob.Name)
// This is where the job gets suspended.
cronJob.Spec.Suspend = boolPointer(!true) // TODO: set to true

duration := ""
if durationStr, ok := annotations[AsyncRIADurationAnnotation]; ok && len(durationStr) != 0 {
_, err := time.ParseDuration(durationStr)
if err == nil {
duration = durationStr
}
}
if duration == "" && input.Restore.Annotations != nil {
if durationStr, ok := input.Restore.Annotations[AsyncRIADurationAnnotation]; ok && len(durationStr) != 0 {
_, err := time.ParseDuration(durationStr)
if err == nil {
duration = durationStr
}
}
}
out := velero.NewRestoreItemActionExecuteOutput(input.Item)
// If duration is empty, we don't have an operation so just return the item.
if duration != "" {
out = out.WithOperationID(string(metadata.GetName()) + "/" + duration)
}

return out, nil
}

func (p *RestorePluginV2) Progress(operationID string, restore *v1.Restore) (velero.OperationProgress, error) {
progress := velero.OperationProgress{}
if operationID == "" {
return progress, riav2.InvalidOperationIDError(operationID)
}
splitOp := strings.Split(operationID, "/")
if len(splitOp) != 2 {
return progress, riav2.InvalidOperationIDError(operationID)
}
duration, err := time.ParseDuration(splitOp[1])
// Convert the CronJob back to unstructured data:
cronJobUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&cronJob)
if err != nil {
return progress, riav2.InvalidOperationIDError(operationID)
return nil, err
}
elapsed := time.Since(restore.Status.StartTimestamp.Time).Seconds()
if elapsed >= duration.Seconds() {
progress.Completed = true
progress.NCompleted = int64(duration.Seconds())
} else {
progress.NCompleted = int64(elapsed)
}
progress.NTotal = int64(duration.Seconds())
progress.OperationUnits = "seconds"
progress.Updated = time.Now()
input.Item.SetUnstructuredContent(cronJobUnstructured)
return velero.NewRestoreItemActionExecuteOutput(input.Item), nil
}

return progress, nil
func (p *RestorePluginV2) Progress(_ string, _ *v1.Restore) (velero.OperationProgress, error) {
return velero.OperationProgress{Completed: true}, nil
}

func (p *RestorePluginV2) Cancel(operationID string, restore *v1.Restore) error {
Expand Down
5 changes: 5 additions & 0 deletions internal/plugin/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package plugin

func boolPointer(b bool) *bool {
return &b
}

0 comments on commit 1791783

Please sign in to comment.