Skip to content

Commit 36077c4

Browse files
clyang82claude
andauthored
Allow adding/removing manifests (#485)
* remove check for number and immutable Signed-off-by: clyang82 <chuyang@redhat.com> * add comprehensive tests for manifest bundle validation Added test coverage for ValidateManifestBundle and ValidateManifestBundleUpdate: - Tests for different number of manifests between new and old bundles - Tests for empty manifests arrays - Tests for decoding failures and invalid manifest structures - Tests for missing required fields and forbidden metadata fields - E2E tests for adding and removing manifests from workloads Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: clyang82 <chuyang@redhat.com> * Add a new case to replace the existing manifest Signed-off-by: clyang82 <chuyang@redhat.com> * add tests for duplicate manifest validation Adds comprehensive test coverage for duplicate manifest detection in ValidateManifestBundleUpdate, including tests for duplicates with same namespace, different kinds, cluster-scoped resources, and edge cases to ensure manifests with same name but different namespace or kind are correctly allowed. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: clyang82 <chuyang@redhat.com> * combine ValidateManifestBundleUpdate with ValidateManifestBundle Signed-off-by: clyang82 <chuyang@redhat.com> --------- Signed-off-by: clyang82 <chuyang@redhat.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent cf18ed9 commit 36077c4

File tree

4 files changed

+200
-129
lines changed

4 files changed

+200
-129
lines changed

pkg/services/resource.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ func (s *sqlResourceService) Update(ctx context.Context, resource *api.Resource)
124124
return found, nil
125125
}
126126

