mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-08-02 05:16:06 +00:00
134 lines
4.9 KiB
Go
134 lines
4.9 KiB
Go
package datamover
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/cockroachdb/errors"
|
|
"github.com/sirupsen/logrus"
|
|
corev1api "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
|
velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1"
|
|
"github.com/vmware-tanzu/velero/pkg/label"
|
|
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
|
repotypes "github.com/vmware-tanzu/velero/pkg/repository/types"
|
|
datamoverutil "github.com/vmware-tanzu/velero/pkg/util/datamover"
|
|
)
|
|
|
|
type DataUploadDeleteAction struct {
|
|
logger logrus.FieldLogger
|
|
client client.Client
|
|
}
|
|
|
|
func (d *DataUploadDeleteAction) AppliesTo() (velero.ResourceSelector, error) {
|
|
return velero.ResourceSelector{
|
|
IncludedResources: []string{"datauploads.velero.io"},
|
|
}, nil
|
|
}
|
|
|
|
func (d *DataUploadDeleteAction) Execute(input *velero.DeleteItemActionExecuteInput) error {
|
|
d.logger.Infof("Executing DataUploadDeleteAction")
|
|
du := &velerov2alpha1.DataUpload{}
|
|
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), &du); err != nil {
|
|
return errors.WithStack(errors.Wrapf(err, "failed to convert input.Item from unstructured"))
|
|
}
|
|
// Only create a snapshot-info ConfigMap when the DataUpload's owning
|
|
// backup (its velero.io/backup-name label) matches the backup currently
|
|
// being deleted. Two other cases reach this code path and must be
|
|
// skipped, because the resulting CM would be unmatchable and only adds
|
|
// etcd churn:
|
|
//
|
|
// 1. The label is missing. We have no verifiable owner, so a CM created
|
|
// with the executing backup's label is a guess that deleteMovedSnapshots
|
|
// cannot rely on.
|
|
// 2. The label names a different backup. Velero does not support
|
|
// self-protection, so this almost always means the velero namespace
|
|
// was captured in a backup tarball and the DataUpload CR belongs to
|
|
// an unrelated backup. Creating a CM labeled with the executing
|
|
// backup mislabels the snapshot and causes the real owning backup's
|
|
// deleteMovedSnapshots query to miss it, leaking the Kopia snapshot
|
|
// in the object store.
|
|
//
|
|
// Both cases warn so misconfigured installs surface in logs.
|
|
owner := du.Labels[velerov1.BackupNameLabel]
|
|
switch {
|
|
case owner == "":
|
|
d.logger.Warnf(
|
|
"DataUpload %q has no %q label, so its owning backup cannot be verified; "+
|
|
"skipping snapshot-info ConfigMap creation because a CM without a verifiable owner "+
|
|
"cannot be matched back to its snapshot at backup deletion time.",
|
|
du.Name, velerov1.BackupNameLabel,
|
|
)
|
|
return nil
|
|
case owner != label.GetValidName(input.Backup.Name):
|
|
d.logger.Warnf(
|
|
"DataUpload %q belongs to backup %q but is being deleted under backup %q; "+
|
|
"this almost always means the velero namespace was included in a backup tarball. "+
|
|
"Velero does not support self-protection — exclude the velero namespace from your schedules. "+
|
|
"Skipping snapshot-info ConfigMap creation to avoid mislabeling.",
|
|
du.Name, owner, input.Backup.Name,
|
|
)
|
|
return nil
|
|
}
|
|
cm := genConfigmap(input.Backup, *du)
|
|
if cm == nil {
|
|
// will not fail the backup deletion
|
|
return nil
|
|
}
|
|
err := d.client.Create(context.Background(), cm)
|
|
if err != nil {
|
|
return errors.WithStack(errors.Wrapf(err, "failed to create the configmap for DataUpload %s/%s", du.Namespace, du.Name))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// generate the configmap which is to be created and used as a way to communicate the snapshot info to the backup deletion controller
|
|
func genConfigmap(bak *velerov1.Backup, du velerov2alpha1.DataUpload) *corev1api.ConfigMap {
|
|
if !datamoverutil.IsBuiltInDataMover(du.Spec.DataMover) || du.Status.SnapshotID == "" {
|
|
return nil
|
|
}
|
|
snapshot := repotypes.SnapshotIdentifier{
|
|
VolumeNamespace: du.Spec.SourceNamespace,
|
|
BackupStorageLocation: bak.Spec.StorageLocation,
|
|
SnapshotID: du.Status.SnapshotID,
|
|
RepositoryType: velerov1.BackupRepositoryTypeKopia,
|
|
UploaderType: GetUploaderType(du.Spec.DataMover),
|
|
Source: GetRealSource(du.Spec.SourceNamespace, du.Spec.SourcePVC),
|
|
}
|
|
b, err := json.Marshal(snapshot)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
data := make(map[string]string)
|
|
if err := json.Unmarshal(b, &data); err != nil {
|
|
return nil
|
|
}
|
|
return &corev1api.ConfigMap{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: corev1api.SchemeGroupVersion.String(),
|
|
Kind: "ConfigMap",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: bak.Namespace,
|
|
Name: fmt.Sprintf("%s-info", du.Name),
|
|
Labels: map[string]string{
|
|
velerov1.BackupNameLabel: bak.Name,
|
|
velerov1.DataUploadSnapshotInfoLabel: "true",
|
|
},
|
|
},
|
|
Data: data,
|
|
}
|
|
}
|
|
|
|
func NewDataUploadDeleteAction(logger logrus.FieldLogger, client client.Client) *DataUploadDeleteAction {
|
|
return &DataUploadDeleteAction{
|
|
logger: logger,
|
|
client: client,
|
|
}
|
|
}
|