Change:
Presumably this would occur when changing the default from one class to another. E.g. Class X exists and is marked default Class Y is created Class Y is marked as default Uh oh!! 2 defaults Class X is marked as not default At the case in point, when there are 2 defaults, we currently do not assign default values to new PVCs. We should change this semantic - while there are two, pick one randomly or heuristically. It's still correct. Example: Suppose that you have one user changing the default and another user creating a PVC at exactly the same time. What happens? Depending on network, CPU scheduling, memory allocations, (etc - totally non-deterministic) the API server might see either of those operations first. The PVC creator will either win the race and get the old default class or it will lose the race and get the new default class. The difference could literally be whose network has a faster path to the API server. My point is that whichever one you pick, as long as it is one of those two defaults, it is a correct choice! Any other FooClass things we do should probably change, too. I think that's IngressClass and RuntimeClass.
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions 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
/assign
GitHub didn't allow me to assign the following users: Octopusjust. Note that only , repo collaborators and people who have commented on this issue/PR can be assigned. Additionally, 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
picking either sounds reasonable but what if we allow only one default to be set and disallow/warn users if they try to create another default. I can think of issues with this too, mainly when switching over to a new default by removing an existing class A and then setting B as a default but managing that orphaned period while the transition happens, maybe we can have the heuristic there? I'm sure I'm missing something here.
/assign
GitHub didn't allow me to assign the following users: Octopusjust. Note that only , repo collaborators and people who have commented on this issue/PR can be assigned. Additionally, 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
There is a problem when there is no default storage class for a while and another user creates a PVC. Such PVC won't get the new SC. We're trying to fix it in Until then, having two default storage classes for a brief moment is IMO slightly better, because at least users get an error message and can retry PVC creation. In addition, (in-tree) admission plugin in HA environment could be racy, as different API servers are admitting updates of different StorageClasses in parallel.
/assign
I wouldn't make 2 an error - just pick one.
Ok, I'd take a shot at this. Thanks for clearing things up.
+1 Still, having two default classes for a longer time is IMO bad, there should be a metric + alert if there are 2 or more default SCs for X minutes.
it's not ideal - a metric seems OK
hello there, im new to open source. i don't know how to contribute. can anyone hep me please?
I see this issue was labeled as "good first issue" and I would like to start working on it!
I see there is a pr related to this issue , are you still working on this?
Is this issue still open?
/assign
Yes, there's already a . It's linked to the issue itself.
The PR is specifically for StorageClass, but we also need to audit other FooClass things - they should probably change, too. I think that's IngressClass and RuntimeClass. Someone can open new PRs for these.
Oh, I see the second PR, too. Will look at them ASAP
/remove-sig network
Do we emit a response header when someone makes a request that ends up setting two IngressClasses as default?
that seems a nice thing to do, I didn't know you can add warnings on admissions plugins but seems implemented it here
Is this issue still open??
StorageClass implementation is in I am not sure about those. I'd suggest opening separate issues. /close
Closing this issue. <detailsIn response to : whichever one you pick, as long as it is one of those two defaults, it is a correct choice! </details

