Skip to content

Commit

Permalink
Convert update_available payload to structured data
Browse files Browse the repository at this point in the history
  • Loading branch information
ConnorRigby committed Feb 10, 2021
1 parent 83d54cb commit db64d19
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 12 deletions.
6 changes: 5 additions & 1 deletion lib/nerves_hub_link_common/downloader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ defmodule NervesHubLinkCommon.Downloader do
require Logger
use GenServer

alias NervesHubLinkCommon.{Downloader, Downloader.RetryConfig, Downloader.TimeoutCalculation}
alias NervesHubLinkCommon.{
Downloader,
Downloader.RetryConfig,
Downloader.TimeoutCalculation
}

defstruct uri: nil,
conn: nil,
Expand Down
5 changes: 4 additions & 1 deletion lib/nerves_hub_link_common/fwup_config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule NervesHubLinkCommon.FwupConfig do
Config structure responsible for handling callbacks from FWUP,
applying a fwupdate, and storing fwup task configuration
"""
alias NervesHubLinkCommon.UpdateAvailable

defstruct fwup_public_keys: [],
fwup_devpath: "/dev/mmcblk0",
handle_fwup_message: nil,
Expand All @@ -25,7 +27,8 @@ defmodule NervesHubLinkCommon.FwupConfig do
@typedoc """
Called when an update has been dispatched via `NervesHubLinkCommon.UpdateManager.apply_update/2`
"""
@type update_available_fun() :: (map() -> :ignore | {:reschedule, timeout()} | :apply)
@type update_available_fun() ::
(UpdateAvailable.t() -> :ignore | {:reschedule, timeout()} | :apply)

@type t :: %__MODULE__{
fwup_public_keys: [String.t()],
Expand Down
85 changes: 85 additions & 0 deletions lib/nerves_hub_link_common/update_available.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
defmodule NervesHubLinkCommon.UpdateAvailable do
defmodule FirmwareMetadata do
@moduledoc "Metadata about an update"
defstruct [
:architecture,
:author,
:description,
:fwup_version,
:id,
:misc,
:platform,
:product,
:uuid,
:vcs_identifier,
:version
]

@type t :: %__MODULE__{
architecture: String.t(),
author: String.t() | nil,
description: String.t() | nil,
fwup_version: Version.build() | nil,
id: Ecto.UUID.t(),
misc: String.t() | nil,
platform: String.t(),
product: String.t(),
uuid: Ecto.UUID.t(),
vcs_identifier: String.t() | nil,
version: Version.build()
}

@spec parse(map()) :: t()
def parse(params) do
%__MODULE__{
architecture: params["architecture"],
author: params["author"],
description: params["description"],
fwup_version: params["fwup_version"],
id: params["id"],
misc: params["misc"],
platform: params["platform"],
product: params["product"],
uuid: params["uuid"],
vcs_identifier: params["vcs_identifier"],
version: params["version"]
}
end
end

defstruct update_available: false,
firmware_url: nil,
firmware_meta: nil

@typedoc """
Payload that gets dispatched down to devices upon an update
`firmware_url` and `firmware_meta` are only available
when `update_available` is true.
"""
@type t() :: %__MODULE__{
update_available: boolean(),
firmware_url: String.t() | nil,
firmware_meta: FirmwareMetadata.t() | nil
}

@doc "Validates the payload from NervesHub"
@spec parse(map()) :: t()
def parse(params) do
firmware_meta =
if params["update_available"] do
FirmwareMetadata.parse(params["firmware_meta"])
end

firmware_url =
if params["firmware_url"] do
URI.parse(params["firmware_url"])
end

%__MODULE__{
update_available: params["update_available"],
firmware_url: firmware_url,
firmware_meta: firmware_meta
}
end
end
28 changes: 18 additions & 10 deletions lib/nerves_hub_link_common/update_manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ defmodule NervesHubLinkCommon.UpdateManager do

require Logger
use GenServer
alias NervesHubLinkCommon.{FwupConfig, Downloader, Downloader.RetryConfig}

alias NervesHubLinkCommon.{
Downloader,
Downloader.RetryConfig,
FwupConfig,
UpdateAvailable
}

defmodule State do
@moduledoc """
Expand Down Expand Up @@ -63,8 +69,9 @@ defmodule NervesHubLinkCommon.UpdateManager do
NervesHub. the map must contain a `"firmware_url"` key.
"""
@spec apply_update(GenServer.server(), map()) :: State.status()
def apply_update(manager \\ __MODULE__, %{"firmware_url" => _} = update) do
GenServer.call(manager, {:apply_update, update})
def apply_update(manager \\ __MODULE__, %{"firmware_url" => _} = params) do
update_available = UpdateAvailable.parse(params)
GenServer.call(manager, {:apply_update, update_available})
end

@doc """
Expand Down Expand Up @@ -102,7 +109,7 @@ defmodule NervesHubLinkCommon.UpdateManager do
end

@impl GenServer
def handle_call({:apply_update, update}, _from, %State{} = state) do
def handle_call({:apply_update, %UpdateAvailable{} = update}, _from, %State{} = state) do
state = maybe_update_firmware(update, state)
{:reply, state.status, state}
end
Expand Down Expand Up @@ -155,7 +162,7 @@ defmodule NervesHubLinkCommon.UpdateManager do
{:noreply, state}
end

@spec maybe_update_firmware(map(), State.t()) ::
@spec maybe_update_firmware(UpdateAvailable.t(), State.t()) ::
State.download_started() | State.download_rescheduled() | State.t()
defp maybe_update_firmware(_data, %State{status: {:updating, _percent}} = state) do
# Received an update message from NervesHub, but we're already in progress.
Expand All @@ -167,7 +174,7 @@ defmodule NervesHubLinkCommon.UpdateManager do
state
end

defp maybe_update_firmware(%{"firmware_url" => _url} = data, %State{} = state) do
defp maybe_update_firmware(%UpdateAvailable{} = data, %State{} = state) do
# Cancel an existing timer if it exists.
# This prevents rescheduled updates`
# from compounding.
Expand Down Expand Up @@ -200,14 +207,15 @@ defmodule NervesHubLinkCommon.UpdateManager do
%{state | update_reschedule_timer: nil}
end

@spec start_fwup_stream(map(), State.t()) :: State.download_started()
defp start_fwup_stream(%{"firmware_url" => url}, state) do
@spec start_fwup_stream(UpdateAvailable.t(), State.t()) :: State.download_started()
defp start_fwup_stream(%UpdateAvailable{} = update, state) do
pid = self()
fun = &send(pid, {:download, &1})

with {:ok, fwup} <- Fwup.stream(pid, fwup_args(state.fwup_config)),
{:ok, download} <- Downloader.start_download(url, fun, state.retry_config) do
Logger.info("[NervesHubLink] Downloading firmware: #{url}")
{:ok, download} <-
Downloader.start_download(update.firmware_url, fun, state.retry_config) do
Logger.info("[NervesHubLink] Downloading firmware: #{update.firmware_url}")
%State{state | status: {:updating, 0}, download: download, fwup: fwup}
end
end
Expand Down

0 comments on commit db64d19

Please sign in to comment.