@@ -32,12 +32,6 @@ var usedStatus = memberPhase(coderv1alpha1.CoderWorkspacePoolMemberPhaseReady,
3232 memberCondition (coderv1alpha1 .CoderWorkspacePoolMemberConditionReady , metav1 .ConditionTrue , "WorkspaceReady" ),
3333 memberCondition (coderv1alpha1 .CoderWorkspacePoolMemberConditionUsed , metav1 .ConditionTrue , "Used" ))
3434
35- // gone reports whether the member no longer exists.
36- func (e * poolMemberEnv ) gone (t * testing.T , key types.NamespacedName ) bool {
37- t .Helper ()
38- return e .reconcileMember (t , key , 0 ) == nil
39- }
40-
4135// TestPoolDeleteOrder: on a scale-down the pool deletes current members
4236// without a create request first, then members that are not ready (newest
4337// first), then ready members (oldest first).
@@ -46,6 +40,7 @@ func TestPoolDeleteOrder(t *testing.T) {
4640 e := newPoolMemberEnv (t )
4741 e .editPool (t , func (s * coderv1alpha1.CoderWorkspacePoolSpec ) { s .Replicas = new (int32 (5 )) })
4842 now := e .clock .Now ()
43+ pending := e .createMember (t , nil ) // The oldest, but it goes first.
4944 readyNew := e .createMember (t , nil )
5045 e .setMemberStatus (t , readyNew , withWorkspace (e .readyAt (now .Add (- time .Minute ))))
5146 readyOld := e .createMember (t , nil )
@@ -55,7 +50,6 @@ func TestPoolDeleteOrder(t *testing.T) {
5550 time .Sleep (1100 * time .Millisecond ) // creationTimestamp has second precision.
5651 buildingNew := e .createMember (t , nil )
5752 e .setMemberStatus (t , buildingNew , withWorkspace (nil ))
58- pending := e .createMember (t , nil )
5953 e .reconcilePool (t , 1 )
6054 require .Len (t , e .poolMembers (t , e .pool .Name ), 5 )
6155
@@ -190,3 +184,30 @@ func TestPoolConsentLossDeletesUnclaimed(t *testing.T) {
190184 })
191185 }
192186}
187+
188+ // TestPoolScaleDownAndLoweredCap: replicas and maxWorkspaces go down
189+ // together while members wait with finalizers. The pool deletes each member
190+ // once: the scale-down skips the members that the cap already deleted.
191+ func TestPoolScaleDownAndLoweredCap (t * testing.T ) {
192+ t .Parallel ()
193+ e := newPoolMemberEnv (t )
194+ e .editPool (t , func (s * coderv1alpha1.CoderWorkspacePoolSpec ) {
195+ s .Replicas , s .MaxInFlight = new (int32 (3 )), new (int32 (3 ))
196+ })
197+ e .reconcilePool (t , 2 )
198+ members := e .poolMembers (t , e .pool .Name )
199+ require .Len (t , members , 3 )
200+ for _ , m := range members {
201+ e .reconcileMember (t , memberKey (m ), 1 ) // The finalizer only.
202+ }
203+ e .setOwnerCap (t , 2 )
204+ e .editPool (t , func (s * coderv1alpha1.CoderWorkspacePoolSpec ) { s .Replicas = new (int32 (1 )) })
205+ e .reconcilePool (t , 1 ) // Fails on a 409 if one member is deleted twice.
206+ deleting := 0
207+ for _ , m := range e .poolMembers (t , e .pool .Name ) {
208+ if ! m .DeletionTimestamp .IsZero () {
209+ deleting ++
210+ }
211+ }
212+ require .Equal (t , 2 , deleting , "the cap deletes one, the scale-down one more" )
213+ }
0 commit comments