Discussion:
{ storageClasses: []*storagev1.StorageClass{ makeDefaultStorageClass(classGold, &modeImmediate), <del> makeDefaultStorageClass(classSilver, &modeImmediate)}, </del> <ins> makeStorageClass(classSilver, &modeImmediate), }, </ins> tests: []controllerTest{ { <del> name:            "15-2 - pvc storage class is not assigned retroactively if there are multiple default storage classes", </del> <ins> name:            "15-3 - pvc storage class is not assigned retroactively if claim is already bound", </ins> initialVolumes:  novolumes, expectedVolumes: novolumes, <del> initialClaims:   newClaimArray("claim15-2", "uid15-2", "1Gi", "", v1.ClaimPending, nil), expectedClaims:  newClaimArray("claim15-2", "uid15-2", "1Gi", "", v1.ClaimPending, nil), </del> <ins> initialClaims:   newClaimArray("claim15-3", "uid15-3", "1Gi", "test", v1.ClaimBound, &classCopper, volume.AnnBoundByController, volume.AnnBindCompleted), expectedClaims:  newClaimArray("claim15-3", "uid15-3", "1Gi", "test", v1.ClaimLost, &classCopper, volume.AnnBoundByController, volume.AnnBindCompleted), </ins> expectedEvents:  noevents, errors:          noerrors, test:            testSyncClaim,

}, tests: []controllerTest{ { <del> name:            "15-3 - pvc storage class is not assigned retroactively if claim is already bound", </del> <ins> name:            "15-4 - pvc storage class is not assigned retroactively if claim is already bound but annotations are missing", </ins> initialVolumes:  novolumes, expectedVolumes: novolumes, <del> initialClaims:   newClaimArray("claim15-3", "uid15-3", "1Gi", "test", v1.ClaimBound, &classCopper, volume.AnnBoundByController, volume.AnnBindCompleted), expectedClaims:  newClaimArray("claim15-3", "uid15-3", "1Gi", "test", v1.ClaimLost, &classCopper, volume.AnnBoundByController, volume.AnnBindCompleted), </del> <ins> initialClaims:   newClaimArray("claim15-4", "uid15-4", "1Gi", "test", v1.ClaimBound, &classCopper), expectedClaims:  newClaimArray("claim15-4", "uid15-4", "1Gi", "test", v1.ClaimPending, &classCopper), </ins> expectedEvents:  noevents, errors:          noerrors, test:            testSyncClaim,

}, tests: []controllerTest{ { <del> name:            "15-4 - pvc storage class is not assigned retroactively if claim is already bound but annotations are missing", </del> <ins> name:            "15-5 - pvc storage class is assigned retroactively if there is a default", </ins> initialVolumes:  novolumes, expectedVolumes: novolumes, <del> initialClaims:   newClaimArray("claim15-4", "uid15-4", "1Gi", "test", v1.ClaimBound, &classCopper), expectedClaims:  newClaimArray("claim15-4", "uid15-4", "1Gi", "test", v1.ClaimPending, &classCopper), </del> <ins> initialClaims:   newClaimArray("claim15-5", "uid15-5", "1Gi", "", v1.ClaimPending, nil), expectedClaims:  newClaimArray("claim15-5", "uid15-5", "1Gi", "", v1.ClaimPending, &classGold), </ins> expectedEvents:  noevents, errors:          noerrors, test:            testSyncClaim,

{ storageClasses: []*storagev1.StorageClass{ makeDefaultStorageClass(classGold, &modeImmediate), <del> makeStorageClass(classSilver, &modeImmediate), }, </del> <ins> makeDefaultStorageClass(classSilver, &modeImmediate)}, </ins> tests: []controllerTest{ { <del> name:            "15-5 - pvc storage class is assigned retroactively if there is a default", </del> <ins> name:            "15-2 - pvc storage class is assigned retroactively if there are multiple default storage classes", </ins> initialVolumes:  novolumes, expectedVolumes: novolumes, <del> initialClaims:   newClaimArray("claim15-5", "uid15-5", "1Gi", "", v1.ClaimPending, nil), expectedClaims:  newClaimArray("claim15-5", "uid15-5", "1Gi", "", v1.ClaimPending, &classGold), </del> <ins> initialClaims:   newClaimArray("claim15-2", "uid15-2", "1Gi", "", v1.ClaimPending, nil), expectedClaims:  newClaimArray("claim15-2", "uid15-2", "1Gi", "", v1.ClaimPending, &classGold), </ins> expectedEvents:  noevents, errors:          noerrors, test:            testSyncClaim,

package util import ( <del> "fmt" </del> <ins> "sort" </ins> storagev1 "k8s.io/api/storage/v1" <del> "k8s.io/apimachinery/pkg/api/errors" </del> metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" storagev1listers "k8s.io/client-go/listers/storage/v1"

if len(defaultClasses) == 0 { return nil, nil } <ins> // Primary sort by creation timestamp, newest first // Secondary sort by class name, ascending order sort.Slice(defaultClasses, func(i, j int) bool { if defaultClasses[i].CreationTimestamp.UnixNano() == defaultClasses[j].CreationTimestamp.UnixNano() { return defaultClasses[i].Name < defaultClasses[j].Name } return defaultClasses[i].CreationTimestamp.UnixNano() > defaultClasses[j].CreationTimestamp.UnixNano() }) </ins> if len(defaultClasses) > 1 { <del> klog.V(4).Infof("GetDefaultClass %d defaults found", len(defaultClasses)) return nil, errors.NewInternalError(fmt.Errorf("%d default StorageClasses were found", len(defaultClasses))) </del> <ins> klog.V(4).Infof("%d default StorageClasses were found, choosing the newest: %s", len(defaultClasses), defaultClasses[0].Name) </ins> } return defaultClasses[0], nil }

import ( "context" "testing" <ins> "time" </ins> "k8s.io/klog/v2"

}, Provisioner: "nondefault1", } <ins> classWithCreateTime1 := &storagev1.StorageClass{ TypeMeta: metav1.TypeMeta{ Kind: "StorageClass", }, ObjectMeta: metav1.ObjectMeta{ Name:              "default1", CreationTimestamp: metav1.NewTime(time.Date(2022, time.Month(1), 1, 0, 0, 0, 1, time.UTC)), Annotations: map[string]string{ storageutil.IsDefaultStorageClassAnnotation: "true", }, }, } classWithCreateTime2 := &storagev1.StorageClass{ TypeMeta: metav1.TypeMeta{ Kind: "StorageClass", }, ObjectMeta: metav1.ObjectMeta{ Name:              "default2", CreationTimestamp: metav1.NewTime(time.Date(2022, time.Month(1), 1, 0, 0, 0, 0, time.UTC)), Annotations: map[string]string{ storageutil.IsDefaultStorageClassAnnotation: "true", }, }, } </ins> claimWithClass := &api.PersistentVolumeClaim{ TypeMeta: metav1.TypeMeta{

"foo", }, { <del> "two defaults, error with PVC with class=nil", []*storagev1.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithNoClass, true, "", }, { </del> "two defaults, no modification of PVC with class=''", []*storagev1.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithEmptyClass,

false, "foo", }, <ins> { "two defaults with same creation time, choose the one with smaller name", []*storagev1.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithNoClass, false, defaultClass1.Name, }, { "two defaults, choose the one with newer creation time", []*storagev1.StorageClass{classWithCreateTime1, classWithCreateTime2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithNoClass, false, classWithCreateTime1.Name, }, </ins> } for _, test := range tests {