Skip to content

Commit

Permalink
Merge branch 'release/0.16.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
zacksiri committed Oct 21, 2024
2 parents e50b2e6 + accdac7 commit a65fdcf
Show file tree
Hide file tree
Showing 14 changed files with 334 additions and 145 deletions.
22 changes: 22 additions & 0 deletions lib/uplink/instances.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule Uplink.Instances do
alias Uplink.Cache

def mark(state, install_id, instance_name) do
Cache.transaction(
[keys: [{:install, install_id, state}]],
fn ->
Cache.get_and_update(
{:install, install_id, state},
fn current_value ->
executing_instances =
if current_value,
do: current_value ++ [instance_name],
else: [instance_name]

{current_value, Enum.uniq(executing_instances)}
end
)
end
)
end
end
2 changes: 1 addition & 1 deletion lib/uplink/packages.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule Uplink.Packages do
to: Install.Manager,
as: :build_state

defdelegate latest_install(instellar_installation_id),
defdelegate latest_install(instellar_installation_id, option \\ nil),
to: Install.Manager,
as: :latest

Expand Down
18 changes: 2 additions & 16 deletions lib/uplink/packages/install/execute.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule Uplink.Packages.Install.Execute do

alias Uplink.Repo
alias Uplink.Cache
alias Uplink.Instances

alias Uplink.Clients.LXD
alias Uplink.Clients.Instellar
Expand Down Expand Up @@ -96,22 +97,7 @@ defmodule Uplink.Packages.Install.Execute do
)
end)

Cache.transaction(
[keys: [{:install, state.install.id, "executing"}]],
fn ->
Cache.get_and_update(
{:install, state.install.id, "executing"},
fn current_value ->
executing_instances =
if current_value,
do: current_value ++ [instance.slug],
else: [instance.slug]

{current_value, Enum.uniq(executing_instances)}
end
)
end
)
Instances.mark("executing", state.install.id, instance.slug)

case event_name do
"upgrade" ->
Expand Down
12 changes: 11 additions & 1 deletion lib/uplink/packages/install/manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ defmodule Uplink.Packages.Install.Manager do
|> Repo.insert()
end

