Merge pull request #91993 from nodo/89274-change-of-managefields-via-subresources

Do not allow manual changes to manageFields via subresources
This commit is contained in:
Kubernetes Prow Robot
2020-09-15 12:26:43 -07:00
committed by GitHub
10 changed files with 312 additions and 35 deletions

View File

@@ -27,6 +27,7 @@ import (
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
genericfeatures "k8s.io/apiserver/pkg/features"
@@ -129,6 +130,69 @@ spec:
t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
}
verifyReplicas(t, result, 1)
// Try to set managed fields using a subresource and verify that it has no effect
existingManagedFields, err := getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
updateBytes := []byte(`{
"metadata": {
"managedFields": [{
"manager":"testing",
"operation":"Update",
"apiVersion":"v1",
"fieldsType":"FieldsV1",
"fieldsV1":{
"f:spec":{
"f:containers":{
"k:{\"name\":\"testing\"}":{
".":{},
"f:image":{},
"f:name":{}
}
}
}
}
}]
}
}`)
result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
SubResource("status").
Name(name).
Param("fieldManager", "subresource_test").
Body(updateBytes).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("Error updating subresource: %v ", err)
}
newManagedFields, err := getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
if !reflect.DeepEqual(existingManagedFields, newManagedFields) {
t.Fatalf("Expected managed fields to not have changed when trying manually settting them via subresoures.\n\nExpected: %#v\n\nGot: %#v", existingManagedFields, newManagedFields)
}
// However, it is possible to modify managed fields using the main resource
result, err = rest.Patch(types.MergePatchType).
AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
Name(name).
Param("fieldManager", "subresource_test").
Body([]byte(`{"metadata":{"managedFields":[{}]}}`)).
DoRaw(context.TODO())
if err != nil {
t.Fatalf("Error updating managed fields of the main resource: %v ", err)
}
newManagedFields, err = getManagedFields(result)
if err != nil {
t.Fatalf("failed to get managedFields from response: %v", err)
}
if len(newManagedFields) != 0 {
t.Fatalf("Expected managed fields to have been reset, but got: %v", newManagedFields)
}
}
// TestApplyCRDStructuralSchema tests that when a CRD has a structural schema in its validation field,
@@ -753,3 +817,11 @@ spec:
}
verifyReplicas(t, result, 1)
}
func getManagedFields(rawResponse []byte) ([]metav1.ManagedFieldsEntry, error) {
obj := unstructured.Unstructured{}
if err := obj.UnmarshalJSON(rawResponse); err != nil {
return nil, err
}
return obj.GetManagedFields(), nil
}

View File

@@ -1582,7 +1582,108 @@ func TestErrorsDontFailPatch(t *testing.T) {
if err != nil {
t.Fatalf("Failed to patch object with empty FieldsType: %v", err)
}
}
func TestApplyDoesNotChangeManagedFieldsViaSubresources(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
_, client, closeFn := setup(t)
defer closeFn()
podBytes := []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "just-a-pod"
},
"spec": {
"containers": [{
"name": "test-container-a",
"image": "test-image-one"
}]
}
}`)
liveObj, err := client.CoreV1().RESTClient().
Patch(types.ApplyPatchType).
Namespace("default").
Param("fieldManager", "apply_test").
Resource("pods").
Name("just-a-pod").
Body(podBytes).
Do(context.TODO()).
Get()
if err != nil {
t.Fatalf("Failed to create object: %v", err)
}
updateBytes := []byte(`{
"metadata": {
"managedFields": [{
"manager":"testing",
"operation":"Update",
"apiVersion":"v1",
"fieldsType":"FieldsV1",
"fieldsV1":{
"f:spec":{
"f:containers":{
"k:{\"name\":\"testing\"}":{
".":{},
"f:image":{},
"f:name":{}
}
}
}
}
}]
},
"status": {
"conditions": [{"type": "MyStatus", "status":"true"}]
}
}`)
updateActor := "update_managedfields_test"
newObj, err := client.CoreV1().RESTClient().
Patch(types.MergePatchType).
Namespace("default").
Param("fieldManager", updateActor).
Name("just-a-pod").
Resource("pods").
SubResource("status").
Body(updateBytes).
Do(context.TODO()).
Get()
if err != nil {
t.Fatalf("Error updating subresource: %v ", err)
}
liveAccessor, err := meta.Accessor(liveObj)
if err != nil {
t.Fatalf("Failed to get meta accessor for live object: %v", err)
}
newAccessor, err := meta.Accessor(newObj)
if err != nil {
t.Fatalf("Failed to get meta accessor for new object: %v", err)
}
liveManagedFields := liveAccessor.GetManagedFields()
if len(liveManagedFields) != 1 {
t.Fatalf("Expected managedFields in the live object to have exactly one entry, got %d: %v", len(liveManagedFields), liveManagedFields)
}
newManagedFields := newAccessor.GetManagedFields()
if len(newManagedFields) != 2 {
t.Fatalf("Expected managedFields in the new object to have exactly two entries, got %d: %v", len(newManagedFields), newManagedFields)
}
if !reflect.DeepEqual(liveManagedFields[0], newManagedFields[0]) {
t.Fatalf("managedFields updated via subresource:\n\nlive managedFields: %v\nnew managedFields: %v\n\n", liveManagedFields, newManagedFields)
}
if newManagedFields[1].Manager != updateActor {
t.Fatalf(`Expected managerFields to have an entry with manager set to %q`, updateActor)
}
}
// TestClearManagedFieldsWithUpdateEmptyList verifies it's possible to clear the managedFields by sending an empty list.