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