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

Deregistration side effects #1252

Merged
merged 10 commits into from
Mar 14, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defimpl Trento.Support.Middleware.Enrichable,
from d in DatabaseInstanceReadModel,
join: h in HostReadModel,
on: d.host_id == h.id,
where: ^db_host in h.ip_addresses and ^tenant == d.tenant
where: ^db_host in h.ip_addresses and ^tenant == d.tenant and is_nil(h.deregistered_at)

case Repo.one(query) do
%DatabaseInstanceReadModel{sap_system_id: sap_system_id} ->
Expand Down
33 changes: 33 additions & 0 deletions lib/trento/application/projectors/host_projector.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule Trento.HostProjector do
HeartbeatFailed,
HeartbeatSucceded,
HostAddedToCluster,
HostDeregistered,
HostDetailsUpdated,
HostRegistered,
ProviderUpdated
Expand Down Expand Up @@ -47,6 +48,21 @@ defmodule Trento.HostProjector do
end
)

project(
%HostDeregistered{
host_id: id,
deregistered_at: deregistered_at
},
fn multi ->
changeset =
HostReadModel.changeset(%HostReadModel{id: id}, %{
deregistered_at: deregistered_at
})

Ecto.Multi.update(multi, :host, changeset)
end
)

project(
%HostAddedToCluster{
host_id: id,
Expand Down Expand Up @@ -144,6 +160,23 @@ defmodule Trento.HostProjector do
)
end

def after_update(
%HostDeregistered{host_id: id},
_,
_
) do
%HostReadModel{hostname: hostname} = Repo.get!(HostReadModel, id)

TrentoWeb.Endpoint.broadcast(
"monitoring:hosts",
"host_deregistered",
%{
id: id,
hostname: hostname
}
)
end

def after_update(
%HostAddedToCluster{host_id: id, cluster_id: cluster_id},
_,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ defmodule Trento.ApplicationInstanceReadModel do
field :host_id, Ecto.UUID, primary_key: true
field :health, Ecto.Enum, values: Health.values()

has_one :host, HostReadModel, references: :host_id, foreign_key: :id
has_one :host, HostReadModel,
references: :host_id,
foreign_key: :id,
where: [deregistered_at: nil]
end

@spec changeset(t() | Ecto.Changeset.t(), map) :: Ecto.Changeset.t()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ defmodule Trento.DatabaseInstanceReadModel do
field :system_replication_status, :string, default: ""
field :health, Ecto.Enum, values: Health.values()

has_one :host, HostReadModel, references: :host_id, foreign_key: :id
has_one :host, HostReadModel,
references: :host_id,
foreign_key: :id,
where: [deregistered_at: nil]
end

@spec changeset(t() | Ecto.Changeset.t(), map) :: Ecto.Changeset.t()
Expand Down
2 changes: 2 additions & 0 deletions lib/trento/application/read_models/host_read_model.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ defmodule Trento.HostReadModel do
references: :id,
foreign_key: :host_id,
preload_order: [desc: :identifier]

field :deregistered_at, :utc_datetime_usec
end

@spec changeset(t() | Ecto.Changeset.t(), map) :: Ecto.Changeset.t()
Expand Down
2 changes: 1 addition & 1 deletion lib/trento/application/usecases/hosts/hosts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule Trento.Hosts do
@spec get_all_hosts :: [HostReadModel.t()]
def get_all_hosts do
HostReadModel
|> where([h], not is_nil(h.hostname))
|> where([h], not is_nil(h.hostname) and is_nil(h.deregistered_at))
|> order_by(asc: :hostname)
|> Repo.all()
|> Repo.preload([:sles_subscriptions, :tags])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Trento.Repo.Migrations.AddDeregisteredAtToHostReadModel do
use Ecto.Migration

def change do
alter table(:hosts) do
add :deregistered_at, :utc_datetime_usec
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,34 @@ defmodule Trento.EnrichRegisterApplicationInstanceTest do
Enrichable.enrich(command, %{})
end

test "should return a database not found error if the database instance host has been deregistered" do
deregistered_host = insert(:host, deregistered_at: DateTime.utc_now())

%{
tenant: tenant,
host: %{ip_addresses: [ip]}
} =
insert(:database_instance_without_host,
host_id: deregistered_host.id,
host: deregistered_host
)

command =
build(
:register_application_instance_command,
sap_system_id: nil,
sid: Faker.StarWars.planet(),
db_host: ip,
tenant: tenant,
instance_number: "00",
features: Faker.Pokemon.name(),
host_id: Faker.UUID.v4(),
health: :passing
)

assert {:error, :database_not_found} = Enrichable.enrich(command, %{})
end

test "should return an error if the database was not found" do
%{
tenant: tenant
Expand Down
30 changes: 28 additions & 2 deletions test/trento/application/projectors/host_projector_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule Trento.HostProjectorTest do
HeartbeatFailed,
HeartbeatSucceded,
HostAddedToCluster,
HostDeregistered,
HostDetailsUpdated,
ProviderUpdated
}
Expand All @@ -45,9 +46,9 @@ defmodule Trento.HostProjectorTest do
end

setup do
%HostReadModel{id: host_id} = insert(:host)
%HostReadModel{id: host_id, hostname: hostname} = insert(:host)

%{host_id: host_id}
%{host_id: host_id, hostname: hostname}
end

test "should project a new host when HostRegistered event is received" do
Expand Down Expand Up @@ -344,4 +345,29 @@ defmodule Trento.HostProjectorTest do
},
1000
end

test "should update the deregistered_at field when HostDeregistered is received",
%{
host_id: host_id,
hostname: hostname
} do
timestamp = DateTime.utc_now()

event = %HostDeregistered{
host_id: host_id,
deregistered_at: timestamp
}

ProjectorTestHelper.project(HostProjector, event, "host_projector")
host_projection = Repo.get!(HostReadModel, host_id)

assert timestamp == host_projection.deregistered_at

assert_broadcast "host_deregistered",
%{
id: ^host_id,
hostname: ^hostname
},
1000
end
end
13 changes: 13 additions & 0 deletions test/trento/application/usecases/hosts_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,17 @@ defmodule Trento.HostsTest do
assert 6 = Hosts.get_all_sles_subscriptions()
end
end

describe "get_all_hosts/0" do
test "should list all hosts except the deregistered ones" do
registered_hosts = Enum.map(0..9, fn i -> insert(:host, hostname: "hostname_#{i}") end)
deregistered_host = insert(:host, deregistered_at: DateTime.utc_now())

hosts = Hosts.get_all_hosts()
hosts_ids = Enum.map(hosts, & &1.id)

assert Enum.map(registered_hosts, & &1.id) == hosts_ids
refute deregistered_host.id in hosts_ids
end
end
end