127-
if err := ValidateManifestBundleUpdate(resource.Payload, found.Payload); err != nil {
127+
if err := ValidateManifestBundle(resource.Payload); err != nil {
128128
return nil, errors.Validation("the new manifest bundle in the resource is invalid, %v", err)
129129
}
130130

pkg/services/validation.go

Lines changed: 33 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
apivalidation "k8s.io/apimachinery/pkg/api/validation"
99
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1010
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
11+
"k8s.io/apimachinery/pkg/util/sets"
1112
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
1213
"k8s.io/apimachinery/pkg/util/validation/field"
1314

@@ -48,10 +49,24 @@ func ValidateManifestBundle(manifestBundle datatypes.JSONMap) error {
4849
if manifestBundleWrapper == nil {
4950
return fmt.Errorf("manifest bundle is empty")
5051
}
51-
for _, obj := range manifestBundleWrapper.Manifests {
52-
if err := ValidateObject(obj); err != nil {
52+
53+
// Track seen manifests to detect duplicates
54+
seen := sets.New[string]()
55+
56+
for i, manifest := range manifestBundleWrapper.Manifests {
57+
if err := ValidateObject(manifest); err != nil {
5358
return err
5459
}
60+
// Check for duplicate manifests
61+
info, err := extractManifestInfo(manifest)
62+
if err != nil {
63+
return fmt.Errorf("failed to extract metadata from manifest at index %d: %w", i, err)
64+
}
65+
66+
if seen.Has(info.key) {
67+
return fmt.Errorf("duplicate manifest for resource %s/%s with resource type %s", info.namespace, info.name, info.gvk)
68+
}
69+
seen.Insert(info.key)
5570
}
5671

5772
return nil
@@ -87,52 +102,24 @@ func ValidateObject(obj datatypes.JSONMap) error {
87102
return fmt.Errorf("%s", errs.ToAggregate().Error())
88103
}
89104

90-
func ValidateManifestBundleUpdate(new, old datatypes.JSONMap) error {
91-
newManifestBundleWrapper, err := api.DecodeManifestBundle(new)
92-
if err != nil {
93-
return fmt.Errorf("failed to decode new manifest bundle: %v", err)
94-
}
95-
if newManifestBundleWrapper == nil {
96-
return fmt.Errorf("new manifest bundle is empty")
97-
}
98-
oldManifestBundleWrapper, err := api.DecodeManifestBundle(old)
99-
if err != nil {
100-
return fmt.Errorf("failed to decode old manifest bundle: %v", err)
101-
}
102-
if oldManifestBundleWrapper == nil {
103-
return fmt.Errorf("old manifest bundle is empty")
104-
}
105-
if len(newManifestBundleWrapper.Manifests) != len(oldManifestBundleWrapper.Manifests) {
106-
return fmt.Errorf("new and old manifest have different number of objects")
107-
}
108-
for i := range newManifestBundleWrapper.Manifests {
109-
if err := ValidateObjectUpdate(newManifestBundleWrapper.Manifests[i], oldManifestBundleWrapper.Manifests[i]); err != nil {
110-
return err
111-
}
112-
}
113-
114-
return nil
105+
// manifestInfo contains the metadata needed for duplicate detection and error messages.
106+
type manifestInfo struct {
107+
key string // unique key for duplicate detection: apiVersion/kind/namespace/name
108+
name string
109+
namespace string
110+
gvk string // apiVersion.kind format for error messages
115111
}
116112

117-
func ValidateObjectUpdate(new, old datatypes.JSONMap) error {
118-
fldPath := field.NewPath("metadata")
119-
120-
newObj := unstructured.Unstructured{Object: new}
121-
oldObj := unstructured.Unstructured{Object: old}
122-
123-
errs := field.ErrorList{}
124-
125-
errs = append(errs, apivalidation.ValidateImmutableField(newObj.GetAPIVersion(), oldObj.GetAPIVersion(), field.NewPath("apiVersion"))...)
126-
errs = append(errs, apivalidation.ValidateImmutableField(newObj.GetKind(), oldObj.GetKind(), field.NewPath("kind"))...)
127-
errs = append(errs, apivalidation.ValidateImmutableField(newObj.GetName(), oldObj.GetName(), fldPath.Child("name"))...)
128-
errs = append(errs, apivalidation.ValidateImmutableField(newObj.GetNamespace(), oldObj.GetNamespace(), fldPath.Child("namespace"))...)
129-
errs = append(errs, validateMetaData(newObj)...)
130-
131-
if len(errs) == 0 {
132-
return nil
133-
}
134-
135-
return fmt.Errorf("%s", errs.ToAggregate().Error())
113+
// extractManifestInfo extracts metadata from a manifest for duplicate detection.
114+
func extractManifestInfo(manifest datatypes.JSONMap) (*manifestInfo, error) {
115+
unstructuredObj := unstructured.Unstructured{Object: manifest}
116+
return &manifestInfo{
117+
key: fmt.Sprintf("%s/%s/%s/%s", unstructuredObj.GetAPIVersion(), unstructuredObj.GetKind(),
118+
unstructuredObj.GetNamespace(), unstructuredObj.GetName()),
119+
name: unstructuredObj.GetName(),
120+
namespace: unstructuredObj.GetNamespace(),
121+
gvk: fmt.Sprintf("%s.%s", unstructuredObj.GetAPIVersion(), unstructuredObj.GetKind()),
122+
}, nil
136123
}
137124

138125
// validatedAPIVersion tests whether the value passed is a valid apiVersion. A

pkg/services/validation_test.go

Lines changed: 41 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,54 @@ func TestValidateManifestBundle(t *testing.T) {
9898
manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"quay.io/nginx/nginx-unprivileged:latest\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"),
9999
},
100100
{
101-
name: "invalidated manifest bundle",
101+
name: "validated manifest bundle with single manifest",
102+
manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}}]}}"),
103+
},
104+
{
105+
name: "validated manifest bundle with empty manifests array",
106+
manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[]}}"),
107+
},
108+
{
109+
name: "manifest bundle is empty - wrong structure",
102110
manifest: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"),
103-
expectedErrorMsg: "manifest is empty",
111+
expectedErrorMsg: "manifest bundle is empty",
112+
},
113+
{
114+
name: "manifest bundle is nil - no data field",
115+
manifest: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\"}"),
116+
expectedErrorMsg: "failed to decode manifest bundle: failed to convert resource manifest bundle to cloudevent: failed to unmarshal JSONMAP to cloudevent: specversion: no specversion",
117+
},
118+
{
119+
name: "failed to decode - empty object",
120+
manifest: newPayload(t, "{}"),
121+
expectedErrorMsg: "manifest bundle is empty",
122+
},
123+
{
124+
name: "invalid manifest object - missing apiVersion",
125+
manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}}]}}"),
126+
expectedErrorMsg: "apiVersion: Required value: field not set",
127+
},
128+
{
129+
name: "invalid manifest object - missing kind",
130+
manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}}]}}"),
131+
expectedErrorMsg: "kind: Required value: field not set",
132+
},
133+
{
134+
name: "invalid manifest object - missing name",
135+
manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"namespace\":\"default\"}}]}}"),
136+
expectedErrorMsg: "metadata.name: Required value: field not set",
137+
},
138+
{
139+
name: "invalid manifest object - forbidden field generateName",
140+
manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"generateName\":\"nginx-\",\"namespace\":\"default\"}}]}}"),
141+
expectedErrorMsg: "metadata.generateName: Forbidden: field cannot be set",
104142
},
105143
}
106144

