Skip to content

Commit 8bbf42c

Browse files
authored
refactor: name generator interface (#104)
1 parent b52065d commit 8bbf42c

6 files changed

Lines changed: 102 additions & 108 deletions

File tree

controller/history/revision.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"hash"
2424
"hash/fnv"
25+
"maps"
2526
"sort"
2627
"strconv"
2728
"sync"
@@ -52,7 +53,7 @@ const (
5253
// If the length of final name is greater than 63 bytes, the prefix is truncated to allow for a name
5354
// that is no larger than 63 bytes.
5455
func ControllerRevisionName(prefix, hash string) string {
55-
return names.GenerateDNS1035Label(prefix, hash)
56+
return names.DNS1035LabelGenerator.GenerateName(prefix, hash)
5657
}
5758

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

6465
var hashPool = sync.Pool{
65-
New: func() interface{} {
66+
New: func() any {
6667
return fnv.New32()
6768
},
6869
}
@@ -169,9 +170,7 @@ func NewControllerRevision(parent metav1.Object,
169170
collisionCount *int32,
170171
) (*apps.ControllerRevision, error) {
171172
labelMap := make(map[string]string)
172-
for k, v := range templateLabels {
173-
labelMap[k] = v
174-
}
173+
maps.Copy(labelMap, templateLabels)
175174
blockOwnerDeletion := true
176175
isController := true
177176
cr := &apps.ControllerRevision{

controller/names/dns1035.go

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,8 @@ import (
2222
"k8s.io/apimachinery/pkg/util/validation"
2323
)
2424

25-
const (
26-
// copy from k8s.io/apiserver/pkg/storage/names
27-
randomLength = 5
28-
MaxGeneratedNameLength = validation.DNS1035LabelMaxLength - randomLength
29-
)
30-
31-
// GenerateDNS1035Label generates a valid DNS label (compliant with RFC 1035).
32-
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
33-
// And all "." will be replaced with "-". If the generated name is too long, the suffix
34-
// of base will be truncated to ensure the final name is shorter than 63 characters.
35-
// Usually:
36-
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
37-
// - uniqueName is a random string, such as "12345" or ordinal index.
38-
func GenerateDNS1035Label(base, uniqueName string) string {
39-
return generateDNS1035LabelByMaxLength(base, uniqueName, validation.DNS1035LabelMaxLength)
40-
}
41-
42-
// GenerateDNS1035LabelByMaxLength generates a valid DNS label (compliant with RFC 1035)
43-
// limited by the specified maximum length.
44-
func GenerateDNS1035LabelByMaxLength(base, uniqueName string, maxLength int) string {
45-
return generateDNS1035LabelByMaxLength(base, uniqueName, maxLength)
46-
}
47-
48-
func generateDNS1035LabelByMaxLength(base, unique string, maxLength int) string {
49-
return genericNameGenerator(base, unique, maxLength, validation.DNS1035LabelMaxLength, fixDNS1035Label)
50-
}
25+
// DNS1035LabelGenerator generates a valid DNS label (compliant with RFC 1035).
26+
var DNS1035LabelGenerator = newNameGenerator(validation.DNS1035LabelMaxLength, fixDNS1035Label)
5127

5228
func fixDNS1035Label(label string) string {
5329
// Convert to lowercase
@@ -82,29 +58,3 @@ func fixDNS1035Label(label string) string {
8258
result := builder.String()
8359
return strings.TrimRight(result, "-")
8460
}
85-
86-
func genericNameGenerator(base, unique string, maxLength, maxLimit int, fixFn func(string) string) string {
87-
if maxLength <= 0 {
88-
return ""
89-
}
90-
if maxLength > maxLimit {
91-
maxLength = maxLimit
92-
}
93-
94-
result := unique
95-
if len(result) >= maxLength {
96-
result = result[:maxLength]
97-
}
98-
99-
maxPrefixLength := maxLength - len(result)
100-
if maxPrefixLength > 0 {
101-
// add prefix
102-
if len(base) > maxPrefixLength-1 {
103-
base = base[:maxPrefixLength-1]
104-
}
105-
106-
result = base + "-" + result
107-
}
108-
109-
return fixFn(result)
110-
}

controller/names/dns1035_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,28 +183,28 @@ func (s *dns1035TestSuite) Test_GenerateDNS1035Label() {
183183
name: "replace invalid characters to '-'",
184184
base: "test.#@_1",
185185
unique: "0",
186-
maxLength: MaxGeneratedNameLength,
186+
maxLength: 63,
187187
want: "test----1-0",
188188
},
189189
{
190190
name: "to lower case",
191191
base: "TEST.1",
192192
unique: "0",
193-
maxLength: MaxGeneratedNameLength,
193+
maxLength: 63,
194194
want: "test-1-0",
195195
},
196196
{
197197
name: "to lower case",
198198
base: "test-1",
199199
unique: "!@#$%^&*()_+",
200-
maxLength: MaxGeneratedNameLength,
200+
maxLength: 63,
201201
want: "test-1",
202202
},
203203
}
204204

205205
for _, tt := range tests {
206206
s.Run(tt.name, func() {
207-
got := GenerateDNS1035LabelByMaxLength(tt.base, tt.unique, tt.maxLength)
207+
got := DNS1035LabelGenerator.GenerateNameWithMaxLength(tt.base, tt.unique, tt.maxLength)
208208
s.Equal(tt.want, got)
209209
if len(got) > 0 {
210210
s.Empty(validation.IsDNS1035Label(got), "should be a valid DNS1035 label")

controller/names/dns1123.go

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,49 +22,12 @@ import (
2222
"k8s.io/apimachinery/pkg/util/validation"
2323
)
2424

25-
// GenerateDNS1123Label generates a valid DNS label (compliant with RFC 1123).
26-
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
27-
// If the generated name is too long, the suffix of base will be truncated to ensure the
28-
// final name is shorter than 63 characters.
29-
//
30-
// Usually:
31-
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
32-
// - uniqueName is a random string, such as "12345" or ordinal index.
33-
func GenerateDNS1123Label(base, unique string) string {
34-
return genereateDNS1123LabelByMaxLength(base, unique, validation.DNS1123LabelMaxLength)
35-
}
36-
37-
// GenerateDNS1123LabelByMaxLength generates a valid DNS label (compliant with RFC 1123)
38-
// limited by the specified maximum length.
39-
func GenereateDNS1123LabelByMaxLength(base, unique string, maxLength int) string {
40-
return genereateDNS1123LabelByMaxLength(base, unique, maxLength)
41-
}
42-
43-
func genereateDNS1123LabelByMaxLength(base, unique string, maxLength int) string {
44-
return genericNameGenerator(base, unique, maxLength, validation.DNS1123LabelMaxLength, fixDNS1123Label)
45-
}
46-
47-
// GenerateDNS1123Subdomain generates a valid DNS subdomain (compliant with RFC 1123).
48-
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
49-
// If the generated name is too long, the suffix of base will be truncated to ensure the
50-
// final name is shorter than 253 characters.
51-
//
52-
// Usually:
53-
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
54-
// - uniqueName is a random string, such as "12345" or ordinal index.
55-
func GenerateDNS1123Subdomain(base, unique string) string {
56-
return generateDNS1123SubdomainByMaxLength(base, unique, validation.DNS1123SubdomainMaxLength)
57-
}
58-
59-
// GenerateDNS1123SubdomainByMaxLength generates a valid DNS subdomain (compliant with RFC 1123)
60-
// limited by the specified maximum length.
61-
func GenerateDNS1123SubdomainByMaxLength(base, unique string, maxLength int) string {
62-
return generateDNS1123SubdomainByMaxLength(base, unique, maxLength)
63-
}
64-
65-
func generateDNS1123SubdomainByMaxLength(base, unique string, maxLength int) string {
66-
return genericNameGenerator(base, unique, maxLength, validation.DNS1123SubdomainMaxLength, fixDNS1123Subdomain)
67-
}
25+
var (
26+
// DNS1123LabelGenerator generates a valid DNS label (compliant with RFC 1123).
27+
DNS1123LabelGenerator = newNameGenerator(validation.DNS1123LabelMaxLength, fixDNS1123Label)
28+
// DNS1123SubdomainGenerator generates a valid DNS subdomain (compliant with RFC 1123).
29+
DNS1123SubdomainGenerator = newNameGenerator(validation.DNS1123SubdomainMaxLength, fixDNS1123Subdomain)
30+
)
6831

6932
func fixDNS1123Label(label string) string {
7033
// Convert to lowercase
@@ -76,7 +39,6 @@ func fixDNS1123Label(label string) string {
7639
// Process each character in the label
7740
for i := 0; i < len(label); i++ {
7841
c := label[i]
79-
8042
if firstChar {
8143
// First character must be alphanumeric
8244
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') {
@@ -86,7 +48,6 @@ func fixDNS1123Label(label string) string {
8648
// Skip non-alphanumeric characters at the beginning
8749
continue
8850
}
89-
9051
// Subsequent characters: allow alphanumeric and dash
9152
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' {
9253
builder.WriteByte(c)
@@ -97,7 +58,6 @@ func fixDNS1123Label(label string) string {
9758
}
9859

9960
result := builder.String()
100-
10161
return strings.TrimRight(result, "-")
10262
}
10363

controller/names/dns1123_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ func (s *dns1123TestSuite) TestGenerateDNS1123SubdomainByMaxLength() {
291291

292292
for _, tc := range testCases {
293293
s.Run(tc.name, func() {
294-
result := generateDNS1123SubdomainByMaxLength(tc.base, tc.unique, tc.maxLength)
294+
result := DNS1123SubdomainGenerator.GenerateNameWithMaxLength(tc.base, tc.unique, tc.maxLength)
295295
s.Equal(tc.expected, result)
296296
if len(result) > 0 {
297297
s.Empty(validation.IsDNS1123Subdomain(result))

controller/names/generator.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
Copyright 2025 The KusionStack Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package names
17+
18+
// NameGenerator is an interface for generating names for resources.
19+
type NameGenerator interface {
20+
// GenerateName generates a valid name.
21+
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
22+
// The generator may have a default max length limit. If the generated name is too long,
23+
// it will be truncated to ensure the final name is shorter than the limit.
24+
//
25+
// Usually:
26+
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
27+
// - uniqueName is a random string, such as "12345" or ordinal index.
28+
GenerateName(base, unique string) string
29+
// GenerateNameWithMaxLength generates a valid name with max length limit.
30+
// The result is usually combined by the base and uniqueName, such as "base-uniqueName".
31+
// If the generated name is too long, the suffix of base will be truncated to ensure the
32+
// final name is shorter than maxLength.
33+
//
34+
// Usually:
35+
// - base is the name of workload, such as "deployment", "statefulset", "daemonset".
36+
// - uniqueName is a random string, such as "12345" or ordinal index.
37+
GenerateNameWithMaxLength(base, unique string, maxLength int) string
38+
}
39+
40+
type formatFn func(string) string
41+
42+
// nameGenerator is an implementation of NameGenerator.
43+
type nameGenerator struct {
44+
formatFixFn formatFn
45+
maxLangthLimit int
46+
}
47+
48+
func newNameGenerator(lengthLimit int, fixFn formatFn) NameGenerator {
49+
return &nameGenerator{
50+
maxLangthLimit: lengthLimit,
51+
formatFixFn: fixFn,
52+
}
53+
}
54+
55+
// GenerateName generates a valid name.
56+
func (g *nameGenerator) GenerateName(base, unique string) string {
57+
return g.GenerateNameWithMaxLength(base, unique, g.maxLangthLimit)
58+
}
59+
60+
// GenerateNameWithMaxLength generates a valid name with max length limit.
61+
func (g *nameGenerator) GenerateNameWithMaxLength(base, unique string, maxLength int) string {
62+
if maxLength <= 0 {
63+
return ""
64+
}
65+
if maxLength > g.maxLangthLimit {
66+
maxLength = g.maxLangthLimit
67+
}
68+
69+
result := unique
70+
if len(result) >= maxLength {
71+
result = result[:maxLength]
72+
}
73+
74+
maxPrefixLength := maxLength - len(result)
75+
if maxPrefixLength > 0 {
76+
// add prefix
77+
if len(base) > maxPrefixLength-1 {
78+
base = base[:maxPrefixLength-1]
79+
}
80+
81+
result = base + "-" + result
82+
}
83+
84+
return g.formatFixFn(result)
85+
}

0 commit comments

Comments
 (0)