@@ -1072,3 +1072,129 @@ func TestReleaseLeaseConfigMaps(t *testing.T) {
1072
1072
func TestReleaseLeaseLeases (t * testing.T ) {
1073
1073
testReleaseLease (t , "leases" )
1074
1074
}
1075
+
1076
+ func TestReleaseOnCancellation_Endpoints (t * testing.T ) {
1077
+ testReleaseOnCancellation (t , "endpoints" )
1078
+ }
1079
+
1080
+ func TestReleaseOnCancellation_ConfigMaps (t * testing.T ) {
1081
+ testReleaseOnCancellation (t , "configmaps" )
1082
+ }
1083
+
1084
+ func TestReleaseOnCancellation_Leases (t * testing.T ) {
1085
+ testReleaseOnCancellation (t , "leases" )
1086
+ }
1087
+
1088
+ func testReleaseOnCancellation (t * testing.T , objectType string ) {
1089
+ var (
1090
+ onNewLeader = make (chan struct {})
1091
+ onRenewCalled = make (chan struct {})
1092
+ onRenewResume = make (chan struct {})
1093
+ onRelease = make (chan struct {})
1094
+
1095
+ lockObj runtime.Object
1096
+ updates int
1097
+ )
1098
+
1099
+ resourceLockConfig := rl.ResourceLockConfig {
1100
+ Identity : "baz" ,
1101
+ EventRecorder : & record.FakeRecorder {},
1102
+ }
1103
+ c := & fake.Clientset {}
1104
+
1105
+ c .AddReactor ("get" , objectType , func (action fakeclient.Action ) (handled bool , ret runtime.Object , err error ) {
1106
+ if lockObj != nil {
1107
+ return true , lockObj , nil
1108
+ }
1109
+ return true , nil , errors .NewNotFound (action .(fakeclient.GetAction ).GetResource ().GroupResource (), action .(fakeclient.GetAction ).GetName ())
1110
+ })
1111
+
1112
+ // create lock
1113
+ c .AddReactor ("create" , objectType , func (action fakeclient.Action ) (handled bool , ret runtime.Object , err error ) {
1114
+ lockObj = action .(fakeclient.CreateAction ).GetObject ()
1115
+ return true , lockObj , nil
1116
+ })
1117
+
1118
+ c .AddReactor ("update" , objectType , func (action fakeclient.Action ) (handled bool , ret runtime.Object , err error ) {
1119
+ updates ++
1120
+
1121
+ // Second update (first renew) should return our canceled error
1122
+ // FakeClient doesn't do anything with the context so we're doing this ourselves
1123
+ if updates == 2 {
1124
+ close (onRenewCalled )
1125
+ <- onRenewResume
1126
+ return true , nil , context .Canceled
1127
+ } else if updates == 3 {
1128
+ close (onRelease )
1129
+ }
1130
+
1131
+ lockObj = action .(fakeclient.UpdateAction ).GetObject ()
1132
+ return true , lockObj , nil
1133
+
1134
+ })
1135
+
1136
+ c .AddReactor ("*" , "*" , func (action fakeclient.Action ) (bool , runtime.Object , error ) {
1137
+ t .Errorf ("unreachable action. testclient called too many times: %+v" , action )
1138
+ return true , nil , fmt .Errorf ("unreachable action" )
1139
+ })
1140
+
1141
+ lock , err := rl .New (objectType , "foo" , "bar" , c .CoreV1 (), c .CoordinationV1 (), resourceLockConfig )
1142
+ if err != nil {
1143
+ t .Fatal ("resourcelock.New() = " , err )
1144
+ }
1145
+
1146
+ lec := LeaderElectionConfig {
1147
+ Lock : lock ,
1148
+ LeaseDuration : 15 * time .Second ,
1149
+ RenewDeadline : 2 * time .Second ,
1150
+ RetryPeriod : 1 * time .Second ,
1151
+
1152
+ // This is what we're testing
1153
+ ReleaseOnCancel : true ,
1154
+
1155
+ Callbacks : LeaderCallbacks {
1156
+ OnNewLeader : func (identity string ) {},
1157
+ OnStoppedLeading : func () {},
1158
+ OnStartedLeading : func (context.Context ) {
1159
+ close (onNewLeader )
1160
+ },
1161
+ },
1162
+ }
1163
+
1164
+ elector , err := NewLeaderElector (lec )
1165
+ if err != nil {
1166
+ t .Fatal ("Failed to create leader elector: " , err )
1167
+ }
1168
+
1169
+ ctx , cancel := context .WithCancel (context .Background ())
1170
+
1171
+ go elector .Run (ctx )
1172
+
1173
+ // Wait for us to become the leader
1174
+ select {
1175
+ case <- onNewLeader :
1176
+ case <- time .After (10 * time .Second ):
1177
+ t .Fatal ("failed to become the leader" )
1178
+ }
1179
+
1180
+ // Wait for renew (update) to be invoked
1181
+ select {
1182
+ case <- onRenewCalled :
1183
+ case <- time .After (10 * time .Second ):
1184
+ t .Fatal ("the elector failed to renew the lock" )
1185
+ }
1186
+
1187
+ // Cancel the context - stopping the elector while
1188
+ // it's running
1189
+ cancel ()
1190
+
1191
+ // Resume the update call to return the cancellation
1192
+ // which should trigger the release flow
1193
+ close (onRenewResume )
1194
+
1195
+ select {
1196
+ case <- onRelease :
1197
+ case <- time .After (10 * time .Second ):
1198
+ t .Fatal ("the lock was not released" )
1199
+ }
1200
+ }
0 commit comments