javakubernetesyamlfabric8persistent-volume-claims

Fabric8 KubernetesClient sends volumeName as empty string for PersistentVolumeClaim causing Forbidden: spec is immutable after creation


I want to apply the following yaml multiple times with the fabric8 kubernetes-client

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: my-storage-class
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

I apply the yaml using createOrReplace()

Config config = new ConfigBuilder()
        .withMasterUrl("https://my-kubernetes-root:6443")
        .withNamespace("my-namespace")
        .withOauthToken(token)
        .withTrustCerts(true)
        .build();

KubernetesClient client = new DefaultKubernetesClient(config);
ClasspathResource resource = new ClasspathResource("my-pvc.yaml");

client.load(resource.getInputStream()).createOrReplace(); // this works
TimeUnit.MINUTES.sleep(1); // volumeName is dynamically assigned during this period
client.load(resource.getInputStream()).createOrReplace(); // this fails

This works the first time (when the PVC does not exist in the namespace) but fails the second time that createOrReplace() is called for the same yaml with the following error

io.fabric8.kubernetes.client.KubernetesClientException: Failure executing: PUT at: https://my-kubernetes-root:6443/api/v1/namespaces/my-namespace/persistentvolumeclaims/my-pvc. Message: PersistentVolumeClaim "my-pvc" is invalid: spec: Forbidden: spec is immutable after creation except resources.requests for bound claims
  core.PersistentVolumeClaimSpec{
    AccessModes:      []core.PersistentVolumeAccessMode{"ReadWriteMany"},
    Selector:         nil,
    Resources:        core.ResourceRequirements{Requests: core.ResourceList{s"storage": {i: resource.int64Amount{value: 1073741824}, s: "1Gi", Format: "BinarySI"}}},
-   VolumeName:       "",
+   VolumeName:       "pvc-b79ebfcb-d5cb-4450-9f17-d69ec10b8712",
    StorageClassName: &"my-storage-class",
    VolumeMode:       &"Filesystem",
    DataSource:       nil,
  }

Notice how "volumeName" is not present in the yaml (nil) but in the error message "volumeName" is changing from empty string to the dynamically assigned volumeName.

I can reproduce this exact same behavior using kubectl and empty string for volumeName

I can kubectl apply the following yaml as many times as I like

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: my-storage-class
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

But if I kubectl apply a yaml with volumeName of empty string it works the first time and fails the second time (The error message is the same as above)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: my-storage-class
  volumeName: ""
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

How can I get KubernetesClient to behave the same as kubectl apply? Is there any way that I can apply the same PersistentVolumeClaim yaml multiple times with KubernetesClient?


Solution

  • As a workaround, I have switched to using a StatefulSet to manage my Pod which allows me to specify volumeClaimTemplates

    eg the following yaml can be applied multiple times:

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      selector:
        matchLabels:
          app: nginx
      serviceName: "nginx"
      replicas: 1
      template:
        metadata:
          labels:
            app: nginx
        spec:
          terminationGracePeriodSeconds: 10
          containers:
          - name: nginx
            image: k8s.gcr.io/nginx-slim:0.8
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: "my-storage-class"
          resources:
            requests:
              storage: 1Gi