Skip to content

Commit 506a4f5

Browse files
authored
Update the delta file name on upload (#2305)
After a recent update of mine the deltas we uploaded to s3 had the path of: `/firmware/[org id]/generated_delta_zip_file-[random]-[random]-[integer]` This PR changes it to `/firmware/[org id]/[source uuid]/[target uuid].delta.fw` I've also added the Oban Job id to the Logger metadata for easier log searching, and reduced the time diff in the delta generation timeout Oban job from 120,000 seconds (33 hours) to 960 seconds, (15 minutes).
1 parent e7652c4 commit 506a4f5

File tree

10 files changed

+136
-66
lines changed

10 files changed

+136
-66
lines changed

lib/nerves_hub/firmwares.ex

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -421,55 +421,58 @@ defmodule NervesHub.Firmwares do
421421
{:ok, source_url} = firmware_upload_config().download_file(source_firmware)
422422
{:ok, target_url} = firmware_upload_config().download_file(target_firmware)
423423

424-
case update_tool().create_firmware_delta_file(source_url, target_url) do
424+
case update_tool().create_firmware_delta_file(
425+
{source_firmware.uuid, source_url},
426+
{target_firmware.uuid, target_url}
427+
) do
425428
{:ok, created} ->
426-
firmware_delta_filename = Path.basename(created.filepath)
427-
428-
result =
429-
Repo.transaction(
430-
fn ->
431-
with upload_metadata <-
432-
firmware_upload_config().metadata(org.id, firmware_delta_filename),
433-
{:ok, firmware_delta} <-
434-
complete_firmware_delta(
435-
firmware_delta,
436-
created.tool,
437-
created.size,
438-
created.source_size,
439-
created.target_size,
440-
created.tool_metadata,
441-
upload_metadata
442-
),
443-
{:ok, firmware_delta} <- get_firmware_delta(firmware_delta.id),
444-
:ok <-
445-
firmware_upload_config().upload_file(created.filepath, upload_metadata),
446-
:ok <- update_tool().cleanup_firmware_delta_files(created.filepath) do
447-
Logger.info(
448-
"Created firmware delta successfully.",
429+
Repo.transact(
430+
fn ->
431+
with upload_metadata <-
432+
firmware_upload_config().delta_metadata(
433+
org.id,
434+
source_firmware.uuid,
435+
target_firmware.uuid
436+
),
437+
{:ok, firmware_delta} <-
438+
complete_firmware_delta(
439+
firmware_delta,
440+
created.tool,
441+
created.size,
442+
created.source_size,
443+
created.target_size,
444+
created.tool_metadata,
445+
upload_metadata
446+
),
447+
{:ok, firmware_delta} <- get_firmware_delta(firmware_delta.id),
448+
:ok <-
449+
firmware_upload_config().upload_file(created.filepath, upload_metadata),
450+
:ok <- update_tool().cleanup_firmware_delta_files(created.filepath) do
451+
Logger.info(
452+
"Created firmware delta successfully.",
453+
product_id: source_firmware.product_id,
454+
source_firmware: source_firmware.uuid,
455+
target_firmware: target_firmware.uuid
456+
)
457+
458+
{:ok, firmware_delta}
459+
else
460+
{:error, error} ->
461+
update_tool().cleanup_firmware_delta_files(created.filepath)
462+
463+
Logger.error(
464+
"Failed to create firmware delta: #{inspect(error)}",
449465
product_id: source_firmware.product_id,
450466
source_firmware: source_firmware.uuid,
451467
target_firmware: target_firmware.uuid
452468
)
453469

454-
firmware_delta
455-
else
456-
{:error, error} ->
457-
update_tool().cleanup_firmware_delta_files(created.filepath)
458-
459-
Logger.error(
460-
"Failed to create firmware delta: #{inspect(error)}",
461-
product_id: source_firmware.product_id,
462-
source_firmware: source_firmware.uuid,
463-
target_firmware: target_firmware.uuid
464-
)
465-
466-
Repo.rollback(error)
467-
end
468-
end,
469-
timeout: 30_000
470-
)
471-
472-
case result do
470+
{:error, error}
471+
end
472+
end,
473+
timeout: 30_000
474+
)
475+
|> case do
473476
{:ok, _delta} ->
474477
:ok
475478

lib/nerves_hub/firmwares/update_tool.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ defmodule NervesHub.Firmwares.UpdateTool do
8989
@doc """
9090
Called to create a firmware delta file on the local filesystem
9191
"""
92-
@callback create_firmware_delta_file(String.t(), String.t()) ::
92+
@callback create_firmware_delta_file(
93+
{source_id :: String.t(), source_url :: String.t()},
94+
{target_id :: String.t(), target_url :: String.t()}
95+
) ::
9396
{:ok, delta_created()} | {:error, term()}
9497

9598
@doc """

lib/nerves_hub/firmwares/update_tool/fwup.ex

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@ defmodule NervesHub.Firmwares.UpdateTool.Fwup do
4545
end
4646

4747
@impl NervesHub.Firmwares.UpdateTool
48-
def create_firmware_delta_file(source_url, target_url) do
49-
uuid = Ecto.UUID.generate()
50-
work_dir = Path.join(System.tmp_dir(), uuid) |> Path.expand()
48+
def create_firmware_delta_file({source_uuid, source_url}, {target_uuid, target_url}) do
49+
work_dir = Path.join(System.tmp_dir(), "#{source_uuid}_#{target_uuid}")
5150
_ = File.mkdir_p(work_dir)
5251

5352
try do
@@ -57,7 +56,7 @@ defmodule NervesHub.Firmwares.UpdateTool.Fwup do
5756
dl!(source_url, source_path)
5857
dl!(target_url, target_path)
5958

60-
case do_delta_file(source_path, target_path, work_dir) do
59+
case do_delta_file({source_uuid, source_path}, {target_uuid, target_path}, work_dir) do
6160
{:ok, output} ->
6261
{:ok, output}
6362

@@ -139,7 +138,7 @@ defmodule NervesHub.Firmwares.UpdateTool.Fwup do
139138
end)
140139
end
141140

142-
def do_delta_file(source_path, target_path, work_dir) do
141+
def do_delta_file({source_uuid, source_path}, {target_uuid, target_path}, work_dir) do
143142
source_work_dir = Path.join(work_dir, "source")
144143
target_work_dir = Path.join(work_dir, "target")
145144
output_work_dir = Path.join(work_dir, "output")
@@ -210,7 +209,7 @@ defmodule NervesHub.Firmwares.UpdateTool.Fwup do
210209
end
211210
end
212211

213-
{:ok, delta_zip_path} = Plug.Upload.random_file("generated_delta_zip_file")
212+
{:ok, delta_zip_path} = Plug.Upload.random_file("#{source_uuid}_#{target_uuid}_delta")
214213

215214
{:ok, _} =
216215
:zip.create(to_charlist(delta_zip_path), generate_file_list(output_work_dir),

lib/nerves_hub/firmwares/upload.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,13 @@ defmodule NervesHub.Firmwares.Upload do
3333
Called to generate the upload_metadata that will be persisted for later lookup.
3434
"""
3535
@callback metadata(Org.id(), String.t()) :: upload_metadata()
36+
37+
@doc """
38+
Called to generate the upload_metadata specific to delta firmwares that will be persisted for later lookup.
39+
"""
40+
@callback delta_metadata(
41+
Org.id(),
42+
source_firmware_uuid :: String.t(),
43+
target_firmware_uuid :: String.t()
44+
) :: upload_metadata()
3645
end

lib/nerves_hub/firmwares/upload/file.ex

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ defmodule NervesHub.Firmwares.Upload.File do
3535

3636
config = Application.get_env(:nerves_hub, __MODULE__)
3737

38-
common_path = "#{org_id}"
39-
local_path = Path.join([config[:local_path], common_path, filename])
38+
common_path = Path.join([key_prefix(), Integer.to_string(org_id), filename])
39+
40+
local_path = Path.join([config[:local_path], common_path])
4041

4142
port =
4243
if Enum.member?([443, 80], web_config[:url][:port]),
@@ -53,4 +54,42 @@ defmodule NervesHub.Firmwares.Upload.File do
5354
local_path: local_path
5455
}
5556
end
57+
58+
@impl NervesHub.Firmwares.Upload
59+
def delta_metadata(org_id, source_firmware_uuid, target_firmware_uuid) do
60+
web_config = Application.get_env(:nerves_hub, NervesHubWeb.Endpoint)
61+
62+
config = Application.get_env(:nerves_hub, __MODULE__)
63+
64+
common_path =
65+
Path.join([
66+
key_prefix(),
67+
Integer.to_string(org_id),
68+
source_firmware_uuid,
69+
"#{target_firmware_uuid}.delta.fw"
70+
])
71+
72+
local_path = Path.join([config[:local_path], common_path])
73+
74+
port =
75+
if Enum.member?([443, 80], web_config[:url][:port]),
76+
do: "",
77+
else: ":#{web_config[:url][:port]}"
78+
79+
public_path =
80+
Path.join([
81+
"#{web_config[:url][:scheme]}://#{web_config[:url][:host]}#{port}/",
82+
config[:public_path],
83+
common_path
84+
])
85+
86+
%{
87+
public_path: public_path,
88+
local_path: local_path
89+
}
90+
end
91+
92+
def key_prefix() do
93+
"firmware"
94+
end
5695
end

lib/nerves_hub/firmwares/upload/s3.ex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,19 @@ defmodule NervesHub.Firmwares.Upload.S3 do
5555
%{"s3_key" => Path.join([key_prefix(), Integer.to_string(org_id), filename])}
5656
end
5757

58+
@impl NervesHub.Firmwares.Upload
59+
def delta_metadata(org_id, source_firmware_uuid, target_firmware_uuid) do
60+
%{
61+
"s3_key" =>
62+
Path.join([
63+
key_prefix(),
64+
Integer.to_string(org_id),
65+
source_firmware_uuid,
66+
"#{target_firmware_uuid}.delta.fw"
67+
])
68+
}
69+
end
70+
5871
def bucket() do
5972
Application.get_env(:nerves_hub, __MODULE__)[:bucket]
6073
end

lib/nerves_hub/workers/firmware_delta_builder.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ defmodule NervesHub.Workers.FirmwareDeltaBuilder do
1313
alias NervesHub.ManagedDeployments
1414

1515
@impl Oban.Worker
16-
def perform(%Oban.Job{args: %{"source_id" => source_id, "target_id" => target_id}}) do
16+
def perform(%Oban.Job{id: id, args: %{"source_id" => source_id, "target_id" => target_id}}) do
1717
source = Firmwares.get_firmware!(source_id)
1818
target = Firmwares.get_firmware!(target_id)
1919

2020
Logger.metadata(
2121
product_id: source.product_id,
2222
source_firmware: source.uuid,
23-
target_firmware: target.uuid
23+
target_firmware: target.uuid,
24+
job_id: id
2425
)
2526

2627
:ok = maybe_create_firmware_delta(source, target)

lib/nerves_hub/workers/firmware_delta_timeout.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ defmodule NervesHub.Workers.FirmwareDeltaTimeout do
55

66
alias NervesHub.Firmwares
77

8-
@delta_generation_timeout 120_000
8+
# 15min timeout
9+
@delta_generation_timeout 960
910

1011
@impl Oban.Worker
1112
def perform(_) do

test/nerves_hub/firmwares/update_tool_test.exs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
197197
source_size: ^source_size,
198198
target_size: ^target_size
199199
}} =
200-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
200+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
201201