107145
for _, c := range cases {
108146
t.Run(c.name, func(t *testing.T) {
109147
err := ValidateManifestBundle(c.manifest)
110-
if err != nil && err.Error() != c.expectedErrorMsg {
148+
if err != nil && strings.TrimSpace(err.Error()) != c.expectedErrorMsg {
111149
t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err)
112150
}
113151
})
@@ -186,84 +224,6 @@ func TestValidateNewObject(t *testing.T) {
186224
}
187225
}
188226

189-
func TestValidateUpdateManifestBundle(t *testing.T) {
190-
cases := []struct {
191-
name string
192-
newPayload datatypes.JSONMap
193-
oldManifest datatypes.JSONMap
194-
expectedErrorMsg string
195-
}{
196-
{
197-
name: "validated manifest",
198-
newPayload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"quay.io/nginx/nginx-unprivileged:latest\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"),
199-
oldManifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"quay.io/nginx/nginx-unprivileged:latest\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"),
200-
},
201-
{
202-
name: "invalidated manifest",
203-
newPayload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"),
204-
oldManifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"),
205-
expectedErrorMsg: "new or old manifest is empty",
206-
},
207-
}
208-
209-
for _, c := range cases {
210-
t.Run(c.name, func(t *testing.T) {
211-
err := ValidateManifestBundleUpdate(c.newPayload, c.oldManifest)
212-
if err != nil && err.Error() != c.expectedErrorMsg {
213-
t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err)
214-
}
215-
})
216-
}
217-
}
218-
219-
func TestValidateUpdateObject(t *testing.T) {
220-
cases := []struct {
221-
name string
222-
newPayload datatypes.JSONMap
223-
oldManifest datatypes.JSONMap
224-
expectedErrorMsg string
225-
}{
226-
{
227-
name: "validated",
228-
newPayload: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"),
229-
oldManifest: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"),
230-
},
231-
{
232-
name: "apiVersion mismatch",
233-
newPayload: newPayload(t, "{\"apiVersion\":\"v2\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"),
234-
oldManifest: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"),
235-
expectedErrorMsg: "apiVersion: Invalid value: \"v2\": field is immutable",
236-
},
237-
{
238-
name: "kind mismatch",
239-
newPayload: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"Test\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"),
240-
oldManifest: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"),
241-
expectedErrorMsg: "kind: Invalid value: \"Test\": field is immutable",
242-
},
243-
{
244-
name: "name mismatch",
245-
newPayload: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test2\",\"namespace\":\"test\"}}"),
246-
oldManifest: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test1\",\"namespace\":\"test\"}}"),
247-
expectedErrorMsg: "metadata.name: Invalid value: \"test2\": field is immutable",
248-
},
249-
{
250-
name: "namespace mismatch",
251-
newPayload: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test2\"}}"),
252-
oldManifest: newPayload(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test1\"}}"),
253-
expectedErrorMsg: "metadata.namespace: Invalid value: \"test2\": field is immutable",
254-
},
255-
}
256-
257-
for _, c := range cases {
258-
t.Run(c.name, func(t *testing.T) {
259-
err := ValidateObjectUpdate(c.newPayload, c.oldManifest)
260-
if err != nil && err.Error() != c.expectedErrorMsg {
261-
t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err)
262-
}
263-
})
264-
}
265-
}
266-
267227
func newPayload(t *testing.T, data string) datatypes.JSONMap {
268228
payload := map[string]interface{}{}
269229
if err := json.Unmarshal([]byte(data), &payload); err != nil {

0 commit comments

Comments
 (0)