What happened?
ResourceOperations.jsonMergePatch(...) and ResourceOperations.jsonMergePatchPrimary(...) are documented as applying RFC 7386 JSON Merge Patch, but they delegate to Fabric8's no-context .patch() method.
Fabric8's default .patch() intentionally fetches the current resource and computes an RFC 6902 JSON Patch diff. When the supplied resource is a partial metadata patch, the generated diff removes fields that are omitted from the partial object, including spec.
The status variants are not affected because Fabric8's patchStatus() explicitly selects PatchType.JSON_MERGE.
Versions verified
- Java Operator SDK 5.3.5 with Fabric8 7.7.0
- Java Operator SDK 5.5.0 with Fabric8 7.8.0
The behavior is still present in the current main branch.
Minimal reproduction
Assume this custom resource already exists:
apiVersion: example.io/v1
kind: Example
metadata:
name: example
spec:
value: keep-me
Apply a partial metadata patch through ResourceOperations:
Example patch = new Example();
patch.getMetadata().setName("example");
patch.getMetadata().setLabels(Map.of("resolved", "true"));
context.resourceOperations().jsonMergePatchPrimary(patch);
Fabric8 computes a JSON Patch equivalent to:
[
{"op":"add","path":"/metadata/labels/resolved","value":"true"},
{"op":"remove","path":"/spec"},
{"op":"remove","path":"/status"}
]
For a CRD whose root spec is optional, the API server accepts the patch and permanently removes the spec. We encountered this in a real controller where subsequent reconciliation failed because getSpec() returned null.
Expected behavior
Methods named and documented as JSON Merge Patch should send application/merge-patch+json and preserve fields omitted from the partial resource.
Relevant implementation
JOSDK 5.5.0 delegates to .patch() without specifying a patch type:
Fabric8's default .patch() selects JSON Patch and diffs the current and supplied resources:
Suggested fix
Make the non-status JSON Merge Patch operations pass an explicit patch context, for example:
r -> context.getClient()
.resource(r)
.patch(PatchContext.of(PatchType.JSON_MERGE), r)
The cache and own-event filtering behavior can remain unchanged. Regression coverage should create a complete resource, apply a partial metadata merge patch, and assert that spec survives.
What happened?
ResourceOperations.jsonMergePatch(...)andResourceOperations.jsonMergePatchPrimary(...)are documented as applying RFC 7386 JSON Merge Patch, but they delegate to Fabric8's no-context.patch()method.Fabric8's default
.patch()intentionally fetches the current resource and computes an RFC 6902 JSON Patch diff. When the supplied resource is a partial metadata patch, the generated diff removes fields that are omitted from the partial object, includingspec.The status variants are not affected because Fabric8's
patchStatus()explicitly selectsPatchType.JSON_MERGE.Versions verified
The behavior is still present in the current
mainbranch.Minimal reproduction
Assume this custom resource already exists:
Apply a partial metadata patch through
ResourceOperations:Fabric8 computes a JSON Patch equivalent to:
[ {"op":"add","path":"/metadata/labels/resolved","value":"true"}, {"op":"remove","path":"/spec"}, {"op":"remove","path":"/status"} ]For a CRD whose root
specis optional, the API server accepts the patch and permanently removes the spec. We encountered this in a real controller where subsequent reconciliation failed becausegetSpec()returned null.Expected behavior
Methods named and documented as JSON Merge Patch should send
application/merge-patch+jsonand preserve fields omitted from the partial resource.Relevant implementation
JOSDK 5.5.0 delegates to
.patch()without specifying a patch type:ResourceOperations.jsonMergePatchPrimaryFabric8's default
.patch()selects JSON Patch and diffs the current and supplied resources:HasMetadataOperation.patch()OperationSupport.handlePatch(...)Suggested fix
Make the non-status JSON Merge Patch operations pass an explicit patch context, for example:
The cache and own-event filtering behavior can remain unchanged. Regression coverage should create a complete resource, apply a partial metadata merge patch, and assert that
specsurvives.