Kubernetes Java API (Model) Deep Copy

11/16/2021

I am trying to deep copy items from the official Kubernetes Java V1 API models but there aren't any copy constructors, Cloneable, nor Serializable interface implementations in any of the model objects. Does anyone have any ideas on how one may go about achieving this?

<br>

Edit:

Given the confusion of @RoddyoftheFrozenPeas I would like to clarify the situation for those who haven't used the API.

The V1 Objects have other deeply nested V1 Objects. Take for instance the V1Pod has a V1PodSpec. Merely copying the objects fields at the top level will result in references to the nested objects. Modification of any of the nested objects will be observed in the other objects which retain references to said object.

Edit 2:

A simple self-contained test with results after execution. Perhaps I am doing something incorrectly?

V1Container v1Container = new V1ContainerBuilder()
    .withName("original container")
    .addToArgs("original argument 1")
    .build();

V1Pod original = new V1PodBuilder()
    .withNewMetadata()
        .withName("Original Pod")
    .endMetadata()
    .withNewSpec()
        .withContainers(v1Container)
    .endSpec()
    .build();

V1Pod copy = new V1Pod();
copy.setMetadata(original.getMetadata());
copy.getMetadata().setName("copy of pod");
copy.setSpec(original.getSpec());
copy.getSpec().getContainers().get(0).setName("copy container");

Original:

class V1Pod {
    apiVersion: null
    kind: null
    metadata: class V1ObjectMeta {
        annotations: null
        clusterName: null
        creationTimestamp: null
        deletionGracePeriodSeconds: null
        deletionTimestamp: null
        finalizers: null
        generateName: null
        generation: null
        labels: null
        managedFields: null
        name: copy of pod
        namespace: null
        ownerReferences: null
        resourceVersion: null
        selfLink: null
        uid: null
    }
    spec: class V1PodSpec {
        activeDeadlineSeconds: null
        affinity: null
        automountServiceAccountToken: null
        containers: [class V1Container {
            args: [original argument 1]
            command: null
            env: null
            envFrom: null
            image: null
            imagePullPolicy: null
            lifecycle: null
            livenessProbe: null
            name: copy container
            ports: null
            readinessProbe: null
            resources: null
            securityContext: null
            startupProbe: null
            stdin: null
            stdinOnce: null
            terminationMessagePath: null
            terminationMessagePolicy: null
            tty: null
            volumeDevices: null
            volumeMounts: null
            workingDir: null
        }]
        //...
    }
    status: null
}

Copy:

class V1Pod {
    apiVersion: null
    kind: null
    metadata: class V1ObjectMeta {
        annotations: null
        clusterName: null
        creationTimestamp: null
        deletionGracePeriodSeconds: null
        deletionTimestamp: null
        finalizers: null
        generateName: null
        generation: null
        labels: null
        managedFields: null
        name: copy of pod
        namespace: null
        ownerReferences: null
        resourceVersion: null
        selfLink: null
        uid: null
    }
    spec: class V1PodSpec {
        activeDeadlineSeconds: null
        affinity: null
        automountServiceAccountToken: null
        containers: [class V1Container {
            args: [original argument 1]
            command: null
            env: null
            envFrom: null
            image: null
            imagePullPolicy: null
            lifecycle: null
            livenessProbe: null
            name: copy container
            ports: null
            readinessProbe: null
            resources: null
            securityContext: null
            startupProbe: null
            stdin: null
            stdinOnce: null
            terminationMessagePath: null
            terminationMessagePolicy: null
            tty: null
            volumeDevices: null
            volumeMounts: null
            workingDir: null
        }]
        /...
    }
    status: null
}
-- Interrupt
java
kubernetes

1 Answer

11/17/2021

The easiest way to copy K8s Java API resources is to utilize the withNew[field name]Like methods available in the V1 object builder pattern. Here is a simple self-contained example that demonstrates usage for the case above.

Example:

V1Container v1Container = new V1ContainerBuilder()
    .withName("original container")
    .addToArgs("original argument 1")
    .build();

V1Pod original = new V1PodBuilder()
    .withNewMetadata()
        .withName("Original Pod")
    .endMetadata()
    .withNewSpec()
        .withContainers(v1Container)
    .endSpec()
    .build();

V1Pod copy = new V1PodBuilder()
    .withNewMetadataLike(original.getMetadata())
    .endMetadata()
    .withNewSpecLike(original.getSpec())
    .endSpec()
    .build();

copy.getMetadata().setName("copy of pod");
copy.getSpec().getContainers().get(0).setName("copy container");

Original:

class V1Pod {
    apiVersion: null
    kind: null
    metadata: class V1ObjectMeta {
        annotations: null
        clusterName: null
        creationTimestamp: null
        deletionGracePeriodSeconds: null
        deletionTimestamp: null
        finalizers: null
        generateName: null
        generation: null
        labels: null
        managedFields: null
        name: Original Pod
        namespace: null
        ownerReferences: null
        resourceVersion: null
        selfLink: null
        uid: null
    }
    spec: class V1PodSpec {
        activeDeadlineSeconds: null
        affinity: null
        automountServiceAccountToken: null
        containers: [class V1Container {
            args: [original argument 1]
            command: null
            env: null
            envFrom: null
            image: null
            imagePullPolicy: null
            lifecycle: null
            livenessProbe: null
            name: original container
            ports: null
            readinessProbe: null
            resources: null
            securityContext: null
            startupProbe: null
            stdin: null
            stdinOnce: null
            terminationMessagePath: null
            terminationMessagePolicy: null
            tty: null
            volumeDevices: null
            volumeMounts: null
            workingDir: null
        }]
        # ...
    }
    status: null
}

Copy:

class V1Pod {
    apiVersion: null
    kind: null
    metadata: class V1ObjectMeta {
        annotations: null
        clusterName: null
        creationTimestamp: null
        deletionGracePeriodSeconds: null
        deletionTimestamp: null
        finalizers: null
        generateName: null
        generation: null
        labels: null
        managedFields: null
        name: copy of pod
        namespace: null
        ownerReferences: null
        resourceVersion: null
        selfLink: null
        uid: null
    }
    spec: class V1PodSpec {
        activeDeadlineSeconds: null
        affinity: null
        automountServiceAccountToken: null
        containers: [class V1Container {
            args: [original argument 1]
            command: null
            env: null
            envFrom: null
            image: null
            imagePullPolicy: null
            lifecycle: null
            livenessProbe: null
            name: copy container
            ports: null
            readinessProbe: null
            resources: null
            securityContext: null
            startupProbe: null
            stdin: null
            stdinOnce: null
            terminationMessagePath: null
            terminationMessagePolicy: null
            tty: null
            volumeDevices: null
            volumeMounts: null
            workingDir: null
        }]
        # ...
    }
    status: null
}
-- Interrupt
Source: StackOverflow