Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: scheduler (9/): add uniquename utility for preparing binding names #404

Merged
merged 4 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
57 changes: 57 additions & 0 deletions pkg/scheduler/framework/uniquename/uniquename.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/

// package uniquename features some utilities that are used to generate unique names in use
// by the scheduler.
package uniquename

import (
"fmt"

"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/validation"
)

const (
uuidLength = 6
)

// minInt returns the smaller one of two integers.
func minInt(a, b int) int {
if a < b {
return a
}
return b
}

// NewlusterResourceBindingName returns a unique name for a cluster resource binding in the
michaelawyu marked this conversation as resolved.
Show resolved Hide resolved
// format of DNS subdomain names (RFC 1123).
//
// The name is generated using the following format:
// * [CRP-NAME] - [TARGET-CLUSTER-NAME] - [RANDOM-SUFFIX]
//
// Segments will be truncated if necessary.
//
// Note that the name generation is, in essence, a best-effort process, though the chances
// of name collisions are extremely low.
//
// In addition, note that this function assumes that both the CRP name and the cluster name
// are valid DNS subdomain names (RFC 1123).
func NewlusterResourceBindingName(CRPName string, clusterName string) (string, error) {
reservedSlots := 2 + uuidLength // 2 dashs + 6 character UUID string

slotsPerSeg := (validation.DNS1123SubdomainMaxLength - reservedSlots) / 2
uniqueName := fmt.Sprintf("%s-%s-%s",
CRPName[:minInt(slotsPerSeg, len(CRPName))],
clusterName[:minInt(slotsPerSeg, len(clusterName))],
uuid.NewUUID()[:uuidLength],
)

if errs := validation.IsDNS1123Subdomain(uniqueName); len(errs) != 0 {
// Do a sanity check here; normally this would not occur.
return "", fmt.Errorf("failed to format a unique RFC 1123 DNS subdomain name with namespace %s, name %s: %v", CRPName, clusterName, errs)
michaelawyu marked this conversation as resolved.
Show resolved Hide resolved
}
return uniqueName, nil
}
79 changes: 79 additions & 0 deletions pkg/scheduler/framework/uniquename/uniquename_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/

package uniquename

import (
"fmt"
"strings"
"testing"
)

const (
crpName = "app"
clusterName = "bravelion"

longName = "c7t2c6oppjnryqcihwweexeobs7tlmf08ha4qb5htc4cifzpalhb5ec2lbh3" +
"j73reciaz2f0jfd2rl5qba6rzuuwgyw6d9e6la19bo89k41lphln4s4dy1gr" +
"h1dvua17iu4ro61dxo91ayovns8cgnmshlsflmi68e3najm7dw5dqe17pih7" +
"up0dtyvrqxyp90sxedbf"
)

// TO-DO (chenyu1): Expand the test cases as development proceeds.

// TestClusterResourceBindingUniqueName tests the ClusterResourceBindingUniqueName function.
func TestClusterResourceBindingUniqueName(t *testing.T) {
testCases := []struct {
name string
crpName string
clusterName string
wantPrefix string
wantLength int
expectedToFail bool
}{
{
name: "valid name",
crpName: crpName,
clusterName: clusterName,
wantPrefix: fmt.Sprintf("%s-%s", crpName, clusterName),
wantLength: len(crpName) + len(clusterName) + 2 + uuidLength,
},
{
name: "valid name (truncated)",
crpName: longName,
clusterName: longName,
wantPrefix: fmt.Sprintf("%s-%s", longName[:122], longName[:122]),
wantLength: 252,
},
{
name: "invalid name",
crpName: crpName,
clusterName: clusterName + "!",
expectedToFail: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
name, err := NewlusterResourceBindingName(tc.crpName, tc.clusterName)

if tc.expectedToFail {
if err == nil {
t.Errorf("ClusterResourceBindingUniqueName(%s, %s) = %v, %v, want error", tc.crpName, tc.clusterName, name, err)
}
return
}
if err != nil {
t.Errorf("ClusterResourceBindingUniqueName(%s, %s) = %v, %v, want no error", tc.crpName, tc.clusterName, name, err)
}
if !strings.HasPrefix(name, tc.wantPrefix) {
t.Errorf("ClusterResourceBindingUniqueName(%s, %s) = %s, want to have prefix %s", tc.crpName, tc.clusterName, name, tc.wantPrefix)
}
if len(name) != tc.wantLength {
t.Errorf("ClusterResourceBindingUniqueName(%s, %s) = %s, want to have length %d", tc.crpName, tc.clusterName, name, tc.wantLength)
}
})
}
}