Skip to content

Commit d8d1759

Browse files
HyungJunekasonglee
andauthored
leader: check the node status for the leader-election (#24) (#25)
* leader: check the node status for the leader-election (#24) * leader: enhance the coverage Co-authored-by: kasong_lee <kasong_lee@tmax.co.kr>
1 parent 1a2bc9b commit d8d1759

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

leader/leader.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ func Become(ctx context.Context, lockName string, opts ...Option) error {
182182
if err != nil {
183183
log.Error(err, "Leader pod could not be deleted.")
184184
}
185+
case isNotReadyNode(ctx, config.Client, leaderPod.Spec.NodeName):
186+
log.Info("the status of the node where operator pod with leader lock was running has been 'notReady'")
187+
log.Info("Deleting the leader.")
188+
189+
//Mark the termainating status to the leaderPod and Delete the configmap lock
190+
if err := deleteLeader(ctx, config.Client, leaderPod, existing); err != nil {
191+
return err
192+
}
185193

186194
default:
187195
log.Info("Not the leader. Waiting.")
@@ -270,3 +278,44 @@ func getPod(ctx context.Context, client crclient.Client, ns string) (*corev1.Pod
270278

271279
return pod, nil
272280
}
281+
282+
func getNode(ctx context.Context, client crclient.Client, nodeName string, node *corev1.Node) error {
283+
key := crclient.ObjectKey{Namespace: "", Name: nodeName}
284+
err := client.Get(ctx, key, node)
285+
if err != nil {
286+
log.Error(err, "Failed to get Node", "Node.Name", nodeName)
287+
return err
288+
}
289+
return nil
290+
}
291+
292+
func isNotReadyNode(ctx context.Context, client crclient.Client, nodeName string) bool {
293+
leaderNode := &corev1.Node{}
294+
if err := getNode(ctx, client, nodeName, leaderNode); err != nil {
295+
return false
296+
}
297+
for _, condition := range leaderNode.Status.Conditions {
298+
if condition.Type == corev1.NodeReady && condition.Status != corev1.ConditionTrue {
299+
return true
300+
}
301+
}
302+
return false
303+
304+
}
305+
306+
func deleteLeader(ctx context.Context, client crclient.Client, leaderPod *corev1.Pod, existing *corev1.ConfigMap) error {
307+
err := client.Delete(ctx, leaderPod)
308+
if err != nil {
309+
log.Error(err, "Leader pod could not be deleted.")
310+
return err
311+
}
312+
err = client.Delete(ctx, existing)
313+
switch {
314+
case apierrors.IsNotFound(err):
315+
log.Info("ConfigMap has been deleted by prior operator.")
316+
return err
317+
case err != nil:
318+
return err
319+
}
320+
return nil
321+
}

leader/leader_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,143 @@ var _ = Describe("Leader election", func() {
209209
Expect(pod.TypeMeta.Kind).To(Equal("Pod"))
210210
})
211211
})
212+
213+
Describe("getNode", func() {
214+
var (
215+
client crclient.Client
216+
)
217+
BeforeEach(func() {
218+
client = fake.NewFakeClient(
219+
&corev1.Node{
220+
ObjectMeta: metav1.ObjectMeta{
221+
Name: "mynode",
222+
},
223+
},
224+
)
225+
})
226+
It("should return an error if no node is found", func() {
227+
node := corev1.Node{}
228+
err := getNode(context.TODO(), client, "", &node)
229+
Expect(err).ShouldNot(BeNil())
230+
})
231+
It("should return the node with the given name", func() {
232+
node := corev1.Node{}
233+
err := getNode(context.TODO(), client, "mynode", &node)
234+
Expect(err).Should(BeNil())
235+
Expect(node.TypeMeta.APIVersion).To(Equal("v1"))
236+
Expect(node.TypeMeta.Kind).To(Equal("Node"))
237+
})
238+
})
239+
240+
Describe("isNotReadyNode", func() {
241+
var (
242+
nodeName string
243+
node *corev1.Node
244+
client crclient.Client
245+
)
246+
BeforeEach(func() {
247+
nodeName = "mynode"
248+
node = &corev1.Node{
249+
ObjectMeta: metav1.ObjectMeta{
250+
Name: nodeName,
251+
},
252+
Status: corev1.NodeStatus{
253+
Conditions: make([]corev1.NodeCondition, 1),
254+
},
255+
}
256+
})
257+
258+
It("should return false if node is invalid", func() {
259+
client = fake.NewFakeClient()
260+
ret := isNotReadyNode(context.TODO(), client, "")
261+
Expect(ret).To(Equal(false))
262+
})
263+
It("should return false if no NodeCondition is found", func() {
264+
client = fake.NewFakeClient(node)
265+
ret := isNotReadyNode(context.TODO(), client, nodeName)
266+
Expect(ret).To(Equal(false))
267+
})
268+
It("should return false if type is incorrect", func() {
269+
node.Status.Conditions[0].Type = corev1.NodeMemoryPressure
270+
node.Status.Conditions[0].Status = corev1.ConditionFalse
271+
client = fake.NewFakeClient(node)
272+
ret := isNotReadyNode(context.TODO(), client, nodeName)
273+
Expect(ret).To(Equal(false))
274+
})
275+
It("should return false if NodeReady's type is true", func() {
276+
node.Status.Conditions[0].Type = corev1.NodeReady
277+
node.Status.Conditions[0].Status = corev1.ConditionTrue
278+
client = fake.NewFakeClient(node)
279+
ret := isNotReadyNode(context.TODO(), client, nodeName)
280+
Expect(ret).To(Equal(false))
281+
})
282+
It("should return true when Type is set and Status is set to false", func() {
283+
node.Status.Conditions[0].Type = corev1.NodeReady
284+
node.Status.Conditions[0].Status = corev1.ConditionFalse
285+
client = fake.NewFakeClient(node)
286+
ret := isNotReadyNode(context.TODO(), client, nodeName)
287+
Expect(ret).To(Equal(true))
288+
})
289+
})
290+
Describe("deleteLeader", func() {
291+
var (
292+
configmap *corev1.ConfigMap
293+
pod *corev1.Pod
294+
client crclient.Client
295+
)
296+
BeforeEach(func() {
297+
pod = &corev1.Pod{
298+
ObjectMeta: metav1.ObjectMeta{
299+
Name: "leader-test",
300+
Namespace: "testns",
301+
OwnerReferences: []metav1.OwnerReference{
302+
{
303+
APIVersion: "v1",
304+
Kind: "Pod",
305+
Name: "leader-test",
306+
},
307+
},
308+
},
309+
}
310+
configmap = &corev1.ConfigMap{
311+
ObjectMeta: metav1.ObjectMeta{
312+
Name: "leader-test",
313+
Namespace: "testns",
314+
OwnerReferences: []metav1.OwnerReference{
315+
{
316+
APIVersion: "v1",
317+
Kind: "Pod",
318+
Name: "leader-test",
319+
},
320+
},
321+
},
322+
}
323+
})
324+
It("should return an error if existing is not found", func() {
325+
client = fake.NewFakeClient(pod)
326+
err := deleteLeader(context.TODO(), client, pod, configmap)
327+
Expect(err).ShouldNot(BeNil())
328+
})
329+
It("should return an error if pod is not found", func() {
330+
client = fake.NewFakeClient(configmap)
331+
err := deleteLeader(context.TODO(), client, pod, configmap)
332+
Expect(err).ShouldNot(BeNil())
333+
})
334+
It("should return an error if pod is nil", func() {
335+
client = fake.NewFakeClient(pod, configmap)
336+
err := deleteLeader(context.TODO(), client, nil, configmap)
337+
Expect(err).ShouldNot(BeNil())
338+
})
339+
It("should return an error if configmap is nil", func() {
340+
client = fake.NewFakeClient(pod, configmap)
341+
err := deleteLeader(context.TODO(), client, pod, nil)
342+
Expect(err).ShouldNot(BeNil())
343+
})
344+
It("should return nil if pod and configmap exists and configmap's owner is the pod", func() {
345+
client = fake.NewFakeClient(pod, configmap)
346+
err := deleteLeader(context.TODO(), client, pod, configmap)
347+
Expect(err).Should(BeNil())
348+
})
349+
350+
})
212351
})

0 commit comments

Comments
 (0)