Skip to content

Commit

Permalink
chore: standardize generation of resource names (#1472)
Browse files Browse the repository at this point in the history
Signed-off-by: odubajDT <ondrej.dubaj@dynatrace.com>
  • Loading branch information
odubajDT committed May 26, 2023
1 parent a3f5e5b commit f7abcb0
Show file tree
Hide file tree
Showing 16 changed files with 393 additions and 234 deletions.
16 changes: 9 additions & 7 deletions operator/apis/lifecycle/v1alpha3/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package common
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"math/rand"
"strconv"

operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
Expand All @@ -30,10 +30,7 @@ const CreateAppEvalSpanName = "create_%s_app_evaluation"
const CreateWorkloadEvalSpanName = "create_%s_deployment_evaluation"
const AppTypeAnnotation = "keptn.sh/app-type"

const MaxAppNameLength = 25
const MaxWorkloadNameLength = 25
const MaxTaskNameLength = 25
const MaxVersionLength = 12
const MinKLTNameLen = 80
const MaxK8sObjectLength = 253

type AppType string
Expand Down Expand Up @@ -178,12 +175,17 @@ const (

func GenerateTaskName(checkType CheckType, taskName string) string {
randomId := rand.Intn(99_999-10_000) + 10000
return fmt.Sprintf("%s-%s-%d", checkType, TruncateString(taskName, 32), randomId)
return operatorcommon.CreateResourceName(MaxK8sObjectLength, MinKLTNameLen, string(checkType), taskName, strconv.Itoa(randomId))
}

func GenerateJobName(taskName string) string {
randomId := rand.Intn(99_999-10_000) + 10000
return operatorcommon.CreateResourceName(MaxK8sObjectLength, MinKLTNameLen, taskName, strconv.Itoa(randomId))
}

func GenerateEvaluationName(checkType CheckType, evalName string) string {
randomId := rand.Intn(99_999-10_000) + 10000
return fmt.Sprintf("%s-%s-%d", checkType, TruncateString(evalName, 27), randomId)
return operatorcommon.CreateResourceName(MaxK8sObjectLength, MinKLTNameLen, string(checkType), evalName, strconv.Itoa(randomId))
}

// MergeMaps merges two maps into a new map. If a key exists in both maps, the
Expand Down
35 changes: 31 additions & 4 deletions operator/apis/lifecycle/v1alpha3/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/stretchr/testify/require"
)

const ExtraLongName = "loooooooooooooooooooooo00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ooooooo01234567891234567890123456789"

func TestKeptnState_IsCompleted(t *testing.T) {
tests := []struct {
State KeptnState
Expand Down Expand Up @@ -292,8 +294,8 @@ func Test_GenerateTaskName(t *testing.T) {
},
{
Check: PreDeploymentCheckType,
Name: "loooooooooooooooooooooooooooooooooooooong_name",
Want: "pre-looooooooooooooooooooooooooooooo-",
Name: ExtraLongName,
Want: "pre-loooooooooooooooooooooo00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ooooooo0123456789123456-",
},
}
for _, tt := range tests {
Expand Down Expand Up @@ -321,8 +323,8 @@ func Test_GenerateEvaluationName(t *testing.T) {
},
{
Check: PreDeploymentEvaluationCheckType,
Name: "loooooooooooooooooooooooooooooooooooooong_name",
Want: "pre-eval-loooooooooooooooooooooooooo-",
Name: ExtraLongName,
Want: "pre-eval-loooooooooooooooooooooo00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ooooooo01234567891-",
},
}
for _, tt := range tests {
Expand All @@ -332,6 +334,31 @@ func Test_GenerateEvaluationName(t *testing.T) {
}
}

func Test_GenerateJobName(t *testing.T) {
tests := []struct {
Name string
Want string
}{
{
Name: "short-name",
Want: "short-name-",
},
{
Name: "",
Want: "-",
},
{
Name: ExtraLongName,
Want: "loooooooooooooooooooooo00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ooooooo01234567891234567890-",
},
}
for _, tt := range tests {
t.Run("", func(t *testing.T) {
require.True(t, strings.HasPrefix(GenerateJobName(tt.Name), tt.Want))
})
}
}

func Test_MergeMaps(t *testing.T) {
tests := []struct {
In1 map[string]string
Expand Down
6 changes: 2 additions & 4 deletions operator/apis/lifecycle/v1alpha3/keptnapp_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ limitations under the License.
package v1alpha3

import (
"fmt"
"strings"

"github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common"
operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -104,7 +102,7 @@ func init() {
}

func (a KeptnApp) GetAppVersionName() string {
return strings.ToLower(fmt.Sprintf("%s-%s-%s", a.Name, a.Spec.Version, common.Hash(a.Generation)))
return operatorcommon.CreateResourceName(common.MaxK8sObjectLength, common.MinKLTNameLen, a.Name, a.Spec.Version, common.Hash(a.Generation))
}

func (a KeptnApp) SetSpanAttributes(span trace.Span) {
Expand Down
3 changes: 2 additions & 1 deletion operator/apis/lifecycle/v1alpha3/keptnworkload_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"

"github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common"
operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -102,7 +103,7 @@ func init() {
}

func (w KeptnWorkload) GetWorkloadInstanceName() string {
return strings.ToLower(w.Name + "-" + w.Spec.Version)
return operatorcommon.CreateResourceName(common.MaxK8sObjectLength, common.MinKLTNameLen, w.Name, w.Spec.Version)
}

func (w KeptnWorkload) SetSpanAttributes(span trace.Span) {
Expand Down
48 changes: 48 additions & 0 deletions operator/common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package operatorcommon

import "strings"

// CreateResourceName is a function that concatenates the parts from the
// input and checks, if the resulting string matches the maxLen condition.
// If it does not match, it reduces the subparts, starting with the first
// one (but leaving its length at least in minSubstrLen so it's not deleted
// completely) adn continuing with the rest if needed.
// Let's take WorkloadInstance as an example (3 parts: app, workload, version).
// First the app name is reduced if needed (only to minSubstrLen),
// afterwards workload and the version is not reduced at all. This pattern is
// chosen to not reduce only one part of the name (that can be completely gone
// afterwards), but to include all of the parts in the resulting string.
func CreateResourceName(maxLen int, minSubstrLen int, str ...string) string {
// if the minSubstrLen is too long for the number of parts,
// needs to be reduced
for len(str)*minSubstrLen > maxLen {
minSubstrLen = minSubstrLen / 2
}
// looping through the subparts and checking if the resulting string
// matches the maxLen condition
for i := 0; i < len(str)-1; i++ {
newStr := strings.Join(str, "-")
if len(newStr) > maxLen {
// if the part is already smaller than the minSubstrLen,
// this part cannot be reduced anymore, so we continue
if len(str[i]) <= minSubstrLen {
continue
}
// part needs to be reduced
cut := len(newStr) - maxLen
// if the needed reduction is bigger than the allowed
// reduction on the part, it's reduced to the minimum
if cut > len(str[i])-minSubstrLen {
str[i] = str[i][:minSubstrLen]
} else {
// the needed reduction can be completed fully on this
// part, so it's reduced accordingly
str[i] = str[i][:len(str[i])-cut]
}
} else {
return strings.ToLower(newStr)
}
}

return strings.ToLower(strings.Join(str, "-"))
}
100 changes: 100 additions & 0 deletions operator/common/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package operatorcommon

import (
"testing"

"github.com/stretchr/testify/require"
)

func Test_CreateResourceName(t *testing.T) {
tests := []struct {
Name string
Input []string
Max int
Min int
Want string
}{
{
Name: "parts not exceeding max, not min",
Input: []string{
"str1",
"str2",
"str3",
},
Max: 20,
Min: 5,
Want: "str1-str2-str3",
},
{
Name: "1 part exceeding max",
Input: []string{
"str1111111111111111111111",
"str2",
"str3",
},
Max: 20,
Min: 5,
Want: "str1111111-str2-str3",
},
{
Name: "2 part exceeding max",
Input: []string{
"str1",
"str222222222222222222222222",
"str3",
},
Max: 20,
Min: 5,
Want: "str1-str2222222-str3",
},
{
Name: "1 and 2 part exceeding max",
Input: []string{
"str111111111111111111111",
"str22222222",
"str3",
},
Max: 20,
Min: 5,
Want: "str11-str222222-str3",
},
{
Name: "1 and 2 part exceeding max, min needs to be reduced",
Input: []string{
"str111111111111111111111",
"str22222222",
"str3",
},
Max: 20,
Min: 10,
Want: "str11-str222222-str3",
},
{
Name: "1 and 2 part exceeding max, min needs to be reduced",
Input: []string{
"str111111111111111111111",
"str22222222",
"str3",
},
Max: 20,
Min: 20,
Want: "str11-str222222-str3",
},
{
Name: "1 and 2 part exceeding max, min needs to be reduced",
Input: []string{
"str111111111111111111111",
"str22222222",
"str3",
},
Max: 20,
Min: 100,
Want: "str111-str22222-str3",
},
}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
require.Equal(t, tt.Want, CreateResourceName(tt.Max, tt.Min, tt.Input...))
})
}
}
4 changes: 0 additions & 4 deletions operator/controllers/common/helperfunctions.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ func GetItemStatus(name string, instanceStatus []klcv1alpha3.ItemStatus) klcv1al
}
}

func GetAppVersionName(namespace string, appName string, version string) types.NamespacedName {
return types.NamespacedName{Namespace: namespace, Name: appName + "-" + version}
}

// GetOldStatus retrieves the state of the task/evaluation
func GetOldStatus(name string, statuses []klcv1alpha3.ItemStatus) apicommon.KeptnState {
var oldstatus apicommon.KeptnState
Expand Down
53 changes: 0 additions & 53 deletions operator/controllers/common/helperfunctions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common"
"github.com/stretchr/testify/require"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -69,58 +68,6 @@ func Test_GetItemStatus(t *testing.T) {
}
}

func Test_GetAppVersionName(t *testing.T) {
tests := []struct {
namespace string
appName string
version string
want types.NamespacedName
}{
{
namespace: "namespace",
appName: "name",
version: "version",
want: types.NamespacedName{
Namespace: "namespace",
Name: "name-version",
},
},
{
namespace: "",
appName: "name",
version: "version",
want: types.NamespacedName{
Namespace: "",
Name: "name-version",
},
},
{
namespace: "namespace",
appName: "",
version: "version",
want: types.NamespacedName{
Namespace: "namespace",
Name: "-version",
},
},
{
namespace: "namespace",
appName: "name",
version: "",
want: types.NamespacedName{
Namespace: "namespace",
Name: "name-",
},
},
}

for _, tt := range tests {
t.Run("", func(t *testing.T) {
require.Equal(t, GetAppVersionName(tt.namespace, tt.appName, tt.version), tt.want)
})
}
}

func Test_GetOldStatus(t *testing.T) {
tests := []struct {
statuses []klcv1alpha3.ItemStatus
Expand Down
Loading

0 comments on commit f7abcb0

Please sign in to comment.