Skip to content

Commit

Permalink
Add a check for fwup version
Browse files Browse the repository at this point in the history
Why:

* We need to verify that the device fwup supports patching

This change addresses the need by:

* Grabbing fwup from channel params or controller conn depending on nerves_hub_link package
* Attach the fwup version to device.firmware_metadata
* Pass fwup version to `get_firmware_url` function
* Only make request for patch url if fwup version is >=1.6.0

Note:

* For this to work properly nerves_hub_link and nerves_hub_link_http will need to be updated to pass fwup version
* See nerves-hub/nerves_hub_link#43 for more details
  • Loading branch information
Mrjaco12 authored and mobileoverlord committed Sep 17, 2020
1 parent 9ffb86f commit 06c1172
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 25 deletions.
6 changes: 4 additions & 2 deletions apps/nerves_hub_web_core/lib/nerves_hub_web_core/devices.ex
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,15 @@ defmodule NervesHubWebCore.Devices do

with true <- matches_deployment?(device, deployment),
%Device{product: product} <- Repo.preload(device, :product),
fwup_version <- Map.get(device.firmware_metadata, :fwup_version),
%{firmware: target} <- Repo.preload(deployment, :firmware) do
source =
case Firmwares.get_firmware_by_product_and_uuid(product, uuid) do
{:ok, source} -> source
{:error, :not_found} -> nil
end

{:ok, url} = Firmwares.get_firmware_url(source, target)
{:ok, url} = Firmwares.get_firmware_url(source, target, fwup_version)
{:ok, meta} = Firmwares.metadata_from_firmware(target)

Phoenix.PubSub.broadcast(
Expand Down Expand Up @@ -401,14 +402,15 @@ defmodule NervesHubWebCore.Devices do
) do
with {:ok, %{healthy: true}} <- verify_update_eligibility(device, deployment),
%Device{product: product} <- Repo.preload(device, :product),
fwup_version <- Map.get(device.firmware_metadata, :fwup_version),
%{firmware: target} <- Repo.preload(deployment, :firmware) do
source =
case Firmwares.get_firmware_by_product_and_uuid(product, uuid) do
{:ok, source} -> source
{:error, :not_found} -> nil
end

{:ok, url} = Firmwares.get_firmware_url(source, target)
{:ok, url} = Firmwares.get_firmware_url(source, target, fwup_version)
{:ok, meta} = Firmwares.metadata_from_firmware(target)

%{update_available: true, firmware_url: url, firmware_meta: meta}
Expand Down
37 changes: 27 additions & 10 deletions apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ defmodule NervesHubWebCore.Firmwares do

require Logger

@min_fwup_patchable_version ">=1.6.0"

@type upload_file_2 :: (filepath :: String.t(), filename :: String.t() -> :ok | {:error, any()})

@uploader Application.fetch_env!(:nerves_hub_web_core, :firmware_upload)
Expand Down Expand Up @@ -221,6 +223,7 @@ defmodule NervesHubWebCore.Firmwares do
version: get_metadata_req_header(conn, "version"),
author: get_metadata_req_header(conn, "author"),
description: get_metadata_req_header(conn, "description"),
fwup_version: get_metadata_req_header(conn, "fwup_version"),
vcs_identifier: get_metadata_req_header(conn, "vcs-identifier"),
misc: get_metadata_req_header(conn, "misc")
}
Expand Down Expand Up @@ -282,6 +285,7 @@ defmodule NervesHubWebCore.Firmwares do
version: Map.get(metadata, "nerves_fw_version"),
author: Map.get(metadata, "nerves_fw_author"),
description: Map.get(metadata, "nerves_fw_description"),
fwup_version: Map.get(metadata, "nerves_fw_fwup_version"),
vcs_identifier: Map.get(metadata, "nerves_fw_vcs_identifier"),
misc: Map.get(metadata, "nerves_fw_misc")
}
Expand Down Expand Up @@ -338,21 +342,19 @@ defmodule NervesHubWebCore.Firmwares do
end
end

@spec get_firmware_url(Firmware.t(), Firmware.t()) ::
@spec get_firmware_url(Firmware.t(), Firmware.t(), String.t()) ::
{:ok, String.t()}
| {:error, :failure}

def get_firmware_url(%Firmware{patchable: true} = source, %Firmware{patchable: true} = target) do
{:ok, patch} =
case get_patch_by_source_and_target(source, target) do
{:error, :not_found} -> create_patch(source, target)
patch -> patch
end

@uploader.download_file(patch)
def get_firmware_url(source, target, fwup_version) when is_binary(fwup_version) do
if Version.match?(fwup_version, @min_fwup_patchable_version) do
do_get_firmware_url(source, target)
else
@uploader.download_file(target)
end
end

def get_firmware_url(_, target), do: @uploader.download_file(target)
def get_firmware_url(_source, target, _fwup_version), do: @uploader.download_file(target)

@spec create_patch(Firmware.t(), Firmware.t()) ::
{:ok, FirmwarePatch.t()}
Expand Down Expand Up @@ -396,6 +398,21 @@ defmodule NervesHubWebCore.Firmwares do
|> Repo.insert()
end

defp do_get_firmware_url(
%Firmware{patchable: true} = source,
%Firmware{patchable: true} = target
) do
{:ok, patch} =
case get_patch_by_source_and_target(source, target) do
{:error, :not_found} -> create_patch(source, target)
patch -> patch
end

@uploader.download_file(patch)
end

