mirror of
https://github.com/kubevirt/containerized-data-importer.git
synced 2025-06-03 06:30:22 +00:00

* touch up zero restoresize snapshot Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * clone populator only supports PVC source now snapshot coming soon Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * more unit tests Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * unit test for clone populator Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * func tests for clone populator Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * move clone populator cleanup function to planner other review comments verifier pod should bount readonly Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * add readonly flag to test executor pods synchronize get hash calls Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * increase linter timeout Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * better/explicit readonly support for test pods Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * check pv for driver info before looking up storageclass as it may not exist Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * addressed review comments Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * chooseStrategy shoud generate more events Signed-off-by: Michael Henriksen <mhenriks@redhat.com> --------- Signed-off-by: Michael Henriksen <mhenriks@redhat.com>
556 lines
17 KiB
Go
556 lines
17 KiB
Go
package clone
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"sync"
|
|
|
|
"github.com/go-logr/logr"
|
|
snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/client-go/tools/record"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
"sigs.k8s.io/controller-runtime/pkg/source"
|
|
|
|
cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
|
|
cc "kubevirt.io/containerized-data-importer/pkg/controller/common"
|
|
"kubevirt.io/containerized-data-importer/pkg/util"
|
|
)
|
|
|
|
const (
|
|
// CloneValidationFailed reports that a clone wasn't admitted by our validation mechanism (reason)
|
|
CloneValidationFailed = "CloneValidationFailed"
|
|
|
|
// MessageCloneValidationFailed reports that a clone wasn't admitted by our validation mechanism (message)
|
|
MessageCloneValidationFailed = "The clone doesn't meet the validation requirements"
|
|
|
|
// CloneWithoutSource reports that the source of a clone doesn't exists (reason)
|
|
CloneWithoutSource = "CloneWithoutSource"
|
|
|
|
// MessageCloneWithoutSource reports that the source of a clone doesn't exists (message)
|
|
MessageCloneWithoutSource = "The source %s %s doesn't exist"
|
|
|
|
// NoVolumeSnapshotClass reports that no compatible volumesnapshotclass was found (reason)
|
|
NoVolumeSnapshotClass = "NoVolumeSnapshotClass"
|
|
|
|
// MessageNoVolumeSnapshotClass reports that no compatible volumesnapshotclass was found (message)
|
|
MessageNoVolumeSnapshotClass = "No compatible volumesnapshotclass found"
|
|
|
|
// IncompatibleVolumeModes reports that the volume modes of source and target are incompatible (reason)
|
|
IncompatibleVolumeModes = "IncompatibleVolumeModes"
|
|
|
|
// MessageIncompatibleVolumeModes reports that the volume modes of source and target are incompatible (message)
|
|
MessageIncompatibleVolumeModes = "The volume modes of source and target are incompatible"
|
|
|
|
// NoVolumeExpansion reports that no volume expansion is possible (reason)
|
|
NoVolumeExpansion = "NoVolumeExpansion"
|
|
|
|
// MessageNoVolumeExpansion reports that no volume expansion is possible (message)
|
|
MessageNoVolumeExpansion = "No volume expansion is possible"
|
|
)
|
|
|
|
// Planner plans clone operations
|
|
type Planner struct {
|
|
RootObjectType client.ObjectList
|
|
OwnershipLabel string
|
|
UIDField string
|
|
Image string
|
|
PullPolicy corev1.PullPolicy
|
|
InstallerLabels map[string]string
|
|
Client client.Client
|
|
Recorder record.EventRecorder
|
|
Controller controller.Controller
|
|
|
|
watchingCore bool
|
|
watchingSnapshots bool
|
|
watchMutex sync.Mutex
|
|
}
|
|
|
|
// Phase is the interface implemented by all clone phases
|
|
type Phase interface {
|
|
Name() string
|
|
Reconcile(context.Context) (*reconcile.Result, error)
|
|
}
|
|
|
|
// ProgressReporter allows a phase to report progress
|
|
type ProgressReporter interface {
|
|
Progress(context.Context) (string, error)
|
|
}
|
|
|
|
// list of all possible (core) types created
|
|
var coreTypesCreated = []client.Object{
|
|
&corev1.PersistentVolumeClaim{},
|
|
&corev1.Pod{},
|
|
}
|
|
|
|
// all types that may have been created
|
|
var listTypesToDelete = []client.ObjectList{
|
|
&corev1.PersistentVolumeClaimList{},
|
|
&corev1.PodList{},
|
|
&snapshotv1.VolumeSnapshotList{},
|
|
}
|
|
|
|
// AddCoreWatches watches "core" types
|
|
func (p *Planner) AddCoreWatches(log logr.Logger) error {
|
|
p.watchMutex.Lock()
|
|
defer p.watchMutex.Unlock()
|
|
if p.watchingCore {
|
|
return nil
|
|
}
|
|
|
|
for _, obj := range coreTypesCreated {
|
|
if err := p.watchOwned(log, obj); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
p.watchingCore = true
|
|
|
|
return nil
|
|
}
|
|
|
|
// ChooseStrategyArgs are args for ChooseStrategy function
|
|
type ChooseStrategyArgs struct {
|
|
Log logr.Logger
|
|
TargetClaim *corev1.PersistentVolumeClaim
|
|
DataSource *cdiv1.VolumeCloneSource
|
|
}
|
|
|
|
// ChooseStrategy picks the strategy for a clone op
|
|
func (p *Planner) ChooseStrategy(ctx context.Context, args *ChooseStrategyArgs) (*cdiv1.CDICloneStrategy, error) {
|
|
if IsDataSourcePVC(args.DataSource.Spec.Source.Kind) {
|
|
args.Log.V(3).Info("Getting strategy for PVC source")
|
|
return p.computeStrategyForSourcePVC(ctx, args)
|
|
}
|
|
|
|
return nil, fmt.Errorf("unsupported datasource")
|
|
}
|
|
|
|
// PlanArgs are args to plan clone op for populator
|
|
type PlanArgs struct {
|
|
Log logr.Logger
|
|
TargetClaim *corev1.PersistentVolumeClaim
|
|
DataSource *cdiv1.VolumeCloneSource
|
|
Strategy cdiv1.CDICloneStrategy
|
|
}
|
|
|
|
// Plan creates phases for populator clone
|
|
func (p *Planner) Plan(ctx context.Context, args *PlanArgs) ([]Phase, error) {
|
|
if args.Strategy == cdiv1.CloneStrategySnapshot {
|
|
if err := p.watchSnapshots(ctx, args.Log); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if IsDataSourcePVC(args.DataSource.Spec.Source.Kind) {
|
|
if args.Strategy == cdiv1.CloneStrategyHostAssisted {
|
|
args.Log.V(3).Info("Planning host assisted clone from PVC")
|
|
|
|
return p.planHostAssistedFromPVC(ctx, args)
|
|
} else if args.Strategy == cdiv1.CloneStrategySnapshot {
|
|
args.Log.V(3).Info("Planning snapshot clone from PVC")
|
|
|
|
return p.planSnapshotFromPVC(ctx, args)
|
|
} else if args.Strategy == cdiv1.CloneStrategyCsiClone {
|
|
args.Log.V(3).Info("Planning csi clone from PVC")
|
|
|
|
return p.planCSIClone(ctx, args)
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("unknown strategy/source %s", string(args.Strategy))
|
|
}
|
|
|
|
// Cleanup cleans up after a clone op
|
|
func (p *Planner) Cleanup(ctx context.Context, log logr.Logger, owner client.Object) error {
|
|
log.V(3).Info("Cleaning up for obj", "obj", owner)
|
|
|
|
for _, lt := range listTypesToDelete {
|
|
ls, err := labels.Parse(fmt.Sprintf("%s=%s", p.OwnershipLabel, string(owner.GetUID())))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
lo := &client.ListOptions{
|
|
LabelSelector: ls,
|
|
}
|
|
if err := cc.BulkDeleteResources(ctx, p.Client, lt, lo); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Planner) watchSnapshots(ctx context.Context, log logr.Logger) error {
|
|
p.watchMutex.Lock()
|
|
defer p.watchMutex.Unlock()
|
|
if p.watchingSnapshots {
|
|
return nil
|
|
}
|
|
|
|
vsl := &snapshotv1.VolumeSnapshotList{}
|
|
lo := &client.ListOptions{Limit: 1}
|
|
if err := p.Client.List(ctx, vsl, lo); err != nil {
|
|
if meta.IsNoMatchError(err) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if err := p.watchOwned(log, &snapshotv1.VolumeSnapshot{}); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.V(3).Info("watching volumesnapshots now")
|
|
p.watchingSnapshots = true
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Planner) watchOwned(log logr.Logger, obj client.Object) error {
|
|
objList := p.RootObjectType.DeepCopyObject().(client.ObjectList)
|
|
if err := p.Controller.Watch(&source.Kind{Type: obj}, handler.EnqueueRequestsFromMapFunc(
|
|
func(obj client.Object) (reqs []reconcile.Request) {
|
|
uid, ok := obj.GetLabels()[p.OwnershipLabel]
|
|
if !ok {
|
|
return
|
|
}
|
|
matchingFields := client.MatchingFields{
|
|
p.UIDField: uid,
|
|
}
|
|
if err := p.Client.List(context.Background(), objList, matchingFields); err != nil {
|
|
log.Error(err, "Unable to list resource", "matchingFields", matchingFields)
|
|
return
|
|
}
|
|
sv := reflect.ValueOf(objList).Elem()
|
|
iv := sv.FieldByName("Items")
|
|
for i := 0; i < iv.Len(); i++ {
|
|
o := iv.Index(i).Addr().Interface().(client.Object)
|
|
reqs = append(reqs, reconcile.Request{
|
|
NamespacedName: types.NamespacedName{
|
|
Namespace: o.GetNamespace(),
|
|
Name: o.GetName(),
|
|
},
|
|
})
|
|
}
|
|
return
|
|
}),
|
|
); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Planner) computeStrategyForSourcePVC(ctx context.Context, args *ChooseStrategyArgs) (*cdiv1.CDICloneStrategy, error) {
|
|
if ok, err := p.validateTargetStorageClassAssignment(ctx, args); !ok || err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sourceClaim := &corev1.PersistentVolumeClaim{}
|
|
exists, err := getResource(ctx, p.Client, args.DataSource.Namespace, args.DataSource.Spec.Source.Name, sourceClaim)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !exists {
|
|
message := fmt.Sprintf(MessageCloneWithoutSource, "pvc", args.DataSource.Spec.Source.Name)
|
|
p.Recorder.Event(args.TargetClaim, corev1.EventTypeWarning, CloneWithoutSource, message)
|
|
args.Log.V(3).Info("Source PVC does not exist, cannot compute strategy")
|
|
return nil, nil
|
|
}
|
|
|
|
if err = p.validateSourcePVC(args, sourceClaim); err != nil {
|
|
p.Recorder.Event(args.TargetClaim, corev1.EventTypeWarning, CloneValidationFailed, MessageCloneValidationFailed)
|
|
args.Log.V(3).Info("Validation failed", "target", args.TargetClaim, "source", sourceClaim)
|
|
return nil, err
|
|
}
|
|
|
|
strategy := cdiv1.CloneStrategySnapshot
|
|
cs, err := GetGlobalCloneStrategyOverride(ctx, p.Client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if cs != nil {
|
|
strategy = *cs
|
|
} else if args.TargetClaim.Spec.StorageClassName != nil {
|
|
sp := &cdiv1.StorageProfile{}
|
|
exists, err := getResource(ctx, p.Client, "", *args.TargetClaim.Spec.StorageClassName, sp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !exists {
|
|
args.Log.V(3).Info("missing storageprofile for", "name", *args.TargetClaim.Spec.StorageClassName)
|
|
}
|
|
|
|
if exists && sp.Status.CloneStrategy != nil {
|
|
strategy = *sp.Status.CloneStrategy
|
|
}
|
|
}
|
|
|
|
if strategy == cdiv1.CloneStrategySnapshot {
|
|
n, err := GetCompatibleVolumeSnapshotClass(ctx, p.Client, sourceClaim, args.TargetClaim)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if n == nil {
|
|
p.Recorder.Event(args.TargetClaim, corev1.EventTypeWarning, NoVolumeSnapshotClass, MessageNoVolumeSnapshotClass)
|
|
strategy = cdiv1.CloneStrategyHostAssisted
|
|
}
|
|
}
|
|
|
|
if strategy == cdiv1.CloneStrategySnapshot ||
|
|
strategy == cdiv1.CloneStrategyCsiClone {
|
|
ok, err := p.validateAdvancedClonePVC(ctx, args, sourceClaim)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !ok {
|
|
strategy = cdiv1.CloneStrategyHostAssisted
|
|
}
|
|
}
|
|
|
|
return &strategy, nil
|
|
}
|
|
|
|
func (p *Planner) validateTargetStorageClassAssignment(ctx context.Context, args *ChooseStrategyArgs) (bool, error) {
|
|
if args.TargetClaim.Spec.StorageClassName == nil {
|
|
args.Log.V(3).Info("Target PVC has nil storage class, cannot compute strategy")
|
|
return false, nil
|
|
}
|
|
|
|
if *args.TargetClaim.Spec.StorageClassName == "" {
|
|
args.Log.V(3).Info("Target PVC has \"\" storage class, cannot compute strategy")
|
|
return false, fmt.Errorf("claim has emptystring storageclass, will not work")
|
|
}
|
|
|
|
sc, err := GetStorageClassForClaim(ctx, p.Client, args.TargetClaim)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if sc == nil {
|
|
args.Log.V(3).Info("Target PVC has no storage class, cannot compute strategy")
|
|
return false, fmt.Errorf("target storage class not found")
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (p *Planner) validateSourcePVC(args *ChooseStrategyArgs, sourceClaim *corev1.PersistentVolumeClaim) error {
|
|
if err := cc.ValidateRequestedCloneSize(sourceClaim.Spec.Resources, args.TargetClaim.Spec.Resources); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Planner) validateAdvancedClonePVC(ctx context.Context, args *ChooseStrategyArgs, sourceClaim *corev1.PersistentVolumeClaim) (bool, error) {
|
|
if !SameVolumeMode(sourceClaim, args.TargetClaim) {
|
|
p.Recorder.Event(args.TargetClaim, corev1.EventTypeWarning, IncompatibleVolumeModes, MessageIncompatibleVolumeModes)
|
|
args.Log.V(3).Info("volume modes not compatible for advanced clone")
|
|
return false, nil
|
|
}
|
|
|
|
sc, err := GetStorageClassForClaim(ctx, p.Client, args.TargetClaim)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if sc == nil {
|
|
args.Log.V(3).Info("target storage class not found")
|
|
return false, fmt.Errorf("target storage class not found")
|
|
}
|
|
|
|
srcCapacity, hasSrcCapacity := sourceClaim.Status.Capacity[corev1.ResourceStorage]
|
|
targetRequest, hasTargetRequest := args.TargetClaim.Spec.Resources.Requests[corev1.ResourceStorage]
|
|
allowExpansion := sc.AllowVolumeExpansion != nil && *sc.AllowVolumeExpansion
|
|
if !hasSrcCapacity || !hasTargetRequest {
|
|
return false, fmt.Errorf("source/target size info missing")
|
|
}
|
|
|
|
if srcCapacity.Cmp(targetRequest) < 0 && !allowExpansion {
|
|
p.Recorder.Event(args.TargetClaim, corev1.EventTypeWarning, NoVolumeExpansion, MessageNoVolumeExpansion)
|
|
args.Log.V(3).Info("advanced clone not possible, no volume expansion")
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (p *Planner) planHostAssistedFromPVC(ctx context.Context, args *PlanArgs) ([]Phase, error) {
|
|
desiredClaim := createDesiredClaim(args.DataSource.Namespace, args.TargetClaim)
|
|
|
|
if util.ResolveVolumeMode(desiredClaim.Spec.VolumeMode) == corev1.PersistentVolumeFilesystem {
|
|
ds := desiredClaim.Spec.Resources.Requests[corev1.ResourceStorage]
|
|
is, err := cc.InflateSizeWithOverhead(ctx, p.Client, ds.Value(), &args.TargetClaim.Spec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
desiredClaim.Spec.Resources.Requests[corev1.ResourceStorage] = is
|
|
}
|
|
|
|
hcp := &HostClonePhase{
|
|
Owner: args.TargetClaim,
|
|
Namespace: args.DataSource.Namespace,
|
|
SourceName: args.DataSource.Spec.Source.Name,
|
|
DesiredClaim: desiredClaim,
|
|
ImmediateBind: true,
|
|
OwnershipLabel: p.OwnershipLabel,
|
|
Preallocation: cc.GetPreallocation(ctx, p.Client, args.DataSource.Spec.Preallocation),
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
Recorder: p.Recorder,
|
|
}
|
|
|
|
if args.DataSource.Spec.PriorityClassName != nil {
|
|
hcp.PriorityClassName = *args.DataSource.Spec.PriorityClassName
|
|
}
|
|
|
|
rp := &RebindPhase{
|
|
SourceNamespace: desiredClaim.Namespace,
|
|
SourceName: desiredClaim.Name,
|
|
TargetNamespace: args.TargetClaim.Namespace,
|
|
TargetName: args.TargetClaim.Name,
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
Recorder: p.Recorder,
|
|
}
|
|
|
|
return []Phase{hcp, rp}, nil
|
|
}
|
|
|
|
func (p *Planner) planSnapshotFromPVC(ctx context.Context, args *PlanArgs) ([]Phase, error) {
|
|
sourceClaim := &corev1.PersistentVolumeClaim{}
|
|
exists, err := getResource(ctx, p.Client, args.DataSource.Namespace, args.DataSource.Spec.Source.Name, sourceClaim)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !exists {
|
|
return nil, fmt.Errorf("source claim does not exist")
|
|
}
|
|
|
|
vsc, err := GetCompatibleVolumeSnapshotClass(ctx, p.Client, sourceClaim, args.TargetClaim)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if vsc == nil {
|
|
return nil, fmt.Errorf("no compatible volumesnapshotclass")
|
|
}
|
|
|
|
sp := &SnapshotPhase{
|
|
Owner: args.TargetClaim,
|
|
SourceNamespace: args.DataSource.Namespace,
|
|
SourceName: args.DataSource.Spec.Source.Name,
|
|
TargetName: fmt.Sprintf("tmp-snapshot-%s", string(args.TargetClaim.UID)),
|
|
VolumeSnapshotClass: *vsc,
|
|
OwnershipLabel: p.OwnershipLabel,
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
}
|
|
|
|
desiredClaim := createDesiredClaim(args.DataSource.Namespace, args.TargetClaim)
|
|
cfsp := &SnapshotClonePhase{
|
|
Owner: args.TargetClaim,
|
|
Namespace: args.DataSource.Namespace,
|
|
SourceName: sp.TargetName,
|
|
DesiredClaim: desiredClaim.DeepCopy(),
|
|
OwnershipLabel: p.OwnershipLabel,
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
Recorder: p.Recorder,
|
|
}
|
|
|
|
pcp := &PrepClaimPhase{
|
|
Owner: args.TargetClaim,
|
|
DesiredClaim: desiredClaim.DeepCopy(),
|
|
Image: p.Image,
|
|
PullPolicy: p.PullPolicy,
|
|
InstallerLabels: p.InstallerLabels,
|
|
OwnershipLabel: p.OwnershipLabel,
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
Recorder: p.Recorder,
|
|
}
|
|
|
|
rp := &RebindPhase{
|
|
SourceNamespace: desiredClaim.Namespace,
|
|
SourceName: desiredClaim.Name,
|
|
TargetNamespace: args.TargetClaim.Namespace,
|
|
TargetName: args.TargetClaim.Name,
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
Recorder: p.Recorder,
|
|
}
|
|
|
|
return []Phase{sp, cfsp, pcp, rp}, nil
|
|
}
|
|
|
|
func (p *Planner) planCSIClone(ctx context.Context, args *PlanArgs) ([]Phase, error) {
|
|
desiredClaim := createDesiredClaim(args.DataSource.Namespace, args.TargetClaim)
|
|
cp := &CSIClonePhase{
|
|
Owner: args.TargetClaim,
|
|
Namespace: args.DataSource.Namespace,
|
|
SourceName: args.DataSource.Spec.Source.Name,
|
|
DesiredClaim: desiredClaim.DeepCopy(),
|
|
OwnershipLabel: p.OwnershipLabel,
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
Recorder: p.Recorder,
|
|
}
|
|
|
|
pcp := &PrepClaimPhase{
|
|
Owner: args.TargetClaim,
|
|
DesiredClaim: desiredClaim.DeepCopy(),
|
|
Image: p.Image,
|
|
PullPolicy: p.PullPolicy,
|
|
InstallerLabels: p.InstallerLabels,
|
|
OwnershipLabel: p.OwnershipLabel,
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
Recorder: p.Recorder,
|
|
}
|
|
|
|
rp := &RebindPhase{
|
|
SourceNamespace: desiredClaim.Namespace,
|
|
SourceName: desiredClaim.Name,
|
|
TargetNamespace: args.TargetClaim.Namespace,
|
|
TargetName: args.TargetClaim.Name,
|
|
Client: p.Client,
|
|
Log: args.Log,
|
|
Recorder: p.Recorder,
|
|
}
|
|
|
|
return []Phase{cp, pcp, rp}, nil
|
|
}
|
|
|
|
func createDesiredClaim(namespace string, targetClaim *corev1.PersistentVolumeClaim) *corev1.PersistentVolumeClaim {
|
|
targetCpy := targetClaim.DeepCopy()
|
|
desiredClaim := &corev1.PersistentVolumeClaim{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: namespace,
|
|
Name: fmt.Sprintf("tmp-pvc-%s", string(targetClaim.UID)),
|
|
Labels: targetCpy.Labels,
|
|
Annotations: targetCpy.Annotations,
|
|
},
|
|
Spec: targetCpy.Spec,
|
|
}
|
|
desiredClaim.Spec.DataSource = nil
|
|
desiredClaim.Spec.DataSourceRef = nil
|
|
|
|
return desiredClaim
|
|
}
|