Skip to content

Commit

Permalink
Clear deployment and set reason if firmware doesn't match on connect,
Browse files Browse the repository at this point in the history
clean up websocket_test
  • Loading branch information
nshoes committed Nov 26, 2024
1 parent b68fd0f commit d34b3d2
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 538 deletions.
100 changes: 21 additions & 79 deletions lib/nerves_hub/deployments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ defmodule NervesHub.Deployments do
alias NervesHub.AuditLogs
alias NervesHub.Deployments.Deployment
alias NervesHub.Deployments.InflightDeploymentCheck
alias NervesHub.Devices
alias NervesHub.Devices.Device
alias NervesHub.Products.Product
alias NervesHub.Repo
Expand Down Expand Up @@ -276,38 +275,6 @@ defmodule NervesHub.Deployments do
Phoenix.PubSub.broadcast(NervesHub.PubSub, "deployment:#{id}", message)
end

@doc """
Find all potential deployments for a device
Based on the product, firmware platform, firmware architecture, and device tags
"""
def alternate_deployments(device, active \\ [true, false])
def alternate_deployments(%Device{firmware_metadata: nil}, _active), do: []

def alternate_deployments(device, active) do
Deployment
|> join(:inner, [d], assoc(d, :firmware), as: :firmware)
|> preload([_, firmware: f], firmware: f)
|> where([d], d.product_id == ^device.product_id)
|> where([d], d.is_active in ^active)
|> ignore_same_deployment(device)
|> where([d, firmware: f], f.platform == ^device.firmware_metadata.platform)
|> where([d, firmware: f], f.architecture == ^device.firmware_metadata.architecture)
|> where([d], fragment("?->'tags' <@ to_jsonb(?::text[])", d.conditions, ^device.tags))
|> Repo.all()
|> Enum.filter(&version_match?(device, &1))
|> Enum.sort_by(
&{&1.firmware.version, &1.id},
fn {a_vsn, a_id}, {b_vsn, b_id} ->
case Version.compare(a_vsn, b_vsn) do
:lt -> false
:eq -> a_id <= b_id
:gt -> true
end
end
)
end