defp do_get_firmware_url(_, target), do: @uploader.download_file(target)

defp insert_firmware(params) do
%Firmware{}
|> Firmware.create_changeset(params)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ defmodule NervesHubWebCore.Firmwares.FirmwareMetadata do
@optional_params [
:author,
:description,
:fwup_version,
:vcs_identifier,
:misc,
:patchable
:misc
]

@derive Jason.Encoder
Expand All @@ -27,10 +27,10 @@ defmodule NervesHubWebCore.Firmwares.FirmwareMetadata do
field(:product)
field(:version)
field(:architecture)
field(:patchable)
field(:platform)
field(:author)
field(:description)
field(:fwup_version)
field(:vcs_identifier)
field(:misc)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule NervesHubWebCore.FirmwaresTest do
alias Ecto.Changeset

@uploader Application.get_env(:nerves_hub_web_core, :firmware_upload)
@valid_fwup_version "1.6.0"

setup context do
Mox.verify_on_exit!(context)
Expand Down Expand Up @@ -355,36 +356,52 @@ defmodule NervesHubWebCore.FirmwaresTest do
end
end

describe "get_firmware_url/2" do
describe "get_firmware_url/3" do
test "returns target download_file when there is no source", %{firmware: target} do
url = "http://somefilestore.com/firmware.fw"
Mox.expect(UploadMock, :download_file, fn ^target -> {:ok, url} end)

assert {:ok, url} = Firmwares.get_firmware_url(nil, target)
assert {:ok, url} = Firmwares.get_firmware_url(nil, target, @valid_fwup_version)
end

test "returns target download_file when source does not support patching", %{
firmware: source,
org_key: org_key,
product: product
} do
target = Fixtures.firmware_fixture(org_key, product, %{patchable: true})
Mox.stub(NervesHubWebCore.PatcherMock, :patchable?, fn _ -> true end)
target = Fixtures.firmware_fixture(org_key, product)
url = "http://somefilestore.com/firmware.fw"
Mox.expect(UploadMock, :download_file, fn ^target -> {:ok, url} end)

assert {:ok, url} = Firmwares.get_firmware_url(source, target)
assert {:ok, url} = Firmwares.get_firmware_url(source, target, @valid_fwup_version)
end

test "returns target download_file when target does not support patching", %{
firmware: target,
org_key: org_key,
product: product
} do
source = Fixtures.firmware_fixture(org_key, product, %{patchable: true})
Mox.stub(NervesHubWebCore.PatcherMock, :patchable?, fn _ -> true end)
source = Fixtures.firmware_fixture(org_key, product)
url = "http://somefilestore.com/firmware.fw"
Mox.expect(UploadMock, :download_file, fn ^target -> {:ok, url} end)

assert {:ok, url} = Firmwares.get_firmware_url(source, target, @valid_fwup_version)
end

test "returns target download_file when fwup version is too old", %{
firmware: source,
org_key: org_key,
product: product
} do
source = %Firmware{source | patchable: true}
Mox.stub(NervesHubWebCore.PatcherMock, :patchable?, fn _ -> true end)
target = Fixtures.firmware_fixture(org_key, product)
url = "http://somefilestore.com/firmware.fw"
Mox.expect(UploadMock, :download_file, fn ^target -> {:ok, url} end)

assert {:ok, url} = Firmwares.get_firmware_url(source, target)
assert {:ok, url} = Firmwares.get_firmware_url(source, target, "1.5.999")
end

test "returns patch download_file when one exists", %{
Expand All @@ -393,13 +410,14 @@ defmodule NervesHubWebCore.FirmwaresTest do
product: product
} do
source = %Firmware{source | patchable: true}
target = Fixtures.firmware_fixture(org_key, product, %{patchable: true})
Mox.stub(NervesHubWebCore.PatcherMock, :patchable?, fn _ -> true end)
target = Fixtures.firmware_fixture(org_key, product)
patch = Fixtures.firmware_patch_fixture(source, target)
patch_id = patch.id
url = "http://somefilestore.com/firmware.fw"
Mox.expect(UploadMock, :download_file, fn %{id: ^patch_id} -> {:ok, url} end)

assert {:ok, url} = Firmwares.get_firmware_url(source, target)
assert {:ok, url} = Firmwares.get_firmware_url(source, target, @valid_fwup_version)
end

test "returns download_file for a new patch", %{
Expand All @@ -408,15 +426,16 @@ defmodule NervesHubWebCore.FirmwaresTest do
product: product
} do
source = %Firmware{source | patchable: true}
target = Fixtures.firmware_fixture(org_key, product, %{patchable: true})
Mox.stub(NervesHubWebCore.PatcherMock, :patchable?, fn _ -> true end)
target = Fixtures.firmware_fixture(org_key, product)
url = "http://somefilestore.com/firmware.fw"
patch_path = "/path/to/firmware.fw"
Mox.expect(UploadMock, :download_file, 3, fn _ -> {:ok, url} end)
Mox.expect(PatcherMock, :create_patch_file, fn ^url, ^url -> patch_path end)
Mox.expect(UploadMock, :upload_file, fn ^patch_path, _ -> :ok end)
Mox.expect(PatcherMock, :cleanup_patch_files, fn ^patch_path -> :ok end)

assert {:ok, url} = Firmwares.get_firmware_url(source, target)
assert {:ok, url} = Firmwares.get_firmware_url(source, target, @valid_fwup_version)
end
end

Expand Down

0 comments on commit 06c1172

Please sign in to comment.