diff --git a/CHANGELOG.md b/CHANGELOG.md index d8666a5e089..f3a7f1213f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ #### Bugs #### Improvements +* Fix #3135 added mock crud support for patch status, and will return exceptions for unsupported patch types #### Dependency Upgrade diff --git a/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudDispatcher.java b/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudDispatcher.java index 6692a438f3e..2128ae01cd5 100644 --- a/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudDispatcher.java +++ b/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudDispatcher.java @@ -25,6 +25,7 @@ import io.fabric8.kubernetes.api.model.StatusCause; import io.fabric8.kubernetes.api.model.StatusCauseBuilder; import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext; +import io.fabric8.kubernetes.client.dsl.base.OperationSupport; import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.kubernetes.client.utils.Utils; import io.fabric8.mockwebserver.Context; @@ -34,6 +35,7 @@ import io.fabric8.mockwebserver.crud.ResponseComposer; import io.fabric8.zjsonpatch.JsonDiff; import io.fabric8.zjsonpatch.JsonPatch; +import okhttp3.MediaType; import okhttp3.mockwebserver.MockResponse; import java.io.IOException; @@ -98,7 +100,7 @@ public synchronized MockResponse dispatch(RecordedRequest request) { case PUT: return handleReplace(path, request.getBody().readUtf8()); case PATCH: - return handlePatch(path, request.getBody().readUtf8()); + return handlePatch(path, request.getBody().readUtf8(), request.getHeader("Content-Type")); case GET: return detectWatchMode(path)? handleWatch(path): handleGet(path); case DELETE: @@ -167,10 +169,10 @@ public MockResponse handleGet(String path) { * * @param path path of resource * @param s object + * @param contentType * @return The {@link MockResponse} */ - @Override - public MockResponse handlePatch(String path, String s) { + public MockResponse handlePatch(String path, String s, String contentType) { MockResponse response = new MockResponse(); String body = fetchResource(path); if (body == null) { @@ -179,9 +181,27 @@ public MockResponse handlePatch(String path, String s) { try { JsonNode patch = context.getMapper().readTree(s); JsonNode source = context.getMapper().readTree(body); - JsonNode status = removeStatus(source); + JsonNode status = null; + + if (!isStatusPath(path)) { + status = removeStatus(source); + } + + if (contentType != null) { + MediaType type = MediaType.parse(contentType); + if (!type.subtype().equals(OperationSupport.JSON_PATCH.subtype())) { + response.setResponseCode(HttpURLConnection.HTTP_UNSUPPORTED_TYPE); + return response; + } + } JsonNode updated = JsonPatch.apply(patch, source); - // restore the old status + + if (isStatusPath(path)) { + status = removeStatus(updated); + updated = context.getMapper().readTree(body); + } + + // restore the status if (status == null) { removeStatus(updated); } else { @@ -220,7 +240,7 @@ public MockResponse handlePatch(String path, String s) { response.setResponseCode(HttpURLConnection.HTTP_ACCEPTED); response.setBody(updatedAsString); } catch (JsonProcessingException e) { - throw new IllegalArgumentException(e); + response.setResponseCode(HTTP_UNPROCESSABLE_ENTITY); } } return response; diff --git a/kubernetes-server-mock/src/test/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudAttributesExtractorTest.java b/kubernetes-server-mock/src/test/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudAttributesExtractorTest.java index 08c586be34b..94ccb0692b4 100644 --- a/kubernetes-server-mock/src/test/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudAttributesExtractorTest.java +++ b/kubernetes-server-mock/src/test/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudAttributesExtractorTest.java @@ -337,6 +337,68 @@ public void statusHandling() { assertEquals(originalUid, result.getMetadata().getUid()); } + @Test + public void jsonPatchStatus() { + KubernetesServer kubernetesServer = new KubernetesServer(false, true); + kubernetesServer.before(); + KubernetesClient kubernetesClient = kubernetesServer.getClient(); + Pod pod = new PodBuilder().withNewMetadata() + .withName("name") + .withNamespace("test") // required until https://github.com/fabric8io/mockwebserver/pull/59 + .endMetadata() + .build(); + Pod result = kubernetesClient.pods().create(pod); + + // should be null after create + assertNull(result.getStatus()); + + pod.setStatus(new PodStatusBuilder().withHostIP("1.2.3.4").build()); + Map labels = new HashMap<>(); + labels.put("app", "core"); + + pod.getMetadata().setLabels(labels); + + result = kubernetesClient.pods() + .inNamespace(pod.getMetadata().getNamespace()) + .withName(pod.getMetadata().getName()) + .editStatus(p->pod); + + assertNotNull(result.getStatus().getHostIP()); + assertNull(result.getMetadata().getLabels()); + } + + /** + * merge patch is not supported yet + */ + @Test + public void patchStatus() { + KubernetesServer kubernetesServer = new KubernetesServer(false, true); + kubernetesServer.before(); + KubernetesClient kubernetesClient = kubernetesServer.getClient(); + Pod pod = new PodBuilder().withNewMetadata() + .withName("name") + .withNamespace("test") // required until https://github.com/fabric8io/mockwebserver/pull/59 + .endMetadata() + .build(); + Pod result = kubernetesClient.pods().create(pod); + + // should be null after create + assertNull(result.getStatus()); + + pod.setStatus(new PodStatusBuilder().withHostIP("1.2.3.4").build()); + Map labels = new HashMap<>(); + labels.put("app", "core"); + + pod.getMetadata().setLabels(labels); + + KubernetesClientException exception = assertThrows(KubernetesClientException.class, () -> kubernetesClient.pods() + .inNamespace(pod.getMetadata().getNamespace()) + .withName(pod.getMetadata().getName()) + .patchStatus(pod)); + Assertions.assertEquals(HttpURLConnection.HTTP_UNSUPPORTED_TYPE, exception.getCode()); + + } + @Test public void createConflict() { KubernetesServer kubernetesServer = new KubernetesServer(false, true);