Skip to content

Commit

Permalink
dont add new annotations and labels if not improved dr
Browse files Browse the repository at this point in the history
  • Loading branch information
emosbaugh committed Dec 5, 2024
1 parent 9d8cd12 commit 3daa215
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 132 deletions.
12 changes: 10 additions & 2 deletions pkg/handlers/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ func (h *Handler) CreateApplicationRestore(w http.ResponseWriter, r *http.Reques
return
}

if snapshot.IsInstanceBackup(*backup) && snapshot.GetInstanceBackupType(*backup) != snapshottypes.InstanceBackupTypeLegacy {
err := errors.New("only legacy type instance backups are restorable")
logger.Error(err)
createRestoreResponse.Error = err.Error()
JSON(w, http.StatusInternalServerError, createRestoreResponse)
return
}

appID := backup.Annotations["kots.io/app-id"]
sequence, err := strconv.ParseInt(backup.Annotations["kots.io/app-sequence"], 10, 64)
if err != nil {
Expand Down Expand Up @@ -149,7 +157,7 @@ func (h *Handler) RestoreApps(w http.ResponseWriter, r *http.Request) {
return
}

if backup.Annotations["kots.io/instance"] != "true" {
if backup.Annotations[snapshottypes.InstanceBackupAnnotation] != "true" {
err := errors.Errorf("backup %s is not an instance backup", backup.ObjectMeta.Name)
logger.Error(err)
restoreResponse.Error = err.Error()
Expand Down Expand Up @@ -244,7 +252,7 @@ func (h *Handler) GetRestoreAppsStatus(w http.ResponseWriter, r *http.Request) {
return
}

if backup.Annotations["kots.io/instance"] != "true" {
if backup.Annotations[snapshottypes.InstanceBackupAnnotation] != "true" {
err := errors.Errorf("backup %s is not an instance backup", backup.ObjectMeta.Name)
logger.Error(err)
response.Error = err.Error()
Expand Down
83 changes: 24 additions & 59 deletions pkg/kotsadmsnapshot/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,6 @@ import (
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)

const (
// InstanceBackupNameLabel is the label used to store the name of the backup for an instance
// backup.
InstanceBackupNameLabel = "replicated.com/backup-name"
// InstanceBackupTypeAnnotation is the annotation used to store the type of backup for an
// instance backup.
InstanceBackupTypeAnnotation = "replicated.com/backup-type"
// InstanceBackupCountAnnotation is the annotation used to store the expected number of backups
// for an instance backup.
InstanceBackupCountAnnotation = "replicated.com/backup-count"

// InstanceBackupTypeInfra indicates that the backup is of type infrastructure.
InstanceBackupTypeInfra = "infra"
// InstanceBackupTypeApp indicates that the backup is of type application.
InstanceBackupTypeApp = "app"
// InstanceBackupTypeLegacy indicates that the backup is of type legacy (infra + app).
InstanceBackupTypeLegacy = "legacy"
)

func CreateApplicationBackup(ctx context.Context, a *apptypes.App, isScheduled bool) (*velerov1.Backup, error) {
downstreams, err := store.GetStore().ListDownstreamsForApp(a.ID)
if err != nil {
Expand Down Expand Up @@ -307,24 +288,32 @@ func CreateInstanceBackup(ctx context.Context, cluster *downstreamtypes.Downstre

// GetBackupName returns the name of the backup from the velero backup object label.
func GetBackupName(veleroBackup velerov1.Backup) string {
if val, ok := veleroBackup.GetLabels()[InstanceBackupNameLabel]; ok {
if val, ok := veleroBackup.GetLabels()[types.InstanceBackupNameLabel]; ok {
return val
}
return veleroBackup.GetName()
}

// IsInstanceBackup returns true if the backup is an instance backup.
func IsInstanceBackup(veleroBackup velerov1.Backup) bool {
if val, ok := veleroBackup.GetAnnotations()[types.InstanceBackupAnnotation]; ok {
return val == "true"
}
return false
}

// GetInstanceBackupType returns the type of the backup from the velero backup object annotation.
func GetInstanceBackupType(veleroBackup velerov1.Backup) string {
if val, ok := veleroBackup.GetAnnotations()[InstanceBackupTypeAnnotation]; ok {
if val, ok := veleroBackup.GetAnnotations()[types.InstanceBackupTypeAnnotation]; ok {
return val
}
return ""
return types.InstanceBackupTypeLegacy
}

// GetInstanceBackupCount returns the expected number of backups from the velero backup object
// annotation.
func GetInstanceBackupCount(veleroBackup velerov1.Backup) int {
if val, ok := veleroBackup.GetAnnotations()[InstanceBackupCountAnnotation]; ok {
if val, ok := veleroBackup.GetAnnotations()[types.InstanceBackupCountAnnotation]; ok {
num, _ := strconv.Atoi(val)
if num > 0 {
return num
Expand Down Expand Up @@ -505,15 +494,14 @@ func getInfrastructureInstanceBackupSpec(ctx context.Context, k8sClient kubernet
if err != nil {
return nil, errors.Wrap(err, "failed to add annotations to backup")
}
// Add improved disaster recovery annotations and labels
if veleroBackup.Labels == nil {
veleroBackup.Labels = map[string]string{}
}
veleroBackup.Labels[InstanceBackupNameLabel] = metadata.backupName
if hasAppBackup {
veleroBackup.Annotations[InstanceBackupTypeAnnotation] = InstanceBackupTypeInfra
} else {
veleroBackup.Annotations[InstanceBackupTypeAnnotation] = InstanceBackupTypeLegacy
// Only add improved disaster recovery annotations and labels if we have an app backup
if veleroBackup.Labels == nil {
veleroBackup.Labels = map[string]string{}
}
veleroBackup.Labels[types.InstanceBackupNameLabel] = metadata.backupName
veleroBackup.Annotations[types.InstanceBackupTypeAnnotation] = types.InstanceBackupTypeInfra
veleroBackup.Annotations[types.InstanceBackupCountAnnotation] = strconv.Itoa(2)
}

if metadata.ec != nil {
Expand Down Expand Up @@ -582,8 +570,9 @@ func getAppInstanceBackupSpec(k8sClient kubernetes.Interface, metadata instanceB
if appVeleroBackup.Labels == nil {
appVeleroBackup.Labels = map[string]string{}
}
appVeleroBackup.Labels[InstanceBackupNameLabel] = metadata.backupName
appVeleroBackup.Annotations[InstanceBackupTypeAnnotation] = InstanceBackupTypeApp
appVeleroBackup.Labels[types.InstanceBackupNameLabel] = metadata.backupName
appVeleroBackup.Annotations[types.InstanceBackupTypeAnnotation] = types.InstanceBackupTypeApp
appVeleroBackup.Annotations[types.InstanceBackupCountAnnotation] = strconv.Itoa(2)

appVeleroBackup.Spec.StorageLocation = "default"

Expand Down Expand Up @@ -695,17 +684,12 @@ func appendCommonAnnotations(k8sClient kubernetes.Interface, annotations map[str
}
marshalledAppVersions := string(b)

numBackups := 1
if hasAppBackup {
numBackups = 2
}

if annotations == nil {
annotations = make(map[string]string, 0)
}
annotations["kots.io/snapshot-trigger"] = snapshotTrigger
annotations["kots.io/snapshot-requested"] = metadata.backupReqestedAt.Format(time.RFC3339)
annotations["kots.io/instance"] = "true"
annotations[types.InstanceBackupAnnotation] = "true"
annotations["kots.io/kotsadm-image"] = kotsadmImage
annotations["kots.io/kotsadm-deploy-namespace"] = metadata.kotsadmNamespace
annotations["kots.io/apps-sequences"] = marshalledAppSequences
Expand All @@ -716,9 +700,6 @@ func appendCommonAnnotations(k8sClient kubernetes.Interface, annotations map[str
annotations["kots.io/embedded-registry"] = embeddedRegistryHost
}

// Add improved disaster recovery annotation labels
annotations[InstanceBackupCountAnnotation] = strconv.Itoa(numBackups)

if metadata.ec != nil {
annotations = appendECAnnotations(annotations, *metadata.ec)
}
Expand Down Expand Up @@ -893,7 +874,7 @@ func ListInstanceBackups(ctx context.Context, kotsadmNamespace string) ([]*types

for _, veleroBackup := range veleroBackups.Items {
// TODO: Enforce version?
if veleroBackup.Annotations["kots.io/instance"] != "true" {
if !IsInstanceBackup(veleroBackup) {
continue
}

Expand Down Expand Up @@ -1082,22 +1063,6 @@ func getBackupNameFromPrefix(appSlug string) string {
return fmt.Sprintf("%s-%s", backupName, randStr)
}

func getBackupsFromName(ctx context.Context, veleroClient veleroclientv1.VeleroV1Interface, veleroNamespace string, backupName string) ([]velerov1.Backup, error) {
// try to get the restore from the backup name label
backups, err := veleroClient.Backups(veleroNamespace).List(ctx, metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", InstanceBackupNameLabel, velerolabel.GetValidName(backupName)),
})
if len(backups.Items) > 0 {
return backups.Items, nil
}
backup, err := veleroClient.Backups(veleroNamespace).Get(ctx, backupName, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to get restore")
}

return []velerov1.Backup{*backup}, nil
}

func DeleteBackup(ctx context.Context, kotsadmNamespace string, backupID string) error {
cfg, err := k8sutil.GetClusterConfig()
if err != nil {
Expand Down
105 changes: 69 additions & 36 deletions pkg/kotsadmsnapshot/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,6 @@ func Test_appendCommonAnnotations(t *testing.T) {
"kots.io/kotsadm-image": "kotsadm/kotsadm:1.0.0",
"kots.io/snapshot-requested": "2024-01-01T00:00:00Z",
"kots.io/snapshot-trigger": "manual",
"replicated.com/backup-count": "1",
},
},
{
Expand Down Expand Up @@ -1087,7 +1086,6 @@ func Test_appendCommonAnnotations(t *testing.T) {
"kots.io/kotsadm-image": "kotsadm/kotsadm:1.0.0",
"kots.io/snapshot-requested": "2024-01-01T00:00:00Z",
"kots.io/snapshot-trigger": "schedule",
"replicated.com/backup-count": "2",
"kots.io/embedded-cluster": "true",
"kots.io/embedded-cluster-id": "embedded-cluster-id",
"kots.io/embedded-cluster-version": "embedded-cluster-version",
Expand Down Expand Up @@ -2090,37 +2088,6 @@ func Test_getInfrastructureInstanceBackupSpec(t *testing.T) {
args args
assert func(t *testing.T, got *velerov1.Backup, err error)
}{
{
name: "should append backup name label",
setup: func(t *testing.T, mockStore *mock_store.MockStore) {
mockStoreExpectApp1(mockStore)
},
args: args{
k8sClient: fake.NewSimpleClientset(kotsadmSts, registryCredsSecret),
metadata: instanceBackupMetadata{
backupName: "app-1-17332487841234",
backupReqestedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
kotsadmNamespace: "kotsadm",
backupStorageLocationNamespace: "kotsadm-backups",
apps: map[string]appInstanceBackupMetadata{
"app-1": {
app: app1,
kotsKinds: kotsKinds,
parentSequence: 1,
},
},
isScheduled: true,
ec: nil,
},
hasAppBackup: false,
},
assert: func(t *testing.T, got *velerov1.Backup, err error) {
require.NoError(t, err)
if assert.Contains(t, got.Labels, "replicated.com/backup-name") {
assert.Equal(t, "app-1-17332487841234", got.Labels["replicated.com/backup-name"])
}
},
},
{
name: "KOTSADM_TARGET_NAMESPACE should be added to includedNamespaces",
setup: func(t *testing.T, mockStore *mock_store.MockStore) {
Expand Down Expand Up @@ -2293,9 +2260,6 @@ func Test_getInfrastructureInstanceBackupSpec(t *testing.T) {
assert: func(t *testing.T, got *velerov1.Backup, err error) {
require.NoError(t, err)
assert.Contains(t, got.Spec.IncludedNamespaces, "include-namespace-1")
if assert.Contains(t, got.Annotations, "replicated.com/backup-type") {
assert.Equal(t, "legacy", got.Annotations["replicated.com/backup-type"])
}
},
},
{
Expand Down Expand Up @@ -2326,9 +2290,78 @@ func Test_getInfrastructureInstanceBackupSpec(t *testing.T) {
assert: func(t *testing.T, got *velerov1.Backup, err error) {
require.NoError(t, err)
assert.NotContains(t, got.Spec.IncludedNamespaces, "include-namespace-1")
},
},
{
name: "should not add improved dr metadata when not using improved dr",
setup: func(t *testing.T, mockStore *mock_store.MockStore) {
t.Setenv("EMBEDDED_CLUSTER_ID", "embedded-cluster-id")
t.Setenv("EMBEDDED_CLUSTER_VERSION", "embedded-cluster-version")

mockStoreExpectApp1(mockStore)
},
args: args{
k8sClient: fake.NewSimpleClientset(kotsadmSts, registryCredsSecret),
metadata: instanceBackupMetadata{
backupName: "app-1-17332487841234",
backupReqestedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
kotsadmNamespace: "kotsadm",
backupStorageLocationNamespace: "kotsadm-backups",
apps: map[string]appInstanceBackupMetadata{
"app-1": {
app: app1,
kotsKinds: kotsKinds,
parentSequence: 1,
},
},
isScheduled: true,
ec: ecMeta,
},
hasAppBackup: false,
},
assert: func(t *testing.T, got *velerov1.Backup, err error) {
require.NoError(t, err)
assert.NotContains(t, got.Labels, "replicated.com/backup-name")
assert.NotContains(t, got.Annotations, "replicated.com/backup-type")
assert.NotContains(t, got.Annotations, "replicated.com/backup-count")
},
},
{
name: "should add improved dr metadata when not using improved dr",
setup: func(t *testing.T, mockStore *mock_store.MockStore) {
t.Setenv("EMBEDDED_CLUSTER_ID", "embedded-cluster-id")
t.Setenv("EMBEDDED_CLUSTER_VERSION", "embedded-cluster-version")
},
args: args{
k8sClient: fake.NewSimpleClientset(kotsadmSts, registryCredsSecret),
metadata: instanceBackupMetadata{
backupName: "app-1-17332487841234",
backupReqestedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
kotsadmNamespace: "kotsadm",
backupStorageLocationNamespace: "kotsadm-backups",
apps: map[string]appInstanceBackupMetadata{
"app-1": {
app: app1,
kotsKinds: kotsKinds,
parentSequence: 1,
},
},
isScheduled: true,
ec: ecMeta,
},
hasAppBackup: true,
},
assert: func(t *testing.T, got *velerov1.Backup, err error) {
require.NoError(t, err)
if assert.Contains(t, got.Labels, "replicated.com/backup-name") {
assert.Equal(t, "app-1-17332487841234", got.Labels["replicated.com/backup-name"])
}
if assert.Contains(t, got.Annotations, "replicated.com/backup-type") {
assert.Equal(t, "infra", got.Annotations["replicated.com/backup-type"])
}
if assert.Contains(t, got.Annotations, "replicated.com/backup-count") {
assert.Equal(t, "2", got.Annotations["replicated.com/backup-count"])
}
},
},
{
Expand Down
Loading

0 comments on commit 3daa215

Please sign in to comment.