From 93f7ae7c4e0df884ba82db3e34a222fa9e3b6ebf Mon Sep 17 00:00:00 2001 From: Michal Pryc Date: Thu, 3 Oct 2024 22:55:50 +0200 Subject: [PATCH 1/2] Related 89: Function to generate custom UUID for NAC objects This function will be used to link between NAB and Backup and possibly other parts of the objects within Non-Admin-Backup. Signed-off-by: Michal Pryc --- internal/common/constant/constant.go | 3 + internal/common/function/function.go | 46 ++++++++++++++ internal/common/function/function_test.go | 77 +++++++++++++++++++++++ 3 files changed, 126 insertions(+) diff --git a/internal/common/constant/constant.go b/internal/common/constant/constant.go index c7a41a8..a9f8541 100644 --- a/internal/common/constant/constant.go +++ b/internal/common/constant/constant.go @@ -39,6 +39,9 @@ const ( // EmptyString defines a constant for the empty string const EmptyString = "" +// BackupNameDelimiter defines character that is used to separate name aprts +const BackupNameDelimiter = "-" + // TrueString defines a constant for the True string const TrueString = "True" diff --git a/internal/common/function/function.go b/internal/common/function/function.go index 583210b..0f6bdc8 100644 --- a/internal/common/function/function.go +++ b/internal/common/function/function.go @@ -24,6 +24,7 @@ import ( "fmt" "github.com/go-logr/logr" + "github.com/google/uuid" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation" @@ -139,6 +140,51 @@ func GenerateVeleroBackupName(namespace, nabName string) string { return veleroBackupName } +// GenerateVeleroBackupNameWithUUID generates a Velero backup name based on the provided namespace and NonAdminBackup name. +// It includes a UUID suffix. If the name exceeds the maximum length, it truncates nabName first, then namespace. +func GenerateVeleroBackupNameWithUUID(namespace, nabName string) string { + // Generate UUID suffix + uuidSuffix := uuid.New().String() + + // Build the initial backup name based on the presence of namespace and nabName + veleroBackupName := uuidSuffix + if len(nabName) > 0 { + veleroBackupName = nabName + constant.BackupNameDelimiter + veleroBackupName + } + if len(namespace) > 0 { + veleroBackupName = namespace + constant.BackupNameDelimiter + veleroBackupName + } + + // Ensure the name is within the character limit + maxLength := validation.DNS1123SubdomainMaxLength + + if len(veleroBackupName) > maxLength { + // Calculate remaining length after UUID + remainingLength := maxLength - len(uuidSuffix) + + delimeterLength := len(constant.BackupNameDelimiter) + + // Subtract two delimiter lengths to avoid a corner case where the namespace + // and delimiters leave no space for any part of nabName + if len(namespace) > remainingLength-delimeterLength-delimeterLength { + namespace = namespace[:remainingLength-delimeterLength-delimeterLength] + veleroBackupName = namespace + constant.BackupNameDelimiter + uuidSuffix + } else { + remainingLength = remainingLength - len(namespace) - delimeterLength - delimeterLength + nabName = nabName[:remainingLength] + veleroBackupName = uuidSuffix + if len(nabName) > 0 { + veleroBackupName = nabName + constant.BackupNameDelimiter + veleroBackupName + } + if len(namespace) > 0 { + veleroBackupName = namespace + constant.BackupNameDelimiter + veleroBackupName + } + } + } + + return veleroBackupName +} + // CheckVeleroBackupMetadata return true if Velero Backup object has required Non Admin labels and annotations, false otherwise func CheckVeleroBackupMetadata(obj client.Object) bool { labels := obj.GetLabels() diff --git a/internal/common/function/function_test.go b/internal/common/function/function_test.go index ce02091..d1451b9 100644 --- a/internal/common/function/function_test.go +++ b/internal/common/function/function_test.go @@ -23,6 +23,7 @@ import ( "strings" "testing" + "github.com/google/uuid" "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/assert" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -287,6 +288,82 @@ func TestGenerateVeleroBackupName(t *testing.T) { } } +func TestGenerateVeleroBackupNameWithUUID(t *testing.T) { + tests := []struct { + name string + namespace string + nabName string + }{ + { + name: "Valid names without truncation", + namespace: "default", + nabName: "my-backup", + }, + { + name: "Truncate nabName due to length", + namespace: "some", + nabName: strings.Repeat("q", validation.DNS1123SubdomainMaxLength+10), // too long for DNS limit + }, + { + name: "Truncate very long namespace and very long name", + namespace: strings.Repeat("w", validation.DNS1123SubdomainMaxLength+10), + nabName: strings.Repeat("e", validation.DNS1123SubdomainMaxLength+10), + }, + { + name: "nabName empty", + namespace: "example", + nabName: constant.EmptyString, + }, + { + name: "namespace empty", + namespace: constant.EmptyString, + nabName: "my-backup", + }, + { + name: "very long name and namespace empty", + namespace: constant.EmptyString, + nabName: strings.Repeat("r", validation.DNS1123SubdomainMaxLength+10), + }, + { + name: "very long namespace and name empty", + namespace: strings.Repeat("t", validation.DNS1123SubdomainMaxLength+10), + nabName: constant.EmptyString, + }, + { + name: "empty namespace and empty name", + namespace: constant.EmptyString, + nabName: constant.EmptyString, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := GenerateVeleroBackupNameWithUUID(tt.namespace, tt.nabName) + + // Check length + if len(result) > validation.DNS1123SubdomainMaxLength { + t.Errorf("Generated name is too long: %s", result) + } + + // Extract the last 36 characters, which should be the UUID + if len(result) < 36 { + t.Errorf("Generated name is too short to contain a valid UUID: %s", result) + } + uuidPart := result[len(result)-36:] // The UUID is always the last 36 characters + + // Attempt to parse the UUID part + if _, err := uuid.Parse(uuidPart); err != nil { + t.Errorf("Last part is not a valid UUID: %s", uuidPart) + } + + // Check if no double hyphens are present + if strings.Contains(result, "--") { + t.Errorf("Generated name contains double hyphens: %s", result) + } + }) + } +} + func TestGetNonAdminBackupFromVeleroBackup(t *testing.T) { log := zap.New(zap.UseDevMode(true)) ctx := context.Background() From ecdc7f2d6d69c012da792a7782698c27b0703f5e Mon Sep 17 00:00:00 2001 From: Michal Pryc Date: Mon, 7 Oct 2024 13:06:59 +0200 Subject: [PATCH 2/2] Rename BackupNameDelimiter to NameDelimiter Address comment from @shubham-pampattiwar to rename BackupNameDelimiter, because it may be used in the future in other parts of the NAC then just Backup. Signed-off-by: Michal Pryc --- internal/common/constant/constant.go | 4 ++-- internal/common/function/function.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/common/constant/constant.go b/internal/common/constant/constant.go index a9f8541..72dc8e9 100644 --- a/internal/common/constant/constant.go +++ b/internal/common/constant/constant.go @@ -39,8 +39,8 @@ const ( // EmptyString defines a constant for the empty string const EmptyString = "" -// BackupNameDelimiter defines character that is used to separate name aprts -const BackupNameDelimiter = "-" +// NameDelimiter defines character that is used to separate name parts +const NameDelimiter = "-" // TrueString defines a constant for the True string const TrueString = "True" diff --git a/internal/common/function/function.go b/internal/common/function/function.go index 0f6bdc8..eab35ca 100644 --- a/internal/common/function/function.go +++ b/internal/common/function/function.go @@ -149,10 +149,10 @@ func GenerateVeleroBackupNameWithUUID(namespace, nabName string) string { // Build the initial backup name based on the presence of namespace and nabName veleroBackupName := uuidSuffix if len(nabName) > 0 { - veleroBackupName = nabName + constant.BackupNameDelimiter + veleroBackupName + veleroBackupName = nabName + constant.NameDelimiter + veleroBackupName } if len(namespace) > 0 { - veleroBackupName = namespace + constant.BackupNameDelimiter + veleroBackupName + veleroBackupName = namespace + constant.NameDelimiter + veleroBackupName } // Ensure the name is within the character limit @@ -162,22 +162,22 @@ func GenerateVeleroBackupNameWithUUID(namespace, nabName string) string { // Calculate remaining length after UUID remainingLength := maxLength - len(uuidSuffix) - delimeterLength := len(constant.BackupNameDelimiter) + delimeterLength := len(constant.NameDelimiter) // Subtract two delimiter lengths to avoid a corner case where the namespace // and delimiters leave no space for any part of nabName if len(namespace) > remainingLength-delimeterLength-delimeterLength { namespace = namespace[:remainingLength-delimeterLength-delimeterLength] - veleroBackupName = namespace + constant.BackupNameDelimiter + uuidSuffix + veleroBackupName = namespace + constant.NameDelimiter + uuidSuffix } else { remainingLength = remainingLength - len(namespace) - delimeterLength - delimeterLength nabName = nabName[:remainingLength] veleroBackupName = uuidSuffix if len(nabName) > 0 { - veleroBackupName = nabName + constant.BackupNameDelimiter + veleroBackupName + veleroBackupName = nabName + constant.NameDelimiter + veleroBackupName } if len(namespace) > 0 { - veleroBackupName = namespace + constant.BackupNameDelimiter + veleroBackupName + veleroBackupName = namespace + constant.NameDelimiter + veleroBackupName } } }