Home > database >  Fabric8 KubernetesClient sends volumeName as empty string for PersistentVolumeClaim causing Forbidde
Fabric8 KubernetesClient sends volumeName as empty string for PersistentVolumeClaim causing Forbidde

Time:08-15

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?

CodePudding user response:

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

CodePudding user response:

Kubernetes API doesn't allow you to change some fields for a certain resource type at a certain state. For a PVC, after its state is Bound, the volumeName field in its spec is immutable; you can't change the volume your PVC is referencing.
When you apply a bare PersistentVolumeClaim manifest like the one in your question (a manifest that has no volumeName key or the value of its volumeName is nil), If the cluster has a default StorageClass, it automatically creates a corresponding PersistentVolume object that's the abstraction of your volume in Kubernetes from the underlying storage provider, then assigns its name to your PersistentVolumeClaim with the volumeName key.

After a PersistentVolumeClaim gets bound to a PersistentVolume and its volumeName field gets populated, you can not edit or patch its volumeName field anymore; that's the reason your kubectl apply command fails for the second time.
It's now clear that a Replace verb is not gonna work for a PVC in Bound state. You can Create one and edit or Patch 'some' of its fields (like increasing the size). I'm not familiar with Java SDK, but it should include some APIs for creating, getting, patching, and deleting a PVC. You can first check for the existence of your PVC using the GetNamespacedPersistentVolumeClaim API; then, if it's not in the cluster, create it using CreateNamespacedPersistentVolumeClaim API. Referencing the axiom: "Explicit is better than Implicit."

  • Related