Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring back deployment device matching when a device connects #1720

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions lib/nerves_hub/deployments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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 @@ -342,4 +343,95 @@ defmodule NervesHub.Deployments do
end

def verify_deployment_membership(device), do: device

@doc """
If the device is missing a deployment, find a matching deployment

Do nothing if a deployment is already set
"""
def set_deployment(%{deployment_id: nil} = device) do
case matching_deployments(device, [true]) do
[] ->
set_deployment_telemetry(:none_found, device)

%{device | deployment: nil}

[deployment] ->
set_deployment_telemetry(:one_found, device, deployment)

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

[deployment | _] ->
set_deployment_telemetry(:multiple_found, device, deployment)

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

def set_deployment(device) do
preload_with_firmware_and_archive(device)
end

defp set_deployment_telemetry(result, device, deployment \\ nil) do
metadata = %{device: device}

metadata =
if deployment do
Map.put(metadata, :deployment, deployment)
else
metadata
end

:telemetry.execute(
[:nerves_hub, :deployments, :set_deployment, result],
%{count: 1},
metadata
)
end

def preload_with_firmware_and_archive(device, force \\ false) do
Repo.preload(device, [deployment: [:archive, :firmware]], force: force)
end

@doc """
Find all potential deployments for a device

Based on the product, firmware platform, firmware architecture, and device tags
"""
def matching_deployments(device, active \\ [true, false])
def matching_deployments(%Device{firmware_metadata: nil}, _active), do: []

def matching_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

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
end
29 changes: 28 additions & 1 deletion lib/nerves_hub/logger.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ defmodule NervesHub.Logger do
[:nerves_hub, :devices, :connect],
[:nerves_hub, :devices, :disconnect],
[:nerves_hub, :devices, :duplicate_connection],
[:nerves_hub, :devices, :update, :automatic]
[:nerves_hub, :devices, :update, :automatic],
[:nerves_hub, :devices, :update, :successful],
[:nerves_hub, :deployments, :set_deployment, :none_found],
[:nerves_hub, :deployments, :set_deployment, :one_found],
[:nerves_hub, :deployments, :set_deployment, :multiple_found]
]

Enum.each(events, fn event ->
Expand Down Expand Up @@ -107,6 +111,29 @@ defmodule NervesHub.Logger do
)
end

def log_event([:nerves_hub, :deployments, :set_deployment, :none_found], _, metadata, _) do
Logger.info("No matching deployments",
event: "nerves_hub.deployments.set_deployment.none_found",
identifier: metadata[:device].identifier
)
end

def log_event([:nerves_hub, :deployments, :set_deployment, :one_found], _, metadata, _) do
Logger.info("Deployment match found",
event: "nerves_hub.deployments.set_deployment.one_found",
identifier: metadata[:device].identifier,
deployment_id: metadata[:deployment].id
)
end

def log_event([:nerves_hub, :deployments, :set_deployment, :multiple_found], _, metadata, _) do
Logger.info("More than one deployment match found, setting to the first",
event: "nerves_hub.deployments.set_deployment.multiple_found",
identifier: metadata[:device].identifier,
deployment_id: metadata[:deployment].id
)
end

# Helper functions

defp ignore_list() do
Expand Down
5 changes: 4 additions & 1 deletion lib/nerves_hub_web/channels/device_channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ defmodule NervesHubWeb.DeviceChannel do

@decorate with_span("Channels.DeviceChannel.handle_info:after_join")
def handle_info({:after_join, params}, %{assigns: %{device: device}} = socket) do
device = Deployments.verify_deployment_membership(device)
device =
device
|> Deployments.verify_deployment_membership()
|> Deployments.set_deployment()

maybe_send_public_keys(device, socket, params)

Expand Down
Loading