mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-09-20 10:05:57 +08:00
Implements Livebook Agent authentication (#2403)
This commit is contained in:
parent
eb812601a3
commit
8ba6baec3a
|
@ -25,6 +25,7 @@ config :mime, :types, %{
|
|||
|
||||
config :livebook,
|
||||
teams_url: "https://teams.livebook.dev",
|
||||
agent_name: nil,
|
||||
app_service_name: nil,
|
||||
app_service_url: nil,
|
||||
authentication_mode: :token,
|
||||
|
|
|
@ -22,9 +22,10 @@ if File.exists?(data_path) do
|
|||
File.rm_rf!(data_path)
|
||||
end
|
||||
|
||||
config :livebook, :data_path, data_path
|
||||
|
||||
config :livebook, :feature_flags, deployment_groups: true
|
||||
config :livebook,
|
||||
data_path: data_path,
|
||||
feature_flags: [deployment_groups: true],
|
||||
agent_name: "chonky-cat"
|
||||
|
||||
# Use longnames when running tests in CI, so that no host resolution is required,
|
||||
# see https://github.com/livebook-dev/livebook/pull/173#issuecomment-819468549
|
||||
|
|
|
@ -229,6 +229,10 @@ defmodule Livebook do
|
|||
if dns_cluster_query = Livebook.Config.dns_cluster_query!("LIVEBOOK_CLUSTER") do
|
||||
config :livebook, :dns_cluster_query, dns_cluster_query
|
||||
end
|
||||
|
||||
if agent_name = Livebook.Config.agent_name!("LIVEBOOK_AGENT_NAME") do
|
||||
config :livebook, :agent_name, agent_name
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
@ -261,8 +261,14 @@ defmodule Livebook.Application do
|
|||
cond do
|
||||
teams_key && auth ->
|
||||
case String.split(auth, ":") do
|
||||
["offline", name, public_key] -> create_offline_hub(teams_key, name, public_key)
|
||||
_ -> Livebook.Config.abort!("Invalid LIVEBOOK_TEAMS_AUTH configuration.")
|
||||
["offline", name, public_key] ->
|
||||
create_offline_hub(teams_key, name, public_key)
|
||||
|
||||
["online", name, org_id, org_key_id, agent_key] ->
|
||||
create_online_hub(teams_key, name, org_id, org_key_id, agent_key)
|
||||
|
||||
_ ->
|
||||
Livebook.Config.abort!("Invalid LIVEBOOK_TEAMS_AUTH configuration.")
|
||||
end
|
||||
|
||||
teams_key || auth ->
|
||||
|
@ -321,9 +327,9 @@ defmodule Livebook.Application do
|
|||
id: "team-#{name}",
|
||||
hub_name: name,
|
||||
hub_emoji: "⭐️",
|
||||
user_id: 0,
|
||||
org_id: 0,
|
||||
org_key_id: 0,
|
||||
user_id: nil,
|
||||
org_id: nil,
|
||||
org_key_id: nil,
|
||||
session_token: "",
|
||||
teams_key: teams_key,
|
||||
org_public_key: public_key,
|
||||
|
@ -334,6 +340,21 @@ defmodule Livebook.Application do
|
|||
})
|
||||
end
|
||||
|
||||
defp create_online_hub(teams_key, name, org_id, org_key_id, agent_key) do
|
||||
Livebook.Hubs.save_hub(%Livebook.Hubs.Team{
|
||||
id: "team-#{name}",
|
||||
hub_name: name,
|
||||
hub_emoji: "💡",
|
||||
user_id: nil,
|
||||
org_id: org_id,
|
||||
org_key_id: org_key_id,
|
||||
session_token: agent_key,
|
||||
teams_key: teams_key,
|
||||
org_public_key: nil,
|
||||
offline: nil
|
||||
})
|
||||
end
|
||||
|
||||
defp config_env_var?("LIVEBOOK_" <> _), do: true
|
||||
defp config_env_var?("RELEASE_" <> _), do: true
|
||||
defp config_env_var?("MIX_ENV"), do: true
|
||||
|
|
|
@ -220,6 +220,14 @@ defmodule Livebook.Config do
|
|||
Application.fetch_env!(:livebook, :teams_url)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the configured name for the Livebook Agent session.
|
||||
"""
|
||||
@spec agent_name() :: String.t()
|
||||
def agent_name() do
|
||||
Application.fetch_env!(:livebook, :agent_name)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns if aws_credentials is enabled.
|
||||
"""
|
||||
|
@ -573,6 +581,13 @@ defmodule Livebook.Config do
|
|||
System.get_env(env)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Parses agent name from env.
|
||||
"""
|
||||
def agent_name!(env) do
|
||||
System.get_env(env)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Parses and validates default runtime from env.
|
||||
"""
|
||||
|
|
|
@ -230,7 +230,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Personal do
|
|||
:ok = Broadcasts.secret_deleted(secret)
|
||||
end
|
||||
|
||||
def connection_error(_personal), do: raise("not implemented")
|
||||
def connection_status(_personal), do: raise("not implemented")
|
||||
|
||||
def notebook_stamp(_hub, _notebook_source, metadata) when metadata == %{} do
|
||||
:skip
|
||||
|
|
|
@ -80,10 +80,10 @@ defprotocol Livebook.Hubs.Provider do
|
|||
def delete_secret(hub, secret)
|
||||
|
||||
@doc """
|
||||
Gets the connection error from hub.
|
||||
Gets the connection status from hub.
|
||||
"""
|
||||
@spec connection_error(t()) :: String.t() | nil
|
||||
def connection_error(hub)
|
||||
@spec connection_status(t()) :: String.t() | nil
|
||||
def connection_status(hub)
|
||||
|
||||
@doc """
|
||||
Generates a notebook stamp.
|
||||
|
|
|
@ -30,7 +30,7 @@ defmodule Livebook.Hubs.Team do
|
|||
offline: Offline.t() | nil
|
||||
}
|
||||
|
||||
@enforce_keys [:user_id, :org_id, :org_key_id, :session_token, :org_public_key, :teams_key]
|
||||
@enforce_keys [:org_id, :org_key_id, :session_token, :teams_key]
|
||||
|
||||
embedded_schema do
|
||||
field :org_id, :integer
|
||||
|
@ -140,12 +140,15 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do
|
|||
|
||||
def disconnect(team), do: TeamClient.stop(team.id)
|
||||
|
||||
def connection_error(team) do
|
||||
def connection_status(team) do
|
||||
cond do
|
||||
team.offline ->
|
||||
"You are running an offline Hub for deployment. You cannot modify its settings."
|
||||
|
||||
reason = TeamClient.get_connection_error(team.id) ->
|
||||
team.user_id == nil ->
|
||||
"You are running a Livebook Agent instance with online Hub for deployment. You are in read-only mode."
|
||||
|
||||
reason = TeamClient.get_connection_status(team.id) ->
|
||||
"Cannot connect to Hub: #{reason}.\nWill attempt to reconnect automatically..."
|
||||
|
||||
true ->
|
||||
|
|
|
@ -14,8 +14,9 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
|
||||
defstruct [
|
||||
:hub,
|
||||
:connection_error,
|
||||
:connection_status,
|
||||
:derived_key,
|
||||
:deployment_group_id,
|
||||
connected?: false,
|
||||
secrets: [],
|
||||
file_systems: [],
|
||||
|
@ -61,11 +62,11 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Returns the latest error from connection.
|
||||
Returns the latest status from connection.
|
||||
"""
|
||||
@spec get_connection_error(String.t()) :: String.t() | nil
|
||||
def get_connection_error(id) do
|
||||
GenServer.call(registry_name(id), :get_connection_error)
|
||||
@spec get_connection_status(String.t()) :: String.t() | nil
|
||||
def get_connection_status(id) do
|
||||
GenServer.call(registry_name(id), :get_connection_status)
|
||||
catch
|
||||
:exit, _ -> "connection refused"
|
||||
end
|
||||
|
@ -102,13 +103,24 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
def init(%Hubs.Team{offline: nil} = team) do
|
||||
derived_key = Teams.derive_key(team.teams_key)
|
||||
|
||||
headers = [
|
||||
{"x-lb-version", Livebook.Config.app_version()},
|
||||
{"x-user", to_string(team.user_id)},
|
||||
{"x-org", to_string(team.org_id)},
|
||||
{"x-org-key", to_string(team.org_key_id)},
|
||||
{"x-session-token", team.session_token}
|
||||
]
|
||||
headers =
|
||||
if team.user_id do
|
||||
[
|
||||
{"x-lb-version", Livebook.Config.app_version()},
|
||||
{"x-user", to_string(team.user_id)},
|
||||
{"x-org", to_string(team.org_id)},
|
||||
{"x-org-key", to_string(team.org_key_id)},
|
||||
{"x-session-token", team.session_token}
|
||||
]
|
||||
else
|
||||
[
|
||||
{"x-lb-version", Livebook.Config.app_version()},
|
||||
{"x-org", to_string(team.org_id)},
|
||||
{"x-org-key", to_string(team.org_key_id)},
|
||||
{"x-agent-name", Livebook.Config.agent_name()},
|
||||
{"x-agent-key", team.session_token}
|
||||
]
|
||||
end
|
||||
|
||||
{:ok, _pid} = Teams.Connection.start_link(self(), headers)
|
||||
{:ok, %__MODULE__{hub: team, derived_key: derived_key}}
|
||||
|
@ -127,18 +139,28 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:get_connection_error, _caller, state) do
|
||||
{:reply, state.connection_error, state}
|
||||
def handle_call(:get_connection_status, _caller, state) do
|
||||
{:reply, state.connection_status, state}
|
||||
end
|
||||
|
||||
def handle_call(:connected?, _caller, state) do
|
||||
{:reply, state.connected?, state}
|
||||
end
|
||||
|
||||
def handle_call(:get_secrets, _caller, state) do
|
||||
def handle_call(:get_secrets, _caller, %{deployment_group_id: nil} = state) do
|
||||
{:reply, state.secrets, state}
|
||||
end
|
||||
|
||||
def handle_call(:get_secrets, _caller, state) do
|
||||
case find_deployment_group(state) do
|
||||
nil ->
|
||||
{:reply, state.secrets, state}
|
||||
|
||||
%{secrets: agent_secrets} ->
|
||||
{:reply, Enum.uniq_by(agent_secrets ++ state.secrets, & &1.name), state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call(:get_file_systems, _caller, state) do
|
||||
{:reply, state.file_systems, state}
|
||||
end
|
||||
|
@ -151,13 +173,13 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
def handle_info(:connected, state) do
|
||||
Hubs.Broadcasts.hub_connected(state.hub.id)
|
||||
|
||||
{:noreply, %{state | connected?: true, connection_error: nil}}
|
||||
{:noreply, %{state | connected?: true, connection_status: nil}}
|
||||
end
|
||||
|
||||
def handle_info({:connection_error, reason}, state) do
|
||||
Hubs.Broadcasts.hub_connection_failed(state.hub.id, reason)
|
||||
|
||||
{:noreply, %{state | connected?: false, connection_error: reason}}
|
||||
{:noreply, %{state | connected?: false, connection_status: reason}}
|
||||
end
|
||||
|
||||
def handle_info({:server_error, reason}, state) do
|
||||
|
@ -360,11 +382,19 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
end
|
||||
end
|
||||
|
||||
defp handle_event(:user_connected, user_connected, state) do
|
||||
defp handle_event(:user_connected, connected, state) do
|
||||
state
|
||||
|> dispatch_secrets(user_connected)
|
||||
|> dispatch_file_systems(user_connected)
|
||||
|> dispatch_deployment_groups(user_connected)
|
||||
|> dispatch_secrets(connected)
|
||||
|> dispatch_file_systems(connected)
|
||||
|> dispatch_deployment_groups(connected)
|
||||
end
|
||||
|
||||
defp handle_event(:agent_connected, agent_connected, state) do
|
||||
%{state | deployment_group_id: to_string(agent_connected.deployment_group_id)}
|
||||
|> update_hub(agent_connected)
|
||||
|> dispatch_secrets(agent_connected)
|
||||
|> dispatch_file_systems(agent_connected)
|
||||
|> dispatch_deployment_groups(agent_connected)
|
||||
end
|
||||
|
||||
defp dispatch_secrets(state, %{secrets: secrets}) do
|
||||
|
@ -418,6 +448,15 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
)
|
||||
end
|
||||
|
||||
defp update_hub(state, %{public_key: org_public_key}) do
|
||||
hub = %{state.hub | org_public_key: org_public_key}
|
||||
|
||||
# TODO: Fix this before merging
|
||||
# ^hub = Hubs.save_hub(hub)
|
||||
|
||||
%{state | hub: hub}
|
||||
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
|
||||
|
@ -435,4 +474,10 @@ defmodule Livebook.Hubs.TeamClient do
|
|||
reduce: state,
|
||||
do: (acc -> handle_event(topic, event, acc))
|
||||
end
|
||||
|
||||
defp find_deployment_group(%{deployment_group_id: nil}),
|
||||
do: nil
|
||||
|
||||
defp find_deployment_group(%{deployment_group_id: id, deployment_groups: groups}),
|
||||
do: Enum.find(groups, &(&1.id == id))
|
||||
end
|
||||
|
|
|
@ -227,7 +227,12 @@ defmodule Livebook.Teams.Requests do
|
|||
end
|
||||
|
||||
defp auth_headers(team) do
|
||||
token = "#{team.user_id}:#{team.org_id}:#{team.org_key_id}:#{team.session_token}"
|
||||
token =
|
||||
if team.user_id do
|
||||
"#{team.user_id}:#{team.org_id}:#{team.org_key_id}:#{team.session_token}"
|
||||
else
|
||||
"#{team.session_token}:#{Livebook.Config.agent_name()}:#{team.org_id}:#{team.org_key_id}"
|
||||
end
|
||||
|
||||
[
|
||||
{"x-lb-version", Livebook.Config.app_version()},
|
||||
|
@ -282,6 +287,10 @@ defmodule Livebook.Teams.Requests do
|
|||
do: {:error, Jason.decode!(body)},
|
||||
else: {:transport_error, body}
|
||||
|
||||
{:ok, 401, _headers, _body} ->
|
||||
{:transport_error,
|
||||
"You are not authorized to perform this action, make sure you have the access or you are not in a Livebook Agent instance"}
|
||||
|
||||
_otherwise ->
|
||||
{:transport_error,
|
||||
"Something went wrong, try again later or please file a bug if it persists"}
|
||||
|
|
|
@ -65,11 +65,8 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
|||
def render(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<LayoutHelpers.topbar
|
||||
:if={not @hub_metadata.connected? && Provider.connection_error(@hub)}
|
||||
variant={:warning}
|
||||
>
|
||||
<%= Provider.connection_error(@hub) %>
|
||||
<LayoutHelpers.topbar :if={Provider.connection_status(@hub)} variant={:warning}>
|
||||
<%= Provider.connection_status(@hub) %>
|
||||
</LayoutHelpers.topbar>
|
||||
|
||||
<div class="p-4 md:px-12 md:py-7 max-w-screen-md mx-auto">
|
||||
|
|
|
@ -64,11 +64,8 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupLive do
|
|||
saved_hubs={@saved_hubs}
|
||||
>
|
||||
<div>
|
||||
<LayoutHelpers.topbar
|
||||
:if={not @hub_metadata.connected? && Provider.connection_error(@hub)}
|
||||
variant={:warning}
|
||||
>
|
||||
<%= Provider.connection_error(@hub) %>
|
||||
<LayoutHelpers.topbar :if={Provider.connection_status(@hub)} variant={:warning}>
|
||||
<%= Provider.connection_status(@hub) %>
|
||||
</LayoutHelpers.topbar>
|
||||
|
||||
<div class="p-4 md:px-12 md:py-7 max-w-screen-md mx-auto">
|
||||
|
|
|
@ -237,22 +237,22 @@ defmodule LivebookWeb.LayoutHelpers do
|
|||
defp sidebar_link_border_color(to, current) when to == current, do: "border-white"
|
||||
defp sidebar_link_border_color(_to, _current), do: "border-transparent"
|
||||
|
||||
defp hub_connection_link_opts(hub, to, current) do
|
||||
defp hub_connection_link_opts(%{provider: hub}, to, current) do
|
||||
text_color = sidebar_link_text_color(to, current)
|
||||
border_color = sidebar_link_border_color(to, current)
|
||||
|
||||
class =
|
||||
"h-7 flex items-center hover:text-white #{text_color} border-l-4 #{border_color} hover:border-white"
|
||||
|
||||
if hub.connected? do
|
||||
[id: "hub-#{hub.id}", navigate: to, class: class]
|
||||
else
|
||||
if message = Provider.connection_status(hub) do
|
||||
[
|
||||
id: "hub-#{hub.id}",
|
||||
navigate: to,
|
||||
"data-tooltip": Provider.connection_error(hub.provider),
|
||||
"data-tooltip": message,
|
||||
class: "tooltip right " <> class
|
||||
]
|
||||
else
|
||||
[id: "hub-#{hub.id}", navigate: to, class: class]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
15
proto/lib/livebook_proto/agent_connected.pb.ex
Normal file
15
proto/lib/livebook_proto/agent_connected.pb.ex
Normal file
|
@ -0,0 +1,15 @@
|
|||
defmodule LivebookProto.AgentConnected do
|
||||
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0"
|
||||
|
||||
field :id, 1, type: :int32
|
||||
field :name, 2, type: :string
|
||||
field :public_key, 3, type: :string, json_name: "publicKey"
|
||||
field :deployment_group_id, 4, type: :int32, json_name: "deploymentGroupId"
|
||||
field :secrets, 5, repeated: true, type: LivebookProto.Secret
|
||||
field :file_systems, 6, repeated: true, type: LivebookProto.FileSystem, json_name: "fileSystems"
|
||||
|
||||
field :deployment_groups, 7,
|
||||
repeated: true,
|
||||
type: LivebookProto.DeploymentGroup,
|
||||
json_name: "deploymentGroups"
|
||||
end
|
|
@ -3,5 +3,5 @@ defmodule LivebookProto.DeploymentGroupSecret do
|
|||
|
||||
field :name, 1, type: :string
|
||||
field :value, 2, type: :string
|
||||
field :deployment_group_id, 3, type: :string
|
||||
field :deployment_group_id, 3, type: :string, json_name: "deploymentGroupId"
|
||||
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,
|
||||
|
@ -52,4 +52,9 @@ defmodule LivebookProto.Event do
|
|||
type: LivebookProto.DeploymentGroupDeleted,
|
||||
json_name: "deploymentGroupDeleted",
|
||||
oneof: 0
|
||||
|
||||
field :agent_connected, 11,
|
||||
type: LivebookProto.AgentConnected,
|
||||
json_name: "agentConnected",
|
||||
oneof: 0
|
||||
end
|
||||
|
|
|
@ -83,6 +83,17 @@ message UserConnected {
|
|||
string name = 1;
|
||||
repeated Secret secrets = 2;
|
||||
repeated FileSystem file_systems = 3;
|
||||
repeated DeploymentGroup deployment_groups = 4;
|
||||
}
|
||||
|
||||
message AgentConnected {
|
||||
int32 id = 1;
|
||||
string name = 2;
|
||||
string public_key = 3;
|
||||
int32 deployment_group_id = 4;
|
||||
repeated Secret secrets = 5;
|
||||
repeated FileSystem file_systems = 6;
|
||||
repeated DeploymentGroup deployment_groups = 7;
|
||||
}
|
||||
|
||||
message Event {
|
||||
|
@ -97,5 +108,6 @@ message Event {
|
|||
DeploymentGroupCreated deployment_group_created = 8;
|
||||
DeploymentGroupUpdated deployment_group_updated = 9;
|
||||
DeploymentGroupDeleted deployment_group_deleted = 10;
|
||||
AgentConnected agent_connected = 11;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,8 +55,8 @@ defmodule Livebook.Hubs.ProviderTest do
|
|||
refute secret in Provider.get_secrets(hub)
|
||||
end
|
||||
|
||||
test "connection_error/1", %{hub: hub} do
|
||||
assert_raise RuntimeError, "not implemented", fn -> Provider.connection_error(hub) end
|
||||
test "connection_status/1", %{hub: hub} do
|
||||
assert_raise RuntimeError, "not implemented", fn -> Provider.connection_status(hub) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,132 +11,68 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
:ok
|
||||
end
|
||||
|
||||
describe "start_link/1" do
|
||||
test "successfully authenticates the web socket connection", %{user: user, node: node} do
|
||||
team = build_team_hub(user, node)
|
||||
id = team.id
|
||||
test "rejects the web socket connection with invalid credentials", %{user: user, token: token} do
|
||||
team =
|
||||
build(:team,
|
||||
user_id: user.id,
|
||||
org_id: 123_456,
|
||||
org_key_id: 123_456,
|
||||
session_token: token
|
||||
)
|
||||
|
||||
refute TeamClient.connected?(team.id)
|
||||
id = team.id
|
||||
|
||||
TeamClient.start_link(team)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
assert TeamClient.connected?(team.id)
|
||||
end
|
||||
TeamClient.start_link(team)
|
||||
|
||||
test "rejects the web socket connection with invalid credentials", %{user: user, token: token} do
|
||||
team =
|
||||
build(:team,
|
||||
user_id: user.id,
|
||||
org_id: 123_456,
|
||||
org_key_id: 123_456,
|
||||
session_token: token
|
||||
)
|
||||
assert_receive {:hub_server_error, ^id, error}
|
||||
|
||||
id = team.id
|
||||
assert error ==
|
||||
"#{team.hub_name}: Your session is out-of-date. Please re-join the organization."
|
||||
|
||||
TeamClient.start_link(team)
|
||||
|
||||
assert_receive {:hub_server_error, ^id, error}
|
||||
|
||||
assert error ==
|
||||
"#{team.hub_name}: Your session is out-of-date. Please re-join the organization."
|
||||
|
||||
refute Livebook.Hubs.hub_exists?(team.id)
|
||||
end
|
||||
refute Livebook.Hubs.hub_exists?(team.id)
|
||||
end
|
||||
|
||||
describe "handle events" do
|
||||
test "receives the secret_created event", %{user: user, node: node} do
|
||||
setup %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
secret = build(:secret, name: "SECRET_CREATED_FOO", value: "BAR")
|
||||
assert Livebook.Hubs.create_secret(team, secret) == :ok
|
||||
|
||||
name = secret.name
|
||||
value = secret.value
|
||||
|
||||
# receives `{:event, :secret_created, secret_created}` event
|
||||
# with the value decrypted
|
||||
assert_receive {:secret_created, %{name: ^name, value: ^value}}
|
||||
{:ok, team: team}
|
||||
end
|
||||
|
||||
test "receives the secret_updated event", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
secret = build(:secret, name: "SECRET_UPDATED_FOO", value: "BAR")
|
||||
test "receives the secret events", %{team: team} do
|
||||
secret = build(:secret, name: "SECRET", value: "BAR")
|
||||
assert Livebook.Hubs.create_secret(team, secret) == :ok
|
||||
|
||||
name = secret.name
|
||||
value = secret.value
|
||||
|
||||
# receives `{:secret_created, secret_created}` event
|
||||
# with the value decrypted
|
||||
assert_receive {:secret_created, %{name: ^name, value: ^value}}
|
||||
|
||||
# updates the secret
|
||||
update_secret = Map.replace!(secret, :value, "BAZ")
|
||||
assert Livebook.Hubs.update_secret(team, update_secret) == :ok
|
||||
updated_secret = Map.replace!(secret, :value, "BAZ")
|
||||
assert Livebook.Hubs.update_secret(team, updated_secret) == :ok
|
||||
|
||||
new_value = update_secret.value
|
||||
new_value = updated_secret.value
|
||||
|
||||
# receives `{:secret_updated, secret_updated}` event
|
||||
# with the value decrypted
|
||||
assert_receive {:secret_updated, %{name: ^name, value: ^new_value}}
|
||||
end
|
||||
|
||||
test "receives the secret_deleted event", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
secret = build(:secret, name: "SECRET_DELETED_FOO", value: "BAR")
|
||||
assert Livebook.Hubs.create_secret(team, secret) == :ok
|
||||
|
||||
name = secret.name
|
||||
value = secret.value
|
||||
|
||||
# receives `{:secret_created, secret_created}` event
|
||||
assert_receive {:secret_created, %{name: ^name, value: ^value}}
|
||||
|
||||
# deletes the secret
|
||||
assert Livebook.Hubs.delete_secret(team, secret) == :ok
|
||||
assert Livebook.Hubs.delete_secret(team, updated_secret) == :ok
|
||||
|
||||
# receives `{:secret_deleted, secret_deleted}` event
|
||||
assert_receive {:secret_deleted, %{name: ^name, value: ^value}}
|
||||
assert_receive {:secret_deleted, %{name: ^name, value: ^new_value}}
|
||||
end
|
||||
|
||||
test "receives the file_system_created event", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
file_system = build(:fs_s3, bucket_url: "https://file_system_created.s3.amazonaws.com")
|
||||
assert Livebook.Hubs.create_file_system(team, file_system) == :ok
|
||||
|
||||
bucket_url = file_system.bucket_url
|
||||
|
||||
# receives `{:event, :file_system_created, file_system_created}` event
|
||||
assert_receive {:file_system_created, %{bucket_url: ^bucket_url}}
|
||||
end
|
||||
|
||||
test "receives the file_system_updated event", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
test "receives the file system events", %{team: team} do
|
||||
file_system =
|
||||
build(:fs_s3,
|
||||
bucket_url: "https://file_system_updated.s3.amazonaws.com",
|
||||
region: "us-east-1"
|
||||
)
|
||||
build(:fs_s3, bucket_url: "https://file_system.s3.amazonaws.com", region: "us-east-1")
|
||||
|
||||
assert Livebook.Hubs.create_file_system(team, file_system) == :ok
|
||||
|
||||
|
@ -148,70 +84,31 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
%{external_id: id, bucket_url: ^bucket_url, region: ^region}}
|
||||
|
||||
# updates the file system
|
||||
update_file_system = %{file_system | region: "eu-central-1", external_id: id}
|
||||
assert Livebook.Hubs.update_file_system(team, update_file_system) == :ok
|
||||
updated_file_system = %{file_system | region: "eu-central-1", external_id: id}
|
||||
assert Livebook.Hubs.update_file_system(team, updated_file_system) == :ok
|
||||
|
||||
new_region = update_file_system.region
|
||||
new_region = updated_file_system.region
|
||||
|
||||
# receives `{:file_system_updated, file_system_updated}` event
|
||||
assert_receive {:file_system_updated,
|
||||
%{external_id: ^id, bucket_url: ^bucket_url, region: ^new_region}}
|
||||
end
|
||||
|
||||
test "receives the file_system_deleted event", %{user: user, node: node} do
|
||||
team = create_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
file_system = build(:fs_s3, bucket_url: "https://file_system_deleted.s3.amazonaws.com")
|
||||
assert Livebook.Hubs.create_file_system(team, file_system) == :ok
|
||||
|
||||
bucket_url = file_system.bucket_url
|
||||
|
||||
# receives `{:file_system_created, file_system_created}` event
|
||||
assert_receive {:file_system_created, %{external_id: id, bucket_url: ^bucket_url}}
|
||||
|
||||
# deletes the file system
|
||||
delete_file_system = %{file_system | external_id: id}
|
||||
assert Livebook.Hubs.delete_file_system(team, delete_file_system) == :ok
|
||||
assert Livebook.Hubs.delete_file_system(team, updated_file_system) == :ok
|
||||
|
||||
# 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")
|
||||
test "receives the deployment group events", %{team: team} do
|
||||
deployment_group = build(:deployment_group, name: "DEPLOYMENT_GROUP", mode: "online")
|
||||
|
||||
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
|
||||
# receives `{:event, :deployment_group_created, deployment_group_created}` event
|
||||
assert_receive {:deployment_group_created, %{name: ^name, mode: ^mode}}
|
||||
|
||||
# updates the deployment group
|
||||
|
@ -227,170 +124,74 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
|
||||
# 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
|
||||
assert Livebook.Teams.delete_deployment_group(team, update_deployment_group) == :ok
|
||||
|
||||
# receives `{:deployment_group_deleted, deployment_group_deleted}` event
|
||||
assert_receive {:deployment_group_deleted, %{name: ^name, mode: ^mode}}
|
||||
assert_receive {:deployment_group_deleted, %{name: ^name, mode: ^new_mode}}
|
||||
end
|
||||
end
|
||||
|
||||
describe "user connected event" do
|
||||
test "fills the secrets list", %{user: user, node: node} do
|
||||
describe "handle user_connected event" do
|
||||
setup %{user: user, node: node} do
|
||||
team = build_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [],
|
||||
file_systems: [],
|
||||
deployment_groups: []
|
||||
)
|
||||
|
||||
{:ok, team: team, user_connected: user_connected}
|
||||
end
|
||||
|
||||
test "dispatches the secrets list", %{team: team, user_connected: user_connected} do
|
||||
secret =
|
||||
build(:secret,
|
||||
name: "SECRET_CREATED",
|
||||
name: "CHONKY_CAT",
|
||||
value: "an encrypted value",
|
||||
hub_id: id
|
||||
hub_id: team.id
|
||||
)
|
||||
|
||||
secret_key = Livebook.Teams.derive_key(team.teams_key)
|
||||
secret_value = Livebook.Teams.encrypt(secret.value, secret_key)
|
||||
livebook_proto_secret = LivebookProto.Secret.new!(name: secret.name, value: secret_value)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [livebook_proto_secret],
|
||||
file_systems: []
|
||||
)
|
||||
|
||||
{:ok, pid} = TeamClient.start_link(team)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
# creates the secret
|
||||
user_connected = %{user_connected | secrets: [livebook_proto_secret]}
|
||||
pid = connect_to_teams(team)
|
||||
refute_receive {:secret_created, ^secret}
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:secret_created, ^secret}
|
||||
assert secret in TeamClient.get_secrets(team.id)
|
||||
end
|
||||
|
||||
test "replaces the secret with updated value", %{user: user, node: node} do
|
||||
team = build_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
secret =
|
||||
build(:secret,
|
||||
name: "SECRET_UPDATED",
|
||||
value: "an encrypted value",
|
||||
hub_id: id
|
||||
)
|
||||
|
||||
secret_key = Livebook.Teams.derive_key(team.teams_key)
|
||||
secret_value = Livebook.Teams.encrypt(secret.value, secret_key)
|
||||
livebook_proto_secret = LivebookProto.Secret.new!(name: secret.name, value: secret_value)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [livebook_proto_secret],
|
||||
file_systems: []
|
||||
)
|
||||
|
||||
{:ok, pid} = TeamClient.start_link(team)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
refute_receive {:secret_created, ^secret}
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:secret_created, ^secret}
|
||||
|
||||
# updates the secret
|
||||
updated_secret = %{secret | value: "an updated value"}
|
||||
secret_value = Livebook.Teams.encrypt(updated_secret.value, secret_key)
|
||||
|
||||
updated_livebook_proto_secret =
|
||||
LivebookProto.Secret.new!(name: updated_secret.name, value: secret_value)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [updated_livebook_proto_secret],
|
||||
file_systems: []
|
||||
)
|
||||
|
||||
updated_livebook_proto_secret = %{livebook_proto_secret | value: secret_value}
|
||||
user_connected = %{user_connected | secrets: [updated_livebook_proto_secret]}
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:secret_updated, ^updated_secret}
|
||||
|
||||
refute secret in TeamClient.get_secrets(team.id)
|
||||
assert updated_secret in TeamClient.get_secrets(team.id)
|
||||
|
||||
# deletes the secret
|
||||
user_connected = %{user_connected | secrets: []}
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:secret_deleted, ^updated_secret}
|
||||
refute updated_secret in TeamClient.get_secrets(team.id)
|
||||
end
|
||||
|
||||
test "deletes the secret", %{user: user, node: node} do
|
||||
team = build_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
secret =
|
||||
build(:secret,
|
||||
name: "SECRET_UPDATED",
|
||||
value: "an encrypted value",
|
||||
hub_id: id
|
||||
)
|
||||
|
||||
secret_key = Livebook.Teams.derive_key(team.teams_key)
|
||||
secret_value = Livebook.Teams.encrypt(secret.value, secret_key)
|
||||
livebook_proto_secret = LivebookProto.Secret.new!(name: secret.name, value: secret_value)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [livebook_proto_secret],
|
||||
file_systems: []
|
||||
)
|
||||
|
||||
{:ok, pid} = TeamClient.start_link(team)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
refute_receive {:secret_created, ^secret}
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:secret_created, ^secret}
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [],
|
||||
file_systems: []
|
||||
)
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:secret_deleted, ^secret}
|
||||
|
||||
refute secret in TeamClient.get_secrets(team.id)
|
||||
end
|
||||
|
||||
test "fills the file systems list", %{user: user, node: node} do
|
||||
team = build_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
test "dispatches the file systems list", %{team: team, user_connected: user_connected} do
|
||||
bucket_url = "https://mybucket.s3.amazonaws.com"
|
||||
hash = :crypto.hash(:sha256, bucket_url)
|
||||
fs_id = "#{id}-s3-#{Base.url_encode64(hash, padding: false)}"
|
||||
fs_id = "#{team.id}-s3-#{Base.url_encode64(hash, padding: false)}"
|
||||
|
||||
file_system =
|
||||
build(:fs_s3, id: fs_id, bucket_url: bucket_url, external_id: "123456", hub_id: id)
|
||||
build(:fs_s3, id: fs_id, bucket_url: bucket_url, external_id: "123456", hub_id: team.id)
|
||||
|
||||
type = Livebook.FileSystems.type(file_system)
|
||||
%{name: name} = Livebook.FileSystem.external_metadata(file_system)
|
||||
|
@ -408,66 +209,18 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
value: value
|
||||
)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [],
|
||||
file_systems: [livebook_proto_file_system]
|
||||
)
|
||||
|
||||
{:ok, pid} = TeamClient.start_link(team)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
# creates the file system
|
||||
user_connected = %{user_connected | file_systems: [livebook_proto_file_system]}
|
||||
pid = connect_to_teams(team)
|
||||
refute_receive {:file_system_created, ^file_system}
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:file_system_created, ^file_system}
|
||||
assert file_system in TeamClient.get_file_systems(team.id)
|
||||
end
|
||||
|
||||
test "replaces the file system with updated value", %{user: user, node: node} do
|
||||
team = build_team_hub(user, node)
|
||||
id = team.id
|
||||
|
||||
bucket_url = "https://update_fs_994641.s3.amazonaws.com"
|
||||
hash = :crypto.hash(:sha256, bucket_url)
|
||||
fs_id = "#{id}-s3-#{Base.url_encode64(hash, padding: false)}"
|
||||
|
||||
file_system =
|
||||
build(:fs_s3, id: fs_id, bucket_url: bucket_url, external_id: "994641", hub_id: id)
|
||||
|
||||
type = Livebook.FileSystems.type(file_system)
|
||||
%{name: name} = Livebook.FileSystem.external_metadata(file_system)
|
||||
attrs = Livebook.FileSystem.dump(file_system)
|
||||
credentials = Jason.encode!(attrs)
|
||||
|
||||
secret_key = Livebook.Teams.derive_key(team.teams_key)
|
||||
value = Livebook.Teams.encrypt(credentials, secret_key)
|
||||
|
||||
livebook_proto_file_system =
|
||||
LivebookProto.FileSystem.new!(
|
||||
id: file_system.external_id,
|
||||
name: name,
|
||||
type: to_string(type),
|
||||
value: value
|
||||
)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [],
|
||||
file_systems: [livebook_proto_file_system]
|
||||
)
|
||||
|
||||
{:ok, pid} = TeamClient.start_link(team)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
refute_receive {:file_system_created, ^file_system}
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:file_system_created, ^file_system}
|
||||
|
||||
# updates the file system
|
||||
updated_file_system = %{
|
||||
file_system
|
||||
| id: "#{id}-s3-ATC52Lo7d-bS21OLjPQ8KFBPJN8ku4hCn2nic2jTGeI",
|
||||
| id: "#{team.id}-s3-ATC52Lo7d-bS21OLjPQ8KFBPJN8ku4hCn2nic2jTGeI",
|
||||
bucket_url: "https://updated_name.s3.amazonaws.com"
|
||||
}
|
||||
|
||||
|
@ -475,38 +228,140 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
updated_credentials = Jason.encode!(updated_attrs)
|
||||
updated_value = Livebook.Teams.encrypt(updated_credentials, secret_key)
|
||||
|
||||
updated_livebook_proto_file_system =
|
||||
LivebookProto.FileSystem.new!(
|
||||
id: updated_file_system.external_id,
|
||||
name: updated_file_system.bucket_url,
|
||||
type: to_string(type),
|
||||
updated_livebook_proto_file_system = %{
|
||||
livebook_proto_file_system
|
||||
| name: updated_file_system.bucket_url,
|
||||
value: updated_value
|
||||
)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [],
|
||||
file_systems: [updated_livebook_proto_file_system]
|
||||
)
|
||||
}
|
||||
|
||||
user_connected = %{user_connected | file_systems: [updated_livebook_proto_file_system]}
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:file_system_updated, ^updated_file_system}
|
||||
|
||||
refute file_system in TeamClient.get_file_systems(team.id)
|
||||
assert updated_file_system in TeamClient.get_file_systems(team.id)
|
||||
|
||||
# deletes the file system
|
||||
user_connected = %{user_connected | file_systems: []}
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:file_system_deleted, ^updated_file_system}
|
||||
refute updated_file_system in TeamClient.get_file_systems(team.id)
|
||||
end
|
||||
|
||||
test "deletes the file system", %{user: user, node: node} do
|
||||
team = build_team_hub(user, node)
|
||||
id = team.id
|
||||
test "dispatches the deployment groups list", %{team: team, user_connected: user_connected} do
|
||||
deployment_group =
|
||||
build(:deployment_group,
|
||||
id: "1",
|
||||
name: "sleepy-cat-#{System.unique_integer([:positive])}",
|
||||
mode: "offline",
|
||||
hub_id: team.id,
|
||||
secrets: []
|
||||
)
|
||||
|
||||
bucket_url = "https://delete_fs_45465641.s3.amazonaws.com"
|
||||
livebook_proto_deployment_group =
|
||||
LivebookProto.DeploymentGroup.new!(
|
||||
id: to_string(deployment_group.id),
|
||||
name: deployment_group.name,
|
||||
mode: to_string(deployment_group.mode),
|
||||
secrets: []
|
||||
)
|
||||
|
||||
# creates the deployment group
|
||||
user_connected = %{user_connected | deployment_groups: [livebook_proto_deployment_group]}
|
||||
pid = connect_to_teams(team)
|
||||
refute_receive {:deployment_group_created, ^deployment_group}
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:deployment_group_created, ^deployment_group}
|
||||
assert deployment_group in TeamClient.get_deployment_groups(team.id)
|
||||
|
||||
# updates the deployment group
|
||||
updated_deployment_group = %{deployment_group | mode: "online"}
|
||||
|
||||
updated_livebook_proto_deployment_group = %{
|
||||
livebook_proto_deployment_group
|
||||
| mode: updated_deployment_group.mode
|
||||
}
|
||||
|
||||
user_connected = %{
|
||||
user_connected
|
||||
| deployment_groups: [updated_livebook_proto_deployment_group]
|
||||
}
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:deployment_group_updated, ^updated_deployment_group}
|
||||
refute deployment_group in TeamClient.get_deployment_groups(team.id)
|
||||
assert updated_deployment_group in TeamClient.get_deployment_groups(team.id)
|
||||
|
||||
# deletes the deployment group
|
||||
user_connected = %{user_connected | deployment_groups: []}
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:deployment_group_deleted, ^updated_deployment_group}
|
||||
refute updated_deployment_group in TeamClient.get_deployment_groups(team.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "handle agent_connected event" do
|
||||
setup %{node: node} do
|
||||
{agent_key, org, deployment_group, team} = build_agent_team_hub(node)
|
||||
org_key_pair = erpc_call(node, :create_org_key_pair, [[org: org]])
|
||||
|
||||
agent_connected =
|
||||
LivebookProto.AgentConnected.new!(
|
||||
id: agent_key.id,
|
||||
name: Livebook.Config.agent_name(),
|
||||
public_key: org_key_pair.public_key,
|
||||
deployment_group_id: deployment_group.id,
|
||||
secrets: [],
|
||||
file_systems: [],
|
||||
deployment_groups: []
|
||||
)
|
||||
|
||||
{:ok, team: team, deployment_group: deployment_group, agent_connected: agent_connected}
|
||||
end
|
||||
|
||||
test "dispatches the secrets list", %{team: team, agent_connected: agent_connected} do
|
||||
secret =
|
||||
build(:secret,
|
||||
name: "AGENT_SECRET",
|
||||
value: "an encrypted value",
|
||||
hub_id: team.id
|
||||
)
|
||||
|
||||
secret_key = Livebook.Teams.derive_key(team.teams_key)
|
||||
secret_value = Livebook.Teams.encrypt(secret.value, secret_key)
|
||||
livebook_proto_secret = LivebookProto.Secret.new!(name: secret.name, value: secret_value)
|
||||
|
||||
# creates the secret
|
||||
agent_connected = %{agent_connected | secrets: [livebook_proto_secret]}
|
||||
pid = connect_to_teams(team)
|
||||
refute_receive {:secret_created, ^secret}
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:secret_created, ^secret}
|
||||
assert secret in TeamClient.get_secrets(team.id)
|
||||
|
||||
# updates the secret
|
||||
updated_secret = %{secret | value: "an updated value"}
|
||||
secret_value = Livebook.Teams.encrypt(updated_secret.value, secret_key)
|
||||
updated_livebook_proto_secret = %{livebook_proto_secret | value: secret_value}
|
||||
agent_connected = %{agent_connected | secrets: [updated_livebook_proto_secret]}
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:secret_updated, ^updated_secret}
|
||||
refute secret in TeamClient.get_secrets(team.id)
|
||||
assert updated_secret in TeamClient.get_secrets(team.id)
|
||||
|
||||
# deletes the secret
|
||||
agent_connected = %{agent_connected | secrets: []}
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:secret_deleted, ^updated_secret}
|
||||
refute updated_secret in TeamClient.get_secrets(team.id)
|
||||
end
|
||||
|
||||
test "dispatches the file systems list", %{team: team, agent_connected: agent_connected} do
|
||||
bucket_url = "https://mybucket.s3.amazonaws.com"
|
||||
hash = :crypto.hash(:sha256, bucket_url)
|
||||
fs_id = "#{id}-s3-#{Base.url_encode64(hash, padding: false)}"
|
||||
fs_id = "#{team.id}-s3-#{Base.url_encode64(hash, padding: false)}"
|
||||
|
||||
file_system =
|
||||
build(:fs_s3, id: fs_id, bucket_url: bucket_url, external_id: "45465641", hub_id: id)
|
||||
build(:fs_s3, id: fs_id, bucket_url: bucket_url, external_id: "123456", hub_id: team.id)
|
||||
|
||||
type = Livebook.FileSystems.type(file_system)
|
||||
%{name: name} = Livebook.FileSystem.external_metadata(file_system)
|
||||
|
@ -524,31 +379,161 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
value: value
|
||||
)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [],
|
||||
file_systems: [livebook_proto_file_system]
|
||||
)
|
||||
|
||||
{:ok, pid} = TeamClient.start_link(team)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
# creates the file system
|
||||
agent_connected = %{agent_connected | file_systems: [livebook_proto_file_system]}
|
||||
pid = connect_to_teams(team)
|
||||
refute_receive {:file_system_created, ^file_system}
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:file_system_created, ^file_system}
|
||||
assert file_system in TeamClient.get_file_systems(team.id)
|
||||
|
||||
user_connected =
|
||||
LivebookProto.UserConnected.new!(
|
||||
name: team.hub_name,
|
||||
secrets: [],
|
||||
file_systems: []
|
||||
# updates the file system
|
||||
updated_file_system = %{
|
||||
file_system
|
||||
| id: "#{team.id}-s3-ATC52Lo7d-bS21OLjPQ8KFBPJN8ku4hCn2nic2jTGeI",
|
||||
bucket_url: "https://updated_name.s3.amazonaws.com"
|
||||
}
|
||||
|
||||
updated_attrs = Livebook.FileSystem.dump(updated_file_system)
|
||||
updated_credentials = Jason.encode!(updated_attrs)
|
||||
updated_value = Livebook.Teams.encrypt(updated_credentials, secret_key)
|
||||
|
||||
updated_livebook_proto_file_system = %{
|
||||
livebook_proto_file_system
|
||||
| name: updated_file_system.bucket_url,
|
||||
value: updated_value
|
||||
}
|
||||
|
||||
agent_connected = %{agent_connected | file_systems: [updated_livebook_proto_file_system]}
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:file_system_updated, ^updated_file_system}
|
||||
refute file_system in TeamClient.get_file_systems(team.id)
|
||||
assert updated_file_system in TeamClient.get_file_systems(team.id)
|
||||
|
||||
# deletes the file system
|
||||
agent_connected = %{agent_connected | file_systems: []}
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:file_system_deleted, ^updated_file_system}
|
||||
refute updated_file_system in TeamClient.get_file_systems(team.id)
|
||||
end
|
||||
|
||||
test "dispatches the deployment groups list",
|
||||
%{team: team, deployment_group: teams_deployment_group, agent_connected: agent_connected} do
|
||||
deployment_group =
|
||||
build(:deployment_group,
|
||||
id: to_string(teams_deployment_group.id),
|
||||
name: teams_deployment_group.name,
|
||||
mode: to_string(teams_deployment_group.mode),
|
||||
hub_id: team.id,
|
||||
secrets: []
|
||||
)
|
||||
|
||||
send(pid, {:event, :user_connected, user_connected})
|
||||
assert_receive {:file_system_deleted, ^file_system}
|
||||
livebook_proto_deployment_group =
|
||||
LivebookProto.DeploymentGroup.new!(
|
||||
id: to_string(deployment_group.id),
|
||||
name: deployment_group.name,
|
||||
mode: to_string(deployment_group.mode),
|
||||
secrets: []
|
||||
)
|
||||
|
||||
refute file_system in TeamClient.get_file_systems(team.id)
|
||||
# creates the deployment group
|
||||
agent_connected = %{agent_connected | deployment_groups: [livebook_proto_deployment_group]}
|
||||
pid = connect_to_teams(team)
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:deployment_group_created, ^deployment_group}
|
||||
assert deployment_group in TeamClient.get_deployment_groups(team.id)
|
||||
|
||||
# updates the deployment group
|
||||
updated_deployment_group = %{deployment_group | mode: "offline"}
|
||||
|
||||
updated_livebook_proto_deployment_group = %{
|
||||
livebook_proto_deployment_group
|
||||
| mode: updated_deployment_group.mode
|
||||
}
|
||||
|
||||
agent_connected = %{
|
||||
agent_connected
|
||||
| deployment_groups: [updated_livebook_proto_deployment_group]
|
||||
}
|
||||
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:deployment_group_updated, ^updated_deployment_group}
|
||||
refute deployment_group in TeamClient.get_deployment_groups(team.id)
|
||||
assert updated_deployment_group in TeamClient.get_deployment_groups(team.id)
|
||||
|
||||
# deletes the deployment group
|
||||
agent_connected = %{agent_connected | deployment_groups: []}
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:deployment_group_deleted, ^updated_deployment_group}
|
||||
refute updated_deployment_group in TeamClient.get_deployment_groups(team.id)
|
||||
end
|
||||
|
||||
test "dispatches the secrets list and override with deployment group secret",
|
||||
%{team: team, deployment_group: teams_deployment_group, agent_connected: agent_connected} do
|
||||
secret =
|
||||
build(:secret,
|
||||
name: "ORG_SECRET",
|
||||
value: "an encrypted value",
|
||||
hub_id: team.id
|
||||
)
|
||||
|
||||
secret_key = Livebook.Teams.derive_key(team.teams_key)
|
||||
secret_value = Livebook.Teams.encrypt(secret.value, secret_key)
|
||||
livebook_proto_secret = LivebookProto.Secret.new!(name: secret.name, value: secret_value)
|
||||
|
||||
# creates the secret
|
||||
agent_connected = %{agent_connected | secrets: [livebook_proto_secret]}
|
||||
pid = connect_to_teams(team)
|
||||
refute_receive {:secret_created, ^secret}
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:secret_created, ^secret}
|
||||
assert secret in TeamClient.get_secrets(team.id)
|
||||
|
||||
# overrides the secret with deployment group secret
|
||||
override_secret = %{
|
||||
secret
|
||||
| value: "an updated value",
|
||||
deployment_group_id: teams_deployment_group.id
|
||||
}
|
||||
|
||||
secret_value = Livebook.Teams.encrypt(override_secret.value, secret_key)
|
||||
|
||||
livebook_proto_deployment_group_secret =
|
||||
LivebookProto.DeploymentGroupSecret.new!(
|
||||
name: override_secret.name,
|
||||
value: secret_value,
|
||||
deployment_group_id: override_secret.deployment_group_id
|
||||
)
|
||||
|
||||
deployment_group =
|
||||
build(:deployment_group,
|
||||
id: to_string(teams_deployment_group.id),
|
||||
name: teams_deployment_group.name,
|
||||
mode: to_string(teams_deployment_group.mode),
|
||||
hub_id: team.id,
|
||||
secrets: [override_secret]
|
||||
)
|
||||
|
||||
livebook_proto_deployment_group =
|
||||
LivebookProto.DeploymentGroup.new!(
|
||||
id: to_string(deployment_group.id),
|
||||
name: deployment_group.name,
|
||||
mode: to_string(deployment_group.mode),
|
||||
secrets: [livebook_proto_deployment_group_secret]
|
||||
)
|
||||
|
||||
agent_connected = %{agent_connected | deployment_groups: [livebook_proto_deployment_group]}
|
||||
send(pid, {:event, :agent_connected, agent_connected})
|
||||
assert_receive {:deployment_group_created, ^deployment_group}
|
||||
refute secret in TeamClient.get_secrets(team.id)
|
||||
assert override_secret in TeamClient.get_secrets(team.id)
|
||||
end
|
||||
end
|
||||
|
||||
defp connect_to_teams(%{id: id} = team) do
|
||||
{:ok, pid} = TeamClient.start_link(team)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
pid
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
test "creates a deployment group", %{conn: conn, hub: hub} do
|
||||
deployment_group =
|
||||
build(:deployment_group,
|
||||
name: "TEAM_ADD_DEPLOYMENT_GROUP",
|
||||
name: "TEAMS_ADD_DEPLOYMENT_GROUP",
|
||||
mode: "offline",
|
||||
hub_id: hub.id
|
||||
)
|
||||
|
@ -49,11 +49,11 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
|> render_submit(attrs)
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{id: id, name: "TEAM_ADD_DEPLOYMENT_GROUP"} =
|
||||
%DeploymentGroup{id: id, name: "TEAMS_ADD_DEPLOYMENT_GROUP"} =
|
||||
deployment_group}
|
||||
|
||||
assert_patch(view, "/hub/#{hub.id}/deployment-groups/edit/#{id}")
|
||||
assert render(view) =~ "Deployment group TEAM_ADD_DEPLOYMENT_GROUP added successfully"
|
||||
assert render(view) =~ "Deployment group TEAMS_ADD_DEPLOYMENT_GROUP added successfully"
|
||||
assert deployment_group in Livebook.Teams.get_deployment_groups(hub)
|
||||
|
||||
# Guarantee it shows the error from API
|
||||
|
@ -69,13 +69,13 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
|
||||
test "updates an existing deployment group", %{conn: conn, hub: hub} do
|
||||
insert_deployment_group(
|
||||
name: "TEAM_EDIT_DEPLOYMENT_GROUP",
|
||||
name: "TEAMS_EDIT_DEPLOYMENT_GROUP",
|
||||
mode: "online",
|
||||
hub_id: hub.id
|
||||
)
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{name: "TEAM_EDIT_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
%DeploymentGroup{name: "TEAMS_EDIT_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
|
||||
attrs = %{
|
||||
deployment_group: %{
|
||||
|
@ -110,19 +110,24 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
|
||||
assert_receive {:deployment_group_updated, ^updated_deployment_group}
|
||||
assert_patch(view, "/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}")
|
||||
assert render(view) =~ "Deployment group TEAM_EDIT_DEPLOYMENT_GROUP updated successfully"
|
||||
assert render(view) =~ "Deployment group TEAMS_EDIT_DEPLOYMENT_GROUP updated successfully"
|
||||
assert updated_deployment_group in Livebook.Teams.get_deployment_groups(hub)
|
||||
end
|
||||
|
||||
test "creates a secret", %{conn: conn, hub: hub} do
|
||||
insert_deployment_group(
|
||||
name: "TEAM_EDIT_DEPLOYMENT_GROUP",
|
||||
name: "TEAMS_EDIT_DEPLOYMENT_GROUP",
|
||||
mode: "online",
|
||||
hub_id: hub.id
|
||||
)
|
||||
|
||||
hub_id = hub.id
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{name: "TEAM_EDIT_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
%DeploymentGroup{name: "TEAMS_EDIT_DEPLOYMENT_GROUP", hub_id: ^hub_id} =
|
||||
deployment_group}
|
||||
|
||||
id = deployment_group.id
|
||||
|
||||
{:ok, view, _html} =
|
||||
live(conn, ~p"/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}")
|
||||
|
@ -130,8 +135,8 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
secret =
|
||||
build(:secret,
|
||||
name: "DEPLOYMENT_GROUP_SECRET",
|
||||
hub_id: hub.id,
|
||||
deployment_group_id: deployment_group.id
|
||||
hub_id: hub_id,
|
||||
deployment_group_id: id
|
||||
)
|
||||
|
||||
attrs = %{
|
||||
|
@ -151,7 +156,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
|
||||
assert_patch(
|
||||
view,
|
||||
~p"/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}/secrets/new"
|
||||
~p"/hub/#{hub_id}/deployment-groups/edit/#{id}/secrets/new"
|
||||
)
|
||||
|
||||
assert render(view) =~ "Add secret"
|
||||
|
@ -169,9 +174,10 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
|> render_submit(attrs)
|
||||
|
||||
assert_receive {:deployment_group_updated,
|
||||
%Livebook.Teams.DeploymentGroup{secrets: [^secret]} = deployment_group}
|
||||
%Livebook.Teams.DeploymentGroup{id: ^id, secrets: [^secret]} =
|
||||
deployment_group}
|
||||
|
||||
assert_patch(view, "/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}")
|
||||
assert_patch(view, "/hub/#{hub_id}/deployment-groups/edit/#{id}")
|
||||
assert render(view) =~ "Secret DEPLOYMENT_GROUP_SECRET added successfully"
|
||||
assert render(element(view, "#deployment-group-secrets-list")) =~ secret.name
|
||||
assert secret in deployment_group.secrets
|
||||
|
@ -179,7 +185,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
# Guarantee it shows the error from API
|
||||
|
||||
{:ok, view, _html} =
|
||||
live(conn, ~p"/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}/secrets/new")
|
||||
live(conn, ~p"/hub/#{hub_id}/deployment-groups/edit/#{id}/secrets/new")
|
||||
|
||||
view
|
||||
|> element("#secrets-form")
|
||||
|
@ -190,26 +196,31 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
|
||||
test "updates an existing secret", %{conn: conn, hub: hub} do
|
||||
insert_deployment_group(
|
||||
name: "TEAM_EDIT_DEPLOYMENT_GROUP",
|
||||
name: "TEAMS_EDIT_DEPLOYMENT_GROUP",
|
||||
mode: "online",
|
||||
hub_id: hub.id
|
||||
)
|
||||
|
||||
hub_id = hub.id
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{name: "TEAM_EDIT_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
%DeploymentGroup{name: "TEAMS_EDIT_DEPLOYMENT_GROUP", hub_id: ^hub_id} =
|
||||
deployment_group}
|
||||
|
||||
id = deployment_group.id
|
||||
|
||||
secret =
|
||||
insert_secret(
|
||||
name: "DEPLOYMENT_GROUP_EDIT_SECRET",
|
||||
hub_id: hub.id,
|
||||
deployment_group_id: deployment_group.id
|
||||
hub_id: hub_id,
|
||||
deployment_group_id: id
|
||||
)
|
||||
|
||||
assert_receive {:deployment_group_updated,
|
||||
%Livebook.Teams.DeploymentGroup{secrets: [^secret]} = deployment_group}
|
||||
%Livebook.Teams.DeploymentGroup{id: ^id, secrets: [^secret]}}
|
||||
|
||||
{:ok, view, _html} =
|
||||
live(conn, ~p"/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}")
|
||||
live(conn, ~p"/hub/#{hub_id}/deployment-groups/edit/#{id}")
|
||||
|
||||
attrs = %{
|
||||
secret: %{
|
||||
|
@ -227,7 +238,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
|
||||
assert_patch(
|
||||
view,
|
||||
~p"/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}/secrets/edit/#{secret.name}"
|
||||
~p"/hub/#{hub_id}/deployment-groups/edit/#{id}/secrets/edit/#{secret.name}"
|
||||
)
|
||||
|
||||
assert render(view) =~ "Edit secret"
|
||||
|
@ -247,9 +258,10 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
updated_secret = %{secret | value: new_value}
|
||||
|
||||
assert_receive {:deployment_group_updated,
|
||||
%Livebook.Teams.DeploymentGroup{secrets: [^updated_secret]} = deployment_group}
|
||||
%Livebook.Teams.DeploymentGroup{id: ^id, secrets: [^updated_secret]} =
|
||||
deployment_group}
|
||||
|
||||
assert_patch(view, "/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}")
|
||||
assert_patch(view, "/hub/#{hub_id}/deployment-groups/edit/#{id}")
|
||||
assert render(view) =~ "Secret DEPLOYMENT_GROUP_EDIT_SECRET updated successfully"
|
||||
assert render(element(view, "#deployment-group-secrets-list")) =~ secret.name
|
||||
assert updated_secret in deployment_group.secrets
|
||||
|
@ -257,26 +269,31 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
|
||||
test "deletes an existing secret", %{conn: conn, hub: hub} do
|
||||
insert_deployment_group(
|
||||
name: "TEAM_EDIT_DEPLOYMENT_GROUP",
|
||||
name: "TEAMS_EDIT_DEPLOYMENT_GROUP",
|
||||
mode: "online",
|
||||
hub_id: hub.id
|
||||
)
|
||||
|
||||
hub_id = hub.id
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{name: "TEAM_EDIT_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
%DeploymentGroup{name: "TEAMS_EDIT_DEPLOYMENT_GROUP", hub_id: ^hub_id} =
|
||||
deployment_group}
|
||||
|
||||
id = deployment_group.id
|
||||
|
||||
secret =
|
||||
insert_secret(
|
||||
name: "DEPLOYMENT_GROUP_DELETE_SECRET",
|
||||
hub_id: hub.id,
|
||||
deployment_group_id: deployment_group.id
|
||||
hub_id: hub_id,
|
||||
deployment_group_id: id
|
||||
)
|
||||
|
||||
assert_receive {:deployment_group_updated,
|
||||
%Livebook.Teams.DeploymentGroup{secrets: [^secret]} = deployment_group}
|
||||
%Livebook.Teams.DeploymentGroup{id: ^id, secrets: [^secret]}}
|
||||
|
||||
{:ok, view, _html} =
|
||||
live(conn, ~p"/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}")
|
||||
live(conn, ~p"/hub/#{hub_id}/deployment-groups/edit/#{id}")
|
||||
|
||||
refute view
|
||||
|> element("#secrets-form button[disabled]")
|
||||
|
@ -289,9 +306,9 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupLiveTest do
|
|||
render_confirm(view)
|
||||
|
||||
assert_receive {:deployment_group_updated,
|
||||
%Livebook.Teams.DeploymentGroup{secrets: []} = deployment_group}
|
||||
%Livebook.Teams.DeploymentGroup{id: ^id, secrets: []} = deployment_group}
|
||||
|
||||
assert_patch(view, "/hub/#{hub.id}/deployment-groups/edit/#{deployment_group.id}")
|
||||
assert_patch(view, "/hub/#{hub_id}/deployment-groups/edit/#{id}")
|
||||
assert render(view) =~ "Secret DEPLOYMENT_GROUP_DELETE_SECRET deleted successfully"
|
||||
refute render(element(view, "#deployment-group-secrets-list")) =~ secret.name
|
||||
refute secret in deployment_group.secrets
|
||||
|
|
|
@ -7,18 +7,18 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
alias Livebook.Hubs
|
||||
alias Livebook.Teams.DeploymentGroup
|
||||
|
||||
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
|
||||
describe "user" 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
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
{:ok, hub: hub}
|
||||
end
|
||||
{:ok, hub: hub}
|
||||
end
|
||||
|
||||
describe "team" do
|
||||
test "updates the hub", %{conn: conn, hub: hub} do
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
|
@ -447,6 +447,99 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "agent" do
|
||||
setup %{node: node} do
|
||||
Livebook.Hubs.Broadcasts.subscribe([:crud, :connection])
|
||||
{agent_key, org, deployment_group, hub} = create_agent_team_hub(node)
|
||||
id = hub.id
|
||||
|
||||
assert_receive {:hub_changed, ^id}
|
||||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
{:ok, hub: hub, agent_key: agent_key, org: org, deployment_group: deployment_group}
|
||||
end
|
||||
|
||||
test "shows an error when creating a secret", %{conn: conn, hub: hub} do
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
secret = build(:secret, name: "TEAM_ADD_SECRET", hub_id: hub.id)
|
||||
|
||||
attrs = %{
|
||||
secret: %{
|
||||
name: secret.name,
|
||||
value: secret.value,
|
||||
hub_id: secret.hub_id
|
||||
}
|
||||
}
|
||||
|
||||
refute render(view) =~ secret.name
|
||||
|
||||
view
|
||||
|> element("#add-secret")
|
||||
|> render_click(%{})
|
||||
|
||||
assert_patch(view, ~p"/hub/#{hub.id}/secrets/new")
|
||||
assert render(view) =~ "Add secret"
|
||||
|
||||
view
|
||||
|> element("#secrets-form")
|
||||
|> render_change(attrs)
|
||||
|
||||
refute view
|
||||
|> element("#secrets-form button[disabled]")
|
||||
|> has_element?()
|
||||
|
||||
view
|
||||
|> element("#secrets-form")
|
||||
|> render_submit(attrs)
|
||||
|
||||
refute_receive {:secret_created, ^secret}
|
||||
|
||||
assert render(view) =~
|
||||
"You are not authorized to perform this action, make sure you have the access or you are not in a Livebook Agent instance"
|
||||
|
||||
refute secret in Livebook.Hubs.get_secrets(hub)
|
||||
end
|
||||
|
||||
test "shows an error when creating a file system", %{conn: conn, hub: hub} do
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
bypass = Bypass.open()
|
||||
file_system = build_bypass_file_system(bypass, hub.id)
|
||||
id = file_system.id
|
||||
attrs = %{file_system: Livebook.FileSystem.dump(file_system)}
|
||||
|
||||
expect_s3_listing(bypass)
|
||||
|
||||
refute render(view) =~ file_system.bucket_url
|
||||
|
||||
view
|
||||
|> element("#add-file-system")
|
||||
|> render_click(%{})
|
||||
|
||||
assert_patch(view, ~p"/hub/#{hub.id}/file-systems/new")
|
||||
assert render(view) =~ "Add file storage"
|
||||
|
||||
view
|
||||
|> element("#file-systems-form")
|
||||
|> render_change(attrs)
|
||||
|
||||
refute view
|
||||
|> element("#file-systems-form button[disabled]")
|
||||
|> has_element?()
|
||||
|
||||
view
|
||||
|> element("#file-systems-form")
|
||||
|> render_submit(attrs)
|
||||
|
||||
refute_receive {:file_system_created, %{id: ^id}}
|
||||
|
||||
assert render(view) =~
|
||||
"You are not authorized to perform this action, make sure you have the access or you are not in a Livebook Agent instance"
|
||||
|
||||
refute file_system in Livebook.Hubs.get_file_systems(hub)
|
||||
end
|
||||
end
|
||||
|
||||
defp expect_s3_listing(bypass) do
|
||||
Bypass.expect_once(bypass, "GET", "/mybucket", fn conn ->
|
||||
conn
|
||||
|
|
|
@ -403,11 +403,12 @@ defmodule LivebookWeb.Integration.SessionLiveTest do
|
|||
hub_id: team_id
|
||||
)
|
||||
|
||||
insert_deployment_group(
|
||||
name: "DEPLOYMENT_GROUP_TOBIAS",
|
||||
mode: "online",
|
||||
hub_id: team_id
|
||||
)
|
||||
deployment_group =
|
||||
insert_deployment_group(
|
||||
name: "DEPLOYMENT_GROUP_TOBIAS",
|
||||
mode: "online",
|
||||
hub_id: team_id
|
||||
)
|
||||
|
||||
Session.subscribe(session.id)
|
||||
Session.set_notebook_hub(session.pid, team_id)
|
||||
|
@ -426,11 +427,13 @@ defmodule LivebookWeb.Integration.SessionLiveTest do
|
|||
assert render(view) =~ "Deployment Group"
|
||||
assert has_element?(view, "#select_deployment_group_form")
|
||||
|
||||
id = deployment_group.id
|
||||
|
||||
view
|
||||
|> form("#select_deployment_group_form", %{id: "2"})
|
||||
|> form("#select_deployment_group_form", %{deployment_group_id: id})
|
||||
|> render_change()
|
||||
|
||||
assert_receive {:operation, {:set_notebook_deployment_group, _client, "2"}}
|
||||
assert_receive {:operation, {:set_notebook_deployment_group, _client, ^id}}
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
|
|
|
@ -59,7 +59,10 @@ defmodule Livebook.Factory do
|
|||
def build(:deployment_group) do
|
||||
%Livebook.Teams.DeploymentGroup{
|
||||
name: "FOO",
|
||||
mode: "offline"
|
||||
mode: "offline",
|
||||
clustering: "",
|
||||
zta_key: "",
|
||||
zta_provider: :""
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -107,8 +110,9 @@ defmodule Livebook.Factory do
|
|||
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
|
||||
{:ok, id} = Livebook.Teams.create_deployment_group(hub, deployment_group)
|
||||
|
||||
%{deployment_group | id: to_string(id)}
|
||||
end
|
||||
|
||||
def insert_env_var(factory_name, attrs \\ %{}) do
|
||||
|
|
|
@ -13,9 +13,9 @@ defmodule Livebook.HubHelpers do
|
|||
teams_key: @offline_hub_key,
|
||||
org_public_key: @offline_hub_org_public_key,
|
||||
hub_name: @offline_hub_org_name,
|
||||
user_id: 0,
|
||||
org_id: 0,
|
||||
org_key_id: 0,
|
||||
user_id: nil,
|
||||
org_id: nil,
|
||||
org_key_id: nil,
|
||||
session_token: "",
|
||||
offline: %Livebook.Hubs.Team.Offline{
|
||||
secrets: []
|
||||
|
@ -27,6 +27,14 @@ defmodule Livebook.HubHelpers do
|
|||
Livebook.Hubs.save_hub(hub)
|
||||
end
|
||||
|
||||
def create_agent_team_hub(node) do
|
||||
{agent_key, org, deployment_group, hub} = build_agent_team_hub(node)
|
||||
erpc_call(node, :create_org_key_pair, [[org: org]])
|
||||
^hub = Livebook.Hubs.save_hub(hub)
|
||||
|
||||
{agent_key, org, deployment_group, hub}
|
||||
end
|
||||
|
||||
def build_team_headers(user, node) do
|
||||
hub = build_team_hub(user, node)
|
||||
|
||||
|
@ -62,6 +70,40 @@ defmodule Livebook.HubHelpers do
|
|||
)
|
||||
end
|
||||
|
||||
def build_agent_team_hub(node) do
|
||||
teams_org = build(:org)
|
||||
teams_key = teams_org.teams_key
|
||||
key_hash = Livebook.Teams.Org.key_hash(teams_org)
|
||||
|
||||
org = erpc_call(node, :create_org, [])
|
||||
org_key = erpc_call(node, :create_org_key, [[org: org, key_hash: key_hash]])
|
||||
|
||||
deployment_group =
|
||||
erpc_call(node, :create_deployment_group, [
|
||||
[
|
||||
name: "sleepy-cat-#{Ecto.UUID.generate()}",
|
||||
mode: :online,
|
||||
org: org
|
||||
]
|
||||
])
|
||||
|
||||
agent_key = erpc_call(node, :create_agent_key, [[deployment_group: deployment_group]])
|
||||
|
||||
team =
|
||||
build(:team,
|
||||
id: "team-#{org.name}",
|
||||
hub_name: org.name,
|
||||
user_id: nil,
|
||||
org_id: org.id,
|
||||
org_key_id: org_key.id,
|
||||
org_public_key: nil,
|
||||
session_token: agent_key.key,
|
||||
teams_key: teams_key
|
||||
)
|
||||
|
||||
{agent_key, org, deployment_group, team}
|
||||
end
|
||||
|
||||
def build_offline_team_hub(user, node) do
|
||||
teams_org = build(:org, teams_key: @offline_hub_key, name: @offline_hub_org_name)
|
||||
key_hash = Livebook.Teams.Org.key_hash(teams_org)
|
||||
|
@ -184,8 +226,8 @@ defmodule Livebook.HubHelpers do
|
|||
send(pid, {:event, :file_system_deleted, file_system_deleted})
|
||||
end
|
||||
|
||||
def create_teams_file_system(hub, node) do
|
||||
org_key = erpc_call(node, :get_org_key!, [hub.org_key_id])
|
||||
def create_teams_file_system(hub, node, org_key \\ nil) do
|
||||
org_key = if org_key, do: org_key, else: erpc_call(node, :get_org_key!, [hub.org_key_id])
|
||||
erpc_call(node, :create_file_system, [[org_key: org_key]])
|
||||
end
|
||||
|
||||
|
@ -208,6 +250,10 @@ defmodule Livebook.HubHelpers do
|
|||
:ok = Livebook.Hubs.create_file_system(hub, file_system)
|
||||
end
|
||||
|
||||
def erpc_call(node, fun, args) do
|
||||
:erpc.call(node, TeamsRPC, fun, args)
|
||||
end
|
||||
|
||||
defp hub_pid(hub) do
|
||||
if pid = GenServer.whereis({:via, Registry, {Livebook.HubsRegistry, hub.id}}) do
|
||||
{:ok, pid}
|
||||
|
@ -215,8 +261,4 @@ defmodule Livebook.HubHelpers do
|
|||
end
|
||||
|
||||
defp hub_element_id(id), do: "#hubs #hub-#{id}"
|
||||
|
||||
defp erpc_call(node, fun, args) do
|
||||
:erpc.call(node, TeamsRPC, fun, args)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue