Skip to content

Commit 8fca248

Browse files
committed
Add support to detect for shutdown nodes
The CCM's node controller will call upon InstanceShutdownByProviderID() to determine whether or not the node has been powered off when it goes `NotReady`, and adds shutdown taints to the node to force pods to be evicted from the node.
1 parent 1428580 commit 8fca248

2 files changed

Lines changed: 77 additions & 4 deletions

File tree

cloud/linode/instances.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,5 +176,29 @@ func (i *instances) InstanceExistsByProviderID(ctx context.Context, providerID s
176176
}
177177

178178
func (i *instances) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) {
179-
return false, cloudprovider.NotImplemented
179+
ctx = sentry.SetHubOnContext(ctx)
180+
sentry.SetTag(ctx, "provider_id", providerID)
181+
182+
id, err := parseProviderID(providerID)
183+
if err != nil {
184+
sentry.CaptureError(ctx, err)
185+
return false, err
186+
}
187+
188+
sentry.SetTag(ctx, "linode_id", strconv.Itoa(id))
189+
190+
instance, err := linodeByID(ctx, i.client, id)
191+
if err != nil {
192+
sentry.CaptureError(ctx, err)
193+
return false, err
194+
}
195+
196+
// An instance is considered to be "shutdown" when it is
197+
// in the process of shutting down, or already offline.
198+
if instance.Status == linodego.InstanceOffline ||
199+
instance.Status == linodego.InstanceShuttingDown {
200+
return true, nil
201+
}
202+
203+
return false, nil
180204
}

cloud/linode/instances_test.go

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,56 @@ func TestAddSSHKeyToAllInstances(t *testing.T) {
314314
}
315315

316316
func TestInstanceShutdownByProviderID(t *testing.T) {
317-
instances := newInstances(nil)
318-
_, err := instances.InstanceShutdownByProviderID(context.TODO(), providerIDPrefix+"12345")
319-
assert.ErrorIs(t, err, cloudprovider.NotImplemented)
317+
ctx := context.TODO()
318+
ctrl := gomock.NewController(t)
319+
defer ctrl.Finish()
320+
321+
client := NewMockClient(ctrl)
322+
instances := newInstances(client)
323+
324+
t.Run("fails when instance not found", func(t *testing.T) {
325+
id := 12345
326+
providerID := providerIDPrefix + strconv.Itoa(id)
327+
client.EXPECT().GetInstance(gomock.Any(), id).Times(1).Return(nil, linodego.Error{Code: http.StatusNotFound})
328+
shutdown, err := instances.InstanceShutdownByProviderID(ctx, providerID)
329+
330+
assert.Error(t, err)
331+
assert.False(t, shutdown)
332+
})
333+
334+
t.Run("returns true when instance is shut down", func(t *testing.T) {
335+
id := 12345
336+
providerID := providerIDPrefix + strconv.Itoa(id)
337+
client.EXPECT().GetInstance(gomock.Any(), id).Times(1).Return(&linodego.Instance{
338+
ID: id, Label: "offline-linode", Status: linodego.InstanceOffline,
339+
}, nil)
340+
shutdown, err := instances.InstanceShutdownByProviderID(ctx, providerID)
341+
342+
assert.NoError(t, err)
343+
assert.True(t, shutdown)
344+
})
345+
346+
t.Run("returns true when instance is shutting down", func(t *testing.T) {
347+
id := 12345
348+
providerID := providerIDPrefix + strconv.Itoa(id)
349+
client.EXPECT().GetInstance(gomock.Any(), id).Times(1).Return(&linodego.Instance{
350+
ID: id, Label: "shutting-down-linode", Status: linodego.InstanceShuttingDown,
351+
}, nil)
352+
shutdown, err := instances.InstanceShutdownByProviderID(ctx, providerID)
353+
354+
assert.NoError(t, err)
355+
assert.True(t, shutdown)
356+
})
357+
358+
t.Run("returns false when instance is running", func(t *testing.T) {
359+
id := 12345
360+
providerID := providerIDPrefix + strconv.Itoa(id)
361+
client.EXPECT().GetInstance(gomock.Any(), id).Times(1).Return(&linodego.Instance{
362+
ID: id, Label: "running-linode", Status: linodego.InstanceRunning,
363+
}, nil)
364+
shutdown, err := instances.InstanceShutdownByProviderID(ctx, providerID)
365+
366+
assert.NoError(t, err)
367+
assert.False(t, shutdown)
368+
})
320369
}

0 commit comments

Comments
 (0)