Files
open-cluster-management/pkg/work/webhook/common/validator_test.go
xuezhao d83c822129 Add duplicate manifest detection in ManifestWork webhook validation (#1310)
This commit adds validation to detect and reject duplicate manifests
in ManifestWork resources. A manifest is considered duplicate when
it has the same apiVersion, kind, namespace, and name as another
manifest in the same ManifestWork.

This prevents issues where duplicate manifests with different specs
can cause state inconsistency, as the Work Agent applies manifests
sequentially and later entries would overwrite earlier ones.

The validation returns a clear error message indicating the duplicate
manifest's index and the index of its first occurrence.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: xuezhaojun <zxue@redhat.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 06:09:25 +00:00

130 lines
3.3 KiB
Go

package common
import (
"fmt"
"reflect"
"testing"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
workv1 "open-cluster-management.io/api/work/v1"
)
func newManifest(size int) workv1.Manifest {
data := ""
for i := 0; i < size; i++ {
data += "a"
}
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"namespace": "test",
"name": "test",
},
"data": data,
},
}
objectStr, _ := obj.MarshalJSON()
manifest := workv1.Manifest{}
manifest.Raw = objectStr
return manifest
}
func newManifestWithNameAndNamespace(name, namespace string) workv1.Manifest {
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"namespace": namespace,
"name": name,
},
},
}
objectStr, _ := obj.MarshalJSON()
manifest := workv1.Manifest{}
manifest.Raw = objectStr
return manifest
}
func newManifestWithKind(name, namespace, kind string) workv1.Manifest {
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": kind,
"metadata": map[string]interface{}{
"namespace": namespace,
"name": name,
},
},
}
objectStr, _ := obj.MarshalJSON()
manifest := workv1.Manifest{}
manifest.Raw = objectStr
return manifest
}
func Test_Validator(t *testing.T) {
cases := []struct {
name string
manifests []workv1.Manifest
expectedError error
}{
{
name: "duplicate manifests from newManifest helper",
manifests: []workv1.Manifest{newManifest(100 * 1024), newManifest(100 * 1024)},
expectedError: fmt.Errorf("duplicate manifest for resource test/test with resource type v1.Secret"),
},
{
name: "exceed the limit",
manifests: []workv1.Manifest{newManifest(300 * 1024), newManifest(200 * 1024)},
expectedError: fmt.Errorf("the size of manifests is 512192 bytes which exceeds the 512000 limit"),
},
{
name: "duplicate manifests",
manifests: []workv1.Manifest{
newManifestWithNameAndNamespace("test1", "default"),
newManifestWithNameAndNamespace("test2", "default"),
newManifestWithNameAndNamespace("test1", "default"),
},
expectedError: fmt.Errorf("duplicate manifest for resource default/test1 with resource type v1.ConfigMap"),
},
{
name: "same name different namespace",
manifests: []workv1.Manifest{
newManifestWithNameAndNamespace("test", "ns1"),
newManifestWithNameAndNamespace("test", "ns2"),
},
expectedError: nil,
},
{
name: "same name different kind",
manifests: []workv1.Manifest{
newManifestWithKind("test", "default", "ConfigMap"),
newManifestWithKind("test", "default", "Secret"),
},
expectedError: nil,
},
{
name: "unique manifests",
manifests: []workv1.Manifest{
newManifestWithNameAndNamespace("cm1", "default"),
newManifestWithNameAndNamespace("cm2", "default"),
newManifestWithNameAndNamespace("cm3", "default"),
},
expectedError: nil,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
err := ManifestValidator.ValidateManifests(c.manifests)
if !reflect.DeepEqual(err, c.expectedError) {
t.Errorf("expected %#v but got: %#v", c.expectedError, err)
}
})
}
}