Change:
What happened: When creating a StatefulSet that uses volumeClaimTemplates, the resulting PersistentVolumeClaims have incorrect labels. Instead of using the labels defined in their metadata, they use the labels from the StatefulSet's selector. What you expected to happen: I expect the created PersistentVolumeClaims to have the labels defined in their metadata. How to reproduce it (as minimally and precisely as possible): Resulting PersistentVolumeClaim: Environment: Kubernetes version (use ):Cloud provider or hardware configuration: GKE Install tools: Others: . /sig apps
+1 just ran into this myself.
/assign
GitHub didn't allow me to assign the following users: cwdsuzhou. Note that only and repo collaborators can be assigned and that issues/PRs can only have 10 assignees at the same time. For more information please see <detailsIn response to : /assign Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/assign
As in . Even thought the StatefulSet controller looks for the PVC by name (not by label) we generate labels for the PVC based on the StatefulSet's selector. Removing this would be a backward incompatible change to a v1 API contract (can't do that). Adding additional labels might be acceptable, but, If you want to change the behavior of the controller wrt to this, I'd like to see a (small) KEP for it so it can be discussed a bit more broadly in the SIG.
I very much think this would be a good discussion to have. If you could, please let me know what's required to start a KEP on this topic. As I see it, the current implementation is in violation of the API: the volumeClaimTemplates data type is listed as " array". A PersistentVolumeClaim's metadata is listed as a standard type. The fact that the PersistentVolumeClaim's metadata is being handled differently when defined in a StatefulSet means that the PersistentVolumeClaim API isn't being properly followed in that context. I'd also like to argue that the change wouldn't be backwards incompatible. The StatefulSet's volumeClaimTemplates description even says that the matching is done on name: "Every claim in this list must have at least one matching (by name) volumeMount in one container in the template." Respecting the PersistentVolumeClaim's metadata wouldn't necessarily cause any incompatibility from a technical perspective (although it may affect some users' implementations that rely on it). In summary, the current implementation violates the principle of least surprise, makes it impossible to label individual volume claims separately, and it directly contradicts the API specification.
The KEP process is described . You can open a KEP via a PR against the SIG Apps repository. You can use me as your approver. As to the compatibility of the current PR, the current label generation is v1 and has worked the same way for several years now. Users that select the PVCs created by the controller have a reasonable expectation that this should continue to function in subsequent releases. If you add additional labels but generate labels that match the selector this would allow for additional labels while maintaining compatibility (imo).

Discussion:
claim := templates[i] claim.Name = getPersistentVolumeClaimName(set, &claim, ordinal) claim.Namespace = set.Namespace <del> claim.Labels = set.Spec.Selector.MatchLabels </del> <ins> if claim.Labels != nil { for key, value := range set.Spec.Selector.MatchLabels { claim.Labels[key] = value } } else { claim.Labels = set.Spec.Selector.MatchLabels } </ins> claims[templates[i].Name] = claim } return claims

import ( "fmt" "math/rand" <ins> "reflect" </ins> "sort" "strconv" "testing"

} } <ins> func TestGetPersistentVolumeClaims(t *testing.T) { // nil inherits statefulset labels pod := newPod() statefulSet := newStatefulSet(1) statefulSet.Spec.Selector.MatchLabels = nil claims := getPersistentVolumeClaims(statefulSet, pod) pvc := newPVC("datadir-foo-0") pvc.SetNamespace(v1.NamespaceDefault) resultClaims := map[string]v1.PersistentVolumeClaim{"datadir": pvc} if !reflect.DeepEqual(claims, resultClaims) { t.Fatalf("Unexpected pvc:n %+vn, desired pvc:n %+v", claims, resultClaims) } // nil inherits statefulset labels statefulSet.Spec.Selector.MatchLabels = map[string]string{"test": "test"} claims = getPersistentVolumeClaims(statefulSet, pod) pvc.SetLabels(map[string]string{"test": "test"}) resultClaims = map[string]v1.PersistentVolumeClaim{"datadir": pvc} if !reflect.DeepEqual(claims, resultClaims) { t.Fatalf("Unexpected pvc:n %+vn, desired pvc:n %+v", claims, resultClaims) } // non-nil with non-overlapping labels merge pvc and statefulset labels statefulSet.Spec.Selector.MatchLabels = map[string]string{"name": "foo"} statefulSet.Spec.VolumeClaimTemplates[0].ObjectMeta.Labels = map[string]string{"test": "test"} claims = getPersistentVolumeClaims(statefulSet, pod) pvc.SetLabels(map[string]string{"test": "test", "name": "foo"}) resultClaims = map[string]v1.PersistentVolumeClaim{"datadir": pvc} if !reflect.DeepEqual(claims, resultClaims) { t.Fatalf("Unexpected pvc:n %+vn, desired pvc:n %+v", claims, resultClaims) } // non-nil with overlapping labels merge pvc and statefulset labels and prefer statefulset labels statefulSet.Spec.Selector.MatchLabels = map[string]string{"test": "foo"} statefulSet.Spec.VolumeClaimTemplates[0].ObjectMeta.Labels = map[string]string{"test": "test"} claims = getPersistentVolumeClaims(statefulSet, pod) pvc.SetLabels(map[string]string{"test": "foo"}) resultClaims = map[string]v1.PersistentVolumeClaim{"datadir": pvc} if !reflect.DeepEqual(claims, resultClaims) { t.Fatalf("Unexpected pvc:n %+vn, desired pvc:n %+v", claims, resultClaims) } } func newPod() *v1.Pod { return &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name:      "foo-0", Namespace: v1.NamespaceDefault, }, Spec: v1.PodSpec{ Containers: []v1.Container{ { Name:  "nginx", Image: "nginx", }, }, }, } } </ins> func newPVC(name string) v1.PersistentVolumeClaim { return v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{