Skip to content

Commit 2dcd646

Browse files
committed
New string generator for the hash string in NSX resources
1. Use chars 0-9,a-z,A-Z to generate the hash string for ID/DisplayName in NSX resources with VPC scenario. It also works on the Security Policy related resources' display name in T1. 2. The length is 6 chars when using the new hash string function.
1 parent ccc66e7 commit 2dcd646

File tree

4 files changed

+88
-32
lines changed

4 files changed

+88
-32
lines changed

pkg/nsx/services/securitypolicy/builder_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,8 +1274,8 @@ func Test_BuildSecurityPolicyName(t *testing.T) {
12741274
},
12751275
},
12761276
createdFor: common.ResourceTypeNetworkPolicy,
1277-
expName: fmt.Sprintf("%s_c64163f0", strings.Repeat("a", 246)),
1278-
expId: fmt.Sprintf("%s_fb85d834", strings.Repeat("a", 246)),
1277+
expName: fmt.Sprintf("%s_shQDwf", strings.Repeat("a", 248)),
1278+
expId: fmt.Sprintf("%s_zT4Byn", strings.Repeat("a", 248)),
12791279
},
12801280
} {
12811281
t.Run(tc.name, func(t *testing.T) {

pkg/nsx/services/vpc/builder_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func TestBuildNSXVPC(t *testing.T) {
171171
},
172172
expVPC: &model.Vpc{
173173
Id: common.String("test-ns-03a2def3-0087-4077-904e-23e4dd788fb7_ecc6eb9f-92b5-4893-b809-e3ebc1fcf59e"),
174-
DisplayName: common.String("test-ns-03a2def3-0087-4077-904e-23e4dd788fb7_f4f0080e"),
174+
DisplayName: common.String("test-ns-03a2def3-0087-4077-904e-23e4dd788fb7_yWOLBB"),
175175
PrivateIps: []string{"192.168.3.0/24"},
176176
IpAddressType: common.String("IPV4"),
177177
Tags: []model.Tag{

pkg/util/utils.go

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/binary"
1010
"errors"
1111
"fmt"
12+
"math/big"
1213
"net"
1314
"strconv"
1415
"strings"
@@ -31,6 +32,7 @@ import (
3132

3233
const (
3334
wcpSystemResource = "vmware-system-shared-t1"
35+
base62Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
3436
)
3537

3638
var (
@@ -39,44 +41,53 @@ var (
3941

4042
var log = &logger.Log
4143

44+
func truncateLabelHash(data string) string {
45+
return Sha1(data)[:common.HashLength]
46+
}
4247
func NormalizeLabels(matchLabels *map[string]string) *map[string]string {
4348
newLabels := make(map[string]string)
4449
for k, v := range *matchLabels {
45-
newLabels[NormalizeLabelKey(k)] = NormalizeName(v)
50+
newLabels[NormalizeLabelKey(k, truncateLabelHash)] = NormalizeName(v, truncateLabelHash)
4651
}
4752
return &newLabels
4853
}
4954

50-
func NormalizeLabelKey(key string) string {
55+
func NormalizeLabelKey(key string, shaFn func(data string) string) string {
5156
if len(key) <= common.MaxTagScopeLength {
5257
return key
5358
}
5459
splitted := strings.Split(key, "/")
5560
key = splitted[len(splitted)-1]
56-
return normalizeNameByLimit(key, "", common.MaxTagScopeLength)
61+
return normalizeNameByLimit(key, "", common.MaxTagScopeLength, shaFn)
5762
}
5863

59-
func NormalizeName(name string) string {
60-
return normalizeNameByLimit(name, "", common.MaxTagValueLength)
64+
func NormalizeName(name string, shaFn func(data string) string) string {
65+
return normalizeNameByLimit(name, "", common.MaxTagValueLength, shaFn)
6166
}
6267

63-
func normalizeNameByLimit(name string, suffix string, limit int) string {
68+
func normalizeNameByLimit(name string, suffix string, limit int, hashFn func(data string) string) string {
6469
newName := connectStrings(common.ConnectorUnderline, name, suffix)
6570
if len(newName) <= limit {
6671
return newName
6772
}
6873

69-
var hashString string
74+
hashedTarget := name
7075
if len(suffix) > 0 {
71-
hashString = Sha1(suffix)
76+
hashedTarget = suffix
77+
}
78+
79+
var hashString string
80+
if hashFn != nil {
81+
hashString = hashFn(hashedTarget)
7282
} else {
73-
hashString = Sha1(name)
83+
hashString = Sha1WithBase62(hashedTarget)[:6]
7484
}
75-
nameLength := limit - common.HashLength - 1
85+
86+
nameLength := limit - len(hashString) - 1
7687
if len(name) < nameLength {
7788
nameLength = len(name)
7889
}
79-
return strings.Join([]string{name[:nameLength], hashString[:common.HashLength]}, common.ConnectorUnderline)
90+
return strings.Join([]string{name[:nameLength], hashString}, common.ConnectorUnderline)
8091
}
8192

8293
func NormalizeId(name string) string {
@@ -94,10 +105,36 @@ func NormalizeId(name string) string {
94105
}
95106

96107
func Sha1(data string) string {
108+
sum := getSha1Bytes(data)
109+
return fmt.Sprintf("%x", sum)
110+
}
111+
112+
func getSha1Bytes(data string) []byte {
97113
h := sha1.New() // #nosec G401: not used for security purposes
98114
h.Write([]byte(data))
99115
sum := h.Sum(nil)
100-
return fmt.Sprintf("%x", sum)
116+
return sum
117+
}
118+
119+
// Sha1WithBase62 uses the chars in `base62Chars` to present the hash result on the input data. We now use Sha1 as
120+
// the hash algorithm.
121+
func Sha1WithBase62(data string) string {
122+
sum := getSha1Bytes(data)
123+
value := new(big.Int).SetBytes(sum[:])
124+
base := big.NewInt(int64(len(base62Chars)))
125+
var result []byte
126+
for value.Cmp(big.NewInt(0)) > 0 {
127+
mod := new(big.Int).Mod(value, base)
128+
result = append(result, base62Chars[mod.Int64()])
129+
value.Div(value, base)
130+
}
131+
132+
// Reverse the result because the encoding process generates characters in reverse order
133+
for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
134+
result[i], result[j] = result[j], result[i]
135+
}
136+
137+
return string(result)
101138
}
102139

103140
func RemoveDuplicateStr(strSlice []string) []string {
@@ -343,7 +380,7 @@ func UpdateK8sResourceAnnotation(client client.Client, ctx context.Context, k8sO
343380
// this function is used on the resources with VPC scenario, and the provided obj is the K8s CR which is
344381
// used to generate the NSX resource.
345382
func GenerateIDByObject(obj metav1.Object) string {
346-
return normalizeNameByLimit(obj.GetName(), string(obj.GetUID()), common.MaxIdLength)
383+
return normalizeNameByLimit(obj.GetName(), string(obj.GetUID()), common.MaxIdLength, nil)
347384
}
348385

349386
// GenerateIDByObjectByLimit generate string id for NSX resource using the provided Object's name and uid,
@@ -352,13 +389,13 @@ func GenerateIDByObjectByLimit(obj metav1.Object, limit int) string {
352389
if limit == 0 {
353390
limit = common.MaxIdLength
354391
}
355-
return normalizeNameByLimit(obj.GetName(), string(obj.GetUID()), limit)
392+
return normalizeNameByLimit(obj.GetName(), string(obj.GetUID()), limit, nil)
356393
}
357394

358395
func GenerateIDByObjectWithSuffix(obj metav1.Object, suffix string) string {
359396
limit := common.MaxIdLength
360397
limit -= len(suffix) + 1
361-
return connectStrings(common.ConnectorUnderline, normalizeNameByLimit(obj.GetName(), string(obj.GetUID()), limit), suffix)
398+
return connectStrings(common.ConnectorUnderline, normalizeNameByLimit(obj.GetName(), string(obj.GetUID()), limit, nil), suffix)
362399
}
363400

364401
// GenerateID generate id for NSX resource, some resources has complex index, so set it type to string
@@ -391,7 +428,7 @@ func GenerateTruncName(limit int, resName string, prefix, suffix, project, clust
391428
}
392429
oldName := generateDisplayName(common.ConnectorUnderline, resName, "", "", project, cluster)
393430
if len(oldName) > adjustedLimit {
394-
newName := normalizeNameByLimit(oldName, "", adjustedLimit)
431+
newName := normalizeNameByLimit(oldName, "", adjustedLimit, nil)
395432
return generateDisplayName(common.ConnectorUnderline, newName, prefix, suffix, "", "")
396433
}
397434
return generateDisplayName(common.ConnectorUnderline, resName, prefix, suffix, project, cluster)

pkg/util/utils_test.go

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ import (
1212
"strings"
1313
"testing"
1414

15+
"github.com/google/uuid"
1516
"github.com/stretchr/testify/assert"
16-
17-
"github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common"
18-
17+
"github.com/stretchr/testify/require"
1918
v1 "k8s.io/api/core/v1"
2019
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2120
"k8s.io/apimachinery/pkg/types"
21+
"k8s.io/apimachinery/pkg/util/sets"
2222
"sigs.k8s.io/controller-runtime/pkg/client/fake"
23+
24+
"github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common"
2325
)
2426

2527
func TestSha1(t *testing.T) {
@@ -28,16 +30,16 @@ func TestSha1(t *testing.T) {
2830

2931
func TestNormalizeName(t *testing.T) {
3032
shortName := strings.Repeat("a", 256)
31-
assert.Equal(t, NormalizeName(shortName), shortName)
33+
assert.Equal(t, NormalizeName(shortName, truncateLabelHash), shortName)
3234
longName := strings.Repeat("a", 257)
33-
assert.Equal(t, NormalizeName(longName), fmt.Sprintf("%s_%s", strings.Repeat("a", 256-common.HashLength-1), "0c103888"))
35+
assert.Equal(t, NormalizeName(longName, truncateLabelHash), fmt.Sprintf("%s_%s", strings.Repeat("a", 256-common.HashLength-1), "0c103888"))
3436
}
3537

3638
func TestNormalizeLabelKey(t *testing.T) {
3739
shortKey := strings.Repeat("a", 128)
38-
assert.Equal(t, NormalizeLabelKey(shortKey), shortKey)
40+
assert.Equal(t, NormalizeLabelKey(shortKey, truncateLabelHash), shortKey)
3941
longKey := strings.Repeat("a", 129) + "/def"
40-
assert.Equal(t, NormalizeLabelKey(longKey), "def")
42+
assert.Equal(t, NormalizeLabelKey(longKey, truncateLabelHash), "def")
4143
}
4244

4345
func TestNormalizeLabels(t *testing.T) {
@@ -55,7 +57,7 @@ func TestNormalizeLabels(t *testing.T) {
5557
longKey: longValue,
5658
},
5759
expectedLabels: &map[string]string{
58-
"def": NormalizeName(longValue),
60+
"def": NormalizeName(longValue, truncateLabelHash),
5961
},
6062
},
6163
{
@@ -64,7 +66,7 @@ func TestNormalizeLabels(t *testing.T) {
6466
shortKey: longValue,
6567
},
6668
expectedLabels: &map[string]string{
67-
shortKey: NormalizeName(longValue),
69+
shortKey: NormalizeName(longValue, truncateLabelHash),
6870
},
6971
},
7072
}
@@ -500,7 +502,7 @@ func TestGenerateTruncName(t *testing.T) {
500502
project: strings.Repeat("s", 300),
501503
cluster: "k8scl-one",
502504
},
503-
want: "sr_k8scl-one_1234-456_ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss_e89b45cc_scope",
505+
want: "sr_k8scl-one_1234-456_ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss_xbJrtX_scope",
504506
},
505507
}
506508
for _, tt := range tests {
@@ -660,13 +662,13 @@ func TestGenerateIDByObject(t *testing.T) {
660662
name: "truncate with hash on uid",
661663
obj: &metav1.ObjectMeta{Name: "abcdefg", UID: "b720ee2c-5788-4680-9796-0f93db33d8a9"},
662664
limit: 20,
663-
expID: "abcdefg_df78acb2",
665+
expID: "abcdefg_vSV1eZ",
664666
},
665667
{
666668
name: "longer name with truncate",
667669
obj: &metav1.ObjectMeta{Name: strings.Repeat("a", 256), UID: "b720ee2c-5788-4680-9796-0f93db33d8a9"},
668670
limit: 0,
669-
expID: fmt.Sprintf("%s_df78acb2", strings.Repeat("a", 246)),
671+
expID: fmt.Sprintf("%s_vSV1eZ", strings.Repeat("a", 248)),
670672
},
671673
} {
672674
t.Run(tc.name, func(t *testing.T) {
@@ -701,7 +703,7 @@ func TestGenerateIDByObjectWithSuffix(t *testing.T) {
701703
obj: &metav1.ObjectMeta{Name: strings.Repeat("a", 256), UID: "b720ee2c-5788-4680-9796-0f93db33d8a9"},
702704
limit: 0,
703705
suffix: "28e85c0b-21e4-4cab-b1c3-597639dfe752",
704-
expID: fmt.Sprintf("%s_df78acb2_28e85c0b-21e4-4cab-b1c3-597639dfe752", strings.Repeat("a", 209)),
706+
expID: fmt.Sprintf("%s_vSV1eZ_28e85c0b-21e4-4cab-b1c3-597639dfe752", strings.Repeat("a", 211)),
705707
},
706708
} {
707709
t.Run(tc.name, func(t *testing.T) {
@@ -734,3 +736,20 @@ func TestConnectStrings(t *testing.T) {
734736
expString = fmt.Sprintf("%s%s%d", string1, common.ConnectorUnderline, int2)
735737
assert.Equal(t, connectString, expString)
736738
}
739+
740+
func TestNewSha1(t *testing.T) {
741+
assert.Equal(t, "ffN5UpVkkQYbocYDKFXOAMN4AsA", Sha1WithBase62("name"))
742+
assert.Equal(t, "hZBZpydbX1XIFhgs9m6Lt2for9m", Sha1WithBase62("namee"))
743+
744+
allowedChars := sets.New[rune]()
745+
for _, c := range base62Chars {
746+
allowedChars.Insert(c)
747+
}
748+
randUID, err := uuid.NewRandom()
749+
require.NoError(t, err)
750+
hashString := Sha1WithBase62(randUID.String())
751+
// Verify all chars in the hash string are contained in base62Chars.
752+
for _, c := range hashString {
753+
assert.True(t, allowedChars.Has(c))
754+
}
755+
}

0 commit comments

Comments
 (0)