Files
velero/pkg/datamover/dataupload_delete_action.go
T
Xun Jiang/Bruce JiangandGitHub 25402b6209
Run the E2E test on kind / get-go-version (push) Successful in 59s
Run the E2E test on kind / setup-test-matrix (push) Successful in 4s
Main CI / get-go-version (push) Successful in 14s
Run the E2E test on kind / build (push) Failing after 1m48s
Run the E2E test on kind / run-e2e-test (push) Has been skipped
Main CI / Build (push) Failing after 25s
[1.18] Deprecate inactive maintained packages (#9912)
* Replace github.com/robfig/cron/v3 by github.com/netresearch/go-cron

Replace k8s.io/utils/pointer with k8s.io/utils/ptr

Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>

* Replace gopkg.in/yaml.v3 by go.yaml.in/yaml/v3

Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>

* Replace github.com/joho/godotenv.

Move the needed code into Velero repository.

Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>

* Replace github.com/pkg/errors by github.com/cockroachdb/errors

Change errors.Cause to errors.Is, because github.com/cockroachdb/errors
New() function create a error with error stack with depth 1, but
github.com/pkg/errors's New() function create error with no depth.

Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>

---------

Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>
Signed-off-by: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com>
2026-07-10 11:20:34 +08:00

133 lines
4.8 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"
)
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 !IsBuiltInUploader(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,
}
}