From 3c0a318507a51270edba8c6b47fb6fb06fa483e9 Mon Sep 17 00:00:00 2001 From: Carmine Di Monaco Date: Mon, 27 Mar 2023 11:01:46 +0200 Subject: [PATCH] Cluster registration also for nodes not DC (#1275) * Cluster registration process happens also when the discovered node is not a DC * Address docs and testing review feedbacks * Removed useless tests from cluster_tests * Add host to cluster when a message from a dc host arrives for a cluster Co-Authored-By: Fabrizio Sestito * Add register cluster host test when the host is and is not a DC --------- Co-authored-by: Fabrizio Sestito --- lib/trento/domain/cluster/cluster.ex | 55 +++++++++-- test/trento/domain/cluster/cluster_test.exs | 102 +++++++++++++++++--- 2 files changed, 135 insertions(+), 22 deletions(-) diff --git a/lib/trento/domain/cluster/cluster.ex b/lib/trento/domain/cluster/cluster.ex index 6c9c380df6..ddd50e3264 100644 --- a/lib/trento/domain/cluster/cluster.ex +++ b/lib/trento/domain/cluster/cluster.ex @@ -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: @@ -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{ @@ -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{ @@ -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) diff --git a/test/trento/domain/cluster/cluster_test.exs b/test/trento/domain/cluster/cluster_test.exs index 3f7b57db9e..06a43dd515 100644 --- a/test/trento/domain/cluster/cluster_test.exs +++ b/test/trento/domain/cluster/cluster_test.exs @@ -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() @@ -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() @@ -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 @@ -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() @@ -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,