diff --git a/lib/trento/domain/sap_system/sap_system.ex b/lib/trento/domain/sap_system/sap_system.ex index 7d3c14ed24..51d3a4738d 100644 --- a/lib/trento/domain/sap_system/sap_system.ex +++ b/lib/trento/domain/sap_system/sap_system.ex @@ -54,12 +54,14 @@ defmodule Trento.Domain.SapSystem do } alias Trento.Domain.Commands.{ + DeregisterApplicationInstance, RegisterApplicationInstance, RegisterDatabaseInstance, RollUpSapSystem } alias Trento.Domain.Events.{ + ApplicationInstanceDeregistered, ApplicationInstanceHealthChanged, ApplicationInstanceRegistered, DatabaseHealthChanged, @@ -67,6 +69,7 @@ defmodule Trento.Domain.SapSystem do DatabaseInstanceRegistered, DatabaseInstanceSystemReplicationChanged, DatabaseRegistered, + SapSystemDeregistered, SapSystemHealthChanged, SapSystemRegistered, SapSystemRolledUp, @@ -81,9 +84,10 @@ defmodule Trento.Domain.SapSystem do deftype do field :sap_system_id, Ecto.UUID - field :sid, :string + field :sid, :string, default: nil field :health, Ecto.Enum, values: Health.values() field :rolling_up, :boolean, default: false + field :deregistered_at, :utc_datetime_usec, default: nil embeds_one :database, Database embeds_one :application, Application @@ -290,6 +294,71 @@ defmodule Trento.Domain.SapSystem do } end + # Deregister an application instance and emit a ApplicationInstanceDeregistered + # also emit SapSystemDeregistered event if this was the last application instance + def execute( + %SapSystem{ + sap_system_id: sap_system_id + } = sap_system, + %DeregisterApplicationInstance{ + instance_number: instance_number, + sap_system_id: sap_system_id, + host_id: host_id, + deregistered_at: deregistered_at + } + ) do + sap_system + |> Multi.new() + |> Multi.execute(fn _ -> + %ApplicationInstanceDeregistered{ + sap_system_id: sap_system_id, + instance_number: instance_number, + host_id: host_id, + deregistered_at: deregistered_at + } + end) + |> Multi.execute(fn sap_system -> + maybe_emit_sap_system_deregistered_event( + sap_system, + deregistered_at + ) + end) + end + + def apply( + %SapSystem{application: %Application{instances: instances}} = sap_system, + %ApplicationInstanceDeregistered{instance_number: instance_number, host_id: host_id} + ) do + instances = + Enum.reject(instances, fn + %Instance{instance_number: ^instance_number, host_id: ^host_id} -> + true + + _ -> + false + end) + + %SapSystem{ + sap_system + | application: %Application{ + instances: instances + } + } + end + + def apply( + %SapSystem{} = sap_system, + %SapSystemDeregistered{ + deregistered_at: deregistered_at + } + ) do + %SapSystem{ + sap_system + | deregistered_at: deregistered_at, + sid: nil + } + end + def apply( %SapSystem{sap_system_id: nil}, %DatabaseRegistered{ @@ -633,6 +702,31 @@ defmodule Trento.Domain.SapSystem do end end + defp maybe_emit_sap_system_deregistered_event( + %SapSystem{sid: nil}, + _deregistered_at + ), + do: [] + + defp maybe_emit_sap_system_deregistered_event( + %SapSystem{ + sap_system_id: sap_system_id, + application: %Application{ + instances: instances + } + }, + deregistered_at + ) do + has_abap? = Enum.any?(instances, fn %{features: features} -> features =~ "ABAP" end) + + has_messageserver? = + Enum.any?(instances, fn %{features: features} -> features =~ "MESSAGESERVER" end) + + unless has_abap? and has_messageserver? do + %SapSystemDeregistered{sap_system_id: sap_system_id, deregistered_at: deregistered_at} + end + end + defp get_instance(instances, host_id, instance_number) do Enum.find(instances, fn %Instance{host_id: ^host_id, instance_number: ^instance_number} -> diff --git a/lib/trento/infrastructure/router.ex b/lib/trento/infrastructure/router.ex index 48124954ff..a8c2acd520 100644 --- a/lib/trento/infrastructure/router.ex +++ b/lib/trento/infrastructure/router.ex @@ -11,6 +11,7 @@ defmodule Trento.Router do alias Trento.Domain.Commands.{ CompleteChecksExecution, + DeregisterApplicationInstance, DeregisterClusterHost, DeregisterHost, RegisterApplicationInstance, @@ -58,7 +59,12 @@ defmodule Trento.Router do identify SapSystem, by: :sap_system_id - dispatch [RegisterApplicationInstance, RegisterDatabaseInstance, RollUpSapSystem], - to: SapSystem, - lifespan: SapSystem.Lifespan + dispatch [ + DeregisterApplicationInstance, + RegisterApplicationInstance, + RegisterDatabaseInstance, + RollUpSapSystem + ], + to: SapSystem, + lifespan: SapSystem.Lifespan end diff --git a/test/trento/domain/sap_system/sap_system_test.exs b/test/trento/domain/sap_system/sap_system_test.exs index 3be52d9d9c..83d771506c 100644 --- a/test/trento/domain/sap_system/sap_system_test.exs +++ b/test/trento/domain/sap_system/sap_system_test.exs @@ -4,12 +4,14 @@ defmodule Trento.SapSystemTest do import Trento.Factory alias Trento.Domain.Commands.{ + DeregisterApplicationInstance, RegisterApplicationInstance, RegisterDatabaseInstance, RollUpSapSystem } alias Trento.Domain.Events.{ + ApplicationInstanceDeregistered, ApplicationInstanceHealthChanged, ApplicationInstanceRegistered, DatabaseHealthChanged, @@ -17,6 +19,7 @@ defmodule Trento.SapSystemTest do DatabaseInstanceRegistered, DatabaseInstanceSystemReplicationChanged, DatabaseRegistered, + SapSystemDeregistered, SapSystemHealthChanged, SapSystemRegistered, SapSystemRolledUp, @@ -25,6 +28,12 @@ defmodule Trento.SapSystemTest do alias Trento.Domain.SapSystem + alias Trento.Domain.SapSystem.{ + Application, + Database, + Instance + } + describe "SAP System registration" do test "should fail when a sap system does not exists and the database instance has Secondary role" do command = @@ -1064,4 +1073,467 @@ defmodule Trento.SapSystemTest do ) end end + + describe "deregistration" do + test "should deregister an ENQREP Application Instance" do + sap_system_id = UUID.uuid4() + deregistered_at = DateTime.utc_now() + + database_host_id = UUID.uuid4() + message_server_host_id = UUID.uuid4() + abap_host_id = UUID.uuid4() + enqrep_host_id = UUID.uuid4() + + database_instance_number = "00" + message_server_instance_number = "01" + abap_instance_number = "02" + enqrep_server_instance_number = "03" + + assert_events_and_state( + [ + build( + :database_registered_event, + sap_system_id: sap_system_id + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + host_id: database_host_id, + instance_number: database_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "MESSAGESERVER|ENQUE", + host_id: message_server_host_id, + instance_number: message_server_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "ABAP|GATEWAY|ICMAN|IGS", + host_id: abap_host_id, + instance_number: abap_instance_number + ), + build( + :sap_system_registered_event, + sap_system_id: sap_system_id + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + host_id: enqrep_host_id, + instance_number: enqrep_server_instance_number, + features: "ENQREP" + ) + ], + %DeregisterApplicationInstance{ + sap_system_id: sap_system_id, + host_id: enqrep_host_id, + instance_number: enqrep_server_instance_number, + deregistered_at: deregistered_at + }, + [ + %ApplicationInstanceDeregistered{ + sap_system_id: sap_system_id, + host_id: enqrep_host_id, + instance_number: enqrep_server_instance_number, + deregistered_at: deregistered_at + } + ], + fn sap_system -> + assert %SapSystem{ + database: %Database{ + instances: [ + %Instance{ + instance_number: ^database_instance_number, + host_id: ^database_host_id + } + ] + }, + application: %Application{ + instances: [ + %Instance{ + host_id: ^abap_host_id, + instance_number: ^abap_instance_number + }, + %Instance{ + host_id: ^message_server_host_id, + instance_number: ^message_server_instance_number + } + ] + } + } = sap_system + end + ) + end + + test "should deregister an ABAP Application Instance without deregistering the SAP system" do + sap_system_id = UUID.uuid4() + deregistered_at = DateTime.utc_now() + + database_host_id = UUID.uuid4() + message_server_host_id = UUID.uuid4() + abap_host_id = UUID.uuid4() + abap_2_host_id = UUID.uuid4() + enqrep_host_id = UUID.uuid4() + + database_instance_number = "00" + message_server_instance_number = "01" + abap_instance_number = "02" + abap_2_instance_number = "03" + enqrep_server_instance_number = "04" + + assert_events_and_state( + [ + build( + :database_registered_event, + sap_system_id: sap_system_id + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + host_id: database_host_id, + instance_number: database_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "MESSAGESERVER|ENQUE", + host_id: message_server_host_id, + instance_number: message_server_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "ABAP|GATEWAY|ICMAN|IGS", + host_id: abap_host_id, + instance_number: abap_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "ABAP|GATEWAY|ICMAN|IGS", + host_id: abap_2_host_id, + instance_number: abap_2_instance_number + ), + build( + :sap_system_registered_event, + sap_system_id: sap_system_id + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + host_id: enqrep_host_id, + instance_number: enqrep_server_instance_number, + features: "ENQREP" + ) + ], + %DeregisterApplicationInstance{ + sap_system_id: sap_system_id, + host_id: abap_2_host_id, + instance_number: abap_2_instance_number, + deregistered_at: deregistered_at + }, + [ + %ApplicationInstanceDeregistered{ + sap_system_id: sap_system_id, + host_id: abap_2_host_id, + instance_number: abap_2_instance_number, + deregistered_at: deregistered_at + } + ], + fn sap_system -> + assert %SapSystem{ + database: %Database{ + instances: [ + %Instance{ + instance_number: ^database_instance_number, + host_id: ^database_host_id + } + ] + }, + application: %Application{ + instances: [ + %Instance{ + host_id: ^enqrep_host_id, + instance_number: ^enqrep_server_instance_number + }, + %Instance{ + host_id: ^abap_host_id, + instance_number: ^abap_instance_number + }, + %Instance{ + host_id: ^message_server_host_id, + instance_number: ^message_server_instance_number + } + ] + } + } = sap_system + end + ) + end + + test "should deregister last ABAP Application Instance, and deregister SAP System" do + sap_system_id = UUID.uuid4() + deregistered_at = DateTime.utc_now() + + database_host_id = UUID.uuid4() + message_server_host_id = UUID.uuid4() + abap_host_id = UUID.uuid4() + enqrep_host_id = UUID.uuid4() + + database_instance_number = "00" + message_server_instance_number = "01" + abap_instance_number = "02" + enqrep_server_instance_number = "03" + + assert_events_and_state( + [ + build( + :database_registered_event, + sap_system_id: sap_system_id + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + host_id: database_host_id, + instance_number: database_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "MESSAGESERVER|ENQUE", + host_id: message_server_host_id, + instance_number: message_server_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "ABAP|GATEWAY|ICMAN|IGS", + host_id: abap_host_id, + instance_number: abap_instance_number + ), + build( + :sap_system_registered_event, + sap_system_id: sap_system_id + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + host_id: enqrep_host_id, + instance_number: enqrep_server_instance_number, + features: "ENQREP" + ) + ], + %DeregisterApplicationInstance{ + sap_system_id: sap_system_id, + host_id: abap_host_id, + instance_number: abap_instance_number, + deregistered_at: deregistered_at + }, + [ + %ApplicationInstanceDeregistered{ + sap_system_id: sap_system_id, + host_id: abap_host_id, + instance_number: abap_instance_number, + deregistered_at: deregistered_at + }, + %SapSystemDeregistered{ + sap_system_id: sap_system_id, + deregistered_at: deregistered_at + } + ], + fn sap_system -> + assert %SapSystem{ + sid: nil, + database: %Database{ + instances: [ + %Instance{ + instance_number: ^database_instance_number, + host_id: ^database_host_id + } + ] + }, + application: %Application{ + instances: [ + %Instance{ + instance_number: ^enqrep_server_instance_number, + host_id: ^enqrep_host_id + }, + %Instance{ + instance_number: ^message_server_instance_number, + host_id: ^message_server_host_id + } + ] + }, + deregistered_at: ^deregistered_at + } = sap_system + end + ) + end + + test "should deregister just a Message Server from a not-fully-registered SAP system" do + sap_system_id = UUID.uuid4() + deregistered_at = DateTime.utc_now() + + database_host_id = UUID.uuid4() + message_server_host_id = UUID.uuid4() + + database_instance_number = "00" + message_server_instance_number = "01" + + assert_events_and_state( + [ + build( + :database_registered_event, + sid: nil, + sap_system_id: sap_system_id + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + sid: nil, + host_id: database_host_id, + instance_number: database_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + sid: nil, + features: "MESSAGESERVER|ENQUE", + host_id: message_server_host_id, + instance_number: message_server_instance_number + ) + ], + %DeregisterApplicationInstance{ + sap_system_id: sap_system_id, + host_id: message_server_host_id, + instance_number: message_server_instance_number, + deregistered_at: deregistered_at + }, + %ApplicationInstanceDeregistered{ + sap_system_id: sap_system_id, + host_id: message_server_host_id, + instance_number: message_server_instance_number, + deregistered_at: deregistered_at + }, + fn sap_system -> + assert %SapSystem{ + sid: nil, + database: %Database{ + instances: [ + %Instance{ + instance_number: ^database_instance_number, + host_id: ^database_host_id + } + ] + }, + application: %Application{ + instances: [] + }, + deregistered_at: nil + } = sap_system + end + ) + end + + test "should deregister Message Server, and deregister SAP System" do + sap_system_id = UUID.uuid4() + deregistered_at = DateTime.utc_now() + + database_host_id = UUID.uuid4() + message_server_host_id = UUID.uuid4() + abap_host_id = UUID.uuid4() + enqrep_host_id = UUID.uuid4() + + database_instance_number = "00" + message_server_instance_number = "01" + abap_instance_number = "02" + enqrep_server_instance_number = "03" + + assert_events_and_state( + [ + build( + :database_registered_event, + sap_system_id: sap_system_id + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + host_id: database_host_id, + instance_number: database_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "MESSAGESERVER|ENQUE", + host_id: message_server_host_id, + instance_number: message_server_instance_number + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: "ABAP|GATEWAY|ICMAN|IGS", + host_id: abap_host_id, + instance_number: abap_instance_number + ), + build( + :sap_system_registered_event, + sap_system_id: sap_system_id + ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + host_id: enqrep_host_id, + instance_number: enqrep_server_instance_number, + features: "ENQREP" + ) + ], + %DeregisterApplicationInstance{ + sap_system_id: sap_system_id, + host_id: message_server_host_id, + instance_number: message_server_instance_number, + deregistered_at: deregistered_at + }, + [ + %ApplicationInstanceDeregistered{ + sap_system_id: sap_system_id, + host_id: message_server_host_id, + instance_number: message_server_instance_number, + deregistered_at: deregistered_at + }, + %SapSystemDeregistered{ + sap_system_id: sap_system_id, + deregistered_at: deregistered_at + } + ], + fn sap_system -> + assert %SapSystem{ + database: %Database{ + instances: [ + %Instance{ + instance_number: ^database_instance_number, + host_id: ^database_host_id + } + ] + }, + application: %Application{ + instances: [ + %Instance{ + host_id: ^enqrep_host_id, + instance_number: ^enqrep_server_instance_number + }, + %Instance{ + host_id: ^abap_host_id, + instance_number: ^abap_instance_number + } + ] + }, + deregistered_at: ^deregistered_at + } = sap_system + end + ) + end + end end