Skip to content

Commit

Permalink
Clear inflight updates for unhealthy devices (#1620)
Browse files Browse the repository at this point in the history
  • Loading branch information
nshoes authored Oct 29, 2024
1 parent e7aeaf9 commit 2789336
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 7 deletions.
4 changes: 2 additions & 2 deletions lib/nerves_hub/deployments/orchestrator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ defmodule NervesHub.Deployments.Orchestrator do
@moduledoc """
Orchestration process to handle passing out updates to devices
When a deployment is updated, the orchestraor will tell every
device local to its node that there is a new update. This will
When a deployment is updated, the orchestrator will tell every
device local to its node that there is a new update. This
hook will allow the orchestrator to start slowly handing out
updates instead of blasting every device at once.
"""
Expand Down
8 changes: 6 additions & 2 deletions lib/nerves_hub/devices.ex
Original file line number Diff line number Diff line change
Expand Up @@ -865,8 +865,8 @@ defmodule NervesHub.Devices do
end

@doc """
Devices that haven't been automatically blocked are not in the penalty window
Devices that have a time greater than now are in the penalty window
Devices that haven't been automatically blocked are not in the penalty window.
Devices that have a time greater than now are in the penalty window.
"""
def device_in_penalty_box?(device, now \\ DateTime.utc_now())

Expand All @@ -890,6 +890,8 @@ defmodule NervesHub.Devices do
{:error, :up_to_date, device}

updates_blocked?(device, now) ->
clear_inflight_update(device)

{:error, :updates_blocked, device}

failure_rate_met?(device, deployment) ->
Expand All @@ -904,6 +906,7 @@ defmodule NervesHub.Devices do
"""

AuditLogs.audit!(deployment, device, description)
clear_inflight_update(device)

{:ok, device} = update_device(device, %{updates_blocked_until: blocked_until})

Expand All @@ -921,6 +924,7 @@ defmodule NervesHub.Devices do
"""

AuditLogs.audit!(deployment, device, description)
clear_inflight_update(device)

{:ok, device} = update_device(device, %{updates_blocked_until: blocked_until})

Expand Down
14 changes: 11 additions & 3 deletions test/nerves_hub/devices_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -646,13 +646,14 @@ defmodule NervesHub.DevicesTest do
{:error, :up_to_date, _device} = Devices.verify_update_eligibility(device, deployment)
end

test "device is unhealthy and should be put in the penalty box based on total attemps",
test "device should be rejected for updates based on threshold rate and have it's inflight updates cleared",
state do
%{device: device, deployment: deployment} = state
deployment = Repo.preload(deployment, [:firmware])
deployment = %{deployment | device_failure_threshold: 6}

{:ok, device} = update_firmware_uuid(device, Ecto.UUID.generate())
{:ok, _inflight_update} = Devices.told_to_update(device, deployment)

now = DateTime.utc_now()

Expand All @@ -666,14 +667,16 @@ defmodule NervesHub.DevicesTest do
{:error, :updates_blocked, device} = Devices.verify_update_eligibility(device, deployment)

assert device.updates_blocked_until
assert Devices.count_inflight_updates_for(deployment) == 0
end

test "device is unhealthy and should be put in the penalty box based on attempt rate",
test "device should be rejected for updates based on attempt rate and have it's inflight updates cleared",
state do
%{device: device, deployment: deployment} = state
deployment = Repo.preload(deployment, [:firmware])

{:ok, device} = update_firmware_uuid(device, Ecto.UUID.generate())
{:ok, _inflight_update} = Devices.told_to_update(device, deployment)

now = DateTime.utc_now()

Expand All @@ -686,9 +689,11 @@ defmodule NervesHub.DevicesTest do
{:error, :updates_blocked, device} = Devices.verify_update_eligibility(device, deployment)

assert device.updates_blocked_until
assert Devices.count_inflight_updates_for(deployment) == 0
end

test "device is in the penalty box and should be rejected for updates", state do
test "device in penalty box should be rejected for updates and have it's inflight updates cleared",
state do
%{device: device, deployment: deployment} = state
deployment = Repo.preload(deployment, [:firmware])

Expand All @@ -698,10 +703,13 @@ defmodule NervesHub.DevicesTest do

# future time
device = %{device | updates_blocked_until: DateTime.add(now, 1, :second)}
{:ok, _inflight_update} = Devices.told_to_update(device, deployment)

{:error, :updates_blocked, _device} =
Devices.verify_update_eligibility(device, deployment, now)

assert Devices.count_inflight_updates_for(deployment) == 0

# now
device = %{device | updates_blocked_until: now}
{:ok, _device} = Devices.verify_update_eligibility(device, deployment, now)
Expand Down

0 comments on commit 2789336

Please sign in to comment.