From 744d2fe9ef696ff2038de0752b4d4bd82d9ceb34 Mon Sep 17 00:00:00 2001 From: benbot Date: Tue, 19 Dec 2023 11:19:50 -0500 Subject: [PATCH 1/4] Adds mounts to fly backend --- lib/flame/fly_backend.ex | 7 +++++-- lib/flame/fly_backend/mounts.ex | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 lib/flame/fly_backend/mounts.ex diff --git a/lib/flame/fly_backend.ex b/lib/flame/fly_backend.ex index b99701f..7ed3ba8 100644 --- a/lib/flame/fly_backend.ex +++ b/lib/flame/fly_backend.ex @@ -54,6 +54,7 @@ defmodule FLAME.FlyBackend do :cpu_kind, :cpus, :gpu_kind, + :mounts, :memory_mb, :image, :app, @@ -73,6 +74,7 @@ defmodule FLAME.FlyBackend do cpus: nil, memory_mb: nil, gpu_kind: nil, + mounts: [], image: nil, services: [], app: nil, @@ -86,7 +88,7 @@ defmodule FLAME.FlyBackend do runner_private_ip: nil, runner_node_name: nil - @valid_opts ~w(app image token host cpu_kind cpus memory_mb gpu_kind boot_timeout env terminator_sup log services)a + @valid_opts ~w(app image token host cpu_kind cpus mounts memory_mb gpu_kind boot_timeout env terminator_sup log services)a @impl true def init(opts) do @@ -179,11 +181,12 @@ defmodule FLAME.FlyBackend do name: "#{state.app}-flame-#{rand_id(20)}", config: %{ image: state.image, + mounts: state.mounts, guest: %{ cpu_kind: state.cpu_kind, cpus: state.cpus, memory_mb: state.memory_mb, - gpu_kind: state.gpu_kind + gpu_kind: state.gpu_kind, }, auto_destroy: true, restart: %{policy: "no"}, diff --git a/lib/flame/fly_backend/mounts.ex b/lib/flame/fly_backend/mounts.ex new file mode 100644 index 0000000..842df40 --- /dev/null +++ b/lib/flame/fly_backend/mounts.ex @@ -0,0 +1,8 @@ +defmodule FLAME.FlyBackend.Mounts do + @derive Jason.Encoder + defstruct name: nil, + path: nil + # extend_threshold_percent: 0, + # add_size_gb: 0, + # size_gb_limit: 0 +end From bb98ffdd9830e6a4ce86d5be20e61fd64268ed82 Mon Sep 17 00:00:00 2001 From: benbot Date: Thu, 21 Dec 2023 12:05:01 -0500 Subject: [PATCH 2/4] Adds automatic volume id discovery and matching --- lib/flame/fly_backend.ex | 64 +++++++++++++++++++++++++++++++-- lib/flame/fly_backend/mounts.ex | 9 ++--- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/lib/flame/fly_backend.ex b/lib/flame/fly_backend.ex index 7ed3ba8..77f803c 100644 --- a/lib/flame/fly_backend.ex +++ b/lib/flame/fly_backend.ex @@ -105,7 +105,8 @@ defmodule FLAME.FlyBackend do memory_mb: 4096, boot_timeout: 30_000, runner_node_basename: node_base, - services: [] + services: [], + mounts: [] } provided_opts = @@ -168,8 +169,65 @@ defmodule FLAME.FlyBackend do {result, div(micro, 1000)} end + defp get_volume_id(%FlyBackend{ mounts: mounts } = state) when is_list(mounts) do + case mounts do + [] -> + {nil, 0} + mounts -> + {vols, time} = get_volumes(state) + + case vols do + [] -> + {:error, "no volumes to mount"} + all_vols -> + vols = + all_vols + |> Enum.filter(fn vol -> + vol["attached_machine_id"] == nil + end) + + volume_ids_by_name = + vols + |> Enum.group_by(fn vol -> + vol["name"] + end) + |> Enum.map(fn {name, vols} -> + {name, Enum.map(vols, fn vol -> vol["id"] end)} + end) + + {new_mounts, leftover_vols} = Enum.reduce(mounts, {[], volume_ids_by_name}, fn mount, {new_mounts, leftover_vols} -> + case Enum.find(leftover_vols, fn {name, ids} -> name == mount.name end) do + nil -> + raise ArgumentError, "not enough fly volumes with the name \"#{mount.name}\" to a FLAME child" + {_, [id | rest]} -> + {new_mount, leftover_vols} = Enum.split(rest, 1) + {new_mounts ++ [%{mount | volume: id}], leftover_vols} + end + end) + + {new_mounts, time} + end + end + end + defp get_volume_id(_) do + raise ArgumentError, "expected a list of mounts" + end + + defp get_volumes(%FlyBackend{} = state) do + {vols, get_vols_time} = with_elapsed_ms(fn -> + Req.get!("#{state.host}/v1/apps/#{state.app}/volumes", + connect_options: [timeout: state.boot_timeout], + retry: false, + auth: {:bearer, state.token}, + ) + end) + + {vols.body, get_vols_time} + end + @impl true def remote_boot(%FlyBackend{parent_ref: parent_ref} = state) do + {mounts, volume_validate_time} = get_volume_id(state) {req, req_connect_time} = with_elapsed_ms(fn -> Req.post!("#{state.host}/v1/apps/#{state.app}/machines", @@ -181,7 +239,7 @@ defmodule FLAME.FlyBackend do name: "#{state.app}-flame-#{rand_id(20)}", config: %{ image: state.image, - mounts: state.mounts, + mounts: mounts, guest: %{ cpu_kind: state.cpu_kind, cpus: state.cpus, @@ -197,7 +255,7 @@ defmodule FLAME.FlyBackend do ) end) - remaining_connect_window = state.boot_timeout - req_connect_time + remaining_connect_window = state.boot_timeout - req_connect_time - volume_validate_time case req.body do %{"id" => id, "instance_id" => instance_id, "private_ip" => ip} -> diff --git a/lib/flame/fly_backend/mounts.ex b/lib/flame/fly_backend/mounts.ex index 842df40..4ccd0af 100644 --- a/lib/flame/fly_backend/mounts.ex +++ b/lib/flame/fly_backend/mounts.ex @@ -1,8 +1,9 @@ defmodule FLAME.FlyBackend.Mounts do @derive Jason.Encoder defstruct name: nil, - path: nil - # extend_threshold_percent: 0, - # add_size_gb: 0, - # size_gb_limit: 0 + path: nil, + volume: nil, + extend_threshold_percent: 0, + add_size_gb: 0, + size_gb_limit: 0 end From 8a10c24c8e1b3c915e2af39b9c97184d07f2928d Mon Sep 17 00:00:00 2001 From: benbot Date: Sat, 23 Dec 2023 01:29:17 -0500 Subject: [PATCH 3/4] checks for created state --- lib/flame/fly_backend.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/flame/fly_backend.ex b/lib/flame/fly_backend.ex index 77f803c..8ad3895 100644 --- a/lib/flame/fly_backend.ex +++ b/lib/flame/fly_backend.ex @@ -184,6 +184,7 @@ defmodule FLAME.FlyBackend do all_vols |> Enum.filter(fn vol -> vol["attached_machine_id"] == nil + and vol["state"] == "created" end) volume_ids_by_name = From fdf71645d0b744ccf4eca863f9928cf7c215b2f7 Mon Sep 17 00:00:00 2001 From: "Ben Botwin (benbot)" Date: Sun, 31 Dec 2023 14:10:14 -0500 Subject: [PATCH 4/4] cleaned up volume code based on PR comments --- lib/flame/fly_backend.ex | 49 ++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/lib/flame/fly_backend.ex b/lib/flame/fly_backend.ex index 8ad3895..a331c92 100644 --- a/lib/flame/fly_backend.ex +++ b/lib/flame/fly_backend.ex @@ -169,46 +169,37 @@ defmodule FLAME.FlyBackend do {result, div(micro, 1000)} end + defp get_volume_id(%FlyBackend{ mounts: [] }), do: {nil, 0} defp get_volume_id(%FlyBackend{ mounts: mounts } = state) when is_list(mounts) do - case mounts do - [] -> - {nil, 0} - mounts -> - {vols, time} = get_volumes(state) + {volumes, time} = get_volumes(state) - case vols do + case volumes do [] -> {:error, "no volumes to mount"} - all_vols -> - vols = - all_vols + all_volumes -> + volume_ids_by_name = + all_volumes |> Enum.filter(fn vol -> vol["attached_machine_id"] == nil and vol["state"] == "created" end) - - volume_ids_by_name = - vols - |> Enum.group_by(fn vol -> - vol["name"] - end) - |> Enum.map(fn {name, vols} -> - {name, Enum.map(vols, fn vol -> vol["id"] end)} - end) - - {new_mounts, leftover_vols} = Enum.reduce(mounts, {[], volume_ids_by_name}, fn mount, {new_mounts, leftover_vols} -> - case Enum.find(leftover_vols, fn {name, ids} -> name == mount.name end) do - nil -> - raise ArgumentError, "not enough fly volumes with the name \"#{mount.name}\" to a FLAME child" - {_, [id | rest]} -> - {new_mount, leftover_vols} = Enum.split(rest, 1) - {new_mounts ++ [%{mount | volume: id}], leftover_vols} + |> Enum.group_by(&(&1["name"]), &(&1["id"])) + + new_mounts = Enum.map_reduce( + mounts, + volume_ids_by_name, + fn mount, leftover_vols -> + case List.wrap(leftover_vols[mount.name]) do + [] -> + raise ArgumentError, "not enough fly volumes with the name \"#{mount.name}\" to a FLAME child" + [volume_id | rest] -> + {%{mount | volume: volume_id}, %{leftover_vols | mount.name => rest}} + end end - end) + ) {new_mounts, time} end - end end defp get_volume_id(_) do raise ArgumentError, "expected a list of mounts" @@ -219,7 +210,7 @@ defmodule FLAME.FlyBackend do Req.get!("#{state.host}/v1/apps/#{state.app}/volumes", connect_options: [timeout: state.boot_timeout], retry: false, - auth: {:bearer, state.token}, + auth: {:bearer, state.token} ) end)