mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-09-20 10:05:57 +08:00
Implement the instances running count with connected Agents (#2546)
This commit is contained in:
parent
a4aedebb4e
commit
741e7d2b20
|
@ -20,7 +20,8 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
secrets: [],
|
secrets: [],
|
||||||
file_systems: [],
|
file_systems: [],
|
||||||
deployment_groups: [],
|
deployment_groups: [],
|
||||||
app_deployments: []
|
app_deployments: [],
|
||||||
|
agents: []
|
||||||
]
|
]
|
||||||
|
|
||||||
@type registry_name :: {:via, Registry, {Livebook.HubsRegistry, String.t()}}
|
@type registry_name :: {:via, Registry, {Livebook.HubsRegistry, String.t()}}
|
||||||
|
@ -114,6 +115,14 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
GenServer.call(registry_name(id), {:get_app_deployment_download_info, app_deployment_id})
|
GenServer.call(registry_name(id), {:get_app_deployment_download_info, app_deployment_id})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns a list of cached agents.
|
||||||
|
"""
|
||||||
|
@spec get_agents(String.t()) :: list(Teams.Agent.t())
|
||||||
|
def get_agents(id) do
|
||||||
|
GenServer.call(registry_name(id), :get_agents)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns if the Team client is connected.
|
Returns if the Team client is connected.
|
||||||
"""
|
"""
|
||||||
|
@ -230,6 +239,10 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
{:reply, reply, state}
|
{:reply, reply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_call(:get_agents, _caller, state) do
|
||||||
|
{:reply, state.agents, state}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info(:connected, state) do
|
def handle_info(:connected, state) do
|
||||||
Hubs.Broadcasts.hub_connected(state.hub.id)
|
Hubs.Broadcasts.hub_connected(state.hub.id)
|
||||||
|
@ -400,12 +413,13 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_app_deployment(%LivebookProto.AppDeployment{} = app_deployment) do
|
defp build_app_deployment(state, %LivebookProto.AppDeployment{} = app_deployment) do
|
||||||
%Teams.AppDeployment{
|
%Teams.AppDeployment{
|
||||||
id: app_deployment.id,
|
id: app_deployment.id,
|
||||||
slug: app_deployment.slug,
|
slug: app_deployment.slug,
|
||||||
sha: app_deployment.sha,
|
sha: app_deployment.sha,
|
||||||
title: app_deployment.title,
|
title: app_deployment.title,
|
||||||
|
hub_id: state.hub.id,
|
||||||
deployment_group_id: app_deployment.deployment_group_id,
|
deployment_group_id: app_deployment.deployment_group_id,
|
||||||
file: nil,
|
file: nil,
|
||||||
deployed_by: app_deployment.deployed_by,
|
deployed_by: app_deployment.deployed_by,
|
||||||
|
@ -413,6 +427,26 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp put_agent(state, agent) do
|
||||||
|
state = remove_agent(state, agent)
|
||||||
|
|
||||||
|
%{state | agents: [agent | state.agents]}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remove_agent(state, agent) do
|
||||||
|
%{state | agents: Enum.reject(state.agents, &(&1.id == agent.id))}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_agent(state, %LivebookProto.Agent{} = agent) do
|
||||||
|
%Livebook.Teams.Agent{
|
||||||
|
id: agent.id,
|
||||||
|
name: agent.name,
|
||||||
|
hub_id: state.hub.id,
|
||||||
|
org_id: agent.org_id,
|
||||||
|
deployment_group_id: agent.deployment_group_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
defp handle_event(:secret_created, %Secrets.Secret{} = secret, state) do
|
defp handle_event(:secret_created, %Secrets.Secret{} = secret, state) do
|
||||||
Hubs.Broadcasts.secret_created(secret)
|
Hubs.Broadcasts.secret_created(secret)
|
||||||
|
|
||||||
|
@ -433,12 +467,14 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
handle_event(:secret_updated, build_secret(state, secret_updated), state)
|
handle_event(:secret_updated, build_secret(state, secret_updated), state)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_event(:secret_deleted, secret_deleted, state) do
|
defp handle_event(:secret_deleted, %Secrets.Secret{} = secret, state) do
|
||||||
if secret = Enum.find(state.secrets, &(&1.name == secret_deleted.name)) do
|
Hubs.Broadcasts.secret_deleted(secret)
|
||||||
Hubs.Broadcasts.secret_deleted(secret)
|
remove_secret(state, secret)
|
||||||
remove_secret(state, secret)
|
end
|
||||||
else
|
|
||||||
state
|
defp handle_event(:secret_deleted, %{name: name}, state) do
|
||||||
|
with {:ok, secret} <- fetch_secret(name, state) do
|
||||||
|
handle_event(:secret_deleted, secret, state)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -464,15 +500,12 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
|
|
||||||
defp handle_event(:file_system_deleted, %{external_id: _} = file_system, state) do
|
defp handle_event(:file_system_deleted, %{external_id: _} = file_system, state) do
|
||||||
Hubs.Broadcasts.file_system_deleted(file_system)
|
Hubs.Broadcasts.file_system_deleted(file_system)
|
||||||
|
|
||||||
remove_file_system(state, file_system)
|
remove_file_system(state, file_system)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_event(:file_system_deleted, %{id: id}, state) do
|
defp handle_event(:file_system_deleted, %{id: external_id}, state) do
|
||||||
if file_system = Enum.find(state.file_systems, &(&1.external_id == id)) do
|
with {:ok, file_system} <- fetch_file_system(external_id, state) do
|
||||||
handle_event(:file_system_deleted, file_system, state)
|
handle_event(:file_system_deleted, file_system, state)
|
||||||
else
|
|
||||||
state
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -503,11 +536,14 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_event(:deployment_group_deleted, deployment_group_deleted, state) do
|
defp handle_event(:deployment_group_deleted, %Teams.DeploymentGroup{} = deployment_group, state) do
|
||||||
with {:ok, deployment_group} <- fetch_deployment_group(deployment_group_deleted.id, state) do
|
Teams.Broadcasts.deployment_group_deleted(deployment_group)
|
||||||
Teams.Broadcasts.deployment_group_deleted(deployment_group)
|
remove_deployment_group(state, deployment_group)
|
||||||
|
end
|
||||||
|
|
||||||
remove_deployment_group(state, deployment_group)
|
defp handle_event(:deployment_group_deleted, %{id: id}, state) do
|
||||||
|
with {:ok, deployment_group} <- fetch_deployment_group(id, state) do
|
||||||
|
handle_event(:deployment_group_deleted, deployment_group, state)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -517,6 +553,7 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
|> dispatch_file_systems(user_connected)
|
|> dispatch_file_systems(user_connected)
|
||||||
|> dispatch_deployment_groups(user_connected)
|
|> dispatch_deployment_groups(user_connected)
|
||||||
|> dispatch_app_deployments(user_connected)
|
|> dispatch_app_deployments(user_connected)
|
||||||
|
|> dispatch_agents(user_connected)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_event(:agent_connected, agent_connected, state) do
|
defp handle_event(:agent_connected, agent_connected, state) do
|
||||||
|
@ -526,14 +563,15 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
|> dispatch_file_systems(agent_connected)
|
|> dispatch_file_systems(agent_connected)
|
||||||
|> dispatch_deployment_groups(agent_connected)
|
|> dispatch_deployment_groups(agent_connected)
|
||||||
|> dispatch_app_deployments(agent_connected)
|
|> dispatch_app_deployments(agent_connected)
|
||||||
|
|> dispatch_agents(agent_connected)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_event(:app_deployment_created, %Teams.AppDeployment{} = app_deployment, state) do
|
defp handle_event(:app_deployment_created, %Teams.AppDeployment{} = app_deployment, state) do
|
||||||
deployment_group_id = app_deployment.deployment_group_id
|
deployment_group_id = app_deployment.deployment_group_id
|
||||||
|
|
||||||
with {:ok, deployment_group} <- fetch_deployment_group(deployment_group_id, state) do
|
with {:ok, deployment_group} <- fetch_deployment_group(deployment_group_id, state) do
|
||||||
state = put_app_deployment(state, app_deployment)
|
|
||||||
Teams.Broadcasts.app_deployment_created(app_deployment)
|
Teams.Broadcasts.app_deployment_created(app_deployment)
|
||||||
|
state = put_app_deployment(state, app_deployment)
|
||||||
|
|
||||||
if deployment_group.id == state.deployment_group_id do
|
if deployment_group.id == state.deployment_group_id do
|
||||||
manager_sync()
|
manager_sync()
|
||||||
|
@ -546,11 +584,31 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
defp handle_event(:app_deployment_created, app_deployment_created, state) do
|
defp handle_event(:app_deployment_created, app_deployment_created, state) do
|
||||||
handle_event(
|
handle_event(
|
||||||
:app_deployment_created,
|
:app_deployment_created,
|
||||||
build_app_deployment(app_deployment_created.app_deployment),
|
build_app_deployment(state, app_deployment_created.app_deployment),
|
||||||
state
|
state
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp handle_event(:agent_joined, %Teams.Agent{} = agent, state) do
|
||||||
|
Teams.Broadcasts.agent_joined(agent)
|
||||||
|
put_agent(state, agent)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp handle_event(:agent_joined, agent_joined, state) do
|
||||||
|
handle_event(:agent_joined, build_agent(state, agent_joined.agent), state)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp handle_event(:agent_left, %Teams.Agent{} = agent, state) do
|
||||||
|
Teams.Broadcasts.agent_left(agent)
|
||||||
|
remove_agent(state, agent)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp handle_event(:agent_left, %{id: id}, state) do
|
||||||
|
with {:ok, agent} <- fetch_agent(id, state) do
|
||||||
|
handle_event(:agent_left, agent, state)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp dispatch_secrets(state, %{secrets: secrets}) do
|
defp dispatch_secrets(state, %{secrets: secrets}) do
|
||||||
decrypted_secrets = Enum.map(secrets, &build_secret(state, &1))
|
decrypted_secrets = Enum.map(secrets, &build_secret(state, &1))
|
||||||
|
|
||||||
|
@ -601,12 +659,19 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp dispatch_app_deployments(state, %{app_deployments: app_deployments}) do
|
defp dispatch_app_deployments(state, %{app_deployments: app_deployments}) do
|
||||||
app_deployments = Enum.map(app_deployments, &build_app_deployment/1)
|
app_deployments = Enum.map(app_deployments, &build_app_deployment(state, &1))
|
||||||
{created, _, _} = diff(state.app_deployments, app_deployments, &(&1.id == &2.id))
|
{created, _, _} = diff(state.app_deployments, app_deployments, &(&1.id == &2.id))
|
||||||
|
|
||||||
dispatch_events(state, app_deployment_created: created)
|
dispatch_events(state, app_deployment_created: created)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp dispatch_agents(state, %{agents: agents}) do
|
||||||
|
agents = Enum.map(agents, &build_agent(state, &1))
|
||||||
|
{joined, left, _} = diff(state.agents, agents, &(&1.id == &2.id))
|
||||||
|
|
||||||
|
dispatch_events(state, agent_joined: joined, agent_left: left)
|
||||||
|
end
|
||||||
|
|
||||||
defp update_hub(state, %{public_key: org_public_key}) do
|
defp update_hub(state, %{public_key: org_public_key}) do
|
||||||
hub = %{state.hub | org_public_key: org_public_key}
|
hub = %{state.hub | org_public_key: org_public_key}
|
||||||
|
|
||||||
|
@ -638,9 +703,19 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
defp find_deployment_group(nil, _), do: nil
|
defp find_deployment_group(nil, _), do: nil
|
||||||
defp find_deployment_group(id, groups), do: Enum.find(groups, &(&1.id == id))
|
defp find_deployment_group(id, groups), do: Enum.find(groups, &(&1.id == id))
|
||||||
|
|
||||||
defp fetch_deployment_group(id, state) do
|
defp fetch_deployment_group(id, state),
|
||||||
if deployment_group = find_deployment_group(id, state.deployment_groups) do
|
do: fetch_entry(state.deployment_groups, &(&1.id == id), state)
|
||||||
{:ok, deployment_group}
|
|
||||||
|
defp fetch_secret(name, state), do: fetch_entry(state.secrets, &(&1.name == name), state)
|
||||||
|
|
||||||
|
defp fetch_file_system(external_id, state),
|
||||||
|
do: fetch_entry(state.file_systems, &(&1.external_id == external_id), state)
|
||||||
|
|
||||||
|
defp fetch_agent(id, state), do: fetch_entry(state.agents, &(&1.id == id), state)
|
||||||
|
|
||||||
|
defp fetch_entry(entries, fun, state) do
|
||||||
|
if entry = Enum.find(entries, fun) do
|
||||||
|
{:ok, entry}
|
||||||
else
|
else
|
||||||
state
|
state
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@ defmodule Livebook.Teams do
|
||||||
alias Livebook.Hubs
|
alias Livebook.Hubs
|
||||||
alias Livebook.Hubs.Team
|
alias Livebook.Hubs.Team
|
||||||
alias Livebook.Hubs.TeamClient
|
alias Livebook.Hubs.TeamClient
|
||||||
alias Livebook.Teams.{AppDeployment, DeploymentGroup, Org, Requests}
|
alias Livebook.Teams.{Agent, AppDeployment, DeploymentGroup, Org, Requests}
|
||||||
|
|
||||||
import Ecto.Changeset,
|
import Ecto.Changeset,
|
||||||
only: [add_error: 3, apply_action: 2, apply_action!: 2, get_field: 2]
|
only: [add_error: 3, apply_action: 2, apply_action!: 2, get_field: 2]
|
||||||
|
@ -221,4 +221,12 @@ defmodule Livebook.Teams do
|
||||||
def get_app_deployments(team) do
|
def get_app_deployments(team) do
|
||||||
TeamClient.get_app_deployments(team.id)
|
TeamClient.get_app_deployments(team.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets a list of agents for a given Hub.
|
||||||
|
"""
|
||||||
|
@spec get_agents(Team.t()) :: list(Agent.t())
|
||||||
|
def get_agents(team) do
|
||||||
|
TeamClient.get_agents(team.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
19
lib/livebook/teams/agent.ex
Normal file
19
lib/livebook/teams/agent.ex
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
defmodule Livebook.Teams.Agent do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
id: String.t() | nil,
|
||||||
|
name: String.t() | nil,
|
||||||
|
hub_id: String.t() | nil,
|
||||||
|
org_id: String.t() | nil,
|
||||||
|
deployment_group_id: String.t() | nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@primary_key {:id, :string, autogenerate: false}
|
||||||
|
embedded_schema do
|
||||||
|
field :name, :string
|
||||||
|
field :hub_id, :string
|
||||||
|
field :org_id, :string
|
||||||
|
field :deployment_group_id, :string
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,7 @@ defmodule Livebook.Teams.AppDeployment do
|
||||||
slug: String.t() | nil,
|
slug: String.t() | nil,
|
||||||
sha: String.t() | nil,
|
sha: String.t() | nil,
|
||||||
title: String.t() | nil,
|
title: String.t() | nil,
|
||||||
|
hub_id: String.t() | nil,
|
||||||
deployment_group_id: String.t() | nil,
|
deployment_group_id: String.t() | nil,
|
||||||
file: binary() | nil,
|
file: binary() | nil,
|
||||||
deployed_by: String.t() | nil,
|
deployed_by: String.t() | nil,
|
||||||
|
@ -20,6 +21,7 @@ defmodule Livebook.Teams.AppDeployment do
|
||||||
field :slug, :string
|
field :slug, :string
|
||||||
field :sha, :string
|
field :sha, :string
|
||||||
field :title, :string
|
field :title, :string
|
||||||
|
field :hub_id, :string
|
||||||
field :deployment_group_id, :string
|
field :deployment_group_id, :string
|
||||||
field :file, :string
|
field :file, :string
|
||||||
field :deployed_by, :string
|
field :deployed_by, :string
|
||||||
|
@ -45,6 +47,7 @@ defmodule Livebook.Teams.AppDeployment do
|
||||||
slug: notebook.app_settings.slug,
|
slug: notebook.app_settings.slug,
|
||||||
sha: shasum,
|
sha: shasum,
|
||||||
title: notebook.name,
|
title: notebook.name,
|
||||||
|
hub_id: notebook.hub_id,
|
||||||
deployment_group_id: notebook.deployment_group_id,
|
deployment_group_id: notebook.deployment_group_id,
|
||||||
file: zip_content
|
file: zip_content
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
defmodule Livebook.Teams.Broadcasts do
|
defmodule Livebook.Teams.Broadcasts do
|
||||||
alias Livebook.Teams.{AppDeployment, DeploymentGroup}
|
alias Livebook.Teams.{Agent, AppDeployment, DeploymentGroup}
|
||||||
|
|
||||||
@type broadcast :: :ok | {:error, term()}
|
@type broadcast :: :ok | {:error, term()}
|
||||||
|
|
||||||
@deployment_groups_topic "teams:deployment_groups"
|
@deployment_groups_topic "teams:deployment_groups"
|
||||||
@app_deployments_topic "teams:app_deployments"
|
@app_deployments_topic "teams:app_deployments"
|
||||||
|
@agents_topic "teams:agents"
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Subscribes to one or more subtopics in `"teams"`.
|
Subscribes to one or more subtopics in `"teams"`.
|
||||||
|
@ -21,6 +22,11 @@ defmodule Livebook.Teams.Broadcasts do
|
||||||
|
|
||||||
* `{:app_deployment_created, AppDeployment.t()}`
|
* `{:app_deployment_created, AppDeployment.t()}`
|
||||||
|
|
||||||
|
Topic `#{@agents_topic}`:
|
||||||
|
|
||||||
|
* `{:agent_joined, Agent.t()}`
|
||||||
|
* `{:agent_left, Agent.t()}`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@spec subscribe(atom() | list(atom())) :: :ok | {:error, term()}
|
@spec subscribe(atom() | list(atom())) :: :ok | {:error, term()}
|
||||||
def subscribe(topics) when is_list(topics) do
|
def subscribe(topics) when is_list(topics) do
|
||||||
|
@ -79,6 +85,22 @@ defmodule Livebook.Teams.Broadcasts do
|
||||||
broadcast(@app_deployments_topic, {:app_deployment_created, app_deployment})
|
broadcast(@app_deployments_topic, {:app_deployment_created, app_deployment})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Broadcasts under `#{@agents_topic}` topic when hub received a new agent.
|
||||||
|
"""
|
||||||
|
@spec agent_joined(Agent.t()) :: broadcast()
|
||||||
|
def agent_joined(%Agent{} = agent) do
|
||||||
|
broadcast(@agents_topic, {:agent_joined, agent})
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Broadcasts under `#{@agents_topic}` topic when hub received a deleted agent.
|
||||||
|
"""
|
||||||
|
@spec agent_left(Agent.t()) :: broadcast()
|
||||||
|
def agent_left(%Agent{} = agent) do
|
||||||
|
broadcast(@agents_topic, {:agent_left, agent})
|
||||||
|
end
|
||||||
|
|
||||||
defp broadcast(topic, message) do
|
defp broadcast(topic, message) do
|
||||||
Phoenix.PubSub.broadcast(Livebook.PubSub, topic, message)
|
Phoenix.PubSub.broadcast(Livebook.PubSub, topic, message)
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
||||||
file_systems = Hubs.get_file_systems(assigns.hub, hub_only: true)
|
file_systems = Hubs.get_file_systems(assigns.hub, hub_only: true)
|
||||||
deployment_groups = Teams.get_deployment_groups(assigns.hub)
|
deployment_groups = Teams.get_deployment_groups(assigns.hub)
|
||||||
app_deployments = Teams.get_app_deployments(assigns.hub)
|
app_deployments = Teams.get_app_deployments(assigns.hub)
|
||||||
|
agents = Teams.get_agents(assigns.hub)
|
||||||
secret_name = assigns.params["secret_name"]
|
secret_name = assigns.params["secret_name"]
|
||||||
file_system_id = assigns.params["file_system_id"]
|
file_system_id = assigns.params["file_system_id"]
|
||||||
default? = default_hub?(assigns.hub)
|
default? = default_hub?(assigns.hub)
|
||||||
|
@ -41,6 +42,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
||||||
file_systems: file_systems,
|
file_systems: file_systems,
|
||||||
deployment_groups: Enum.sort_by(deployment_groups, & &1.name),
|
deployment_groups: Enum.sort_by(deployment_groups, & &1.name),
|
||||||
app_deployments: Enum.frequencies_by(app_deployments, & &1.deployment_group_id),
|
app_deployments: Enum.frequencies_by(app_deployments, & &1.deployment_group_id),
|
||||||
|
agents: Enum.frequencies_by(agents, & &1.deployment_group_id),
|
||||||
show_key: show_key,
|
show_key: show_key,
|
||||||
secret_name: secret_name,
|
secret_name: secret_name,
|
||||||
secret_value: secret_value,
|
secret_value: secret_value,
|
||||||
|
@ -222,6 +224,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
|
||||||
hub={@hub}
|
hub={@hub}
|
||||||
deployment_group={deployment_group}
|
deployment_group={deployment_group}
|
||||||
app_deployments_count={Map.get(@app_deployments, deployment_group.id, 0)}
|
app_deployments_count={Map.get(@app_deployments, deployment_group.id, 0)}
|
||||||
|
agents_count={Map.get(@agents, deployment_group.id, 0)}
|
||||||
live_action={@live_action}
|
live_action={@live_action}
|
||||||
params={@params}
|
params={@params}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule LivebookWeb.Hub.EditLive do
|
||||||
def mount(_params, _session, socket) do
|
def mount(_params, _session, socket) do
|
||||||
if connected?(socket) do
|
if connected?(socket) do
|
||||||
Hubs.Broadcasts.subscribe([:connection])
|
Hubs.Broadcasts.subscribe([:connection])
|
||||||
Livebook.Teams.Broadcasts.subscribe([:deployment_groups, :app_deployments])
|
Livebook.Teams.Broadcasts.subscribe([:deployment_groups, :app_deployments, :agents])
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
|
|
|
@ -54,7 +54,9 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupComponent do
|
||||||
<!-- Overview -->
|
<!-- Overview -->
|
||||||
<div :if={@deployment_group.mode == :online} class="flex flex-col lg:flex-row justify-center">
|
<div :if={@deployment_group.mode == :online} class="flex flex-col lg:flex-row justify-center">
|
||||||
<.labeled_text class="grow mt-6 lg:border-l lg:pl-4" label="Instances running">
|
<.labeled_text class="grow mt-6 lg:border-l lg:pl-4" label="Instances running">
|
||||||
<span class="text-lg font-normal">-1</span>
|
<span class="text-lg font-normal" aria-label="instances running">
|
||||||
|
<%= @agents_count %>
|
||||||
|
</span>
|
||||||
<.link
|
<.link
|
||||||
patch={~p"/hub/#{@hub.id}/groups/#{@deployment_group.id}/agents/new"}
|
patch={~p"/hub/#{@hub.id}/groups/#{@deployment_group.id}/agents/new"}
|
||||||
class="pl-2 text-blue-600"
|
class="pl-2 text-blue-600"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule LivebookProto.AgentLeft do
|
defmodule LivebookProto.AgentLeft do
|
||||||
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0"
|
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0"
|
||||||
|
|
||||||
field :agent, 1, type: LivebookProto.Agent
|
field :id, 1, type: :string
|
||||||
end
|
end
|
||||||
|
|
|
@ -140,7 +140,7 @@ message AgentJoined {
|
||||||
}
|
}
|
||||||
|
|
||||||
message AgentLeft {
|
message AgentLeft {
|
||||||
Agent agent = 1;
|
string id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Agent {
|
message Agent {
|
||||||
|
|
|
@ -5,7 +5,7 @@ defmodule Livebook.Hubs.TeamClientTest do
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Livebook.Hubs.Broadcasts.subscribe([:connection, :file_systems, :secrets])
|
Livebook.Hubs.Broadcasts.subscribe([:connection, :file_systems, :secrets])
|
||||||
Livebook.Teams.Broadcasts.subscribe([:deployment_groups, :app_deployments])
|
Livebook.Teams.Broadcasts.subscribe([:deployment_groups, :app_deployments, :agents])
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -332,7 +332,13 @@ defmodule Livebook.Hubs.TeamClientTest do
|
||||||
agent_keys: []
|
agent_keys: []
|
||||||
}
|
}
|
||||||
|
|
||||||
app_deployment = build(:app_deployment, file: nil, deployment_group_id: deployment_group.id)
|
app_deployment =
|
||||||
|
build(:app_deployment,
|
||||||
|
hub_id: team.id,
|
||||||
|
file: nil,
|
||||||
|
deployment_group_id: deployment_group.id
|
||||||
|
)
|
||||||
|
|
||||||
{seconds, 0} = NaiveDateTime.to_gregorian_seconds(app_deployment.deployed_at)
|
{seconds, 0} = NaiveDateTime.to_gregorian_seconds(app_deployment.deployed_at)
|
||||||
|
|
||||||
livebook_proto_app_deployment =
|
livebook_proto_app_deployment =
|
||||||
|
@ -357,6 +363,31 @@ defmodule Livebook.Hubs.TeamClientTest do
|
||||||
assert_receive {:app_deployment_created, ^app_deployment}, 5000
|
assert_receive {:app_deployment_created, ^app_deployment}, 5000
|
||||||
assert app_deployment in TeamClient.get_app_deployments(team.id)
|
assert app_deployment in TeamClient.get_app_deployments(team.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "dispatches the agents list", %{team: team, user_connected: user_connected} do
|
||||||
|
pid = connect_to_teams(team)
|
||||||
|
agent = build(:agent, hub_id: team.id, org_id: to_string(team.org_id))
|
||||||
|
|
||||||
|
livebook_proto_agent =
|
||||||
|
%LivebookProto.Agent{
|
||||||
|
id: agent.id,
|
||||||
|
name: agent.name,
|
||||||
|
org_id: agent.org_id,
|
||||||
|
deployment_group_id: agent.deployment_group_id
|
||||||
|
}
|
||||||
|
|
||||||
|
user_connected = %{user_connected | agents: [livebook_proto_agent]}
|
||||||
|
|
||||||
|
send(pid, {:event, :user_connected, user_connected})
|
||||||
|
assert_receive {:agent_joined, ^agent}
|
||||||
|
assert agent in TeamClient.get_agents(team.id)
|
||||||
|
|
||||||
|
user_connected = %{user_connected | agents: []}
|
||||||
|
|
||||||
|
send(pid, {:event, :user_connected, user_connected})
|
||||||
|
assert_receive {:agent_left, ^agent}
|
||||||
|
refute agent in TeamClient.get_agents(team.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "handle agent_connected event" do
|
describe "handle agent_connected event" do
|
||||||
|
@ -373,7 +404,8 @@ defmodule Livebook.Hubs.TeamClientTest do
|
||||||
secrets: [],
|
secrets: [],
|
||||||
file_systems: [],
|
file_systems: [],
|
||||||
deployment_groups: [],
|
deployment_groups: [],
|
||||||
app_deployments: []
|
app_deployments: [],
|
||||||
|
agents: []
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
|
@ -723,6 +755,36 @@ defmodule Livebook.Hubs.TeamClientTest do
|
||||||
Livebook.Hubs.delete_hub(team.id)
|
Livebook.Hubs.delete_hub(team.id)
|
||||||
Livebook.App.close(app_pid)
|
Livebook.App.close(app_pid)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "dispatches the agents list", %{team: team, agent_connected: agent_connected} do
|
||||||
|
pid = connect_to_teams(team)
|
||||||
|
|
||||||
|
# Since we're connecting as Agent, we should receive the
|
||||||
|
# `:agent_joined` event from `:agent_connected` event
|
||||||
|
assert_receive {:agent_joined, agent}
|
||||||
|
assert agent in TeamClient.get_agents(team.id)
|
||||||
|
|
||||||
|
assert_receive {:deployment_group_created, deployment_group}
|
||||||
|
|
||||||
|
livebook_proto_deployment_group =
|
||||||
|
%LivebookProto.DeploymentGroup{
|
||||||
|
id: to_string(deployment_group.id),
|
||||||
|
name: deployment_group.name,
|
||||||
|
mode: to_string(deployment_group.mode),
|
||||||
|
secrets: [],
|
||||||
|
agent_keys: []
|
||||||
|
}
|
||||||
|
|
||||||
|
agent_connected = %{
|
||||||
|
agent_connected
|
||||||
|
| deployment_groups: [livebook_proto_deployment_group],
|
||||||
|
agents: []
|
||||||
|
}
|
||||||
|
|
||||||
|
send(pid, {:event, :agent_connected, agent_connected})
|
||||||
|
assert_receive {:agent_left, ^agent}
|
||||||
|
refute agent in TeamClient.get_agents(team.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp connect_to_teams(%{id: id} = team) do
|
defp connect_to_teams(%{id: id} = team) do
|
||||||
|
|
|
@ -8,7 +8,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
||||||
|
|
||||||
setup %{user: user, node: node} do
|
setup %{user: user, node: node} do
|
||||||
Livebook.Hubs.Broadcasts.subscribe([:crud, :connection, :secrets, :file_systems])
|
Livebook.Hubs.Broadcasts.subscribe([:crud, :connection, :secrets, :file_systems])
|
||||||
Livebook.Teams.Broadcasts.subscribe([:deployment_groups])
|
Livebook.Teams.Broadcasts.subscribe([:deployment_groups, :agents])
|
||||||
hub = create_team_hub(user, node)
|
hub = create_team_hub(user, node)
|
||||||
id = hub.id
|
id = hub.id
|
||||||
|
|
||||||
|
@ -212,4 +212,43 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
||||||
assert render(view) =~ "Secret DEPLOYMENT_GROUP_DELETE_SECRET deleted successfully"
|
assert render(view) =~ "Secret DEPLOYMENT_GROUP_DELETE_SECRET deleted successfully"
|
||||||
refute render(element(view, "#hub-deployment-group-#{id}")) =~ secret.name
|
refute render(element(view, "#hub-deployment-group-#{id}")) =~ secret.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "shows the agent count", %{conn: conn, hub: hub} do
|
||||||
|
name = "TEAMS_EDIT_DEPLOYMENT_GROUP"
|
||||||
|
%{id: id} = insert_deployment_group(name: name, mode: :online, hub_id: hub.id)
|
||||||
|
|
||||||
|
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||||
|
|
||||||
|
assert view
|
||||||
|
|> element("#hub-deployment-group-#{id} [aria-label=\"instances running\"]")
|
||||||
|
|> render()
|
||||||
|
|> Floki.parse_fragment!()
|
||||||
|
|> Floki.text()
|
||||||
|
|> String.trim() == "0"
|
||||||
|
|
||||||
|
org_id = to_string(hub.org_id)
|
||||||
|
|
||||||
|
# Simulates the agent join event
|
||||||
|
pid = Livebook.Hubs.TeamClient.get_pid(hub.id)
|
||||||
|
agent = build(:agent, hub_id: hub.id, org_id: org_id, deployment_group_id: to_string(id))
|
||||||
|
|
||||||
|
livebook_proto_agent =
|
||||||
|
%LivebookProto.Agent{
|
||||||
|
id: agent.id,
|
||||||
|
name: agent.name,
|
||||||
|
org_id: agent.org_id,
|
||||||
|
deployment_group_id: agent.deployment_group_id
|
||||||
|
}
|
||||||
|
|
||||||
|
livebook_proto_agent_joined = %LivebookProto.AgentJoined{agent: livebook_proto_agent}
|
||||||
|
send(pid, {:event, :agent_joined, livebook_proto_agent_joined})
|
||||||
|
assert_receive {:agent_joined, ^agent}
|
||||||
|
|
||||||
|
assert view
|
||||||
|
|> element("#hub-deployment-group-#{id} [aria-label=\"instances running\"]")
|
||||||
|
|> render()
|
||||||
|
|> Floki.parse_fragment!()
|
||||||
|
|> Floki.text()
|
||||||
|
|> String.trim() == "1"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -112,12 +112,23 @@ defmodule Livebook.Factory do
|
||||||
sha: shasum,
|
sha: shasum,
|
||||||
slug: slug,
|
slug: slug,
|
||||||
file: content,
|
file: content,
|
||||||
|
hub_id: Livebook.Hubs.Personal.id(),
|
||||||
deployment_group_id: "1",
|
deployment_group_id: "1",
|
||||||
deployed_by: "Ada Lovelace",
|
deployed_by: "Ada Lovelace",
|
||||||
deployed_at: NaiveDateTime.truncate(deployed_at, :second)
|
deployed_at: NaiveDateTime.truncate(deployed_at, :second)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build(:agent) do
|
||||||
|
%Livebook.Teams.Agent{
|
||||||
|
id: "agent_name-#{Livebook.Utils.random_short_id()}",
|
||||||
|
name: "agent_name",
|
||||||
|
hub_id: Livebook.Hubs.Personal.id(),
|
||||||
|
org_id: "1",
|
||||||
|
deployment_group_id: "1"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def build(factory_name, attrs) do
|
def build(factory_name, attrs) do
|
||||||
factory_name |> build() |> struct!(attrs)
|
factory_name |> build() |> struct!(attrs)
|
||||||
end
|
end
|
||||||
|
|
|
@ -93,7 +93,10 @@ defmodule Livebook.TeamsServer do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({_port, {:data, message}}, state) do
|
def handle_info({_port, {:data, message}}, state) do
|
||||||
info(message)
|
if Livebook.Config.boolean!("TEAMS_DEBUG", false) do
|
||||||
|
info(message)
|
||||||
|
end
|
||||||
|
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue