mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-09-20 10:05:57 +08:00
Manage deployment groups (#2330)
This commit is contained in:
parent
22d8e40ab6
commit
f633748b37
|
@ -112,7 +112,7 @@ defmodule Livebook.Hubs.Personal do
|
|||
@doc """
|
||||
Unset secret from given id.
|
||||
"""
|
||||
@spec set_secret(String.t()) :: :ok
|
||||
@spec unset_secret(String.t()) :: :ok
|
||||
def unset_secret(id) do
|
||||
Storage.delete(@secrets_namespace, id)
|
||||
:ok
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
alias Livebook.Hubs
|
||||
alias Livebook.Secrets
|
||||
alias Livebook.Teams
|
||||
alias Livebook.Teams.DeploymentGroup
|
||||
|
||||
@registry Livebook.HubsRegistry
|
||||
@supervisor Livebook.HubsSupervisor
|
||||
|
@ -17,7 +18,8 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
:derived_key,
|
||||
connected?: false,
|
||||
secrets: [],
|
||||
file_systems: []
|
||||
file_systems: [],
|
||||
deployment_groups: []
|
||||
]
|
||||
|
||||
@type registry_name :: {:via, Registry, {Livebook.HubsRegistry, String.t()}}
|
||||
|
@ -68,6 +70,14 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
:exit, _ -> "connection refused"
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a list of cached deployment groups.
|
||||
"""
|
||||
@spec get_deployment_groups(String.t()) :: list(DeploymentGroup.t())
|
||||
def get_deployment_groups(id) do
|
||||
GenServer.call(registry_name(id), :get_deployment_groups)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns if the Team client is connected.
|
||||
"""
|
||||
|
@ -125,6 +135,10 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
{:reply, state.file_systems, state}
|
||||
end
|
||||
|
||||
def handle_call(:get_deployment_groups, _caller, state) do
|
||||
{:reply, state.deployment_groups, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:connected, state) do
|
||||
Hubs.Broadcasts.hub_connected(state.hub.id)
|
||||
|
@ -198,6 +212,22 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
FileSystems.load(file_system.type, dumped_data)
|
||||
end
|
||||
|
||||
defp put_deployment_group(state, deployment_group) do
|
||||
state = remove_deployment_group(state, deployment_group)
|
||||
%{state | deployment_groups: [deployment_group | state.deployment_groups]}
|
||||
end
|
||||
|
||||
defp remove_deployment_group(state, deployment_group) do
|
||||
%{
|
||||
state
|
||||
| deployment_groups: Enum.reject(state.deployment_groups, &(&1.id == deployment_group.id))
|
||||
}
|
||||
end
|
||||
|
||||
defp build_deployment_group(state, %{id: id, name: name, mode: mode}) do
|
||||
%DeploymentGroup{id: id, name: name, mode: mode, hub_id: state.hub.id}
|
||||
end
|
||||
|
||||
defp handle_event(:secret_created, %Secrets.Secret{} = secret, state) do
|
||||
Hubs.Broadcasts.secret_created(secret)
|
||||
|
||||
|
@ -261,10 +291,49 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
end
|
||||
end
|
||||
|
||||
defp handle_event(:deployment_group_created, %DeploymentGroup{} = deployment_group, state) do
|
||||
Teams.Broadcasts.deployment_group_created(deployment_group)
|
||||
|
||||
put_deployment_group(state, deployment_group)
|
||||
end
|
||||
|
||||
defp handle_event(:deployment_group_created, deployment_group_created, state) do
|
||||
handle_event(
|
||||
:deployment_group_created,
|
||||
build_deployment_group(state, deployment_group_created),
|
||||
state
|
||||
)
|
||||
end
|
||||
|
||||
defp handle_event(:deployment_group_updated, %DeploymentGroup{} = deployment_group, state) do
|
||||
Teams.Broadcasts.deployment_group_updated(deployment_group)
|
||||
|
||||
put_deployment_group(state, deployment_group)
|
||||
end
|
||||
|
||||
defp handle_event(:deployment_group_updated, deployment_group_updated, state) do
|
||||
handle_event(
|
||||
:deployment_group_updated,
|
||||
build_deployment_group(state, deployment_group_updated),
|
||||
state
|
||||
)
|
||||
end
|
||||
|
||||
defp handle_event(:deployment_group_deleted, deployment_group_deleted, state) do
|
||||
if deployment_group =
|
||||
Enum.find(state.deployment_groups, &(&1.id == deployment_group_deleted.id)) do
|
||||
Teams.Broadcasts.deployment_group_deleted(deployment_group)
|
||||
remove_deployment_group(state, deployment_group)
|
||||
else
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_event(:user_connected, user_connected, state) do
|
||||
state
|
||||
|> dispatch_secrets(user_connected)
|
||||
|> dispatch_file_systems(user_connected)
|
||||
|> dispatch_deployment_groups(user_connected)
|
||||
end
|
||||
|
||||
defp dispatch_secrets(state, %{secrets: secrets}) do
|
||||
|
@ -305,6 +374,19 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
|
||||
defp dispatch_file_systems(state, _), do: state
|
||||
|
||||
defp dispatch_deployment_groups(state, %{deployment_groups: deployment_groups}) do
|
||||
decrypted_deployment_groups = Enum.map(deployment_groups, &build_deployment_group(state, &1))
|
||||
|
||||
{created, deleted, updated} =
|
||||
diff(state.deployment_groups, decrypted_deployment_groups, &(&1.id == &2.id))
|
||||
|
||||
dispatch_events(state,
|
||||
deployment_group_deleted: deleted,
|
||||
deployment_group_created: created,
|
||||
deployment_group_updated: updated
|
||||
)
|
||||
end
|
||||
|
||||
defp diff(old_list, new_list, fun, deleted_fun \\ nil, updated_fun \\ nil) do
|
||||
deleted_fun = unless deleted_fun, do: fun, else: deleted_fun
|
||||
updated_fun = unless updated_fun, do: fun, else: updated_fun
|
||||
|
|
|
@ -3,7 +3,8 @@ defmodule Livebook.Teams do
|
|||
|
||||
alias Livebook.Hubs
|
||||
alias Livebook.Hubs.Team
|
||||
alias Livebook.Teams.{Requests, Org}
|
||||
alias Livebook.Hubs.TeamClient
|
||||
alias Livebook.Teams.{Requests, Org, DeploymentGroup}
|
||||
|
||||
import Ecto.Changeset,
|
||||
only: [add_error: 3, apply_action: 2, apply_action!: 2, get_field: 2]
|
||||
|
@ -158,4 +159,79 @@ defmodule Livebook.Teams do
|
|||
defp add_org_errors(%Ecto.Changeset{} = changeset, errors_map) do
|
||||
Requests.add_errors(changeset, Org.__schema__(:fields), errors_map)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking deployment group changes.
|
||||
"""
|
||||
@spec change_deployment_group(DeploymentGroup.t(), map()) :: Ecto.Changeset.t()
|
||||
def change_deployment_group(%DeploymentGroup{} = deployment_group, attrs \\ %{}) do
|
||||
DeploymentGroup.changeset(deployment_group, attrs)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a deployment group with the given changes.
|
||||
"""
|
||||
@spec update_deployment_group(Team.t(), DeploymentGroup.t()) ::
|
||||
{:ok, pos_integer()}
|
||||
| {:error, Ecto.Changeset.t()}
|
||||
| {:transport_error, String.t()}
|
||||
def update_deployment_group(%Team{} = team, deployment_group) do
|
||||
case Requests.update_deployment_group(team, deployment_group) do
|
||||
{:ok, %{"id" => id}} ->
|
||||
{:ok, id}
|
||||
|
||||
{:error, %{"errors" => errors}} ->
|
||||
{:error, Requests.add_errors(deployment_group, errors)}
|
||||
|
||||
any ->
|
||||
any
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a Deployment Group.
|
||||
"""
|
||||
@spec create_deployment_group(Team.t(), DeploymentGroup.t()) ::
|
||||
{:ok, pos_integer()}
|
||||
| {:error, Ecto.Changeset.t()}
|
||||
| {:transport_error, String.t()}
|
||||
def create_deployment_group(%Team{} = team, deployment_group) do
|
||||
case Requests.create_deployment_group(team, deployment_group) do
|
||||
{:ok, %{"id" => id}} ->
|
||||
{:ok, id}
|
||||
|
||||
{:error, %{"errors" => errors}} ->
|
||||
{:error, Requests.add_errors(deployment_group, errors)}
|
||||
|
||||
any ->
|
||||
any
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a Deployment Group.
|
||||
"""
|
||||
@spec delete_deployment_group(Team.t(), DeploymentGroup.t()) ::
|
||||
:ok
|
||||
| {:error, Ecto.Changeset.t()}
|
||||
| {:transport_error, String.t()}
|
||||
def delete_deployment_group(%Team{} = team, deployment_group) do
|
||||
case Requests.delete_deployment_group(team, deployment_group) do
|
||||
{:ok, _} ->
|
||||
:ok
|
||||
|
||||
{:error, %{"errors" => errors}} ->
|
||||
{:error, Requests.add_errors(deployment_group, errors)}
|
||||
|
||||
any ->
|
||||
any
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a list of deployment groups for a given Hub.
|
||||
"""
|
||||
def get_deployment_groups(team) do
|
||||
TeamClient.get_deployment_groups(team.id)
|
||||
end
|
||||
end
|
||||
|
|
68
lib/livebook/teams/broadcasts.ex
Normal file
68
lib/livebook/teams/broadcasts.ex
Normal file
|
@ -0,0 +1,68 @@
|
|||
defmodule Livebook.Teams.Broadcasts do
|
||||
alias Livebook.Teams.DeploymentGroup
|
||||
|
||||
@type broadcast :: :ok | {:error, term()}
|
||||
|
||||
@deployment_groups_topic "teams:deployment_groups"
|
||||
|
||||
@doc """
|
||||
Subscribes to one or more subtopics in `"teams"`.
|
||||
## Messages
|
||||
Topic `teams:deployment_groups`:
|
||||
* `{:deployment_group_created, DeploymentGroup.t()}`
|
||||
* `{:deployment_group_updated, DeploymentGroup.t()}`
|
||||
* `{:deployment_group_deleted, DeploymentGroup.t()}`
|
||||
"""
|
||||
@spec subscribe(atom() | list(atom())) :: :ok | {:error, term()}
|
||||
def subscribe(topics) when is_list(topics) do
|
||||
for topic <- topics, do: subscribe(topic)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def subscribe(topic) do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, "teams:#{topic}")
|
||||
end
|
||||
|
||||
@doc """
|
||||
Unsubscribes from `subscribe/0`.
|
||||
"""
|
||||
@spec unsubscribe(atom() | list(atom())) :: :ok
|
||||
def unsubscribe(topics) when is_list(topics) do
|
||||
for topic <- topics, do: unsubscribe(topic)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def unsubscribe(topic) do
|
||||
Phoenix.PubSub.unsubscribe(Livebook.PubSub, "teams:#{topic}")
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts under `#{@deployment_groups_topic}` topic when hub received a new deployment group.
|
||||
"""
|
||||
@spec deployment_group_created(DeploymentGroup.t()) :: broadcast()
|
||||
def deployment_group_created(%DeploymentGroup{} = deployment_group) do
|
||||
broadcast(@deployment_groups_topic, {:deployment_group_created, deployment_group})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts under `#{@deployment_groups_topic}` topic when hub received an updated deployment group.
|
||||
"""
|
||||
@spec deployment_group_updated(DeploymentGroup.t()) :: broadcast()
|
||||
def deployment_group_updated(%DeploymentGroup{} = deployment_group) do
|
||||
broadcast(@deployment_groups_topic, {:deployment_group_updated, deployment_group})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts under `#{@deployment_groups_topic}` topic when hub received a deleted deployment group.
|
||||
"""
|
||||
@spec deployment_group_deleted(DeploymentGroup.t()) :: broadcast()
|
||||
def deployment_group_deleted(%DeploymentGroup{} = deployment_group) do
|
||||
broadcast(@deployment_groups_topic, {:deployment_group_deleted, deployment_group})
|
||||
end
|
||||
|
||||
defp broadcast(topic, message) do
|
||||
Phoenix.PubSub.broadcast(Livebook.PubSub, topic, message)
|
||||
end
|
||||
end
|
24
lib/livebook/teams/deployment_group.ex
Normal file
24
lib/livebook/teams/deployment_group.ex
Normal file
|
@ -0,0 +1,24 @@
|
|||
defmodule Livebook.Teams.DeploymentGroup do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
id: pos_integer() | nil,
|
||||
name: String.t() | nil,
|
||||
mode: :online | :offline,
|
||||
hub_id: String.t() | nil
|
||||
}
|
||||
|
||||
@primary_key {:id, :id, autogenerate: false}
|
||||
embedded_schema do
|
||||
field :name, :string
|
||||
field :mode, Ecto.Enum, values: [:online, :offline]
|
||||
field :hub_id, :string
|
||||
end
|
||||
|
||||
def changeset(deployment_group, attrs \\ %{}) do
|
||||
deployment_group
|
||||
|> cast(attrs, [:id, :name, :mode, :hub_id])
|
||||
|> validate_required([:name, :mode])
|
||||
end
|
||||
end
|
|
@ -6,6 +6,7 @@ defmodule Livebook.Teams.Requests do
|
|||
alias Livebook.Teams
|
||||
alias Livebook.Teams.Org
|
||||
alias Livebook.Utils.HTTP
|
||||
alias Livebook.Teams.DeploymentGroup
|
||||
|
||||
@doc """
|
||||
Send a request to Livebook Team API to create a new org.
|
||||
|
@ -145,6 +146,42 @@ defmodule Livebook.Teams.Requests do
|
|||
delete("/api/v1/org/file-systems", params, headers)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Send a request to Livebook Team API to create a deployment group.
|
||||
"""
|
||||
@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
|
||||
headers = auth_headers(team)
|
||||
params = %{name: deployment_group.name, mode: deployment_group.mode}
|
||||
|
||||
post("/api/v1/org/deployment-groups", params, headers)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Send a request to Livebook Team API to update a deployment group.
|
||||
"""
|
||||
@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
|
||||
headers = auth_headers(team)
|
||||
params = %{id: deployment_group.id, name: deployment_group.name, mode: deployment_group.mode}
|
||||
|
||||
put("/api/v1/org/deployment-groups", params, headers)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Send a request to Livebook Team API to delete a deployment group.
|
||||
"""
|
||||
@spec delete_deployment_group(Team.t(), DeploymentGroup.t()) ::
|
||||
{:ok, String.t()} | {:error, map() | String.t()} | {:transport_error, String.t()}
|
||||
def delete_deployment_group(team, deployment_group) do
|
||||
headers = auth_headers(team)
|
||||
params = %{id: deployment_group.id}
|
||||
|
||||
delete("/api/v1/org/deployment-groups", params, headers)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Add requests errors to a `changeset` for the given `fields`.
|
||||
"""
|
||||
|
|
|
@ -14,8 +14,10 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
|||
show_key = assigns.params["show-key"]
|
||||
secrets = Hubs.get_secrets(assigns.hub)
|
||||
file_systems = Hubs.get_file_systems(assigns.hub, hub_only: true)
|
||||
deployment_groups = Teams.get_deployment_groups(assigns.hub)
|
||||
secret_name = assigns.params["secret_name"]
|
||||
file_system_id = assigns.params["file_system_id"]
|
||||
deployment_group_id = assigns.params["deployment_group_id"]
|
||||
default? = default_hub?(assigns.hub)
|
||||
|
||||
secret_value =
|
||||
|
@ -30,6 +32,15 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
|||
raise(NotFoundError, "could not find file system matching #{inspect(file_system_id)}")
|
||||
end
|
||||
|
||||
deployment_group =
|
||||
if assigns.live_action == :edit_deployment_group do
|
||||
Enum.find_value(deployment_groups, &(&1.id == deployment_group_id && &1)) ||
|
||||
raise(
|
||||
NotFoundError,
|
||||
"could not find deployment group matching #{inspect(deployment_group_id)}"
|
||||
)
|
||||
end
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(
|
||||
|
@ -37,6 +48,9 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
|||
file_system: file_system,
|
||||
file_system_id: file_system_id,
|
||||
file_systems: file_systems,
|
||||
deployment_group_id: deployment_group_id,
|
||||
deployment_group: deployment_group,
|
||||
deployment_groups: deployment_groups,
|
||||
show_key: show_key,
|
||||
secret_name: secret_name,
|
||||
secret_value: secret_value,
|
||||
|
@ -193,6 +207,24 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-medium pb-2 border-b border-gray-200">
|
||||
Deployment groups
|
||||
</h2>
|
||||
|
||||
<p class="text-gray-700">
|
||||
Deployment groups.
|
||||
</p>
|
||||
|
||||
<.live_component
|
||||
module={LivebookWeb.Hub.Teams.DeploymentGroupListComponent}
|
||||
id="hub-deployment-groups-list"
|
||||
hub_id={@hub.id}
|
||||
deployment_groups={@deployment_groups}
|
||||
target={@myself}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-medium pb-2 border-b border-gray-200">
|
||||
Airgapped deployment
|
||||
|
@ -292,6 +324,23 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
|||
return_to={~p"/hub/#{@hub.id}"}
|
||||
/>
|
||||
</.modal>
|
||||
|
||||
<.modal
|
||||
:if={@live_action in [:new_deployment_group, :edit_deployment_group]}
|
||||
id="deployment-groups-modal"
|
||||
show
|
||||
width={:medium}
|
||||
patch={~p"/hub/#{@hub.id}"}
|
||||
>
|
||||
<.live_component
|
||||
module={LivebookWeb.Hub.Teams.DeploymentGroupFormComponent}
|
||||
id="deployment-groups"
|
||||
hub={@hub}
|
||||
deployment_group_id={@deployment_group_id}
|
||||
deployment_group={@deployment_group}
|
||||
return_to={~p"/hub/#{@hub.id}"}
|
||||
/>
|
||||
</.modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
|
||||
use LivebookWeb, :live_component
|
||||
|
||||
alias Livebook.Teams.DeploymentGroup
|
||||
alias Livebook.Teams
|
||||
|
||||
@impl true
|
||||
def update(assigns, socket) do
|
||||
deployment_group = assigns.deployment_group
|
||||
|
||||
deployment_group = deployment_group || %DeploymentGroup{hub_id: assigns.hub.id}
|
||||
changeset = Teams.change_deployment_group(deployment_group)
|
||||
|
||||
socket = assign(socket, assigns)
|
||||
|
||||
{:ok,
|
||||
assign(socket,
|
||||
deployment_group: deployment_group,
|
||||
changeset: changeset,
|
||||
mode: mode(deployment_group),
|
||||
title: title(deployment_group),
|
||||
button: button(deployment_group),
|
||||
error_message: nil
|
||||
)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="p-6 max-w-4xl flex flex-col space-y-5">
|
||||
<h3 class="text-2xl font-semibold text-gray-800">
|
||||
<%= @title %>
|
||||
</h3>
|
||||
<div class="flex flex-columns gap-4">
|
||||
<.form
|
||||
:let={f}
|
||||
id={"#{@id}-form"}
|
||||
for={to_form(@changeset, as: :deployment_group)}
|
||||
phx-target={@myself}
|
||||
phx-change="validate"
|
||||
phx-submit="save"
|
||||
autocomplete="off"
|
||||
class="basis-1/2 grow"
|
||||
>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<.text_field
|
||||
field={f[:name]}
|
||||
label="Name"
|
||||
autofocus="true"
|
||||
spellcheck="false"
|
||||
autocomplete="off"
|
||||
phx-debounce
|
||||
/>
|
||||
<.select_field
|
||||
label="Mode"
|
||||
help={
|
||||
~S'''
|
||||
Deployment group mode.
|
||||
'''
|
||||
}
|
||||
field={f[:mode]}
|
||||
options={[
|
||||
{"Offline", "offline"},
|
||||
{"Online", "online"}
|
||||
]}
|
||||
/>
|
||||
<div class="flex space-x-2">
|
||||
<button class="button-base button-blue" type="submit" disabled={not @changeset.valid?}>
|
||||
<.remix_icon icon={@button.icon} class="align-middle mr-1" />
|
||||
<span class="font-normal"><%= @button.label %></span>
|
||||
</button>
|
||||
<.link patch={@return_to} class="button-base button-outlined-gray">
|
||||
Cancel
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
</.form>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("save", %{"deployment_group" => attrs}, socket) do
|
||||
changeset = Teams.change_deployment_group(socket.assigns.deployment_group, attrs)
|
||||
|
||||
with {:ok, deployment_group} <- Ecto.Changeset.apply_action(changeset, :update),
|
||||
{:ok, _id} <- save_deployment_group(deployment_group, socket) do
|
||||
message =
|
||||
case socket.assigns.mode do
|
||||
:new -> "Deployment group #{deployment_group.name} added successfully"
|
||||
:edit -> "Deployment group #{deployment_group.name} updated successfully"
|
||||
end
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:success, message)
|
||||
|> push_redirect(to: socket.assigns.return_to)}
|
||||
else
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign(socket, changeset: changeset)}
|
||||
|
||||
{:transport_error, message} ->
|
||||
{:noreply, assign(socket, error_message: message)}
|
||||
|
||||
{:error, message} ->
|
||||
{:noreply, assign(socket, error_message: message)}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("validate", %{"deployment_group" => attrs}, socket) do
|
||||
changeset =
|
||||
%DeploymentGroup{}
|
||||
|> DeploymentGroup.changeset(attrs)
|
||||
|> Map.replace!(:action, :validate)
|
||||
|
||||
{:noreply, assign(socket, changeset: changeset)}
|
||||
end
|
||||
|
||||
defp save_deployment_group(deployment_group, socket) do
|
||||
case socket.assigns.mode do
|
||||
:new -> Teams.create_deployment_group(socket.assigns.hub, deployment_group)
|
||||
:edit -> Teams.update_deployment_group(socket.assigns.hub, deployment_group)
|
||||
end
|
||||
end
|
||||
|
||||
defp mode(%DeploymentGroup{name: nil}), do: :new
|
||||
defp mode(_), do: :edit
|
||||
|
||||
defp title(%DeploymentGroup{name: nil}), do: "Add deployment group"
|
||||
defp title(_), do: "Edit deployment group"
|
||||
|
||||
defp button(%DeploymentGroup{name: nil}), do: %{icon: "add-line", label: "Add"}
|
||||
defp button(_), do: %{icon: "save-line", label: "Save"}
|
||||
end
|
|
@ -0,0 +1,100 @@
|
|||
defmodule LivebookWeb.Hub.Teams.DeploymentGroupListComponent do
|
||||
use LivebookWeb, :live_component
|
||||
|
||||
alias Livebook.Teams
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div id={@id} class="flex flex-col space-y-4">
|
||||
<div class="flex flex-col space-y-4">
|
||||
<.no_entries :if={@deployment_groups == []}>
|
||||
No deployment groups in this Hub yet.
|
||||
</.no_entries>
|
||||
<div
|
||||
:for={deployment_group <- @deployment_groups}
|
||||
class="flex items-center justify-between border border-gray-200 rounded-lg p-4"
|
||||
>
|
||||
<div class="flex items-center space-x-12">
|
||||
<.labeled_text label="Name"><%= deployment_group.name %></.labeled_text>
|
||||
<.labeled_text label="Mode"><%= deployment_group.mode %></.labeled_text>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<.menu id={"hub-deployment-group-#{deployment_group.id}-menu"}>
|
||||
<:toggle>
|
||||
<button class="icon-button" aria-label="open deployment group menu" type="button">
|
||||
<.remix_icon icon="more-2-fill" class="text-xl" />
|
||||
</button>
|
||||
</:toggle>
|
||||
<.menu_item>
|
||||
<.link
|
||||
id={"hub-deployment-group-#{deployment_group.id}-edit"}
|
||||
patch={~p"/hub/#{@hub_id}/deployment-groups/edit/#{deployment_group.id}"}
|
||||
type="button"
|
||||
role="menuitem"
|
||||
>
|
||||
<.remix_icon icon="file-edit-line" />
|
||||
<span>Edit</span>
|
||||
</.link>
|
||||
</.menu_item>
|
||||
<.menu_item variant={:danger}>
|
||||
<button
|
||||
id={"hub-deployment-group-#{deployment_group.id}-delete"}
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="text-red-600"
|
||||
phx-click={
|
||||
JS.push("delete_deployment_group",
|
||||
value: %{id: deployment_group.id, name: deployment_group.name}
|
||||
)
|
||||
}
|
||||
phx-target={@myself}
|
||||
>
|
||||
<.remix_icon icon="delete-bin-line" />
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
</.menu_item>
|
||||
</.menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<.link
|
||||
patch={~p"/hub/#{@hub_id}/deployment-groups/new"}
|
||||
class="button-base button-blue"
|
||||
id="add-deployment-group"
|
||||
>
|
||||
Add deployment group
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("delete_deployment_group", %{"id" => id, "name" => name}, socket) do
|
||||
on_confirm = fn socket ->
|
||||
hub = Livebook.Hubs.fetch_hub!(socket.assigns.hub.id)
|
||||
deployment_groups = Teams.get_deployment_groups(hub)
|
||||
deployment_group = Enum.find(deployment_groups, &(&1.id == id))
|
||||
|
||||
case Teams.delete_deployment_group(hub, deployment_group) do
|
||||
:ok ->
|
||||
socket
|
||||
|> put_flash(:success, "Deployment group #{deployment_group.name} deleted successfully")
|
||||
|> push_navigate(to: ~p"/hub/#{hub.id}")
|
||||
|
||||
{:transport_error, reason} ->
|
||||
put_flash(socket, :error, reason)
|
||||
end
|
||||
end
|
||||
|
||||
{:noreply,
|
||||
confirm(socket, on_confirm,
|
||||
title: "Delete hub deployment group",
|
||||
description: "Are you sure you want to delete #{name}?",
|
||||
confirm_text: "delete",
|
||||
confirm_icon: "delete-bin-6-line"
|
||||
)}
|
||||
end
|
||||
end
|
|
@ -83,6 +83,12 @@ defmodule LivebookWeb.Router do
|
|||
live "/hub/:id/secrets/edit/:secret_name", Hub.EditLive, :edit_secret, as: :hub
|
||||
live "/hub/:id/file-systems/new", Hub.EditLive, :new_file_system, as: :hub
|
||||
live "/hub/:id/file-systems/edit/:file_system_id", Hub.EditLive, :edit_file_system, as: :hub
|
||||
live "/hub/:id/deployment-groups/new", Hub.EditLive, :new_deployment_group, as: :hub
|
||||
|
||||
live "/hub/:id/deployment-groups/edit/:deployment_group_id",
|
||||
Hub.EditLive,
|
||||
:edit_deployment_group,
|
||||
as: :hub
|
||||
|
||||
live "/sessions/:id", SessionLive, :page
|
||||
live "/sessions/:id/shortcuts", SessionLive, :shortcuts
|
||||
|
|
|
@ -7,6 +7,9 @@ defmodule LivebookProto do
|
|||
SecretCreated,
|
||||
SecretDeleted,
|
||||
SecretUpdated,
|
||||
DeploymentGroupCreated,
|
||||
DeploymentGroupDeleted,
|
||||
DeploymentGroupUpdated,
|
||||
UserSynchronized
|
||||
}
|
||||
|
||||
|
@ -22,6 +25,9 @@ defmodule LivebookProto do
|
|||
| SecretCreated.t()
|
||||
| SecretDeleted.t()
|
||||
| SecretUpdated.t()
|
||||
| DeploymentGroupCreated.t()
|
||||
| DeploymentGroupDeleted.t()
|
||||
| DeploymentGroupUpdated.t()
|
||||
| UserSynchronized.t()
|
||||
|
||||
@doc """
|
||||
|
|
7
proto/lib/livebook_proto/deployment_group.pb.ex
Normal file
7
proto/lib/livebook_proto/deployment_group.pb.ex
Normal file
|
@ -0,0 +1,7 @@
|
|||
defmodule LivebookProto.DeploymentGroup do
|
||||
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0"
|
||||
|
||||
field :id, 1, type: :string
|
||||
field :name, 2, type: :string
|
||||
field :mode, 3, type: :string
|
||||
end
|
7
proto/lib/livebook_proto/deployment_group_created.pb.ex
Normal file
7
proto/lib/livebook_proto/deployment_group_created.pb.ex
Normal file
|
@ -0,0 +1,7 @@
|
|||
defmodule LivebookProto.DeploymentGroupCreated do
|
||||
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0"
|
||||
|
||||
field :id, 1, type: :string
|
||||
field :name, 2, type: :string
|
||||
field :mode, 3, type: :string
|
||||
end
|
5
proto/lib/livebook_proto/deployment_group_deleted.pb.ex
Normal file
5
proto/lib/livebook_proto/deployment_group_deleted.pb.ex
Normal file
|
@ -0,0 +1,5 @@
|
|||
defmodule LivebookProto.DeploymentGroupDeleted do
|
||||
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0"
|
||||
|
||||
field :id, 1, type: :string
|
||||
end
|
7
proto/lib/livebook_proto/deployment_group_updated.pb.ex
Normal file
7
proto/lib/livebook_proto/deployment_group_updated.pb.ex
Normal file
|
@ -0,0 +1,7 @@
|
|||
defmodule LivebookProto.DeploymentGroupUpdated do
|
||||
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0"
|
||||
|
||||
field :id, 1, type: :string
|
||||
field :name, 2, type: :string
|
||||
field :mode, 3, type: :string
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
defmodule LivebookProto.Event do
|
||||
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0"
|
||||
|
||||
oneof :type, 0
|
||||
oneof(:type, 0)
|
||||
|
||||
field :secret_created, 1,
|
||||
type: LivebookProto.SecretCreated,
|
||||
|
@ -37,4 +37,19 @@ defmodule LivebookProto.Event do
|
|||
type: LivebookProto.FileSystemDeleted,
|
||||
json_name: "fileSystemDeleted",
|
||||
oneof: 0
|
||||
|
||||
field :deployment_group_created, 8,
|
||||
type: LivebookProto.DeploymentGroupCreated,
|
||||
json_name: "deploymentGroupCreated",
|
||||
oneof: 0
|
||||
|
||||
field :deployment_group_updated, 9,
|
||||
type: LivebookProto.DeploymentGroupUpdated,
|
||||
json_name: "deploymentGroupUpdated",
|
||||
oneof: 0
|
||||
|
||||
field :deployment_group_deleted, 10,
|
||||
type: LivebookProto.DeploymentGroupDeleted,
|
||||
json_name: "deploymentGroupDeleted",
|
||||
oneof: 0
|
||||
end
|
||||
|
|
|
@ -4,4 +4,9 @@ defmodule LivebookProto.UserConnected do
|
|||
field :name, 1, type: :string
|
||||
field :secrets, 2, repeated: true, type: LivebookProto.Secret
|
||||
field :file_systems, 3, repeated: true, type: LivebookProto.FileSystem, json_name: "fileSystems"
|
||||
|
||||
field :deployment_groups, 4,
|
||||
repeated: true,
|
||||
type: LivebookProto.DeploymentGroup,
|
||||
json_name: "deploymentGroups"
|
||||
end
|
||||
|
|
|
@ -48,6 +48,28 @@ message FileSystemDeleted {
|
|||
string id = 1;
|
||||
}
|
||||
|
||||
message DeploymentGroup {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string mode = 3;
|
||||
}
|
||||
|
||||
message DeploymentGroupCreated {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string mode = 3;
|
||||
}
|
||||
|
||||
message DeploymentGroupUpdated {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string mode = 3;
|
||||
}
|
||||
|
||||
message DeploymentGroupDeleted {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message UserConnected {
|
||||
string name = 1;
|
||||
repeated Secret secrets = 2;
|
||||
|
@ -63,5 +85,8 @@ message Event {
|
|||
FileSystemCreated file_system_created = 5;
|
||||
FileSystemUpdated file_system_updated = 6;
|
||||
FileSystemDeleted file_system_deleted = 7;
|
||||
DeploymentGroupCreated deployment_group_created = 8;
|
||||
DeploymentGroupUpdated deployment_group_updated = 9;
|
||||
DeploymentGroupDeleted deployment_group_deleted = 10;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
|
||||
setup do
|
||||
Livebook.Hubs.Broadcasts.subscribe([:connection, :file_systems, :secrets])
|
||||
Livebook.Teams.Broadcasts.subscribe([:deployment_groups])
|
||||
:ok
|
||||
end
|
||||
|
||||
|
@ -178,6 +179,83 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
# receives `{:file_system_deleted, file_system_deleted}` event
|
||||
assert_receive {:file_system_deleted, %{external_id: ^id, bucket_url: ^bucket_url}}
|
||||
end
|
||||
|
||||
test "receives the deployment_group_created event", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
deployment_group =
|
||||
build(:deployment_group, name: "DEPLOYMENT_GROUP_CREATED_FOO", mode: "online")
|
||||
|
||||
assert {:ok, _id} =
|
||||
Livebook.Teams.create_deployment_group(team, deployment_group)
|
||||
|
||||
%{name: name, mode: mode} = deployment_group
|
||||
# receives `{:event, :deployment_group_created, deployment_group_created}` event
|
||||
assert_receive {:deployment_group_created, %{name: ^name, mode: ^mode}}
|
||||
end
|
||||
|
||||
test "receives the deployment_group_updated event", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
deployment_group =
|
||||
build(:deployment_group, name: "DEPLOYMENT_GROUP_UPDATED_FOO", mode: "offline")
|
||||
|
||||
assert {:ok, id} =
|
||||
Livebook.Teams.create_deployment_group(team, deployment_group)
|
||||
|
||||
%{name: name, mode: mode} = deployment_group
|
||||
|
||||
# receives `{:deployment_group_created, deployment_group_created}` event
|
||||
assert_receive {:deployment_group_created, %{name: ^name, mode: ^mode}}
|
||||
|
||||
# updates the deployment group
|
||||
update_deployment_group = %{deployment_group | id: id, mode: "online"}
|
||||
|
||||
assert {:ok, ^id} =
|
||||
Livebook.Teams.update_deployment_group(
|
||||
team,
|
||||
update_deployment_group
|
||||
)
|
||||
|
||||
new_mode = update_deployment_group.mode
|
||||
|
||||
# receives `{:deployment_group_updated, deployment_group_updated}` event
|
||||
assert_receive {:deployment_group_updated, %{name: ^name, mode: ^new_mode}}
|
||||
end
|
||||
|
||||
test "receives the deployment_group_deleted event", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
deployment_group =
|
||||
build(:deployment_group, name: "DEPLOYMENT_GROUP_DELETED_FOO", mode: "online")
|
||||
|
||||
assert {:ok, id} =
|
||||
Livebook.Teams.create_deployment_group(team, deployment_group)
|
||||
|
||||
name = deployment_group.name
|
||||
mode = deployment_group.mode
|
||||
|
||||
# receives `{:deployment_group_created, deployment_group_created}` event
|
||||
assert_receive {:deployment_group_created, %{name: ^name, mode: ^mode}}
|
||||
|
||||
# deletes the deployment group
|
||||
assert Livebook.Teams.delete_deployment_group(team, %{
|
||||
deployment_group
|
||||
| id: id
|
||||
}) == :ok
|
||||
|
||||
# receives `{:deployment_group_deleted, deployment_group_deleted}` event
|
||||
assert_receive {:deployment_group_deleted, %{name: ^name, mode: ^mode}}
|
||||
end
|
||||
end
|
||||
|
||||
describe "user connected event" do
|
||||
|
|
|
@ -72,5 +72,23 @@ defmodule Livebook.Teams.ConnectionTest do
|
|||
refute file_system_created.value == FileSystem.dump(file_system)
|
||||
assert is_binary(file_system_created.value)
|
||||
end
|
||||
|
||||
test "receives the deployment_group_created event", %{user: user, node: node} do
|
||||
{hub, headers} = build_team_headers(user, node)
|
||||
|
||||
assert {:ok, _conn} = Connection.start_link(self(), headers)
|
||||
assert_receive :connected
|
||||
|
||||
# creates a new deployment_group
|
||||
deployment_group = build(:deployment_group, name: "FOO", mode: "offline")
|
||||
|
||||
assert {:ok, _id} =
|
||||
Livebook.Teams.create_deployment_group(hub, deployment_group)
|
||||
|
||||
# deployment_group name and mode are not encrypted
|
||||
assert_receive {:event, :deployment_group_created, deployment_group_created}
|
||||
assert deployment_group_created.name == deployment_group.name
|
||||
assert deployment_group_created.mode == deployment_group.mode
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -148,4 +148,91 @@ defmodule Livebook.TeamsTest do
|
|||
{:error, :expired}
|
||||
end
|
||||
end
|
||||
|
||||
describe "create_deployment_group/2" do
|
||||
test "creates a new deployment group when the data is valid", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
deployment_group = build(:deployment_group)
|
||||
|
||||
assert {:ok, _id} = Teams.create_deployment_group(team, deployment_group)
|
||||
|
||||
# Guarantee uniqueness
|
||||
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group)
|
||||
assert "has already been taken" in errors_on(changeset).name
|
||||
end
|
||||
|
||||
test "returns changeset errors when the name is invalid", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
deployment_group = %{build(:deployment_group) | name: ""}
|
||||
|
||||
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group)
|
||||
assert "can't be blank" in errors_on(changeset).name
|
||||
end
|
||||
|
||||
test "returns changeset errors when the mode is blank", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
deployment_group = %{build(:deployment_group) | mode: ""}
|
||||
|
||||
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group)
|
||||
assert "can't be blank" in errors_on(changeset).mode
|
||||
end
|
||||
|
||||
test "returns changeset errors when the mode is invalid", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
deployment_group = %{build(:deployment_group) | mode: "invalid"}
|
||||
|
||||
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group)
|
||||
assert "is invalid" in errors_on(changeset).mode
|
||||
end
|
||||
end
|
||||
|
||||
describe "update_deployment_group/2" do
|
||||
test "updates a deployment group", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
deployment_group = build(:deployment_group, name: "BAR", mode: "online")
|
||||
|
||||
assert {:ok, id} = Teams.create_deployment_group(team, deployment_group)
|
||||
|
||||
update_deployment_group = %{deployment_group | id: id, name: "BAZ"}
|
||||
assert {:ok, ^id} = Teams.update_deployment_group(team, update_deployment_group)
|
||||
end
|
||||
|
||||
test "returns changeset errors when the new name is invalid", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
deployment_group = build(:deployment_group, name: "BAR", mode: "online")
|
||||
|
||||
assert {:ok, id} = Teams.create_deployment_group(team, deployment_group)
|
||||
|
||||
update_deployment_group = %{deployment_group | id: id, name: ""}
|
||||
assert {:error, changeset} = Teams.update_deployment_group(team, update_deployment_group)
|
||||
assert "can't be blank" in errors_on(changeset).name
|
||||
end
|
||||
|
||||
test "returns changeset errors when the new mode is invalid", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
deployment_group = build(:deployment_group, name: "BAR", mode: "online")
|
||||
|
||||
assert {:ok, id} = Teams.create_deployment_group(team, deployment_group)
|
||||
|
||||
update_deployment_group = %{deployment_group | id: id, mode: ""}
|
||||
assert {:error, changeset} = Teams.update_deployment_group(team, update_deployment_group)
|
||||
assert "can't be blank" in errors_on(changeset).mode
|
||||
|
||||
update_deployment_group = %{deployment_group | id: id, mode: "invalid"}
|
||||
assert {:error, changeset} = Teams.update_deployment_group(team, update_deployment_group)
|
||||
assert "is invalid" in errors_on(changeset).mode
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_deployment_group/2" do
|
||||
test "deletes a deployment group", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
deployment_group = build(:deployment_group, name: "BAR", mode: "online")
|
||||
|
||||
assert {:ok, id} = Teams.create_deployment_group(team, deployment_group)
|
||||
|
||||
delete_deployment_group = %{deployment_group | id: id}
|
||||
assert Teams.delete_deployment_group(team, delete_deployment_group) == :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
||||
alias Livebook.Teams.DeploymentGroup
|
||||
use Livebook.TeamsIntegrationCase, async: true
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
|
@ -8,6 +9,7 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
|
||||
setup %{user: user, node: node} do
|
||||
Livebook.Hubs.Broadcasts.subscribe([:crud, :connection, :secrets, :file_systems])
|
||||
Livebook.Teams.Broadcasts.subscribe([:deployment_groups])
|
||||
hub = create_team_hub(user, node)
|
||||
id = hub.id
|
||||
|
||||
|
@ -304,6 +306,151 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
refute render(element(view, "#hub-file-systems-list")) =~ file_system.bucket_url
|
||||
refute file_system in Livebook.Hubs.get_file_systems(hub)
|
||||
end
|
||||
|
||||
test "creates a deployment group", %{conn: conn, hub: hub} do
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
deployment_group =
|
||||
build(:deployment_group,
|
||||
name: "TEAM_ADD_DEPLOYMENT_GROUP",
|
||||
mode: "offline",
|
||||
hub_id: hub.id
|
||||
)
|
||||
|
||||
attrs = %{
|
||||
deployment_group: %{
|
||||
name: deployment_group.name,
|
||||
value: deployment_group.mode,
|
||||
hub_id: deployment_group.hub_id
|
||||
}
|
||||
}
|
||||
|
||||
refute render(view) =~ deployment_group.name
|
||||
|
||||
view
|
||||
|> element("#add-deployment-group")
|
||||
|> render_click(%{})
|
||||
|
||||
assert_patch(view, ~p"/hub/#{hub.id}/deployment-groups/new")
|
||||
assert render(view) =~ "Add deployment group"
|
||||
|
||||
view
|
||||
|> element("#deployment-groups-form")
|
||||
|> render_change(attrs)
|
||||
|
||||
refute view
|
||||
|> element("#deployment-groups-form button[disabled]")
|
||||
|> has_element?()
|
||||
|
||||
view
|
||||
|> element("#deployment-groups-form")
|
||||
|> render_submit(attrs)
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{name: "TEAM_ADD_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
|
||||
%{"success" => "Deployment group TEAM_ADD_DEPLOYMENT_GROUP added successfully"} =
|
||||
assert_redirect(view, "/hub/#{hub.id}")
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
assert render(element(view, "#hub-deployment-groups-list")) =~ deployment_group.name
|
||||
assert deployment_group in Livebook.Teams.get_deployment_groups(hub)
|
||||
end
|
||||
|
||||
test "updates existing deployment group", %{conn: conn, hub: hub} do
|
||||
insert_deployment_group(
|
||||
name: "TEAM_EDIT_DEPLOYMENT_GROUP",
|
||||
mode: "online",
|
||||
hub_id: hub.id
|
||||
)
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{name: "TEAM_EDIT_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
attrs = %{
|
||||
deployment_group: %{
|
||||
id: deployment_group.id,
|
||||
name: deployment_group.name,
|
||||
mode: deployment_group.mode,
|
||||
hub_id: deployment_group.hub_id
|
||||
}
|
||||
}
|
||||
|
||||
new_mode = "offline"
|
||||
|
||||
view
|
||||
|> element("#hub-deployment-group-#{deployment_group.id}-edit")
|
||||
|> render_click(%{"deployment_group_name" => deployment_group.id})
|
||||
|
||||
assert_patch(view, ~p"/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}")
|
||||
assert render(view) =~ "Edit deployment group"
|
||||
|
||||
view
|
||||
|> element("#deployment-groups-form")
|
||||
|> render_change(attrs)
|
||||
|
||||
refute view
|
||||
|> element("#deployment-groups-form button[disabled]")
|
||||
|> has_element?()
|
||||
|
||||
view
|
||||
|> element("#deployment-groups-form")
|
||||
|> render_submit(put_in(attrs.deployment_group.mode, new_mode))
|
||||
|
||||
updated_deployment_group = %{deployment_group | mode: new_mode}
|
||||
|
||||
assert_receive {:deployment_group_updated, ^updated_deployment_group}
|
||||
|
||||
%{"success" => "Deployment group TEAM_EDIT_DEPLOYMENT_GROUP updated successfully"} =
|
||||
assert_redirect(view, "/hub/#{hub.id}")
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
assert render(element(view, "#hub-deployment-groups-list")) =~ deployment_group.name
|
||||
|
||||
assert updated_deployment_group in Livebook.Teams.get_deployment_groups(hub)
|
||||
end
|
||||
|
||||
test "deletes existing deployment group", %{conn: conn, hub: hub} do
|
||||
insert_deployment_group(
|
||||
name: "TEAM_DELETE_DEPLOYMENT_GROUP",
|
||||
mode: "online",
|
||||
hub_id: hub.id
|
||||
)
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{name: "TEAM_DELETE_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
refute view
|
||||
|> element("#deployment-groups-form button[disabled]")
|
||||
|> has_element?()
|
||||
|
||||
view
|
||||
|> element("#hub-deployment-group-#{deployment_group.id}-delete", "Delete")
|
||||
|> render_click()
|
||||
|
||||
render_confirm(view)
|
||||
|
||||
assert_receive {:deployment_group_deleted,
|
||||
%DeploymentGroup{name: "TEAM_DELETE_DEPLOYMENT_GROUP"}}
|
||||
|
||||
%{"success" => "Deployment group TEAM_DELETE_DEPLOYMENT_GROUP deleted successfully"} =
|
||||
assert_redirect(view, "/hub/#{hub.id}")
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
refute render(element(view, "#hub-deployment-groups-list")) =~ deployment_group.name
|
||||
refute deployment_group in Livebook.Teams.get_deployment_groups(hub)
|
||||
end
|
||||
|
||||
test "raises an error if the deployment group does not exist", %{conn: conn, hub: hub} do
|
||||
assert_raise LivebookWeb.NotFoundError, fn ->
|
||||
live(conn, ~p"/hub/#{hub.id}/deployment-groups/edit/9999999")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp expect_s3_listing(bypass) do
|
||||
|
|
|
@ -55,6 +55,13 @@ defmodule Livebook.Factory do
|
|||
}
|
||||
end
|
||||
|
||||
def build(:deployment_group) do
|
||||
%Livebook.Teams.DeploymentGroup{
|
||||
name: "FOO",
|
||||
mode: "offline"
|
||||
}
|
||||
end
|
||||
|
||||
def build(:org) do
|
||||
%Livebook.Teams.Org{
|
||||
id: nil,
|
||||
|
@ -96,6 +103,13 @@ defmodule Livebook.Factory do
|
|||
secret
|
||||
end
|
||||
|
||||
def insert_deployment_group(attrs \\ %{}) do
|
||||
deployment_group = build(:deployment_group, attrs)
|
||||
hub = Livebook.Hubs.fetch_hub!(deployment_group.hub_id)
|
||||
{:ok, _id} = Livebook.Teams.create_deployment_group(hub, deployment_group)
|
||||
deployment_group
|
||||
end
|
||||
|
||||
def insert_env_var(factory_name, attrs \\ %{}) do
|
||||
env_var = build(factory_name, attrs)
|
||||
attributes = env_var |> Map.from_struct() |> Map.to_list()
|
||||
|
|
|
@ -132,6 +132,30 @@ defmodule Livebook.HubHelpers do
|
|||
send(pid, {:event, :secret_deleted, secret_deleted})
|
||||
end
|
||||
|
||||
def put_offline_hub_deployment_group(deployment_group) do
|
||||
hub = offline_hub()
|
||||
{:ok, pid} = hub_pid(hub)
|
||||
|
||||
deployment_group_created =
|
||||
LivebookProto.DeploymentGroupCreated.new(
|
||||
id: deployment_group.id,
|
||||
name: deployment_group.name,
|
||||
mode: deployment_group.mode
|
||||
)
|
||||
|
||||
send(pid, {:event, :deployment_group_created, deployment_group_created})
|
||||
end
|
||||
|
||||
def remove_offline_hub_deployment_group(deployment_group) do
|
||||
hub = offline_hub()
|
||||
{:ok, pid} = hub_pid(hub)
|
||||
|
||||
deployment_group_deleted =
|
||||
LivebookProto.DeploymentGroupDeleted.new(id: deployment_group.id)
|
||||
|
||||
send(pid, {:event, :deployment_group_deleted, deployment_group_deleted})
|
||||
end
|
||||
|
||||
def put_offline_hub_file_system(file_system) do
|
||||
hub = offline_hub()
|
||||
{:ok, pid} = hub_pid(hub)
|
||||
|
|
Loading…
Reference in a new issue