diff --git a/pkg/registry/core/service/storage/rest.go b/pkg/registry/core/service/storage/rest.go index b0a54a21951..fe337d247f7 100644 --- a/pkg/registry/core/service/storage/rest.go +++ b/pkg/registry/core/service/storage/rest.go @@ -337,6 +337,18 @@ func (rs *REST) healthCheckNodePortUpdate(oldService, service *api.Service, node func (rs *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { oldObj, err := rs.services.Get(ctx, name, &metav1.GetOptions{}) if err != nil { + // Support create on update, if forced to. + if forceAllowCreate { + obj, err := objInfo.UpdatedObject(ctx, nil) + if err != nil { + return nil, false, err + } + createdObj, err := rs.Create(ctx, obj, createValidation, &metav1.CreateOptions{DryRun: options.DryRun}) + if err != nil { + return nil, false, err + } + return createdObj, true, nil + } return nil, false, err } oldService := oldObj.(*api.Service) diff --git a/test/integration/apiserver/apply/apply_test.go b/test/integration/apiserver/apply/apply_test.go index 1b48dad72f4..81e937dc678 100644 --- a/test/integration/apiserver/apply/apply_test.go +++ b/test/integration/apiserver/apply/apply_test.go @@ -61,32 +61,62 @@ func TestApplyAlsoCreates(t *testing.T) { _, client, closeFn := setup(t) defer closeFn() - _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType). - Namespace("default"). - Resource("pods"). - Name("test-pod"). - Body([]byte(`{ - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "name": "test-pod" - }, - "spec": { - "containers": [{ - "name": "test-container", - "image": "test-image" - }] - } - }`)). - Do(). - Get() - if err != nil { - t.Fatalf("Failed to create object using Apply patch: %v", err) + testCases := []struct { + resource string + name string + body string + }{ + { + resource: "pods", + name: "test-pod", + body: `{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "test-pod" + }, + "spec": { + "containers": [{ + "name": "test-container", + "image": "test-image" + }] + } + }`, + }, { + resource: "services", + name: "test-svc", + body: `{ + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "name": "test-svc" + }, + "spec": { + "ports": [{ + "port": 8080, + "protocol": "UDP" + }] + } + }`, + }, } - _, err = client.CoreV1().RESTClient().Get().Namespace("default").Resource("pods").Name("test-pod").Do().Get() - if err != nil { - t.Fatalf("Failed to retrieve object: %v", err) + for _, tc := range testCases { + _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType). + Namespace("default"). + Resource(tc.resource). + Name(tc.name). + Body([]byte(tc.body)). + Do(). + Get() + if err != nil { + t.Fatalf("Failed to create object using Apply patch: %v", err) + } + + _, err = client.CoreV1().RESTClient().Get().Namespace("default").Resource(tc.resource).Name(tc.name).Do().Get() + if err != nil { + t.Fatalf("Failed to retrieve object: %v", err) + } } }