Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions controller/history/revision.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"hash"
"hash/fnv"
"maps"
"sort"
"strconv"
"sync"
Expand Down Expand Up @@ -52,7 +53,7 @@ const (
// If the length of final name is greater than 63 bytes, the prefix is truncated to allow for a name
// that is no larger than 63 bytes.
func ControllerRevisionName(prefix, hash string) string {
return names.GenerateDNS1035Label(prefix, hash)
return names.DNS1035LabelGenerator.GenerateName(prefix, hash)
}

// HashControllerRevision hashes the contents of revision's Data using FNV hashing. If probe is not nil, the byte value
Expand All @@ -62,7 +63,7 @@ func HashControllerRevision(revision *apps.ControllerRevision, probe *int32) str
}

var hashPool = sync.Pool{
New: func() interface{} {
New: func() any {
return fnv.New32()
},
}
Expand Down Expand Up @@ -169,9 +170,7 @@ func NewControllerRevision(parent metav1.Object,
collisionCount *int32,
) (*apps.ControllerRevision, error) {
labelMap := make(map[string]string)
for k, v := range templateLabels {
labelMap[k] = v
}
maps.Copy(labelMap, templateLabels)
blockOwnerDeletion := true
isController := true
cr := &apps.ControllerRevision{
Expand Down
54 changes: 2 additions & 52 deletions controller/names/dns1035.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,8 @@ import (
"k8s.io/apimachinery/pkg/util/validation"
)

const (
// copy from k8s.io/apiserver/pkg/storage/names
randomLength = 5
MaxGeneratedNameLength = validation.DNS1035LabelMaxLength - randomLength
)

// GenerateDNS1035Label generates a valid DNS label (compliant with RFC 1035).
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
// And all "." will be replaced with "-". If the generated name is too long, the suffix
// of base will be truncated to ensure the final name is shorter than 63 characters.
// Usually:
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
// - uniqueName is a random string, such as "12345" or ordinal index.
func GenerateDNS1035Label(base, uniqueName string) string {
return generateDNS1035LabelByMaxLength(base, uniqueName, validation.DNS1035LabelMaxLength)
}

// GenerateDNS1035LabelByMaxLength generates a valid DNS label (compliant with RFC 1035)
// limited by the specified maximum length.
func GenerateDNS1035LabelByMaxLength(base, uniqueName string, maxLength int) string {
return generateDNS1035LabelByMaxLength(base, uniqueName, maxLength)
}

func generateDNS1035LabelByMaxLength(base, unique string, maxLength int) string {
return genericNameGenerator(base, unique, maxLength, validation.DNS1035LabelMaxLength, fixDNS1035Label)
}
// DNS1035LabelGenerator generates a valid DNS label (compliant with RFC 1035).
var DNS1035LabelGenerator = newNameGenerator(validation.DNS1035LabelMaxLength, fixDNS1035Label)

func fixDNS1035Label(label string) string {
// Convert to lowercase
Expand Down Expand Up @@ -82,29 +58,3 @@ func fixDNS1035Label(label string) string {
result := builder.String()
return strings.TrimRight(result, "-")
}

func genericNameGenerator(base, unique string, maxLength, maxLimit int, fixFn func(string) string) string {
if maxLength <= 0 {
return ""
}
if maxLength > maxLimit {
maxLength = maxLimit
}

result := unique
if len(result) >= maxLength {
result = result[:maxLength]
}

maxPrefixLength := maxLength - len(result)
if maxPrefixLength > 0 {
// add prefix
if len(base) > maxPrefixLength-1 {
base = base[:maxPrefixLength-1]
}

result = base + "-" + result
}

return fixFn(result)
}
8 changes: 4 additions & 4 deletions controller/names/dns1035_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,28 +183,28 @@ func (s *dns1035TestSuite) Test_GenerateDNS1035Label() {
name: "replace invalid characters to '-'",
base: "test.#@_1",
unique: "0",
maxLength: MaxGeneratedNameLength,
maxLength: 63,
want: "test----1-0",
},
{
name: "to lower case",
base: "TEST.1",
unique: "0",
maxLength: MaxGeneratedNameLength,
maxLength: 63,
want: "test-1-0",
},
{
name: "to lower case",
base: "test-1",
unique: "!@#$%^&*()_+",
maxLength: MaxGeneratedNameLength,
maxLength: 63,
want: "test-1",
},
}

for _, tt := range tests {
s.Run(tt.name, func() {
got := GenerateDNS1035LabelByMaxLength(tt.base, tt.unique, tt.maxLength)
got := DNS1035LabelGenerator.GenerateNameWithMaxLength(tt.base, tt.unique, tt.maxLength)
s.Equal(tt.want, got)
if len(got) > 0 {
s.Empty(validation.IsDNS1035Label(got), "should be a valid DNS1035 label")
Expand Down
52 changes: 6 additions & 46 deletions controller/names/dns1123.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,49 +22,12 @@ import (
"k8s.io/apimachinery/pkg/util/validation"
)

// GenerateDNS1123Label generates a valid DNS label (compliant with RFC 1123).
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
// If the generated name is too long, the suffix of base will be truncated to ensure the
// final name is shorter than 63 characters.
//
// Usually:
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
// - uniqueName is a random string, such as "12345" or ordinal index.
func GenerateDNS1123Label(base, unique string) string {
return genereateDNS1123LabelByMaxLength(base, unique, validation.DNS1123LabelMaxLength)
}

// GenerateDNS1123LabelByMaxLength generates a valid DNS label (compliant with RFC 1123)
// limited by the specified maximum length.
func GenereateDNS1123LabelByMaxLength(base, unique string, maxLength int) string {
return genereateDNS1123LabelByMaxLength(base, unique, maxLength)
}

func genereateDNS1123LabelByMaxLength(base, unique string, maxLength int) string {
return genericNameGenerator(base, unique, maxLength, validation.DNS1123LabelMaxLength, fixDNS1123Label)
}

// GenerateDNS1123Subdomain generates a valid DNS subdomain (compliant with RFC 1123).
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
// If the generated name is too long, the suffix of base will be truncated to ensure the
// final name is shorter than 253 characters.
//
// Usually:
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
// - uniqueName is a random string, such as "12345" or ordinal index.
func GenerateDNS1123Subdomain(base, unique string) string {
return generateDNS1123SubdomainByMaxLength(base, unique, validation.DNS1123SubdomainMaxLength)
}

// GenerateDNS1123SubdomainByMaxLength generates a valid DNS subdomain (compliant with RFC 1123)
// limited by the specified maximum length.
func GenerateDNS1123SubdomainByMaxLength(base, unique string, maxLength int) string {
return generateDNS1123SubdomainByMaxLength(base, unique, maxLength)
}

func generateDNS1123SubdomainByMaxLength(base, unique string, maxLength int) string {
return genericNameGenerator(base, unique, maxLength, validation.DNS1123SubdomainMaxLength, fixDNS1123Subdomain)
}
var (
// DNS1123LabelGenerator generates a valid DNS label (compliant with RFC 1123).
DNS1123LabelGenerator = newNameGenerator(validation.DNS1123LabelMaxLength, fixDNS1123Label)
// DNS1123SubdomainGenerator generates a valid DNS subdomain (compliant with RFC 1123).
DNS1123SubdomainGenerator = newNameGenerator(validation.DNS1123SubdomainMaxLength, fixDNS1123Subdomain)
)

func fixDNS1123Label(label string) string {
// Convert to lowercase
Expand All @@ -76,7 +39,6 @@ func fixDNS1123Label(label string) string {
// Process each character in the label
for i := 0; i < len(label); i++ {
c := label[i]

if firstChar {
// First character must be alphanumeric
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') {
Expand All @@ -86,7 +48,6 @@ func fixDNS1123Label(label string) string {
// Skip non-alphanumeric characters at the beginning
continue
}

// Subsequent characters: allow alphanumeric and dash
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' {
builder.WriteByte(c)
Expand All @@ -97,7 +58,6 @@ func fixDNS1123Label(label string) string {
}

result := builder.String()

return strings.TrimRight(result, "-")
}

Expand Down
2 changes: 1 addition & 1 deletion controller/names/dns1123_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func (s *dns1123TestSuite) TestGenerateDNS1123SubdomainByMaxLength() {

for _, tc := range testCases {
s.Run(tc.name, func() {
result := generateDNS1123SubdomainByMaxLength(tc.base, tc.unique, tc.maxLength)
result := DNS1123SubdomainGenerator.GenerateNameWithMaxLength(tc.base, tc.unique, tc.maxLength)
s.Equal(tc.expected, result)
if len(result) > 0 {
s.Empty(validation.IsDNS1123Subdomain(result))
Expand Down
85 changes: 85 additions & 0 deletions controller/names/generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright 2025 The KusionStack Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package names

// NameGenerator is an interface for generating names for resources.
type NameGenerator interface {
// GenerateName generates a valid name.
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
// The generator may have a default max length limit. If the generated name is too long,
// it will be truncated to ensure the final name is shorter than the limit.
//
// Usually:
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
// - uniqueName is a random string, such as "12345" or ordinal index.
GenerateName(base, unique string) string
// GenerateNameWithMaxLength generates a valid name with max length limit.
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
// If the generated name is too long, the suffix of base will be truncated to ensure the
// final name is shorter than maxLength.
//
// Usually:
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
// - uniqueName is a random string, such as "12345" or ordinal index.
GenerateNameWithMaxLength(base, unique string, maxLength int) string
}

type formatFn func(string) string

// nameGenerator is an implementation of NameGenerator.
type nameGenerator struct {
formatFixFn formatFn
maxLangthLimit int
}

func newNameGenerator(lengthLimit int, fixFn formatFn) NameGenerator {
return &nameGenerator{
maxLangthLimit: lengthLimit,
formatFixFn: fixFn,
}
}

// GenerateName generates a valid name.
func (g *nameGenerator) GenerateName(base, unique string) string {
return g.GenerateNameWithMaxLength(base, unique, g.maxLangthLimit)
}

// GenerateNameWithMaxLength generates a valid name with max length limit.
func (g *nameGenerator) GenerateNameWithMaxLength(base, unique string, maxLength int) string {
if maxLength <= 0 {
return ""
}
if maxLength > g.maxLangthLimit {
maxLength = g.maxLangthLimit
}

result := unique
if len(result) >= maxLength {
result = result[:maxLength]
}

maxPrefixLength := maxLength - len(result)
if maxPrefixLength > 0 {
// add prefix
if len(base) > maxPrefixLength-1 {
base = base[:maxPrefixLength-1]
}

result = base + "-" + result
}

return g.formatFixFn(result)
}
Loading