defmodule LivebookWeb.Hub.Teams.DeploymentGroupAgentComponent do use LivebookWeb, :live_component alias Livebook.Hubs @impl true def mount(socket) do {:ok, assign(socket, messages: [], hide_title: false)} end @impl true def update(assigns, socket) do socket = assign(socket, assigns) socket = if socket.assigns[:agent_key_id] do socket else agent_key_id = case assigns.deployment_group.agent_keys do [%{id: id} | _] -> id _ -> nil end assign(socket, :agent_key_id, agent_key_id) end socket = assign_new(socket, :changeset, fn -> Hubs.Dockerfile.config_changeset(base_config(socket)) end) {:ok, update_instructions(socket)} end defp base_config(socket) do Hubs.Dockerfile.from_deployment_group(socket.assigns.deployment_group) end @impl true def render(assigns) do ~H"""

App server setup

App servers are instances of Livebook running inside your infrastructure that you deploy your apps to. Follow the instructions below to start new instances.

<.message_box :for={{kind, message} <- @messages} kind={kind}> <%= raw(message) %>
<.form :let={f} for={@changeset} as={:data} phx-change="validate" phx-target={@myself}> <.radio_field label="Base Docker image" field={f[:docker_tag]} options={LivebookWeb.AppComponents.docker_tag_options()} /> <.form :let={f} :if={match?([_, _ | _], @deployment_group.agent_keys)} for={%{"id" => @agent_key_id}} as={:agent_key} phx-change="select_agent_key" phx-target={@myself} >
<.select_field label="Server key" field={f[:id]} options={ for key <- @deployment_group.agent_keys do {value_preview(key.key), key.id} end } />
<%= if @agent_key_id do %>
<.tabs id="deployment-instruction" default="docker"> <:tab id="docker" label="Docker">

Deploy an app server to any Docker-based infrastructure. You may want to set the environment variables as secrets, if applicable. Below is an example calling Docker CLI directly, adapt it as necessary.

CLI
<.code_preview source_id="agent-dockerfile-source" source={@instructions.docker_instructions} language="shell" />
<:tab id="fly_io" label="Fly.io">

Deploy an app server to Fly.io with a few commands.

CLI
<.code_preview source_id="agent-dockerfile-source" source={@instructions.fly_instructions} language="shell" />
<% end %>
""" end defp value_preview(string) do preview_length = 10 length = String.length(string) String.slice(string, 0, preview_length) <> String.duplicate("•", length - preview_length) end @impl true def handle_event("select_agent_key", %{"agent_key" => %{"id" => id}}, socket) do id = if(id != "", do: id) {:noreply, assign(socket, agent_key_id: id) |> update_instructions()} end def handle_event("validate", %{"data" => data}, socket) do changeset = socket |> base_config() |> Hubs.Dockerfile.config_changeset(data) |> Map.replace!(:action, :validate) {:noreply, assign(socket, changeset: changeset) |> update_instructions()} end defp update_instructions(socket) do config = Ecto.Changeset.apply_changes(socket.assigns.changeset) warnings = Hubs.Dockerfile.online_warnings(config) messages = Enum.map(warnings, &{:warning, &1}) assign(socket, instructions: instructions(socket), messages: messages) end defp instructions(%{assigns: %{agent_key_id: nil}}), do: nil defp instructions(socket) do hub = socket.assigns.hub deployment_group = socket.assigns.deployment_group agent_key = Enum.find(deployment_group.agent_keys, &(&1.id == socket.assigns.agent_key_id)) config = Ecto.Changeset.apply_changes(socket.assigns.changeset) %{image: image, env: env} = Livebook.Hubs.Dockerfile.online_docker_info(config, hub, agent_key) %{ docker_instructions: docker_instructions(image, env), fly_instructions: fly_instructions(image, env, hub.hub_name, deployment_group.name) } end defp docker_instructions(image, env) do envs = Enum.map_join(env, "\n", fn {key, value} -> ~s/ -e #{key}="#{value}" \\/ end) """ docker run -p 8080:8080 -p 8081:8081 --pull always \\ #{envs} #{image} """ end defp fly_instructions(image, env, hub_name, deployment_group_name) do envs = Enum.map_join(env, " \\\n", fn {key, value} -> ~s/ #{key}="#{value}"/ end) example_dir = "lb-server-#{hub_name}-#{deployment_group_name}" |> String.replace(~r/[^\w-]/, "") |> String.downcase() """ # Create a directory for your Fly app config mkdir #{example_dir} cd #{example_dir} fly launch --image #{image} --no-deploy fly secrets set \\ #{envs} fly deploy --ha=false """ end end