use existing validation code and decoding in fieldManager admission

This commit is contained in:
Kevin Wiesmueller 2021-02-26 20:03:10 +01:00
parent da610d6667
commit fc1841d72f
3 changed files with 138 additions and 20 deletions

View File

@ -21,7 +21,6 @@ import (
"fmt"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/warning"
)
@ -65,7 +64,7 @@ func (admit *managedFieldsValidatingAdmissionController) Admit(ctx context.Conte
return err
}
managedFieldsAfterAdmission := objectMeta.GetManagedFields()
if err := validateManagedFields(managedFieldsAfterAdmission); err != nil {
if err := ValidateManagedFields(managedFieldsAfterAdmission); err != nil {
objectMeta.SetManagedFields(managedFieldsBeforeAdmission)
warning.AddWarning(ctx, "",
fmt.Sprintf(InvalidManagedFieldsAfterMutatingAdmissionWarningFormat,
@ -82,21 +81,3 @@ func (admit *managedFieldsValidatingAdmissionController) Validate(ctx context.Co
}
return nil
}
func validateManagedFields(managedFields []metav1.ManagedFieldsEntry) error {
for i, managed := range managedFields {
if len(managed.APIVersion) < 1 {
return fmt.Errorf(".metadata.managedFields[%d]: missing apiVersion", i)
}
if len(managed.FieldsType) < 1 {
return fmt.Errorf(".metadata.managedFields[%d]: missing fieldsType", i)
}
if len(managed.Manager) < 1 {
return fmt.Errorf(".metadata.managedFields[%d]: missing manager", i)
}
if managed.FieldsV1 == nil {
return fmt.Errorf(".metadata.managedFields[%d]: missing fieldsV1", i)
}
}
return nil
}

View File

@ -0,0 +1,115 @@
package fieldmanager_test
import (
"context"
"reflect"
"testing"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
api "k8s.io/kubernetes/pkg/apis/core"
)
func TestAdmission(t *testing.T) {
wrap := &mockAdmissionController{}
ac := fieldmanager.NewManagedFieldsValidatingAdmissionController(wrap)
tests := []struct {
beforeAdmission []metav1.ManagedFieldsEntry
afterAdmission []metav1.ManagedFieldsEntry
expected []metav1.ManagedFieldsEntry
}{
{
beforeAdmission: []metav1.ManagedFieldsEntry{
{
Manager: "test",
},
},
afterAdmission: []metav1.ManagedFieldsEntry{
{
Manager: "",
},
},
expected: []metav1.ManagedFieldsEntry{
{
Manager: "test",
},
},
},
{
beforeAdmission: []metav1.ManagedFieldsEntry{
{
APIVersion: "test",
},
},
afterAdmission: []metav1.ManagedFieldsEntry{
{
APIVersion: "",
},
},
expected: []metav1.ManagedFieldsEntry{
{
APIVersion: "test",
},
},
},
{
beforeAdmission: []metav1.ManagedFieldsEntry{
{
FieldsType: "FieldsV1",
},
},
afterAdmission: []metav1.ManagedFieldsEntry{
{
FieldsType: "test",
},
},
expected: []metav1.ManagedFieldsEntry{
{
FieldsType: "FieldsV1",
},
},
},
}
for _, test := range tests {
obj := &unstructured.Unstructured{}
obj.SetManagedFields(test.beforeAdmission)
wrap.admit = replaceManagedFields(test.afterAdmission)
attrs := admission.NewAttributesRecord(obj, obj, api.Kind("ConfigMap").WithVersion("version"), "default", "", api.Resource("configmaps").WithVersion("version"), "", admission.Update, nil, false, nil)
if err := ac.(admission.MutationInterface).Admit(context.TODO(), attrs, nil); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(obj.GetManagedFields(), test.expected) {
t.Fatalf("expected: \n%v\ngot:\n%v", test.expected, obj.GetManagedFields())
}
}
}
func replaceManagedFields(with []metav1.ManagedFieldsEntry) func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
return func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
objectMeta, err := meta.Accessor(a.GetObject())
if err != nil {
return err
}
objectMeta.SetManagedFields(with)
return nil
}
}
type mockAdmissionController struct {
admit func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error
}
func (c *mockAdmissionController) Handles(operation admission.Operation) bool {
return true
}
func (c *mockAdmissionController) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
return c.admit(ctx, a, o)
}

View File

@ -0,0 +1,22 @@
package fieldmanager
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
)
// ValidateManagedFields by checking the integrity of every entry and trying to decode
// them to the internal format
func ValidateManagedFields(managedFields []metav1.ManagedFieldsEntry) error {
validationErrs := v1validation.ValidateManagedFields(managedFields, field.NewPath("metadata").Child("managedFields"))
if len(validationErrs) > 0 {
return validationErrs.ToAggregate()
}
if _, err := DecodeManagedFields(managedFields); err != nil {
return err
}
return nil
}