def latest(instellar_installation_id) do
def latest(instellar_installation_id, nil) do
Packages.Install
|> where(
[i],
Expand All @@ -58,6 +58,16 @@ defmodule Uplink.Packages.Install.Manager do
|> Repo.one()
end

def latest(instellar_installation_id, %{"hash" => deployment_hash}) do
Install.by_hash_and_installation(
deployment_hash,
instellar_installation_id
)
|> order_by(desc: :inserted_at)
|> limit(1)
|> Repo.one()
end

def maybe_mark_complete(%Install{} = install, actor) do
completed_instances = Cache.get({:install, install.id, "completed"})
executing_instances = Cache.get({:install, install.id, "executing"})
Expand Down
221 changes: 126 additions & 95 deletions lib/uplink/packages/instance/bootstrap.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Uplink.Packages.Instance.Bootstrap do

alias Uplink.Repo
alias Uplink.Cache
alias Uplink.Instances
alias Uplink.Members.Actor

alias Uplink.Packages
Expand Down Expand Up @@ -46,122 +47,46 @@ defmodule Uplink.Packages.Instance.Bootstrap do

%Actor{} = actor = Repo.get(Actor, actor_id)

%Install{} =
install =
state =
Install
|> preload([:deployment])
|> Repo.get(install_id)

state = Packages.build_install_state(install, actor)
|> Packages.build_install_state(actor)

state
|> handle_placement(instance_params)
|> case do
%{client: _, lxd_project_name: _, placement: _} = updated_state ->
handle_provisioning(updated_state, instance_params)

{:snooze, _} = snooze ->
snooze

error ->
handle_error(state, error, instance_params)
end
end

defp handle_placement(
%{
install: install,
metadata:
%{orchestration: %{placement: placement_strategy}} = metadata
} = state,
%{install: %{deployment: %{current_state: "live"}} = install} = state,
%{"slug" => instance_name}
) do
placement_name = Placement.name(instance_name)

Cache.transaction([keys: [{:available_nodes, placement_name}]], fn ->
with {:ok, %Placement{node: node} = placement} <-
Placement.find(instance_name, placement_strategy),
%Member{architecture: architecture} <-
LXD.list_cluster_members()
|> Enum.find(fn member ->
member.server_name == node
end) do
client = LXD.client()

profile_name = Packages.profile_name(metadata)

size_profile_name = Packages.get_size_profile(metadata)

lxd_project_name = Packages.get_or_create_project_name(client, metadata)

image_server = get_image_server()

profiles = [profile_name, "default"]

profiles =
if size_profile_name do
[size_profile_name | profiles]
else
profiles
end

lxd_instance =
Map.merge(@default_params, %{
"name" => instance_name,
"architecture" => architecture,
"profiles" => profiles,
"source" => %{
"type" => "image",
"mode" => "pull",
"protocol" => "simplestreams",
"server" => image_server,
"alias" => install.deployment.stack
}
})

client
|> Formation.lxd_create(node, lxd_instance, project: lxd_project_name)
|> case do
%Tesla.Client{} = client ->
transition_parameters =
Map.put(
@transition_parameters,
"node",
node
)

Uplink.TaskSupervisor
|> @task_supervisor.async_nolink(
fn ->
Instellar.transition_instance(instance_name, install, "boot",
comment:
"[Uplink.Packages.Instance.Bootstrap] Starting bootstrap with #{placement_strategy} placement...",
parameters: transition_parameters
)
end,
shutdown: 30_000
)
Instances.mark("executing", install.id, instance_name)

Cache.get_and_update(
{:available_nodes, placement_name},
fn current_value ->
if is_list(current_value) do
{current_value, current_value -- [node]}
else
{current_value, []}
end
end
)

state
|> Map.put(:client, client)
|> Map.put(:lxd_project_name, lxd_project_name)
|> Map.put(:placement, placement)

error ->
error
end
end
Cache.transaction([keys: [{:available_nodes, placement_name}]], fn ->
place_instance(state, instance_name)
end)
end

defp handle_placement(
%{install: %{deployment: %{current_state: _}}},
_instance_params
) do
{:snooze, 10}
end

defp handle_provisioning(
%{
client: client,
Expand Down Expand Up @@ -244,12 +169,118 @@ defmodule Uplink.Packages.Instance.Bootstrap do
end
end

defp handle_error(%{install: install, actor: actor}, error, %{
defp handle_error(%{install: install, actor: _actor}, error, %{
"slug" => instance_name
}) do
Packages.transition_install_with(install, actor, "fail",
comment: "#{instance_name} #{inspect(error)}"
Instellar.transition_instance(instance_name, install, "off",
comment:
"[Uplink.Packages.Instance.Bootstrap] #{instance_name} #{inspect(error)}",
parameters: @transition_parameters
)
end

defp place_instance(
%{
install: install,
metadata:
%{orchestration: %{placement: placement_strategy}} = metadata
} = state,
instance_name
) do
with {:ok, %Placement{node: node} = placement} <-
Placement.find(instance_name, placement_strategy),
%Member{architecture: architecture} <-
LXD.list_cluster_members()
|> Enum.find(fn member ->
member.server_name == node
end) do
client = LXD.client()

profile_name = Packages.profile_name(metadata)

size_profile_name = Packages.get_size_profile(metadata)

lxd_project_name = Packages.get_or_create_project_name(client, metadata)

image_server = get_image_server()

profiles = [profile_name, "default"]

profiles =
if size_profile_name do
[size_profile_name | profiles]
else
profiles
end

lxd_instance =
Map.merge(@default_params, %{
"name" => instance_name,
"architecture" => architecture,
"profiles" => profiles,
"source" => %{
"type" => "image",
"mode" => "pull",
"protocol" => "simplestreams",
"server" => image_server,
"alias" => install.deployment.stack
}
})

client
|> Formation.lxd_create(node, lxd_instance, project: lxd_project_name)
|> case do
%Tesla.Client{} = client ->
state
|> Map.put(:client, client)
|> Map.put(:lxd_project_name, lxd_project_name)
|> Map.put(:placement, placement)
|> Map.put(:instance_name, instance_name)
|> Map.put(:node, node)
|> post_instance_creation()

error ->
error
end
end
end

defp post_instance_creation(
%{
install: install,
metadata: %{orchestration: %{placement: placement_strategy}},
node: node,
instance_name: instance_name
} = state
) do
placement_name = Placement.name(instance_name)

transition_parameters = Map.put(@transition_parameters, "node", node)

Uplink.TaskSupervisor
|> @task_supervisor.async_nolink(
fn ->
Instellar.transition_instance(instance_name, install, "boot",
comment:
"[Uplink.Packages.Instance.Bootstrap] Starting bootstrap with #{placement_strategy} placement...",
parameters: transition_parameters
)
end,
shutdown: 30_000
)

Cache.get_and_update(
{:available_nodes, placement_name},
fn current_value ->
if is_list(current_value) do
{current_value, current_value -- [node]}
else
{current_value, []}
end
end
)

state
end

defp get_image_server do
Expand Down
4 changes: 3 additions & 1 deletion lib/uplink/packages/instance/finalize.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ defmodule Uplink.Packages.Instance.Finalize do
node = Map.get(instance_params, "node", %{})

transition_parameters =
Map.put(@transition_parameters, "node", node["slug"])
@transition_parameters
|> Map.put("node", node["slug"])
|> Map.put("hash", install.deployment.hash)

Instellar.transition_instance(name, install, "complete",
comment: comment,
Expand Down
13 changes: 2 additions & 11 deletions lib/uplink/packages/instance/install.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule Uplink.Packages.Instance.Install do

alias Uplink.Repo
alias Uplink.Cache
alias Uplink.Instances

alias Uplink.Clients.LXD
alias Uplink.Clients.Caddy
Expand Down Expand Up @@ -92,17 +93,7 @@ defmodule Uplink.Packages.Instance.Install do
|> Formation.add_package_and_restart_lxd_instance(formation_instance)
|> case do
{:ok, add_package_output} ->
Cache.transaction([keys: [{:install, install_id, "completed"}]], fn ->
Cache.get_and_update(
{:install, install_id, "completed"},
fn current_value ->
completed_instances =
if current_value, do: current_value ++ [name], else: [name]

{current_value, Enum.uniq(completed_instances)}
end
)
end)
Instances.mark("completed", install_id, name)

Caddy.schedule_config_reload(install)

Expand Down
Loading

0 comments on commit a65fdcf

Please sign in to comment.