From 4c97bd338eb3dfd9df37f935841c833dddb689fe Mon Sep 17 00:00:00 2001 From: Cristine Guadelupe Date: Wed, 27 Dec 2023 21:10:17 -0300 Subject: [PATCH] Adds ZTA and clustering to deployment groups (#2413) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Valim --- lib/livebook/hubs/team_client.ex | 16 ++- lib/livebook/teams/deployment_group.ex | 11 +- lib/livebook/teams/requests.ex | 19 ++- .../components/form_components.ex | 1 + lib/livebook_web/live/app_helpers.ex | 133 ++++++++++-------- .../teams/deployment_group_form_component.ex | 1 + .../live/session_live/app_docker_component.ex | 38 ++++- .../lib/livebook_proto/deployment_group.pb.ex | 3 + .../deployment_group_created.pb.ex | 3 + .../deployment_group_updated.pb.ex | 3 + 10 files changed, 160 insertions(+), 68 deletions(-) diff --git a/lib/livebook/hubs/team_client.ex b/lib/livebook/hubs/team_client.ex index 94a6a7b48..27efea2d1 100644 --- a/lib/livebook/hubs/team_client.ex +++ b/lib/livebook/hubs/team_client.ex @@ -244,9 +244,19 @@ defmodule Livebook.Hubs.TeamClient do } end - defp build_deployment_group(state, %{id: id, name: name, mode: mode, secrets: secrets}) do - secrets = Enum.map(secrets, &build_secret(state, &1)) - %DeploymentGroup{id: id, name: name, mode: mode, hub_id: state.hub.id, secrets: secrets} + defp build_deployment_group(state, deployment_group) do + secrets = Enum.map(deployment_group.secrets, &build_secret(state, &1)) + + %DeploymentGroup{ + id: deployment_group.id, + name: deployment_group.name, + mode: deployment_group.mode, + hub_id: state.hub.id, + secrets: secrets, + clustering: deployment_group.clustering, + zta_provider: String.to_atom(deployment_group.zta_provider), + zta_key: deployment_group.zta_key + } end defp handle_event(:secret_created, %Secrets.Secret{} = secret, state) do diff --git a/lib/livebook/teams/deployment_group.ex b/lib/livebook/teams/deployment_group.ex index 94c09f71c..73893e22a 100644 --- a/lib/livebook/teams/deployment_group.ex +++ b/lib/livebook/teams/deployment_group.ex @@ -3,11 +3,17 @@ defmodule Livebook.Teams.DeploymentGroup do import Ecto.Changeset alias Livebook.Secrets.Secret + # If this list is updated, it must also be mirrored on Livebook Teams Server. + @zta_providers ~w(cloudflare google_iap tailscale teleport)a + @type t :: %__MODULE__{ id: String.t() | nil, name: String.t() | nil, mode: :online | :offline, hub_id: String.t() | nil, + clustering: :fly_io | nil, + zta_provider: :cloudflare | :google_iap | :tailscale | :teleport, + zta_key: String.t(), secrets: [Secret.t()] } @@ -16,12 +22,15 @@ defmodule Livebook.Teams.DeploymentGroup do field :name, :string field :mode, Ecto.Enum, values: [:online, :offline] field :hub_id, :string + field :clustering, Ecto.Enum, values: [:fly_io] + field :zta_provider, Ecto.Enum, values: @zta_providers + field :zta_key, :string has_many :secrets, Secret end def changeset(deployment_group, attrs \\ %{}) do deployment_group - |> cast(attrs, [:id, :name, :mode, :hub_id]) + |> cast(attrs, [:id, :name, :mode, :hub_id, :clustering, :zta_provider, :zta_key]) |> validate_required([:name, :mode]) end end diff --git a/lib/livebook/teams/requests.ex b/lib/livebook/teams/requests.ex index c114d3c17..08d5879da 100644 --- a/lib/livebook/teams/requests.ex +++ b/lib/livebook/teams/requests.ex @@ -169,7 +169,14 @@ defmodule Livebook.Teams.Requests do @spec create_deployment_group(Team.t(), DeploymentGroup.t()) :: {:ok, map()} | {:error, map() | String.t()} | {:transport_error, String.t()} def create_deployment_group(team, deployment_group) do - params = %{name: deployment_group.name, mode: deployment_group.mode} + params = %{ + name: deployment_group.name, + mode: deployment_group.mode, + clustering: deployment_group.clustering, + zta_provider: deployment_group.zta_provider, + zta_key: deployment_group.zta_key + } + post("/api/v1/org/deployment-groups", params, team) end @@ -179,7 +186,15 @@ defmodule Livebook.Teams.Requests do @spec update_deployment_group(Team.t(), DeploymentGroup.t()) :: {:ok, map()} | {:error, map() | String.t()} | {:transport_error, String.t()} def update_deployment_group(team, deployment_group) do - params = %{id: deployment_group.id, name: deployment_group.name, mode: deployment_group.mode} + params = %{ + id: deployment_group.id, + name: deployment_group.name, + mode: deployment_group.mode, + clustering: deployment_group.clustering, + zta_provider: deployment_group.zta_provider, + zta_key: deployment_group.zta_key + } + put("/api/v1/org/deployment-groups", params, team) end diff --git a/lib/livebook_web/components/form_components.ex b/lib/livebook_web/components/form_components.ex index 0527dc3b5..27f04d2ae 100644 --- a/lib/livebook_web/components/form_components.ex +++ b/lib/livebook_web/components/form_components.ex @@ -411,6 +411,7 @@ defmodule LivebookWeb.FormComponents do attr :class, :string, default: "" attr :field, Phoenix.HTML.FormField, doc: "a form field struct retrieved from the form" attr :help, :string, default: nil + attr :disabled, :boolean, default: false attr :options, :list, default: [] attr :prompt, :string, default: nil diff --git a/lib/livebook_web/live/app_helpers.ex b/lib/livebook_web/live/app_helpers.ex index 0ee4d0f82..a5775d058 100644 --- a/lib/livebook_web/live/app_helpers.ex +++ b/lib/livebook_web/live/app_helpers.ex @@ -102,68 +102,85 @@ defmodule LivebookWeb.AppHelpers do ]} /> <.radio_field label="Base image" field={@form[:docker_tag]} options={docker_tag_options()} /> -
- <.select_field - label="Clustering" - help={ - ~S''' - When running multiple - instances of Livebook, - they need to be connected - into a single cluster. - You must either deploy - it as a single instance - or choose a platform to - enable clustering on. - ''' - } - field={@form[:clustering]} - options={[ - {"Single instance", ""}, - {"Fly.io", "fly_io"} - ]} - /> -
- <%= if Hubs.Provider.type(@hub) == "team" do %> -
-
- <.select_field - label="Zero Trust Authentication provider" - field={@form[:zta_provider]} - help={ - ~S''' - Enable this option if you want - to deploy your notebooks behind - an authentication proxy - ''' - } - prompt="None" - options={zta_options()} - /> - <.text_field - :if={zta_metadata = zta_metadata(@form[:zta_provider].value)} - field={@form[:zta_key]} - label={zta_metadata.value} - help={zta_help(zta_metadata)} - phx-debounce - /> -
-
- See the - - Authentication with <%= zta_metadata.name %> docs - - for more information. -
-
- <% end %> """ end + @doc """ + Renders form fields for Deployment Group. + """ + attr :form, Phoenix.HTML.Form, required: true + attr :hub, :map, required: true + + def deployment_group_form_content(assigns) do + ~H""" +
+ <.select_field + label="Clustering" + help={ + ~S''' + When running multiple + instances of Livebook, + they need to be connected + into a single cluster. + You must either deploy + it as a single instance + or choose a platform to + enable clustering on. + ''' + } + field={@form[:clustering]} + options={[ + {"Single instance", ""}, + {"Fly.io", "fly_io"} + ]} + disabled={@form[:ready_only].value} + class="disabled:cursor-not-allowed" + /> +
+ <%= if Hubs.Provider.type(@hub) == "team" do %> +
+
+ <.select_field + label="Zero Trust Authentication provider" + field={@form[:zta_provider]} + help={ + ~S''' + Enable this option if you want + to deploy your notebooks behind + an authentication proxy + ''' + } + prompt="None" + options={zta_options()} + disabled={@form[:ready_only].value} + class="disabled:cursor-not-allowed" + /> + <.text_field + :if={zta_metadata = zta_metadata(@form[:zta_provider].value)} + field={@form[:zta_key]} + label={zta_metadata.value} + help={zta_help(zta_metadata)} + phx-debounce + disabled={@form[:ready_only].value} + class="disabled:cursor-not-allowed" + /> +
+
+ See the + + Authentication with <%= zta_metadata.name %> docs + + for more information. +
+
+ <% end %> + """ + end + @zta_options for provider <- Livebook.Config.identity_providers(), do: {provider.name, provider.type} diff --git a/lib/livebook_web/live/hub/teams/deployment_group_form_component.ex b/lib/livebook_web/live/hub/teams/deployment_group_form_component.ex index 367626243..b42ad4f19 100644 --- a/lib/livebook_web/live/hub/teams/deployment_group_form_component.ex +++ b/lib/livebook_web/live/hub/teams/deployment_group_form_component.ex @@ -70,6 +70,7 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do {"Online", "online"} ]} /> +
+ <.form + :let={f} + for={deployment_group_form_content(assigns)} + as={:data} + phx-change="validate" + phx-target={@myself} + > +
+ +
+ <.form :let={f} for={@changeset} as={:data} phx-change="validate" phx-target={@myself}> @@ -191,7 +205,7 @@ defmodule LivebookWeb.SessionLive.AppDockerComponent do {:noreply, assign(socket, save_result: save_result)} end - def handle_event("select_deployment_group", %{"id" => id}, socket) do + def handle_event("select_deployment_group", %{"deployment_group_id" => id}, socket) do Livebook.Session.set_notebook_deployment_group(socket.assigns.session.pid, id) {:noreply, socket} @@ -255,4 +269,20 @@ defmodule LivebookWeb.SessionLive.AppDockerComponent do for deployment_group <- [%{name: "none", id: nil}] ++ deployment_groups, do: {deployment_group.name, deployment_group.id} end + + defp deployment_group_form_content(%{deployment_group_id: id} = assigns) do + deployment_group = if id, do: Enum.find(assigns.deployment_groups, &(&1.id == id)) + + if deployment_group do + %{ + "deployment_group_id" => deployment_group.id, + "clustering" => deployment_group.clustering, + "zta_provider" => deployment_group.zta_provider, + "zta_key" => deployment_group.zta_key, + "ready_only" => true + } + else + assigns.changeset + end + end end diff --git a/proto/lib/livebook_proto/deployment_group.pb.ex b/proto/lib/livebook_proto/deployment_group.pb.ex index 8fe550ef5..6147b808f 100644 --- a/proto/lib/livebook_proto/deployment_group.pb.ex +++ b/proto/lib/livebook_proto/deployment_group.pb.ex @@ -5,4 +5,7 @@ defmodule LivebookProto.DeploymentGroup do field :name, 2, type: :string field :mode, 3, type: :string field :secrets, 4, repeated: true, type: LivebookProto.DeploymentGroupSecret + field :clustering, 5, type: :string + field :zta_provider, 6, type: :string + field :zta_key, 7, type: :string end diff --git a/proto/lib/livebook_proto/deployment_group_created.pb.ex b/proto/lib/livebook_proto/deployment_group_created.pb.ex index 1f461b04e..9ba545c69 100644 --- a/proto/lib/livebook_proto/deployment_group_created.pb.ex +++ b/proto/lib/livebook_proto/deployment_group_created.pb.ex @@ -5,4 +5,7 @@ defmodule LivebookProto.DeploymentGroupCreated do field :name, 2, type: :string field :mode, 3, type: :string field :secrets, 4, repeated: true, type: LivebookProto.DeploymentGroupSecret + field :clustering, 5, type: :string + field :zta_provider, 6, type: :string + field :zta_key, 7, type: :string end diff --git a/proto/lib/livebook_proto/deployment_group_updated.pb.ex b/proto/lib/livebook_proto/deployment_group_updated.pb.ex index 18aa3c35f..5be1ff8f6 100644 --- a/proto/lib/livebook_proto/deployment_group_updated.pb.ex +++ b/proto/lib/livebook_proto/deployment_group_updated.pb.ex @@ -5,4 +5,7 @@ defmodule LivebookProto.DeploymentGroupUpdated do field :name, 2, type: :string field :mode, 3, type: :string field :secrets, 4, repeated: true, type: LivebookProto.DeploymentGroupSecret + field :clustering, 5, type: :string + field :zta_provider, 6, type: :string + field :zta_key, 7, type: :string end