mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-28 02:44:19 +00:00
* Add MustIncludeAdditionalItemPVCs structure in backup. It's used to track PVCs returned by BIA with mustIncluded annotaion and PVC is excluded from backup by global filter. * Modfiy the volumeHelper interface to add a parameter function for ShouldPerformFSBackup. * Modify to support fine-grained backup filters. * Modify according to comments. Use a read-only interface to replace the parameter function. Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>
527 lines
19 KiB
Go
527 lines
19 KiB
Go
package volumehelper
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/cockroachdb/errors"
|
|
"github.com/sirupsen/logrus"
|
|
corev1api "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/vmware-tanzu/velero/internal/resourcepolicies"
|
|
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
|
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
|
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
|
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
|
|
podvolumeutil "github.com/vmware-tanzu/velero/pkg/util/podvolume"
|
|
vhutil "github.com/vmware-tanzu/velero/pkg/util/volumehelper"
|
|
)
|
|
|
|
var errGetPVForPVC = errors.New("fail to get PV for PVC")
|
|
|
|
type volumeHelperImpl struct {
|
|
volumePolicy *resourcepolicies.Policies
|
|
snapshotVolumes *bool
|
|
logger logrus.FieldLogger
|
|
client crclient.Client
|
|
defaultVolumesToFSBackup bool
|
|
// This parameter is used to align the fs-backup with snapshot action,
|
|
// because PVC is already filtered by the resource filter before getting
|
|
// to the volume policy check, but fs-backup is based on the pod resource,
|
|
// the resource filter on PVC and PV doesn't work on this scenario.
|
|
backupExcludePVC bool
|
|
// pvcPodCache provides cached PVC to Pod mappings for improved performance.
|
|
// When there are many PVCs and pods, using this cache avoids O(N*M) lookups.
|
|
pvcPodCache *podvolumeutil.PVCPodCache
|
|
// pvcMustInclusionTracker provides read-only checks for whether a PVC is included
|
|
// in the backup as BIA's additionalItems through annotation
|
|
// backup.velero.io/must-include-additional-items.
|
|
pvcMustInclusionTracker vhutil.PVCMustInclusionTracker
|
|
}
|
|
|
|
// NewVolumeHelperImpl creates a VolumeHelper without PVC-to-Pod caching.
|
|
//
|
|
// Deprecated: Use NewVolumeHelperImplWithNamespaces or NewVolumeHelperImplWithCache instead
|
|
// for better performance. These functions provide PVC-to-Pod caching which avoids O(N*M)
|
|
// complexity when there are many PVCs and pods. See issue #9179 for details.
|
|
func NewVolumeHelperImpl(
|
|
volumePolicy *resourcepolicies.Policies,
|
|
snapshotVolumes *bool,
|
|
logger logrus.FieldLogger,
|
|
client crclient.Client,
|
|
defaultVolumesToFSBackup bool,
|
|
backupExcludePVC bool,
|
|
pvcMustInclusionTracker vhutil.PVCMustInclusionTracker,
|
|
) vhutil.VolumeHelper {
|
|
// Pass nil namespaces - no cache will be built, so this never fails.
|
|
// This is used by plugins that don't need the cache optimization.
|
|
vh, _ := NewVolumeHelperImplWithNamespaces(
|
|
volumePolicy,
|
|
snapshotVolumes,
|
|
logger,
|
|
client,
|
|
defaultVolumesToFSBackup,
|
|
backupExcludePVC,
|
|
nil,
|
|
pvcMustInclusionTracker,
|
|
)
|
|
return vh
|
|
}
|
|
|
|
// NewVolumeHelperImplWithNamespaces creates a VolumeHelper with a PVC-to-Pod cache for improved performance.
|
|
// The cache is built internally from the provided namespaces list.
|
|
// This avoids O(N*M) complexity when there are many PVCs and pods.
|
|
// See issue #9179 for details.
|
|
// Returns an error if cache building fails - callers should not proceed with backup in this case.
|
|
func NewVolumeHelperImplWithNamespaces(
|
|
volumePolicy *resourcepolicies.Policies,
|
|
snapshotVolumes *bool,
|
|
logger logrus.FieldLogger,
|
|
client crclient.Client,
|
|
defaultVolumesToFSBackup bool,
|
|
backupExcludePVC bool,
|
|
namespaces []string,
|
|
pvcMustInclusionTracker vhutil.PVCMustInclusionTracker,
|
|
) (vhutil.VolumeHelper, error) {
|
|
var pvcPodCache *podvolumeutil.PVCPodCache
|
|
if len(namespaces) > 0 {
|
|
pvcPodCache = podvolumeutil.NewPVCPodCache()
|
|
if err := pvcPodCache.BuildCacheForNamespaces(context.Background(), namespaces, client); err != nil {
|
|
return nil, err
|
|
}
|
|
logger.Infof("Built PVC-to-Pod cache for %d namespaces", len(namespaces))
|
|
}
|
|
|
|
return &volumeHelperImpl{
|
|
volumePolicy: volumePolicy,
|
|
snapshotVolumes: snapshotVolumes,
|
|
logger: logger,
|
|
client: client,
|
|
defaultVolumesToFSBackup: defaultVolumesToFSBackup,
|
|
backupExcludePVC: backupExcludePVC,
|
|
pvcPodCache: pvcPodCache,
|
|
pvcMustInclusionTracker: pvcMustInclusionTracker,
|
|
}, nil
|
|
}
|
|
|
|
// NewVolumeHelperImplWithCache creates a VolumeHelper using an externally managed PVC-to-Pod cache.
|
|
// This is used by plugins that build the cache lazily per-namespace (following the pattern from PR #9226).
|
|
// The cache can be nil, in which case PVC-to-Pod lookups will fall back to direct API calls.
|
|
func NewVolumeHelperImplWithCache(
|
|
backup velerov1api.Backup,
|
|
client crclient.Client,
|
|
logger logrus.FieldLogger,
|
|
pvcPodCache *podvolumeutil.PVCPodCache,
|
|
pvcMustInclusionTracker vhutil.PVCMustInclusionTracker,
|
|
) (vhutil.VolumeHelper, error) {
|
|
resourcePolicies, err := resourcepolicies.GetResourcePoliciesFromBackup(backup, client, logger)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to get volume policies from backup")
|
|
}
|
|
|
|
return &volumeHelperImpl{
|
|
volumePolicy: resourcePolicies,
|
|
snapshotVolumes: backup.Spec.SnapshotVolumes,
|
|
logger: logger,
|
|
client: client,
|
|
defaultVolumesToFSBackup: boolptr.IsSetToTrue(backup.Spec.DefaultVolumesToFsBackup),
|
|
backupExcludePVC: boolptr.IsSetToTrue(backup.Spec.SnapshotMoveData),
|
|
pvcPodCache: pvcPodCache,
|
|
pvcMustInclusionTracker: pvcMustInclusionTracker,
|
|
}, nil
|
|
}
|
|
|
|
func (v *volumeHelperImpl) getPVAndMatchAction(obj runtime.Unstructured, groupResource schema.GroupResource) (*resourcepolicies.Action, *corev1api.PersistentVolume, error) {
|
|
pvc := new(corev1api.PersistentVolumeClaim)
|
|
pv := new(corev1api.PersistentVolume)
|
|
var err error
|
|
var getPVErr error
|
|
|
|
if groupResource == kuberesource.PersistentVolumeClaims {
|
|
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pvc); err != nil {
|
|
v.logger.WithError(err).Warn("fail to convert unstructured into PVC")
|
|
return nil, nil, err
|
|
}
|
|
|
|
pv, err = kubeutil.GetPVForPVC(pvc, v.client)
|
|
if err != nil {
|
|
v.logger.WithError(err).Warnf("failed to get PV for PVC %s", pvc.Namespace+"/"+pvc.Name)
|
|
getPVErr = fmt.Errorf("fail to get PV for PVC %s: %w", pvc.Namespace+"/"+pvc.Name, errGetPVForPVC)
|
|
}
|
|
} else if groupResource == kuberesource.PersistentVolumes {
|
|
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pv); err != nil {
|
|
v.logger.WithError(err).Warn("fail to convert unstructured into PV")
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
|
|
if v.volumePolicy != nil {
|
|
vfd := resourcepolicies.NewVolumeFilterData(pv, nil, pvc)
|
|
action, err := v.volumePolicy.GetMatchAction(vfd)
|
|
if err != nil {
|
|
v.logger.WithError(err).Warnf("fail to get VolumePolicy match action for %+v", vfd)
|
|
return nil, nil, err
|
|
}
|
|
|
|
return action, pv, getPVErr
|
|
}
|
|
|
|
return nil, pv, getPVErr
|
|
}
|
|
|
|
func (v *volumeHelperImpl) ShouldPerformSnapshot(obj runtime.Unstructured, groupResource schema.GroupResource) (bool, error) {
|
|
// check if volume policy exists and also check if the object(pv/pvc) fits a volume policy criteria and see if the associated action is snapshot
|
|
// if it is not snapshot then skip the code path for snapshotting the PV/PVC
|
|
pvc := new(corev1api.PersistentVolumeClaim)
|
|
pv := new(corev1api.PersistentVolume)
|
|
var err error
|
|
|
|
var pvNotFoundErr error
|
|
if groupResource == kuberesource.PersistentVolumeClaims {
|
|
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pvc); err != nil {
|
|
v.logger.WithError(err).Error("fail to convert unstructured into PVC")
|
|
return false, err
|
|
}
|
|
|
|
pv, err = kubeutil.GetPVForPVC(pvc, v.client)
|
|
if err != nil {
|
|
// Any error means PV not available - save to return later if no policy matches
|
|
v.logger.Debugf("PV not found for PVC %s: %v", pvc.Namespace+"/"+pvc.Name, err)
|
|
pvNotFoundErr = err
|
|
pv = nil
|
|
}
|
|
}
|
|
|
|
if groupResource == kuberesource.PersistentVolumes {
|
|
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pv); err != nil {
|
|
v.logger.WithError(err).Error("fail to convert unstructured into PV")
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
if v.volumePolicy != nil {
|
|
vfd := resourcepolicies.NewVolumeFilterData(pv, nil, pvc)
|
|
action, err := v.volumePolicy.GetMatchAction(vfd)
|
|
if err != nil {
|
|
v.logger.WithError(err).Errorf("fail to get VolumePolicy match action for %+v", vfd)
|
|
return false, err
|
|
}
|
|
|
|
// If there is a match action, and the action type is snapshot, return true,
|
|
// or the action type is not snapshot, then return false.
|
|
// If there is no match action, go on to the next check.
|
|
if action != nil {
|
|
if action.Type == resourcepolicies.Snapshot {
|
|
v.logger.Infof("performing snapshot action for %+v", vfd)
|
|
return true, nil
|
|
} else {
|
|
v.logger.Infof("Skip snapshot action for %+v as the action type is %s", vfd, action.Type)
|
|
return false, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// If resource is PVC, and PV is nil (e.g., Pending/Lost PVC with no matching policy), return the original error
|
|
if groupResource == kuberesource.PersistentVolumeClaims && pv == nil && pvNotFoundErr != nil {
|
|
v.logger.WithError(pvNotFoundErr).Errorf("fail to get PV for PVC %s", pvc.Namespace+"/"+pvc.Name)
|
|
return false, pvNotFoundErr
|
|
}
|
|
|
|
// If this PV is claimed, see if we've already taken a (pod volume backup)
|
|
// snapshot of the contents of this PV. If so, don't take a snapshot.
|
|
if pv.Spec.ClaimRef != nil {
|
|
// Use cached lookup if available for better performance with many PVCs/pods
|
|
pods, err := podvolumeutil.GetPodsUsingPVCWithCache(
|
|
pv.Spec.ClaimRef.Namespace,
|
|
pv.Spec.ClaimRef.Name,
|
|
v.client,
|
|
v.pvcPodCache,
|
|
)
|
|
if err != nil {
|
|
v.logger.WithError(err).Errorf("fail to get pod for PV %s", pv.Name)
|
|
return false, err
|
|
}
|
|
|
|
for _, pod := range pods {
|
|
for _, vol := range pod.Spec.Volumes {
|
|
if vol.PersistentVolumeClaim != nil &&
|
|
vol.PersistentVolumeClaim.ClaimName == pv.Spec.ClaimRef.Name &&
|
|
v.shouldPerformFSBackupLegacy(vol, pod) {
|
|
v.logger.Infof("Skipping snapshot of pv %s because it is backed up with PodVolumeBackup.", pv.Name)
|
|
return false, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !boolptr.IsSetToFalse(v.snapshotVolumes) {
|
|
// If the backup.Spec.SnapshotVolumes is not set, or set to true, then should take the snapshot.
|
|
v.logger.Infof("performing snapshot action for pv %s as the snapshotVolumes is not set to false", pv.Name)
|
|
return true, nil
|
|
}
|
|
|
|
v.logger.Infof("skipping snapshot action for pv %s possibly due to no volume policy setting or snapshotVolumes is false", pv.Name)
|
|
return false, nil
|
|
}
|
|
|
|
func (v volumeHelperImpl) ShouldPerformFSBackup(volume corev1api.Volume, pod corev1api.Pod) (bool, error) {
|
|
if !v.shouldIncludeVolumeInBackup(volume, pod) {
|
|
v.logger.Debugf("skip fs-backup action for pod %s's volume %s, due to not pass volume check.", pod.Namespace+"/"+pod.Name, volume.Name)
|
|
return false, nil
|
|
}
|
|
|
|
var pvNotFoundErr error
|
|
if v.volumePolicy != nil {
|
|
var resource any
|
|
var err error
|
|
resource = &volume
|
|
var pvc = &corev1api.PersistentVolumeClaim{}
|
|
if volume.VolumeSource.PersistentVolumeClaim != nil {
|
|
pvc, err = kubeutil.GetPVCForPodVolume(&volume, &pod, v.client)
|
|
if err != nil {
|
|
v.logger.WithError(err).Errorf("fail to get PVC for pod %s", pod.Namespace+"/"+pod.Name)
|
|
return false, err
|
|
}
|
|
pvResource, err := kubeutil.GetPVForPVC(pvc, v.client)
|
|
if err != nil {
|
|
// Any error means PV not available - save to return later if no policy matches
|
|
v.logger.Debugf("PV not found for PVC %s: %v", pvc.Namespace+"/"+pvc.Name, err)
|
|
pvNotFoundErr = err
|
|
} else {
|
|
resource = pvResource
|
|
}
|
|
}
|
|
|
|
pv, podVolume, err := v.getVolumeFromResource(resource)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
vfd := resourcepolicies.NewVolumeFilterData(pv, podVolume, pvc)
|
|
action, err := v.volumePolicy.GetMatchAction(vfd)
|
|
if err != nil {
|
|
v.logger.WithError(err).Error("fail to get VolumePolicy match action for volume")
|
|
return false, err
|
|
}
|
|
|
|
if action != nil {
|
|
if action.Type == resourcepolicies.FSBackup {
|
|
v.logger.Infof("Perform fs-backup action for volume %s of pod %s due to volume policy match",
|
|
volume.Name, pod.Namespace+"/"+pod.Name)
|
|
return true, nil
|
|
} else {
|
|
v.logger.Infof("Skip fs-backup action for volume %s for pod %s because the action type is %s",
|
|
volume.Name, pod.Namespace+"/"+pod.Name, action.Type)
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
// If no policy matched and PV was not found, return the original error
|
|
if pvNotFoundErr != nil {
|
|
v.logger.WithError(pvNotFoundErr).Errorf("fail to get PV for PVC %s", pvc.Namespace+"/"+pvc.Name)
|
|
return false, pvNotFoundErr
|
|
}
|
|
}
|
|
|
|
if v.shouldPerformFSBackupLegacy(volume, pod) {
|
|
v.logger.Infof("Perform fs-backup action for volume %s of pod %s due to opt-in/out way",
|
|
volume.Name, pod.Namespace+"/"+pod.Name)
|
|
return true, nil
|
|
} else {
|
|
v.logger.Infof("Skip fs-backup action for volume %s of pod %s due to opt-in/out way",
|
|
volume.Name, pod.Namespace+"/"+pod.Name)
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
func (v volumeHelperImpl) shouldPerformFSBackupLegacy(
|
|
volume corev1api.Volume,
|
|
pod corev1api.Pod,
|
|
) bool {
|
|
// Check volume in opt-in way
|
|
if !v.defaultVolumesToFSBackup {
|
|
optInVolumeNames := podvolumeutil.GetVolumesToBackup(&pod)
|
|
for _, volumeName := range optInVolumeNames {
|
|
if volume.Name == volumeName {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
} else {
|
|
// Check volume in opt-out way
|
|
optOutVolumeNames := podvolumeutil.GetVolumesToExclude(&pod)
|
|
for _, volumeName := range optOutVolumeNames {
|
|
if volume.Name == volumeName {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
}
|
|
|
|
func (v *volumeHelperImpl) ShouldPerformCustomAction(obj runtime.Unstructured, groupResource schema.GroupResource, matchParams map[string]any) (bool, error) {
|
|
action, pv, err := v.getPVAndMatchAction(obj, groupResource)
|
|
if err != nil && !errors.Is(err, errGetPVForPVC) {
|
|
return false, err
|
|
}
|
|
|
|
metadata, metaErr := meta.Accessor(obj)
|
|
if metaErr != nil {
|
|
return false, metaErr
|
|
}
|
|
|
|
if action != nil {
|
|
if action.Type == resourcepolicies.Custom {
|
|
for k, requiredValue := range matchParams {
|
|
if actionValue, ok := action.Parameters[k]; !ok || actionValue != requiredValue {
|
|
v.logger.Infof("Skipping custom action for %s: %s as value for parameter %s is %s rather than the required %s",
|
|
groupResource.String(),
|
|
metadata.GetNamespace()+"/"+metadata.GetName(),
|
|
k, actionValue, requiredValue)
|
|
return false, nil
|
|
}
|
|
}
|
|
v.logger.Infof("performing custom action for %s: %s", groupResource.String(), metadata.GetNamespace()+"/"+metadata.GetName())
|
|
return true, nil
|
|
} else {
|
|
v.logger.Infof("Skipping custom action for %s: %s as the action type is %s",
|
|
groupResource.String(),
|
|
metadata.GetNamespace()+"/"+metadata.GetName(),
|
|
action.Type)
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
if (groupResource == kuberesource.PersistentVolumeClaims) && (pv == nil) && errors.Is(err, errGetPVForPVC) {
|
|
return false, nil
|
|
}
|
|
|
|
v.logger.Infof("skipping custom action for %s: %s due to no matching volume policy",
|
|
groupResource.String(), metadata.GetNamespace()+"/"+metadata.GetName())
|
|
return false, nil
|
|
}
|
|
|
|
// returns false if no matching action found. Returns true with the action name and Parameters map if there is a matching policy
|
|
func (v *volumeHelperImpl) GetActionParameters(obj runtime.Unstructured, groupResource schema.GroupResource) (bool, string, map[string]any, error) {
|
|
action, _, err := v.getPVAndMatchAction(obj, groupResource)
|
|
if err != nil {
|
|
if errors.Is(err, errGetPVForPVC) {
|
|
return false, "", nil, nil
|
|
}
|
|
|
|
return false, "", nil, err
|
|
}
|
|
|
|
metadata, metaErr := meta.Accessor(obj)
|
|
if metaErr != nil {
|
|
return false, "", nil, metaErr
|
|
}
|
|
|
|
if action != nil {
|
|
v.logger.Infof("found matching action for %s: %s, returning parameters",
|
|
groupResource.String(), metadata.GetNamespace()+"/"+metadata.GetName())
|
|
|
|
return true, string(action.Type), action.Parameters, nil
|
|
}
|
|
|
|
v.logger.Infof("no matching volume policy found for %s: %s, no parameters to return",
|
|
groupResource.String(), metadata.GetNamespace()+"/"+metadata.GetName())
|
|
|
|
return false, "", nil, nil
|
|
}
|
|
|
|
func (v *volumeHelperImpl) GetSnapshotClass(obj runtime.Unstructured, groupResource schema.GroupResource) (string, error) {
|
|
matched, actionType, params, err := v.GetActionParameters(obj, groupResource)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if !matched {
|
|
return "", nil
|
|
}
|
|
action := &resourcepolicies.Action{
|
|
Type: resourcepolicies.VolumeActionType(actionType),
|
|
Parameters: params,
|
|
}
|
|
return action.GetSnapshotClass()
|
|
}
|
|
|
|
func (v *volumeHelperImpl) shouldIncludeVolumeInBackup(vol corev1api.Volume, pod corev1api.Pod) bool {
|
|
includeVolumeInBackup := true
|
|
// cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods
|
|
// and therefore not accessible to the node agent daemon set.
|
|
if vol.HostPath != nil {
|
|
includeVolumeInBackup = false
|
|
}
|
|
// don't backup volumes mounting secrets. Secrets will be backed up separately.
|
|
if vol.Secret != nil {
|
|
includeVolumeInBackup = false
|
|
}
|
|
// don't backup volumes mounting ConfigMaps. ConfigMaps will be backed up separately.
|
|
if vol.ConfigMap != nil {
|
|
includeVolumeInBackup = false
|
|
}
|
|
// don't backup volumes mounted as projected volumes, all data in those come from kube state.
|
|
if vol.Projected != nil {
|
|
includeVolumeInBackup = false
|
|
}
|
|
// don't backup DownwardAPI volumes, all data in those come from kube state.
|
|
if vol.DownwardAPI != nil {
|
|
includeVolumeInBackup = false
|
|
}
|
|
if vol.PersistentVolumeClaim != nil {
|
|
if v.backupExcludePVC {
|
|
if v.pvcMustInclusionTracker == nil || !v.pvcMustInclusionTracker.IsPVCIncluded(pod.Namespace, vol.PersistentVolumeClaim.ClaimName) {
|
|
includeVolumeInBackup = false
|
|
}
|
|
}
|
|
}
|
|
// don't include volumes that mount the default service account token.
|
|
if strings.HasPrefix(vol.Name, "default-token") {
|
|
includeVolumeInBackup = false
|
|
}
|
|
return includeVolumeInBackup
|
|
}
|
|
|
|
func (v *volumeHelperImpl) getVolumeFromResource(resource any) (*corev1api.PersistentVolume, *corev1api.Volume, error) {
|
|
if pv, ok := resource.(*corev1api.PersistentVolume); ok {
|
|
return pv, nil, nil
|
|
} else if podVol, ok := resource.(*corev1api.Volume); ok {
|
|
return nil, podVol, nil
|
|
}
|
|
return nil, nil, fmt.Errorf("resource is not a PersistentVolume or Volume")
|
|
}
|
|
|
|
func (v *volumeHelperImpl) GetDataMoverFromActionParameters(obj runtime.Unstructured, groupResource schema.GroupResource) string {
|
|
action, _, err := v.getPVAndMatchAction(obj, groupResource)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
metadata, metaErr := meta.Accessor(obj)
|
|
if metaErr != nil {
|
|
return ""
|
|
}
|
|
|
|
if action != nil {
|
|
dataMover, err := action.GetDataMover()
|
|
if err != nil {
|
|
v.logger.WithError(err).Warn("fail to get data mover.")
|
|
return ""
|
|
}
|
|
v.logger.Infof("found matching action for %s: %s, returning data mover %s",
|
|
groupResource.String(), metadata.GetNamespace()+"/"+metadata.GetName(), dataMover)
|
|
return dataMover
|
|
}
|
|
|
|
v.logger.Debugf("no matching volume policy found for %s: %s, no data mover parameter to return",
|
|
groupResource.String(), metadata.GetNamespace()+"/"+metadata.GetName())
|
|
return ""
|
|
}
|