Skip to content

Commit 8f57b3b

Browse files
adding a manifestival transformer and unit tests for the same for CRD field truncation. It truncates description fields in CRDs to apply a nightly build in the operator and potentially paving way for syncing nightly release of shipwright/builds with shipwright/operator
1 parent 1000268 commit 8f57b3b

File tree

4 files changed

+138
-1
lines changed

4 files changed

+138
-1
lines changed

controllers/shipwrightbuild_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
171171
images := common.ToLowerCaseKeys(common.ImagesFromEnv(common.ShipwrightImagePrefix))
172172

173173
transformerfncs := []manifestival.Transformer{}
174+
transformerfncs = append(transformerfncs, common.TruncateCRDFieldTransformer("description", 50))
174175
if common.IsOpenShiftPlatform() {
175176
transformerfncs = append(transformerfncs, manifestival.InjectNamespace(targetNamespace))
176177
transformerfncs = append(transformerfncs, common.DeploymentImages(images))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
apiVersion: apiextensions.k8s.io/v1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
name: test.crd.com
5+
spec:
6+
group: crd.com
7+
versions:
8+
- name: v1
9+
served: true
10+
description: This is a long string that should be truncated
11+
storage: true
12+
schema:
13+
openAPIV3Schema:
14+
properties:
15+
description: This is a long string that should be truncated
16+
spec:
17+
properties:
18+
field1:
19+
type: string
20+
description: This is a long string that should be truncated
21+
scope: Namespaced
22+
names:
23+
plural: tests
24+
singular: test
25+
kind: Test
26+
shortNames:
27+
- tst

pkg/common/util.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,46 @@ func ToLowerCaseKeys(keyValues map[string]string) map[string]string {
8787
return newMap
8888
}
8989

90+
// truncateNestedFields truncates the named "field" from the given data object and all of its sub-objects to maxLength characters.
91+
func truncateNestedFields(data map[string]interface{}, maxLength int, field string) {
92+
queue := []map[string]interface{}{data}
93+
94+
for len(queue) > 0 {
95+
curr := queue[0]
96+
queue = queue[1:]
97+
98+
for key, value := range curr {
99+
if key == field {
100+
if str, ok := value.(string); ok && len(str) > maxLength {
101+
curr[key] = str[:maxLength]
102+
}
103+
} else {
104+
if subObj, ok := value.(map[string]interface{}); ok {
105+
queue = append(queue, subObj)
106+
} else if subObjs, ok := value.([]interface{}); ok {
107+
for _, subObj := range subObjs {
108+
if subObjMap, ok := subObj.(map[string]interface{}); ok {
109+
queue = append(queue, subObjMap)
110+
}
111+
}
112+
}
113+
}
114+
}
115+
}
116+
}
117+
118+
// TruncateCRDFieldTransformer returns a manifestival.Transformer that truncates the value of the given field within a CRD spec to the provided max length.
119+
func TruncateCRDFieldTransformer(field string, maxLength int) manifestival.Transformer {
120+
return func(u *unstructured.Unstructured) error {
121+
if u.GetKind() != "CustomResourceDefinition" {
122+
return nil
123+
}
124+
data := u.Object
125+
truncateNestedFields(data, maxLength, field)
126+
return nil
127+
}
128+
}
129+
90130
// deploymentImages replaces container and env vars images.
91131
func DeploymentImages(images map[string]string) manifestival.Transformer {
92132
return func(u *unstructured.Unstructured) error {

pkg/common/util_test.go

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package common
22

33
import (
4+
"os"
45
"path"
56
"testing"
67

78
mf "github.com/manifestival/manifestival"
89
. "github.com/onsi/ginkgo/v2"
910
. "github.com/onsi/gomega"
10-
11+
"gopkg.in/yaml.v2"
1112
appsv1 "k8s.io/api/apps/v1"
1213
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1314
"k8s.io/apimachinery/pkg/runtime"
@@ -62,6 +63,74 @@ func TestDeploymentImages(t *testing.T) {
6263
})
6364
}
6465

66+
func TestTruncateNestedFields(t *testing.T) {
67+
RegisterFailHandler(Fail)
68+
t.Run("test truncation of manifests", func(t *testing.T) {
69+
testData := map[string]interface{}{
70+
"field1": "This is a long string that should be truncated",
71+
"field2": map[string]interface{}{
72+
"field1": "This is another long string that should be truncated",
73+
},
74+
}
75+
76+
expected := map[string]interface{}{
77+
"field1": "This is a ",
78+
"field2": map[string]interface{}{
79+
"field1": "This is an",
80+
},
81+
}
82+
83+
truncateNestedFields(testData, 10, "field1")
84+
Expect(testData).To(Equal(expected))
85+
})
86+
}
87+
88+
func CheckNestedFieldLengthWithinLimit(data map[string]interface{}, maxLength int, field string) bool {
89+
isFieldSizeInLimit := true
90+
queue := []map[string]interface{}{data}
91+
92+
for len(queue) > 0 {
93+
curr := queue[0]
94+
queue = queue[1:]
95+
96+
for key, value := range curr {
97+
if key == field {
98+
if str, ok := value.(string); ok {
99+
isFieldSizeInLimit = isFieldSizeInLimit && (len(str) <= maxLength)
100+
}
101+
} else {
102+
if subObj, ok := value.(map[string]interface{}); ok {
103+
queue = append(queue, subObj)
104+
} else if subObjs, ok := value.([]interface{}); ok {
105+
for _, subObj := range subObjs {
106+
if subObjMap, ok := subObj.(map[string]interface{}); ok {
107+
queue = append(queue, subObjMap)
108+
}
109+
}
110+
}
111+
}
112+
}
113+
}
114+
115+
return isFieldSizeInLimit
116+
}
117+
118+
func TestTruncateCRDFieldTransformer(t *testing.T) {
119+
RegisterFailHandler(Fail)
120+
t.Run("test truncate CRD field Transformer", func(t *testing.T) {
121+
testData, err := os.ReadFile(path.Join("testdata", "test-truncate-crd-field.yaml"))
122+
Expect(err).NotTo(HaveOccurred())
123+
124+
u := &unstructured.Unstructured{}
125+
err = yaml.Unmarshal(testData, u)
126+
Expect(err).NotTo(HaveOccurred())
127+
128+
TruncateCRDFieldTransformer("description", 10)(u)
129+
isDscriptionTruncated := CheckNestedFieldLengthWithinLimit(u.Object, 10, "description")
130+
Expect(isDscriptionTruncated).To(Equal(true))
131+
})
132+
}
133+
65134
func deploymentFor(t *testing.T, unstr unstructured.Unstructured) *appsv1.Deployment {
66135
deployment := &appsv1.Deployment{}
67136
err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr.Object, deployment)

0 commit comments

Comments
 (0)