From 0b7c4874808e68cacc541f702c968e87605d8209 Mon Sep 17 00:00:00 2001 From: Jacob Date: Fri, 28 Aug 2020 15:25:44 -0400 Subject: [PATCH] Add a check for fwup version 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 https://github.com/nerves-hub/nerves_hub_link/issues/43 for more details --- .../lib/nerves_hub_web_core/devices.ex | 6 ++- .../lib/nerves_hub_web_core/firmwares.ex | 37 +++++++++++++----- .../firmwares/firmware_metadata.ex | 6 +-- .../firmwares/firmwares_test.exs | 39 ++++++++++++++----- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/apps/nerves_hub_web_core/lib/nerves_hub_web_core/devices.ex b/apps/nerves_hub_web_core/lib/nerves_hub_web_core/devices.ex index 5dba6ab65..00a21a89c 100644 --- a/apps/nerves_hub_web_core/lib/nerves_hub_web_core/devices.ex +++ b/apps/nerves_hub_web_core/lib/nerves_hub_web_core/devices.ex @@ -135,6 +135,7 @@ 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 @@ -142,7 +143,7 @@ defmodule NervesHubWebCore.Devices do {: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( @@ -401,6 +402,7 @@ 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 @@ -408,7 +410,7 @@ defmodule NervesHubWebCore.Devices do {: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} diff --git a/apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares.ex b/apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares.ex index 004249ec0..79c5b822d 100644 --- a/apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares.ex +++ b/apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares.ex @@ -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) @@ -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") } @@ -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") } @@ -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()} @@ -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) diff --git a/apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares/firmware_metadata.ex b/apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares/firmware_metadata.ex index 0d007e233..357aaa116 100644 --- a/apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares/firmware_metadata.ex +++ b/apps/nerves_hub_web_core/lib/nerves_hub_web_core/firmwares/firmware_metadata.ex @@ -16,9 +16,9 @@ defmodule NervesHubWebCore.Firmwares.FirmwareMetadata do @optional_params [ :author, :description, + :fwup_version, :vcs_identifier, - :misc, - :patchable + :misc ] @derive Jason.Encoder @@ -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 diff --git a/apps/nerves_hub_web_core/test/nerves_hub_web_core/firmwares/firmwares_test.exs b/apps/nerves_hub_web_core/test/nerves_hub_web_core/firmwares/firmwares_test.exs index e1c8894c0..25c09c469 100644 --- a/apps/nerves_hub_web_core/test/nerves_hub_web_core/firmwares/firmwares_test.exs +++ b/apps/nerves_hub_web_core/test/nerves_hub_web_core/firmwares/firmwares_test.exs @@ -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) @@ -355,12 +356,12 @@ 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", %{ @@ -368,11 +369,12 @@ defmodule NervesHubWebCore.FirmwaresTest do 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", %{ @@ -380,11 +382,26 @@ defmodule NervesHubWebCore.FirmwaresTest do 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", %{ @@ -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", %{ @@ -408,7 +426,8 @@ 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) @@ -416,7 +435,7 @@ defmodule NervesHubWebCore.FirmwaresTest do 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