From de315f21fa08cb3dfe5114b1e2da5264b21af49c Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Thu, 9 May 2024 02:47:00 +0800 Subject: [PATCH] add backup annotations for air gap specific embedded cluster infra (#4601) * use orLabelSelectors for backups * handle OrLabelSelectors * backup registry namespace * fix unit test * use release=docker-registry as that label is shared by the PVC * refactor to use function instead of duplicating code * preserve nonzero check * return to app label --- pkg/kotsadmsnapshot/backup.go | 84 +++++++++++++++++++++--------- pkg/kotsadmsnapshot/backup_test.go | 45 ++++++++++------ 2 files changed, 87 insertions(+), 42 deletions(-) diff --git a/pkg/kotsadmsnapshot/backup.go b/pkg/kotsadmsnapshot/backup.go index 3d9af3d7d6..b0c25b6d60 100644 --- a/pkg/kotsadmsnapshot/backup.go +++ b/pkg/kotsadmsnapshot/backup.go @@ -396,7 +396,7 @@ func CreateInstanceBackup(ctx context.Context, cluster *downstreamtypes.Downstre IncludedNamespaces: prepareIncludedNamespaces(includedNamespaces, util.IsEmbeddedCluster()), ExcludedNamespaces: excludedNamespaces, IncludeClusterResources: &includeClusterResources, - LabelSelector: instanceBackupLabelSelector(util.IsEmbeddedCluster()), + OrLabelSelectors: instanceBackupLabelSelectors(util.IsEmbeddedCluster()), OrderedResources: backupOrderedResources, Hooks: backupHooks, }, @@ -985,6 +985,7 @@ func prepareIncludedNamespaces(namespaces []string, isEC bool) []string { uniqueNamespaces["embedded-cluster"] = true uniqueNamespaces["kube-system"] = true uniqueNamespaces["openebs"] = true + uniqueNamespaces["registry"] = true } includedNamespaces := make([]string, len(uniqueNamespaces)) @@ -1003,17 +1004,18 @@ func excludeShutdownPodsFromBackup(ctx context.Context, clientset kubernetes.Int } labelSets := []string{} - if veleroBackup.Spec.LabelSelector.MatchLabels != nil && len(veleroBackup.Spec.LabelSelector.MatchLabels) != 0 { - labelSets = []string{labels.SelectorFromSet(veleroBackup.Spec.LabelSelector.MatchLabels).String()} - } else { - for _, expr := range veleroBackup.Spec.LabelSelector.MatchExpressions { - if expr.Operator != metav1.LabelSelectorOpIn { - return fmt.Errorf("unsupported operator %s in label selector %q", expr.Operator, veleroBackup.Spec.LabelSelector.String()) - } - for _, value := range expr.Values { - labelSets = append(labelSets, fmt.Sprintf("%s=%s", expr.Key, value)) - } + staticSet, err := getLabelSetsForLabelSelector(veleroBackup.Spec.LabelSelector) + if err != nil { + return errors.Wrap(err, "failed to get label sets for label selector") + } + labelSets = append(labelSets, staticSet...) + + for _, sel := range veleroBackup.Spec.OrLabelSelectors { + orLabelSet, err := getLabelSetsForLabelSelector(sel) + if err != nil { + return errors.Wrap(err, "failed to get label sets for or label selector") } + labelSets = append(labelSets, orLabelSet...) } for _, labelSet := range labelSets { @@ -1065,27 +1067,59 @@ func excludeShutdownPodsFromBackupInNamespace(ctx context.Context, clientset kub return nil } -func instanceBackupLabelSelector(isEmbeddedCluster bool) *metav1.LabelSelector { +func instanceBackupLabelSelectors(isEmbeddedCluster bool) []*metav1.LabelSelector { if isEmbeddedCluster { // only DR on embedded-cluster - return &metav1.LabelSelector{ - MatchLabels: map[string]string{}, - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "replicated.com/disaster-recovery", - Operator: metav1.LabelSelectorOpIn, - Values: []string{ - "infra", - "app", - "ec-install", + return []*metav1.LabelSelector{ + { + MatchLabels: map[string]string{}, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "replicated.com/disaster-recovery", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "infra", + "app", + "ec-install", + }, }, }, }, + { + // we cannot add new labels to the docker-registry chart as of May 7th 2024 + // so we need to add a label selector for the docker-registry app + // https://github.com/twuni/docker-registry.helm/blob/main/templates/deployment.yaml + MatchLabels: map[string]string{ + "app": "docker-registry", + }, + }, } } - return &metav1.LabelSelector{ - MatchLabels: map[string]string{ - kotsadmtypes.BackupLabel: kotsadmtypes.BackupLabelValue, + return []*metav1.LabelSelector{ + { + MatchLabels: map[string]string{ + kotsadmtypes.BackupLabel: kotsadmtypes.BackupLabelValue, + }, }, } } + +func getLabelSetsForLabelSelector(labelSelector *metav1.LabelSelector) ([]string, error) { + if labelSelector == nil { + return nil, nil + } + + labelSets := []string{} + if labelSelector.MatchLabels != nil && len(labelSelector.MatchLabels) > 0 { + labelSets = append(labelSets, labels.SelectorFromSet(labelSelector.MatchLabels).String()) + } + for _, expr := range labelSelector.MatchExpressions { + if expr.Operator != metav1.LabelSelectorOpIn { + return nil, fmt.Errorf("unsupported operator %s in label selector %q", expr.Operator, labelSelector.String()) + } + for _, value := range expr.Values { + labelSets = append(labelSets, fmt.Sprintf("%s=%s", expr.Key, value)) + } + } + return labelSets, nil +} diff --git a/pkg/kotsadmsnapshot/backup_test.go b/pkg/kotsadmsnapshot/backup_test.go index d5f9256b0a..30ff75ca8a 100644 --- a/pkg/kotsadmsnapshot/backup_test.go +++ b/pkg/kotsadmsnapshot/backup_test.go @@ -85,7 +85,7 @@ func TestPrepareIncludedNamespaces(t *testing.T) { { name: "embedded-cluster install", namespaces: []string{"test", "abcapp"}, - want: []string{"test", "abcapp", "embedded-cluster", "kube-system", "openebs"}, + want: []string{"test", "abcapp", "embedded-cluster", "kube-system", "openebs", "registry"}, isEC: true, }, } @@ -634,43 +634,54 @@ func Test_excludeShutdownPodsFromBackup_check(t *testing.T) { } } -func Test_instanceBackupLabelSelector(t *testing.T) { +func Test_instanceBackupLabelSelectors(t *testing.T) { tests := []struct { name string isEmbeddedCluster bool - want *metav1.LabelSelector + want []*metav1.LabelSelector }{ { name: "not embedded cluster", isEmbeddedCluster: false, - want: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "kots.io/backup": "velero", + want: []*metav1.LabelSelector{ + { + MatchLabels: map[string]string{ + "kots.io/backup": "velero", + }, }, }, }, { name: "embedded cluster", isEmbeddedCluster: true, - want: &metav1.LabelSelector{ - MatchLabels: map[string]string{}, - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "replicated.com/disaster-recovery", - Operator: metav1.LabelSelectorOpIn, - Values: []string{ - "infra", - "app", - "ec-install", + want: []*metav1.LabelSelector{ + { + MatchLabels: map[string]string{}, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "replicated.com/disaster-recovery", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "infra", + "app", + "ec-install", + }, }, }, }, + { + MatchLabels: map[string]string{ + "app": "docker-registry", + }, + }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equalf(t, tt.want, instanceBackupLabelSelector(tt.isEmbeddedCluster), "instanceBackupLabelSelector(%v)", tt.isEmbeddedCluster) + req := require.New(t) + got := instanceBackupLabelSelectors(tt.isEmbeddedCluster) + req.ElementsMatch(tt.want, got) }) } }