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."