diff --git a/internal/command/extensions/tigris/create.go b/internal/command/extensions/tigris/create.go index 8004da51ed..361b74d1a9 100644 --- a/internal/command/extensions/tigris/create.go +++ b/internal/command/extensions/tigris/create.go @@ -37,6 +37,11 @@ func create() (cmd *cobra.Command) { } func runCreate(ctx context.Context) (err error) { + bucketName := flag.GetString(ctx, "name") + if err := validateBucketName(bucketName); err != nil { + return err + } + appName := appconfig.NameFromContext(ctx) params := extensions_core.ExtensionParams{} @@ -121,3 +126,24 @@ func isShadowBucketSpecified(accessKey, secretKey, region, name, endpoint string } return n == len(values), nil } + +// validateBucketName validates that the bucket name length is within the allowed range. +// Bucket names must be between 3 and 63 characters. +// See: https://www.tigrisdata.com/docs/buckets/bucket-rules/ +func validateBucketName(name string) error { + const bucketNamingRulesURL = "https://www.tigrisdata.com/docs/buckets/bucket-rules/" + + if name == "" { + return nil // Empty name will be handled by the prompt or auto-generated + } + + if len(name) < 3 { + return fmt.Errorf("bucket name %q is too short: must be at least 3 characters, got %d. See %s", name, len(name), bucketNamingRulesURL) + } + + if len(name) > 63 { + return fmt.Errorf("bucket name %q is too long: must be at most 63 characters, got %d. See %s", name, len(name), bucketNamingRulesURL) + } + + return nil +} diff --git a/internal/command/extensions/tigris/create_test.go b/internal/command/extensions/tigris/create_test.go new file mode 100644 index 0000000000..a97bd3bb04 --- /dev/null +++ b/internal/command/extensions/tigris/create_test.go @@ -0,0 +1,75 @@ +package tigris + +import ( + "strings" + "testing" +) + +func TestValidateBucketName(t *testing.T) { + tests := []struct { + name string + input string + wantErr bool + errSubstr string + }{ + { + name: "empty name is valid (will be prompted or auto-generated)", + input: "", + wantErr: false, + }, + { + name: "too short - 1 character", + input: "a", + wantErr: true, + errSubstr: "too short", + }, + { + name: "too short - 2 characters", + input: "ab", + wantErr: true, + errSubstr: "too short", + }, + { + name: "valid - minimum 3 characters", + input: "abc", + wantErr: false, + }, + { + name: "valid - typical bucket name", + input: "my-bucket-123", + wantErr: false, + }, + { + name: "valid - maximum 63 characters", + input: "a23456789012345678901234567890123456789012345678901234567890123", + wantErr: false, + }, + { + name: "too long - 64 characters (user's original issue)", + input: "nexus-staging-chore-new-mechanism-to-translate-city-names-assets", + wantErr: true, + errSubstr: "too long", + }, + { + name: "too long - 100 characters", + input: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + wantErr: true, + errSubstr: "too long", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateBucketName(tt.input) + if (err != nil) != tt.wantErr { + t.Errorf("validateBucketName(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr) + return + } + if tt.wantErr && tt.errSubstr != "" { + if err == nil || !strings.Contains(err.Error(), tt.errSubstr) { + t.Errorf("validateBucketName(%q) error = %v, want error containing %q", tt.input, err, tt.errSubstr) + } + } + }) + } +}