@doc """
Find all potential devices for a deployment
Expand Down Expand Up @@ -336,61 +303,36 @@ defmodule NervesHub.Deployments do

def version_match?(_device, _deployment), do: true

defp ignore_same_deployment(query, %{deployment_id: nil}), do: query

defp ignore_same_deployment(query, %{deployment_id: deployment_id}) do
where(query, [d], d.id != ^deployment_id)
end

@doc """
If the device is missing a deployment, find a matching deployment
Do nothing if a deployment is already set
"""
@spec set_deployment(Device.t()) :: Device.t()
def set_deployment(device) do
case alternate_deployments(device, [true]) do
[] ->
Logger.debug("No matching deployments for #{device.identifier}")

device

[deployment] ->
device
|> Devices.update_deployment(deployment)
|> preload_with_firmware_and_archive(true)

[deployment | _] ->
Logger.debug(
"More than one deployment matches for #{device.identifier}, setting to the first"
)

device
|> Devices.update_deployment(deployment)
|> preload_with_firmware_and_archive(true)
end
end

@spec maybe_set_deployment(Device.t()) :: Device.t()
def maybe_set_deployment(%Device{deployment_id: nil} = device), do: set_deployment(device)
def maybe_set_deployment(device), do: device

@spec verify_deployment_membership(Device.t()) :: Device.t()
def verify_deployment_membership(%Device{deployment_id: deployment_id} = device)
when not is_nil(deployment_id) do
%{deployment: deployment} = device = Repo.preload(device, deployment: :firmware)
bad_architecture = device.firmware_metadata.architecture != deployment.firmware.architecture
bad_platform = device.firmware_metadata.platform != deployment.firmware.platform

reason =
cond do
bad_architecture and bad_platform ->
:bad_architecture_and_platform

if device.firmware_metadata.platform == deployment.firmware.platform and
device.firmware_metadata.architecture == deployment.firmware.architecture do
bad_architecture ->
:bad_architecture

bad_platform ->
:bad_platform

true ->
nil
end

if reason do
device
|> Ecto.Changeset.change(%{deployment_id: nil, deployment_conflict: reason})
|> Repo.update!()
else
Devices.clear_deployment(device)
device
end
end

def verify_deployment_membership(device), do: device

def preload_with_firmware_and_archive(device, force \\ false) do
Repo.preload(device, [deployment: [:archive, :firmware]], force: force)
end
end
26 changes: 1 addition & 25 deletions lib/nerves_hub/devices.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ defmodule NervesHub.Devices do
alias NervesHub.Accounts.User
alias NervesHub.AuditLogs
alias NervesHub.Certificate
alias NervesHub.Deployments
alias NervesHub.Deployments.Deployment
alias NervesHub.Deployments.Orchestrator
alias NervesHub.Devices.CACertificate
Expand Down Expand Up @@ -1035,7 +1034,7 @@ defmodule NervesHub.Devices do
|> Repo.transaction()
|> case do
{:ok, %{move: updated}} ->
Deployments.set_deployment(updated)
# Deployments.set_deployment(updated)
{:ok, updated}

err ->
Expand Down Expand Up @@ -1350,29 +1349,6 @@ defmodule NervesHub.Devices do
|> Repo.one()
end

@doc """
Get the firmware status for a device
"latest", "pending", or "updating"
"""
def firmware_status(device) do
device = Repo.preload(device, deployment: [:firmware])

cond do
is_nil(device.deployment_id) ->
"latest"

get_in(device.firmware_metadata.uuid) == get_in(device.deployment.firmware.uuid) ->
"latest"

!Enum.empty?(device.update_attempts) ->
"updating"

true ->
"pending"
end
end

def enable_extension_setting(%Device{} = device, extension_string) do
device = get_device(device.id)

Expand Down
4 changes: 4 additions & 0 deletions lib/nerves_hub/devices/device.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ defmodule NervesHub.Devices.Device do
field(:connecting_code, :string)
field(:connection_metadata, :map, default: %{})

field(:deployment_conflict, Ecto.Enum,
values: [:bad_architecture, :bad_platform, :bad_architecture_and_platform]
)

timestamps()

# Deprecated fields, replaced with device_connections table.
Expand Down
3 changes: 1 addition & 2 deletions lib/nerves_hub_web/channels/device_channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ defmodule NervesHubWeb.DeviceChannel do
device =
device
|> Deployments.verify_deployment_membership()
|> Deployments.maybe_set_deployment()

maybe_send_public_keys(device, socket, params)

Expand Down Expand Up @@ -457,7 +456,7 @@ defmodule NervesHubWeb.DeviceChannel do
end

defp maybe_send_archive(socket) do
device = socket.assigns.device
device = deployment_preload(socket.assigns.device)

updates_enabled = device.updates_enabled && !Devices.device_in_penalty_box?(device)
version_match = Version.match?(socket.assigns.device_api_version, ">= 2.0.0")
Expand Down
21 changes: 7 additions & 14 deletions lib/nerves_hub_web/live/deployments/edit.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,9 @@

<h3 class="mb-2">Conditions</h3>
<div class="form-group">
<div class="help-text tooltip-label help-tooltip">
<label for="tags_input" class="tooltip-label">
Tag(s) distributed to
</label>
<span class="tooltip-info mb-1"></span>
<span class="tooltip-text">Changing tags will reset all devices on this deployment</span>
</div>
<label for="tags_input" class="tooltip-label">
Tag(s) distributed to
</label>
<%= text_input(f, :tags,
class: "form-control",
id: "tags_input",
Expand All @@ -86,13 +82,10 @@
</div>

<div class="form-group">
<div class="help-text tooltip-label help-tooltip">
<label for="version_requirement" class="tooltip-label">
Version requirement
</label>
<span class="tooltip-info mb-1"></span>
<span class="tooltip-text">Changing the version requirement will reset all devices on this deployment</span>
</div>
<label for="version_requirement" class="tooltip-label">
Version requirement
</label>
<span class="tooltip-info mb-1"></span>
<%= text_input(f, :version,
class: "form-control",
id: "version_requirement",
Expand Down
2 changes: 0 additions & 2 deletions lib/nerves_hub_web/views/api/device_view.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule NervesHubWeb.API.DeviceView do
use NervesHubWeb, :api_view

alias NervesHub.Devices
alias NervesHub.Tracker

def render("index.json", %{devices: devices, pagination: pagination}) do
Expand Down Expand Up @@ -29,7 +28,6 @@ defmodule NervesHubWeb.API.DeviceView do
last_communication: connection_last_seen_at(device),
description: device.description,
firmware_metadata: device.firmware_metadata,
firmware_update_status: Devices.firmware_status(device),
deployment: render_one(device.deployment, __MODULE__, "deployment.json", as: :deployment),
updates_enabled: device.updates_enabled,
updates_blocked_until: device.updates_blocked_until,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule NervesHub.Repo.Migrations.AddDeploymentConflictToDevices do
use Ecto.Migration

def change do
alter table(:devices) do
add(:deployment_conflict, :text)
end
end
end
Loading

0 comments on commit d34b3d2

Please sign in to comment.