Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cluster registration also for nodes not DC #1275

Merged
merged 8 commits into from
Mar 27, 2023
Merged
55 changes: 45 additions & 10 deletions lib/trento/domain/cluster/cluster.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@ defmodule Trento.Domain.Cluster do
SAP workloads.
Each deployed cluster is registered as a new aggregate entry, meaning that all the hosts belonging
to the same cluster are part of the same stream. A cluster is registered first time/details updated afterwards
only by cluster discovery messages coming from the **designated controller** node. Once a cluster is
registered other hosts can be added receiving discovery messages coming from other nodes. All the hosts
are listed in the `hosts` field.
to the same cluster are part of the same stream.
A new cluster is registered when a cluster discovery message from any of the nodes of the cluster is received.
The cluster details will be populated if the received discovery message is coming from the **designated controller** node.
Otherwise the cluster details are left as unknown, and filled once a message from the **designated controller** is received.
Once a cluster is registered, other hosts will be added when cluster discovery messages from them are received.
All the hosts are listed in the `hosts` field.
The cluster aggregate stores and updates information coming in the cluster discovery messages such as:
Expand Down Expand Up @@ -112,8 +118,8 @@ defmodule Trento.Domain.Cluster do

def execute(%Cluster{rolling_up: true}, _), do: {:error, :cluster_rolling_up}

# When a DC cluster node is registered for the first time, a cluster is registered
# and the host of the node is added to the cluster
# When a DC node is discovered, a cluster is registered and the host is added to the cluster.
# The cluster details are populated with the information coming from the DC node.
def execute(
%Cluster{cluster_id: nil},
%RegisterClusterHost{
Expand Down Expand Up @@ -149,10 +155,34 @@ defmodule Trento.Domain.Cluster do
]
end

# If no DC node was received yet, no cluster was registered.
def execute(%Cluster{cluster_id: nil}, %RegisterClusterHost{designated_controller: false}),
do: {:error, :cluster_not_found}
# When a non-DC node is discovered, a cluster is registered and the host is added to the cluster.
# The cluster details are left as unknown, and filled once a message from the DC node is received.
def execute(%Cluster{cluster_id: nil}, %RegisterClusterHost{
cluster_id: cluster_id,
name: name,
host_id: host_id,
designated_controller: false
}) do
[
%ClusterRegistered{
cluster_id: cluster_id,
name: name,
type: :unknown,
sid: nil,
provider: :unknown,
resources_number: nil,
hosts_number: nil,
details: nil,
health: :unknown
},
%HostAddedToCluster{
cluster_id: cluster_id,
host_id: host_id
}
]
end

# If the cluster is already registered, and the host was never discovered before, it is added to the cluster.
def execute(
%Cluster{} = cluster,
%RegisterClusterHost{
Expand All @@ -163,14 +193,19 @@ defmodule Trento.Domain.Cluster do
maybe_emit_host_added_to_cluster_event(cluster, host_id)
end

# When a DC node is discovered, if the cluster is already registered,
# the cluster details are updated with the information coming from the DC node.
# The cluster discovered health is updated based on the new details.
def execute(
%Cluster{} = cluster,
%RegisterClusterHost{
designated_controller: true
designated_controller: true,
host_id: host_id
} = command
) do
cluster
|> Multi.new()
|> Multi.execute(fn cluster -> maybe_emit_host_added_to_cluster_event(cluster, host_id) end)
|> Multi.execute(fn cluster -> maybe_emit_cluster_details_updated_event(cluster, command) end)
|> Multi.execute(fn cluster ->
maybe_emit_cluster_discovered_health_changed_event(cluster, command)
Expand Down
102 changes: 90 additions & 12 deletions test/trento/domain/cluster/cluster_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ defmodule Trento.ClusterTest do
require Trento.Domain.Enums.Health, as: Health

describe "cluster registration" do
test "should register a cluster and add the node host to the cluster if the node is a DC" do
test "should register a cluster with full details and add the node host to the cluster if the node is a DC" do
cluster_id = Faker.UUID.v4()
host_id = Faker.UUID.v4()
name = Faker.StarWars.character()
Expand Down Expand Up @@ -81,7 +81,51 @@ defmodule Trento.ClusterTest do
)
end

test "should add a host to the cluster" do
test "should register a cluster with unknown details when the cluster was not registered yet and a message from a non-DC is received" do
cluster_id = Faker.UUID.v4()
host_id = Faker.UUID.v4()
name = Faker.StarWars.character()

assert_events_and_state(
[],
RegisterClusterHost.new!(%{
cluster_id: cluster_id,
host_id: host_id,
name: name,
discovered_health: :unknown,
provider: :unknown,
type: :unknown,
designated_controller: false
}),
[
%ClusterRegistered{
cluster_id: cluster_id,
name: name,
sid: nil,
provider: :unknown,
type: :unknown,
health: :unknown,
details: nil
},
%HostAddedToCluster{
cluster_id: cluster_id,
host_id: host_id
}
],
%Cluster{
cluster_id: cluster_id,
name: name,
sid: nil,
type: :unknown,
provider: :unknown,
hosts: [host_id],
discovered_health: :unknown,
health: :unknown
}
)
end

test "should add a host to the cluster if the host is not a DC and the cluster is already registered" do
cluster_id = Faker.UUID.v4()
host_id = Faker.UUID.v4()
name = Faker.StarWars.character()
Expand Down Expand Up @@ -116,20 +160,47 @@ defmodule Trento.ClusterTest do
)
end

test "should return an error if the cluster was not registered yet and a command from a non-DC is received" do
assert_error(
[],
test "should add a host to the cluster if the host is a DC and the cluster is already registered" do
cluster_id = Faker.UUID.v4()
host_id = Faker.UUID.v4()
name = Faker.StarWars.character()
sid = Faker.StarWars.planet()

assert_events_and_state(
[
build(
:cluster_registered_event,
cluster_id: cluster_id,
provider: :azure,
sid: sid,
name: name,
details: nil
),
build(:host_added_to_cluster_event, cluster_id: cluster_id)
],
RegisterClusterHost.new!(%{
cluster_id: Faker.UUID.v4(),
host_id: Faker.UUID.v4(),
name: Faker.StarWars.character(),
sid: Faker.StarWars.planet(),
discovered_health: :unknown,
cluster_id: cluster_id,
host_id: host_id,
name: name,
sid: sid,
type: :hana_scale_up,
designated_controller: false,
discovered_health: :passing,
resources_number: 8,
hosts_number: 2,
designated_controller: true,
provider: :azure
}),
{:error, :cluster_not_found}
[
%HostAddedToCluster{
cluster_id: cluster_id,
host_id: host_id
}
],
fn cluster ->
assert %Cluster{
hosts: [^host_id | _]
} = cluster
end
)
end
end
Expand Down Expand Up @@ -262,6 +333,7 @@ defmodule Trento.ClusterTest do

test "should use discovered cluster health when no checks are selected" do
cluster_id = Faker.UUID.v4()
host_id = Faker.UUID.v4()
name = Faker.StarWars.character()
sid = Faker.StarWars.planet()

Expand All @@ -274,11 +346,17 @@ defmodule Trento.ClusterTest do
sid: sid,
details: nil,
provider: :azure
),
build(
:host_added_to_cluster_event,
cluster_id: cluster_id,
host_id: host_id
)
],
[
build(
:register_cluster_host,
host_id: host_id,
cluster_id: cluster_id,
name: name,
sid: sid,
Expand Down