202202
assert %{size: ^delta_size} = File.stat!(delta_path)
203203

@@ -243,7 +243,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
243243
source_size: ^source_size,
244244
target_size: ^target_size
245245
}} =
246-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
246+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
247247

248248
assert %{size: ^delta_size} = File.stat!(delta_path)
249249

@@ -289,7 +289,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
289289
source_size: ^source_size,
290290
target_size: ^target_size
291291
}} =
292-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
292+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
293293

294294
assert %{size: ^delta_size} = File.stat!(delta_path)
295295

@@ -335,7 +335,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
335335
source_size: ^source_size,
336336
target_size: ^target_size
337337
}} =
338-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
338+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
339339

340340
assert %{size: ^delta_size} = File.stat!(delta_path)
341341

@@ -403,7 +403,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
403403
source_size: ^source_size,
404404
target_size: ^target_size
405405
}} =
406-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
406+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
407407

408408
assert %{size: ^delta_size} = File.stat!(delta_path)
409409

@@ -443,7 +443,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
443443
fw_b = build_fw!(Path.join(dir, "b.fw"), raw_conf_path, data_path_2)
444444

445445
{:error, [err]} =
446-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
446+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
447447

448448
assert err =~
449449
"Target uses raw deltas and source firmware uses encryption for the same resource but target firmware has no cipher or"
@@ -467,7 +467,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
467467
fw_b = build_fw!(Path.join(dir, "b.fw"), fwup_conf_path, data_path_2)
468468

469469
{:error, :no_delta_support_in_firmware} =
470-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
470+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
471471
end
472472

473473
@tag :tmp_dir
@@ -497,7 +497,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
497497
source_size: ^source_size,
498498
target_size: ^target_size
499499
}} =
500-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
500+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
501501

502502
%{size: ^delta_size} = File.stat!(delta_path)
503503

@@ -561,7 +561,7 @@ defmodule NervesHub.Firmwares.UpdateToolTest do
561561
source_size: ^source_size,
562562
target_size: ^target_size
563563
}} =
564-
Fwup.do_delta_file(fw_a, fw_b, Path.join(dir, "work"))
564+
Fwup.do_delta_file({"aaa", fw_a}, {"bbb", fw_b}, Path.join(dir, "work"))
565565

566566
assert %{size: ^delta_size} = File.stat!(delta_path)
567567

test/nerves_hub/firmwares_test.exs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ defmodule NervesHub.FirmwaresTest do
272272
expect(UploadFile, :download_file, fn ^source -> {:ok, source_url} end)
273273
expect(UploadFile, :download_file, fn ^target -> {:ok, target_url} end)
274274

275-
expect(UpdateToolDefault, :create_firmware_delta_file, fn ^source_url, ^target_url ->
275+
expect(UpdateToolDefault, :create_firmware_delta_file, fn {_, ^source_url},
276+
{_, ^target_url} ->
276277
{:ok,
277278
%{
278279
filepath: firmware_delta_path,
@@ -340,7 +341,8 @@ defmodule NervesHub.FirmwaresTest do
340341
expect(UploadFile, :download_file, fn ^target -> {:ok, target_url} end)
341342

342343
# Force error
343-
expect(UpdateToolDefault, :create_firmware_delta_file, fn ^source_url, ^target_url ->
344+
expect(UpdateToolDefault, :create_firmware_delta_file, fn {_, ^source_url},
345+
{_, ^target_url} ->
344346
{:error, :delta_not_created}
345347
end)
346348

0 commit comments

Comments
 (0)