-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extend Volume Policies feature to support more actions
Signed-off-by: Shubham Pampattiwar <[email protected]> fix volume policy action execution Signed-off-by: Shubham Pampattiwar <[email protected]> remove unused files Signed-off-by: Shubham Pampattiwar <[email protected]> add changelog file Signed-off-by: Shubham Pampattiwar <[email protected]> fix CI linter errors fix linter errors
- Loading branch information
1 parent
3c377bc
commit fd00a0f
Showing
9 changed files
with
424 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Implementation for Extending VolumePolicies to support more actions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
package volumehelper | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/sirupsen/logrus" | ||
corev1api "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
|
||
"github.com/vmware-tanzu/velero/internal/resourcepolicies" | ||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" | ||
"github.com/vmware-tanzu/velero/pkg/client" | ||
"github.com/vmware-tanzu/velero/pkg/kuberesource" | ||
"github.com/vmware-tanzu/velero/pkg/util" | ||
"github.com/vmware-tanzu/velero/pkg/util/boolptr" | ||
pdvolumeutil "github.com/vmware-tanzu/velero/pkg/util/podvolume" | ||
) | ||
|
||
type VolumeHelper interface { | ||
GetVolumesForFSBackup(pod *corev1api.Pod) ([]string, []string, error) | ||
ShouldPerformSnapshot(obj runtime.Unstructured, groupResource schema.GroupResource) (bool, error) | ||
} | ||
|
||
type VolumeHelperImpl struct { | ||
Backup *velerov1api.Backup | ||
VolumePolicy *resourcepolicies.Policies | ||
BackupExcludePVC bool | ||
DefaultVolumesToFsBackup bool | ||
SnapshotVolumes *bool | ||
Logger logrus.FieldLogger | ||
DynamicFactory client.DynamicFactory | ||
} | ||
|
||
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 | ||
if groupResource == kuberesource.PersistentVolumeClaims { | ||
pvc := new(corev1api.PersistentVolumeClaim) | ||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pvc); err != nil { | ||
return false, err | ||
} | ||
|
||
if v.VolumePolicy != nil { | ||
// fetch the PV for PVC and get matching action for it | ||
pv, err := v.GetPVForPVC(pvc) | ||
if err != nil { | ||
return false, err | ||
} | ||
action, err := v.VolumePolicy.GetMatchAction(pv) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
// Also account for SnapshotVolumes flag on backup CR | ||
if action != nil && action.Type == resourcepolicies.Snapshot && boolptr.IsSetToTrue(v.SnapshotVolumes) { | ||
return true, nil | ||
} | ||
return false, nil | ||
} | ||
} | ||
|
||
if groupResource == kuberesource.PersistentVolumes { | ||
pv := new(corev1api.PersistentVolume) | ||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pv); err != nil { | ||
return false, err | ||
} | ||
|
||
if v.VolumePolicy != nil { | ||
action, err := v.VolumePolicy.GetMatchAction(pv) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
// Also account for SnapshotVolumes flag on backup CR | ||
if action != nil && action.Type == resourcepolicies.Snapshot && boolptr.IsSetToTrue(v.SnapshotVolumes) { | ||
return true, nil | ||
} | ||
return false, nil | ||
} | ||
} | ||
|
||
// now if volumepolicy is not specified then just check for snapshotVolumes flag | ||
if boolptr.IsSetToTrue(v.SnapshotVolumes) { | ||
return true, nil | ||
} | ||
return false, nil | ||
} | ||
|
||
func (v *VolumeHelperImpl) GetVolumesForFSBackup(pod *corev1api.Pod) ([]string, []string, error) { | ||
// Check if there is a fs-backup/snapshot volume policy specified by the user, if yes then use the volume policy approach to | ||
// get the list volumes for fs-backup else go via the legacy annotation based approach | ||
var includedVolumes, optedOutVolumes, volsToProcessByLegacyApproach = make([]string, 0), make([]string, 0), make([]string, 0) | ||
var err error | ||
|
||
if v.VolumePolicy != nil { | ||
// Get the list of volumes to back up using pod volume backup for the given pod matching fs-backup volume policy action | ||
v.Logger.Infof("Volume Policy specified by the user, using volume policy approach to segregate pod volumes for fs-backup") | ||
includedVolumes, optedOutVolumes, volsToProcessByLegacyApproach, err = v.GetVolumesMatchingFSBackupAction(pod, v.VolumePolicy) | ||
if err != nil { | ||
return includedVolumes, optedOutVolumes, err | ||
} | ||
} | ||
|
||
// process legacy annotation based approach, this will be done for 2 scenarios: | ||
// 1. volume policy not specified by the user | ||
// 2. there are some volumes for which the volume policy approach did not get any supported matching actions | ||
if v.VolumePolicy == nil || len(volsToProcessByLegacyApproach) > 0 { | ||
// Get the list of volumes to back up using pod volume backup from the pod's annotations. | ||
// We will also pass the list of volume that did not have any supported volume policy action matched in legacy approach so that | ||
// those volumes get processed via legacy annotation based approach, this is a fallback option on annotation based legacy approach | ||
v.Logger.Infof("fs-backup or snapshot Volume Policy not specified by the user, using legacy approach based on annotations") | ||
includedVolumes, optedOutVolumes = pdvolumeutil.GetVolumesByPod(pod, v.DefaultVolumesToFsBackup, v.BackupExcludePVC, volsToProcessByLegacyApproach) | ||
} | ||
return includedVolumes, optedOutVolumes, nil | ||
} | ||
|
||
// GetVolumesMatchingFSBackupAction returns a list of volume names to backup for the provided pod having fs-backup volume policy action | ||
func (v *VolumeHelperImpl) GetVolumesMatchingFSBackupAction(pod *corev1api.Pod, volumePolicies *resourcepolicies.Policies) ([]string, []string, []string, error) { | ||
FSBackupActionMatchingVols := []string{} | ||
FSBackupNonActionMatchingVols := []string{} | ||
NoActionMatchingVols := []string{} | ||
|
||
volsToExclude := pdvolumeutil.GetVolumesToExclude(pod) | ||
for _, vol := range pod.Spec.Volumes { | ||
if !v.ShouldIncludeVolumeInBackup(vol, volsToExclude) { | ||
continue | ||
} | ||
// don't backup volumes that are included in the exclude list. | ||
if util.Contains(volsToExclude, vol.Name) { | ||
FSBackupNonActionMatchingVols = append(FSBackupNonActionMatchingVols, vol.Name) | ||
} | ||
|
||
if vol.PersistentVolumeClaim != nil { | ||
// fetch the associated PVC first | ||
pvc, err := v.GetPVCForPodVolume(vol, pod) | ||
if err != nil { | ||
return FSBackupActionMatchingVols, FSBackupNonActionMatchingVols, NoActionMatchingVols, err | ||
} | ||
// now fetch the PV and call GetMatchAction on it | ||
pv, err := v.GetPVForPVC(pvc) | ||
if err != nil { | ||
return FSBackupActionMatchingVols, FSBackupNonActionMatchingVols, NoActionMatchingVols, err | ||
} | ||
// now get the action for pv | ||
action, err := volumePolicies.GetMatchAction(pv) | ||
if err != nil { | ||
return FSBackupActionMatchingVols, FSBackupNonActionMatchingVols, NoActionMatchingVols, err | ||
} | ||
|
||
// Record volume list having at least some action so that they are not processed in legacy fallback option | ||
if action == nil { | ||
NoActionMatchingVols = append(NoActionMatchingVols, vol.Name) | ||
} | ||
|
||
// Now if the matched action is not nil and is `fs-backup` then add that Volume to the FSBackupActionMatchingVols | ||
// else add that volume to FSBackupNonActionMatchingVols | ||
// we already tracked the volume not matching any kind actions supported by volume policy in NoActionMatchingVols | ||
// The NoActionMatchingVols list will be processed via legacy annotation based approach as a fallback option | ||
if action != nil && action.Type == resourcepolicies.FSBackup { | ||
FSBackupActionMatchingVols = append(FSBackupActionMatchingVols, vol.Name) | ||
} else { | ||
FSBackupNonActionMatchingVols = append(FSBackupNonActionMatchingVols, vol.Name) | ||
} | ||
} | ||
} | ||
return FSBackupActionMatchingVols, FSBackupNonActionMatchingVols, NoActionMatchingVols, nil | ||
} | ||
|
||
func (v *VolumeHelperImpl) ShouldIncludeVolumeInBackup(vol corev1api.Volume, volsToExclude []string) 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 && v.BackupExcludePVC { | ||
includeVolumeInBackup = false | ||
} | ||
// don't backup volumes that are included in the exclude list. | ||
if util.Contains(volsToExclude, vol.Name) { | ||
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) GetPVCForPodVolume(vol corev1api.Volume, pod *corev1api.Pod) (*corev1api.PersistentVolumeClaim, error) { | ||
pvcResource := metav1.APIResource{Name: "persistentvolumeclaims", Namespaced: true} | ||
pvcClient, err := v.DynamicFactory.ClientForGroupVersionResource(schema.GroupVersion{Group: "", Version: "v1"}, pvcResource, pod.Namespace) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pvcObject, err := pvcClient.Get(vol.PersistentVolumeClaim.ClaimName, metav1.GetOptions{}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pvc := new(corev1api.PersistentVolumeClaim) | ||
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(pvcObject.UnstructuredContent(), pvc); err != nil { | ||
return nil, err | ||
} | ||
|
||
return pvc, nil | ||
} | ||
|
||
func (v *VolumeHelperImpl) GetPVForPVC(pvc *corev1api.PersistentVolumeClaim) (*corev1api.PersistentVolume, error) { | ||
pvResource := metav1.APIResource{Name: "persistentvolumes", Namespaced: false} | ||
pvClient, err := v.DynamicFactory.ClientForGroupVersionResource(schema.GroupVersion{Group: "", Version: "v1"}, pvResource, "") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pvObject, err := pvClient.Get(pvc.Spec.VolumeName, metav1.GetOptions{}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pv := new(corev1api.PersistentVolume) | ||
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(pvObject.UnstructuredContent(), pv); err != nil { | ||
return nil, err | ||
} | ||
return pv, nil | ||
} |
Oops, something went wrong.