diff --git a/assets/js/components/ChecksResults.jsx b/assets/js/components/ChecksResults.jsx index 4887c6cef1..42b73bfbe8 100644 --- a/assets/js/components/ChecksResults.jsx +++ b/assets/js/components/ChecksResults.jsx @@ -2,28 +2,12 @@ import React, { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { useSelector, useDispatch } from 'react-redux'; -import { EOS_LENS_FILLED, EOS_ERROR } from 'eos-icons-react'; +import { EOS_LENS_FILLED, EOS_ERROR, EOS_SCHEDULE } from 'eos-icons-react'; import Spinner from './Spinner'; import NotificationBox from './NotificationBox'; import LoadingBox from './LoadingBox'; -const getChecksResults = (cluster) => { - if (cluster) { - return cluster.checks_results - .filter((check_result) => check_result.result != 'skipped') // Filter "skipped" results by now - .reduce((acc, checkResult) => { - acc[checkResult.host_id] = [ - ...(acc[checkResult.host_id] || []), - checkResult, - ]; - - return acc; - }, {}); - } - return {}; -}; - const getHostname = (hosts = []) => (hostId) => { @@ -36,16 +20,26 @@ const getHostname = }, ''); }; -const sortChecksResults = (checksResults = [], group) => { +const sortChecksResults = (checksResults = []) => { return checksResults.sort((a, b) => { - if (a.check_id === b.check_id) { - return group(a.check_id) > group(b.check_id) ? 1 : -1; - } return a.check_id > b.check_id ? 1 : -1; }); }; -const getResultIcon = (result) => { +const sortHosts = (hosts = []) => { + return hosts.sort((a, b) => { + return a.host_id > b.host_id ? 1 : -1; + }); +}; + +const getResultIcon = (checks_execution, result) => { + switch (checks_execution) { + case 'requested': + return ; + case 'running': + return ; + } + switch (result) { case 'passing': return ; @@ -54,7 +48,7 @@ const getResultIcon = (result) => { case 'critical': return ; default: - return ; + return ; } }; @@ -78,8 +72,6 @@ const ChecksResults = () => { }); }; - const checksResults = getChecksResults(cluster); - const hostname = getHostname(useSelector((state) => state.hostsList.hosts)); useEffect(() => { @@ -107,60 +99,83 @@ const ChecksResults = () => { return (
- {Object.keys(checksResults).map((c) => ( -
-
-
-
-
-

- {hostname(c)} -

-
- - - - - - - - - - {sortChecksResults(checksResults[c]).map((checkResult) => ( - - - - +

{msg}

+ + )} + +
( +
+
+
+
+
+

+ {hostname(host_id)} +

+ {reachable == false && ( +
- ID -
- Description - - Result -
- {checkResult.check_id} - - {description(checkResult.check_id)} - - {getResultIcon(checkResult.result)} -
+ + + + + - ))} - -
+ ID + + Description + + Result +
+ + + {sortChecksResults(cluster?.checks_results.slice()) + .filter( + (check_result) => check_result.host_id == host_id + ) + .filter( + (check_result) => check_result.result != 'skipped' + ) // Filter "skipped" results by now + .map((checkResult) => ( + + + {checkResult.check_id} + + + {description(checkResult.check_id)} + + + {getResultIcon( + cluster.checks_execution, + checkResult.result + )} + + + ))} + + +
-
- ))} + ) + )} ); }; diff --git a/assets/js/state/clusters.js b/assets/js/state/clusters.js index 57b908c7d1..dbbbb69207 100644 --- a/assets/js/state/clusters.js +++ b/assets/js/state/clusters.js @@ -58,6 +58,13 @@ export const clustersListSlice = createSlice({ }), ...action.payload.checks_results, ]; + + cluster.hosts_executions = [ + ...cluster.hosts_executions.filter((host_execution) => { + return host_execution.host_id !== action.payload.host_id; + }), + ...action.payload.hosts_executions, + ]; } return cluster; }); diff --git a/lib/trento/application/integration/checks/checks.ex b/lib/trento/application/integration/checks/checks.ex index a05ad6e6f6..0e4ca8e73d 100644 --- a/lib/trento/application/integration/checks/checks.ex +++ b/lib/trento/application/integration/checks/checks.ex @@ -99,9 +99,11 @@ defmodule Trento.Integration.Checks do CompleteChecksExecution.new(%{ cluster_id: cluster_id, hosts_executions: - Enum.map(hosts, fn %{host_id: host_id, results: results} -> + Enum.map(hosts, fn %{host_id: host_id, reachable: reachable, msg: msg, results: results} -> %{ host_id: host_id, + reachable: reachable, + msg: msg, checks_results: Enum.map(results, &Map.from_struct/1) } end) diff --git a/lib/trento/application/integration/checks/dto/execution_completed_event_dto.ex b/lib/trento/application/integration/checks/dto/execution_completed_event_dto.ex index 2fa4dc7512..e2e0fc2bcd 100644 --- a/lib/trento/application/integration/checks/dto/execution_completed_event_dto.ex +++ b/lib/trento/application/integration/checks/dto/execution_completed_event_dto.ex @@ -12,6 +12,8 @@ defmodule Trento.Integration.Checks.ExecutionCompletedEventDto do embeds_many :hosts, Host do field :host_id, Ecto.UUID + field :reachable, :boolean + field :msg, :string embeds_many :results, Result do field :check_id, :string @@ -29,7 +31,7 @@ defmodule Trento.Integration.Checks.ExecutionCompletedEventDto do defp host_changeset(host, attrs) do host - |> cast(attrs, [:host_id]) + |> cast(attrs, [:host_id, :reachable, :msg]) |> cast_embed(:results, with: &result_changeset/2) |> validate_required([:host_id]) end diff --git a/lib/trento/application/projectors/check_result_projector.ex b/lib/trento/application/projectors/check_result_projector.ex index 5731c736d2..da997143a0 100644 --- a/lib/trento/application/projectors/check_result_projector.ex +++ b/lib/trento/application/projectors/check_result_projector.ex @@ -15,7 +15,10 @@ defmodule Trento.CheckResultProjector do HostChecksExecutionCompleted } - alias Trento.CheckResultReadModel + alias Trento.{ + CheckResultReadModel, + HostChecksExecutionsReadModel + } project( %ChecksExecutionRequested{ @@ -24,14 +27,42 @@ defmodule Trento.CheckResultProjector do checks: checks }, fn multi -> + # Delete old hosts executions states + multi = + Ecto.Multi.delete_all( + multi, + :delete_old_hosts_executions, + from(c in CheckResultReadModel, where: c.cluster_id == ^cluster_id) + ) + # Delete old results multi = Ecto.Multi.delete_all( multi, :delete_old_checks_results, - from(c in CheckResultReadModel, where: c.cluster_id == ^cluster_id) + from(h in HostChecksExecutionsReadModel, where: h.cluster_id == ^cluster_id) ) + multi = + hosts + |> Enum.map(fn host_id -> + HostChecksExecutionsReadModel.changeset( + %HostChecksExecutionsReadModel{}, + %{ + cluster_id: cluster_id, + host_id: host_id, + reachable: nil, + msg: nil + } + ) + end) + |> List.flatten() + |> Enum.reduce(multi, fn %{changes: %{cluster_id: cluster_id, host_id: host_id}} = + changeset, + acc -> + Ecto.Multi.insert(acc, "#{cluster_id}_#{host_id}", changeset) + end) + hosts |> Enum.map(fn host_id -> Enum.map(checks, fn check_id -> @@ -58,9 +89,17 @@ defmodule Trento.CheckResultProjector do %HostChecksExecutionCompleted{ cluster_id: cluster_id, host_id: host_id, + reachable: reachable, + msg: msg, checks_results: checks_results }, fn multi -> + hosts_executions_changeset = + %HostChecksExecutionsReadModel{cluster_id: cluster_id, host_id: host_id} + |> HostChecksExecutionsReadModel.changeset(%{reachable: reachable, msg: msg}) + + multi = Ecto.Multi.update(multi, :hosts_executions, hosts_executions_changeset) + checks_results |> Enum.map(fn %{ check_id: check_id, @@ -95,6 +134,7 @@ defmodule Trento.CheckResultProjector do TrentoWeb.Endpoint.broadcast("monitoring:clusters", "checks_results_updated", %{ cluster_id: cluster_id, host_id: host_id, + hosts_executions: [%{cluster_id: cluster_id, host_id: host_id, reachable: true, msg: ""}], checks_results: Enum.map(checks, fn check_id -> %{host_id: host_id, check_id: check_id, result: :unknown} @@ -108,6 +148,8 @@ defmodule Trento.CheckResultProjector do %HostChecksExecutionCompleted{ cluster_id: cluster_id, host_id: host_id, + reachable: reachable, + msg: msg, checks_results: checks_results }, _, @@ -116,6 +158,9 @@ defmodule Trento.CheckResultProjector do TrentoWeb.Endpoint.broadcast("monitoring:clusters", "checks_results_updated", %{ cluster_id: cluster_id, host_id: host_id, + hosts_executions: [ + %{cluster_id: cluster_id, host_id: host_id, reachable: reachable, msg: msg} + ], checks_results: Enum.map(checks_results, fn %{check_id: check_id, result: result} -> %{host_id: host_id, check_id: check_id, result: result} diff --git a/lib/trento/application/read_models/cluster_read_model.ex b/lib/trento/application/read_models/cluster_read_model.ex index f35e2ec85e..fc8bff21f2 100644 --- a/lib/trento/application/read_models/cluster_read_model.ex +++ b/lib/trento/application/read_models/cluster_read_model.ex @@ -7,7 +7,10 @@ defmodule Trento.ClusterReadModel do import Ecto.Changeset - alias Trento.CheckResultReadModel + alias Trento.{ + CheckResultReadModel, + HostChecksExecutionsReadModel + } @type t :: %__MODULE__{} @@ -24,6 +27,7 @@ defmodule Trento.ClusterReadModel do field :details, :map field :checks_execution, Ecto.Enum, values: [:not_running, :requested, :running] + has_many :hosts_executions, HostChecksExecutionsReadModel, foreign_key: :cluster_id has_many :checks_results, CheckResultReadModel, foreign_key: :cluster_id has_many :tags, Trento.Tag, foreign_key: :resource_id end diff --git a/lib/trento/application/read_models/host_checks_executions_read_model.ex b/lib/trento/application/read_models/host_checks_executions_read_model.ex new file mode 100644 index 0000000000..e0c922bb22 --- /dev/null +++ b/lib/trento/application/read_models/host_checks_executions_read_model.ex @@ -0,0 +1,25 @@ +defmodule Trento.HostChecksExecutionsReadModel do + @moduledoc """ + Host checks executions read model + """ + + use Ecto.Schema + + import Ecto.Changeset + + @type t :: %__MODULE__{} + + @derive {Jason.Encoder, except: [:__meta__, :__struct__]} + @primary_key false + schema "hosts_checks_executions" do + field :cluster_id, Ecto.UUID, primary_key: true + field :host_id, Ecto.UUID, primary_key: true + field :reachable, :boolean + field :msg, :string + end + + @spec changeset(t() | Ecto.Changeset.t(), map) :: Ecto.Changeset.t() + def changeset(host_checks_executions, attrs) do + cast(host_checks_executions, attrs, __MODULE__.__schema__(:fields)) + end +end diff --git a/lib/trento/application/usecases/clusters/clusters.ex b/lib/trento/application/usecases/clusters/clusters.ex index 3593fd9d03..96ea89d95c 100644 --- a/lib/trento/application/usecases/clusters/clusters.ex +++ b/lib/trento/application/usecases/clusters/clusters.ex @@ -48,7 +48,7 @@ defmodule Trento.Clusters do ClusterReadModel |> order_by(asc: :name) |> Repo.all() - |> Repo.preload([:tags, :checks_results]) + |> Repo.preload([:tags, :hosts_executions, :checks_results]) end @spec build_check_results([String.t()]) :: {:ok, [CheckResult.t()]} | {:error, any} diff --git a/lib/trento/domain/cluster/cluster.ex b/lib/trento/domain/cluster/cluster.ex index d5549f4bc5..a373190c52 100644 --- a/lib/trento/domain/cluster/cluster.ex +++ b/lib/trento/domain/cluster/cluster.ex @@ -7,7 +7,8 @@ defmodule Trento.Domain.Cluster do CheckResult, Cluster, HanaClusterDetails, - HealthService + HealthService, + HostExecution } alias Trento.Domain.Commands.{ @@ -43,7 +44,7 @@ defmodule Trento.Domain.Cluster do health: :unknown, hosts: [], selected_checks: [], - hosts_checks_results: %{}, + hosts_executions: %{}, checks_execution: :not_running ] @@ -57,7 +58,7 @@ defmodule Trento.Domain.Cluster do details: HanaClusterDetails.t() | nil, hosts: [String.t()], selected_checks: [String.t()], - hosts_checks_results: %{String.t() => [CheckResult.t()]}, + hosts_executions: %{String.t() => HostExecution.t()}, checks_execution: :not_running | :requested | :running } @@ -183,7 +184,8 @@ defmodule Trento.Domain.Cluster do # Store checks results def execute( %Cluster{ - cluster_id: cluster_id + cluster_id: cluster_id, + hosts_executions: old_hosts_executions } = cluster, %CompleteChecksExecution{ hosts_executions: hosts_executions @@ -192,15 +194,7 @@ defmodule Trento.Domain.Cluster do hosts_executions |> Enum.reduce( Multi.new(cluster), - fn %{host_id: host_id, checks_results: results}, multi -> - Multi.execute(multi, fn _ -> - %HostChecksExecutionCompleted{ - cluster_id: cluster_id, - host_id: host_id, - checks_results: results - } - end) - end + &emit_host_execution_completed_event(&1, &2, cluster_id, old_hosts_executions) ) |> Multi.execute(fn _ -> %ChecksExecutionCompleted{cluster_id: cluster_id} end) |> Multi.execute(&maybe_emit_cluster_health_changed_event/1) @@ -292,20 +286,25 @@ defmodule Trento.Domain.Cluster do %Cluster{selected_checks: selected_checks, hosts: hosts} = cluster, %ChecksExecutionRequested{} ) do - hosts_checks_results = - Enum.reduce(hosts, %{}, fn host, acc -> + hosts_executions = + Enum.reduce(hosts, %{}, fn host_id, acc -> Map.put( acc, - host, - Enum.map(selected_checks, fn check_id -> - %CheckResult{check_id: check_id, result: :unknown} - end) + host_id, + %HostExecution{ + host_id: host_id, + reachable: true, + checks_results: + Enum.map(selected_checks, fn check_id -> + %CheckResult{check_id: check_id, result: :unknown} + end) + } ) end) %Cluster{ cluster - | hosts_checks_results: hosts_checks_results, + | hosts_executions: hosts_executions, checks_execution: :requested } end @@ -331,12 +330,43 @@ defmodule Trento.Domain.Cluster do end def apply( - %Cluster{hosts_checks_results: hosts_checks_results} = cluster, - %HostChecksExecutionCompleted{host_id: host_id, checks_results: checks_results} - ) do + %Cluster{hosts_executions: hosts_executions} = cluster, + %HostChecksExecutionCompleted{ + host_id: host_id, + reachable: reachable, + msg: msg, + checks_results: checks_results + } + ) + when reachable == true do + %Cluster{ + cluster + | hosts_executions: + Map.put(hosts_executions, host_id, %HostExecution{ + host_id: host_id, + reachable: reachable, + msg: msg, + checks_results: checks_results + }) + } + end + + def apply( + %Cluster{hosts_executions: hosts_executions} = cluster, + %HostChecksExecutionCompleted{host_id: host_id, reachable: reachable, msg: msg} + ) + when reachable == false do %Cluster{ cluster - | hosts_checks_results: Map.put(hosts_checks_results, host_id, checks_results) + | hosts_executions: + Map.update(hosts_executions, host_id, %HostExecution{}, fn host -> + %HostExecution{ + host_id: host_id, + reachable: reachable, + msg: msg, + checks_results: host.checks_results + } + end) } end @@ -422,13 +452,14 @@ defmodule Trento.Domain.Cluster do defp maybe_emit_cluster_health_changed_event(%Cluster{ cluster_id: cluster_id, - hosts_checks_results: hosts_checks_results, + hosts_executions: hosts_executions, discovered_health: discovered_health, health: health }) do new_health = - hosts_checks_results - |> Enum.flat_map(fn {_, results} -> results end) + hosts_executions + |> Enum.map(fn {_, hosts} -> hosts end) + |> Enum.flat_map(fn %{checks_results: checks_results} -> checks_results end) |> Enum.map(fn %{result: result} -> result end) |> Enum.reject(fn result -> result == :skipped end) |> Kernel.++([discovered_health]) @@ -438,4 +469,40 @@ defmodule Trento.Domain.Cluster do %ClusterHealthChanged{cluster_id: cluster_id, health: new_health} end end + + defp emit_host_execution_completed_event( + %{host_id: host_id, reachable: false, msg: msg}, + multi, + cluster_id, + hosts_executions + ) do + multi + |> Multi.execute(fn _ -> + %HostChecksExecutionCompleted{ + cluster_id: cluster_id, + host_id: host_id, + reachable: false, + msg: msg, + checks_results: Map.get(hosts_executions, host_id).checks_results + } + end) + end + + defp emit_host_execution_completed_event( + %{host_id: host_id, reachable: true, msg: msg, checks_results: results}, + multi, + cluster_id, + _hosts_executions + ) do + multi + |> Multi.execute(fn _ -> + %HostChecksExecutionCompleted{ + cluster_id: cluster_id, + host_id: host_id, + reachable: true, + msg: msg, + checks_results: results + } + end) + end end diff --git a/lib/trento/domain/cluster/commands/complete_checks_execution.ex b/lib/trento/domain/cluster/commands/complete_checks_execution.ex index 52439b5fc9..47c7c1b121 100644 --- a/lib/trento/domain/cluster/commands/complete_checks_execution.ex +++ b/lib/trento/domain/cluster/commands/complete_checks_execution.ex @@ -3,28 +3,11 @@ defmodule Trento.Domain.Commands.CompleteChecksExecution do Store the checks results coming from an execution on a specific cluster. """ - defmodule HostExecution do - @moduledoc """ - Host checks results value object - """ - - @required_fields :all - - use Trento.Type - alias Trento.Domain.CheckResult - - deftype do - field :host_id, Ecto.UUID - - embeds_many :checks_results, CheckResult - end - end - @required_fields :all use Trento.Command - alias Trento.Domain.CheckResult + alias Trento.Domain.HostExecution defcommand do field :cluster_id, Ecto.UUID diff --git a/lib/trento/domain/cluster/events/host_checks_execution_completed.ex b/lib/trento/domain/cluster/events/host_checks_execution_completed.ex index 195084fa97..c6f420ea0c 100644 --- a/lib/trento/domain/cluster/events/host_checks_execution_completed.ex +++ b/lib/trento/domain/cluster/events/host_checks_execution_completed.ex @@ -10,6 +10,8 @@ defmodule Trento.Domain.Events.HostChecksExecutionCompleted do defevent do field :cluster_id, :string field :host_id, :string + field :reachable, :boolean + field :msg, :string embeds_many :checks_results, CheckResult end diff --git a/lib/trento/domain/value_objects/check_result.ex b/lib/trento/domain/value_objects/check_result.ex index 9fe90e3871..e41a8fece5 100644 --- a/lib/trento/domain/value_objects/check_result.ex +++ b/lib/trento/domain/value_objects/check_result.ex @@ -9,6 +9,6 @@ defmodule Trento.Domain.CheckResult do deftype do field :check_id, :string - field :result, Ecto.Enum, values: [:passing, :warning, :critical, :skipped] + field :result, Ecto.Enum, values: [:passing, :warning, :critical, :skipped, :unknown] end end diff --git a/lib/trento/domain/value_objects/host_execution.ex b/lib/trento/domain/value_objects/host_execution.ex new file mode 100644 index 0000000000..2c35bcf846 --- /dev/null +++ b/lib/trento/domain/value_objects/host_execution.ex @@ -0,0 +1,18 @@ +defmodule Trento.Domain.HostExecution do + @moduledoc """ + Host checks results value object + """ + + @required_fields [:host_id, :reachable, :checks_results] + + use Trento.Type + alias Trento.Domain.CheckResult + + deftype do + field :host_id, Ecto.UUID + field :reachable, :boolean + field :msg, :string, default: "" + + embeds_many :checks_results, CheckResult + end +end diff --git a/priv/repo/migrations/20220405142819_create_host_checks_executions.exs b/priv/repo/migrations/20220405142819_create_host_checks_executions.exs new file mode 100644 index 0000000000..eac9978b0e --- /dev/null +++ b/priv/repo/migrations/20220405142819_create_host_checks_executions.exs @@ -0,0 +1,12 @@ +defmodule Trento.Repo.Migrations.CreateHostChecksExecutions do + use Ecto.Migration + + def change do + create table(:hosts_checks_executions, primary_key: false) do + add :cluster_id, :uuid, primary_key: true + add :host_id, :uuid, primary_key: true + add :reachable, :boolean + add :msg, :string + end + end +end diff --git a/test/support/factory.ex b/test/support/factory.ex index 0225122afb..de5842dde7 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -37,6 +37,7 @@ defmodule Trento.Factory do ClusterReadModel, DatabaseInstanceReadModel, DatabaseReadModel, + HostChecksExecutionsReadModel, HostConnectionSettings, HostReadModel, HostTelemetryReadModel, @@ -387,6 +388,15 @@ defmodule Trento.Factory do }) end + def host_checks_result_projection(attrs \\ []) do + Repo.insert!(%HostChecksExecutionsReadModel{ + cluster_id: Keyword.get(attrs, :cluster_id, Faker.UUID.v4()), + host_id: Keyword.get(attrs, :host_id, Faker.UUID.v4()), + reachable: Keyword.get(attrs, :reachable, true), + msg: Keyword.get(attrs, :msg, Faker.StarWars.planet()) + }) + end + def sap_system_with_cluster_and_hosts do %ClusterReadModel{id: cluster_id} = cluster_projection(type: :hana_scale_up, health: :passing) diff --git a/test/trento/application/integration/checks/checks_test.exs b/test/trento/application/integration/checks/checks_test.exs index 40bff99969..de7d948d4d 100644 --- a/test/trento/application/integration/checks/checks_test.exs +++ b/test/trento/application/integration/checks/checks_test.exs @@ -3,6 +3,8 @@ defmodule Trento.Integration.ChecksTest do use Trento.DataCase import Mox + # TODO: Remove Mock usage + import Mock alias Trento.Integration.Checks @@ -15,6 +17,16 @@ defmodule Trento.Integration.ChecksTest do ProviderDto } + alias Trento.Domain.Commands.{ + CompleteChecksExecution, + StartChecksExecution + } + + alias Trento.Domain.{ + CheckResult, + HostExecution + } + @runner_fixtures_path File.cwd!() <> "/test/fixtures/runner" def load_runner_fixture(name) do @@ -24,6 +36,8 @@ defmodule Trento.Integration.ChecksTest do |> Jason.decode!() end + @moduletag :integration + test "should return an error if the runner is not reachable" do expect(Trento.Integration.Checks.Mock, :get_catalog, fn -> {:error, "some error"} @@ -287,4 +301,114 @@ defmodule Trento.Integration.ChecksTest do assert {:ok, catalog_by_provider} == Checks.get_catalog_grouped_by_provider() end + + test "should handle execution started event properly" do + with_mock Trento.Commanded, dispatch: fn _, _ -> :ok end do + execution_id = Faker.UUID.v4() + cluster_id = Faker.UUID.v4() + + Checks.handle_callback(%{ + "event" => "execution_started", + "execution_id" => execution_id, + "payload" => %{ + "cluster_id" => cluster_id + } + }) + + assert_called Trento.Commanded.dispatch( + %StartChecksExecution{ + cluster_id: cluster_id + }, + correlation_id: execution_id + ) + end + end + + test "should handle execution completed event properly" do + with_mock Trento.Commanded, dispatch: fn _, _ -> :ok end do + execution_id = Faker.UUID.v4() + cluster_id = Faker.UUID.v4() + host_id_1 = Faker.UUID.v4() + host_id_2 = Faker.UUID.v4() + + Checks.handle_callback(%{ + "event" => "execution_completed", + "execution_id" => execution_id, + "payload" => %{ + "cluster_id" => cluster_id, + "hosts" => [ + %{ + "host_id" => host_id_1, + "reachable" => true, + "msg" => "", + "results" => [ + %{ + "check_id" => "check1", + "result" => "passing" + }, + %{ + "check_id" => "check2", + "result" => "warning" + } + ] + }, + %{ + "host_id" => host_id_2, + "reachable" => true, + "msg" => "", + "results" => [ + %{ + "check_id" => "check1", + "result" => "critical" + }, + %{ + "check_id" => "check2", + "result" => "warning" + } + ] + } + ] + } + }) + + assert_called Trento.Commanded.dispatch( + %CompleteChecksExecution{ + cluster_id: cluster_id, + hosts_executions: [ + %HostExecution{ + checks_results: [ + %CheckResult{ + check_id: "check1", + result: :passing + }, + %CheckResult{ + check_id: "check2", + result: :warning + } + ], + host_id: host_id_1, + msg: nil, + reachable: true + }, + %HostExecution{ + checks_results: [ + %CheckResult{ + check_id: "check1", + result: :critical + }, + %CheckResult{ + check_id: "check2", + result: :warning + } + ], + host_id: host_id_2, + msg: nil, + reachable: true + } + ] + }, + correlation_id: execution_id + ) + end + end end diff --git a/test/trento/application/projectors/check_result_projector_test.exs b/test/trento/application/projectors/check_result_projector_test.exs index 1483084587..c100bc5346 100644 --- a/test/trento/application/projectors/check_result_projector_test.exs +++ b/test/trento/application/projectors/check_result_projector_test.exs @@ -8,10 +8,14 @@ defmodule Trento.CheckResultProjectorTest do alias Trento.{ CheckResultProjector, - CheckResultReadModel + CheckResultReadModel, + HostChecksExecutionsReadModel } - alias Trento.Domain.Events.HostChecksExecutionCompleted + alias Trento.Domain.Events.{ + ChecksExecutionRequested, + HostChecksExecutionCompleted + } test "should project checks results with result unknown when a ChecksExecutionRequested event is received" do event = checks_execution_requested_event() @@ -32,6 +36,36 @@ defmodule Trento.CheckResultProjectorTest do end) end + test "should project hosts executions with emtpy data when a ChecksExecutionRequested event is received" do + host_checks_result_projection( + cluster_id: cluster_id = Faker.UUID.v4(), + host_id: host_id = Faker.UUID.v4(), + reachable: true, + msg: "" + ) + + event = + ChecksExecutionRequested.new!(%{ + cluster_id: cluster_id, + hosts: [host_id], + checks: ["check1"] + }) + + ProjectorTestHelper.project( + CheckResultProjector, + event, + "check_result_projector" + ) + + hosts_executions = Repo.all(HostChecksExecutionsReadModel) + + assert Enum.all?(hosts_executions, fn %HostChecksExecutionsReadModel{ + reachable: reachable + } -> + reachable == nil + end) + end + test "should update a check result when HostChecksExecutionCompleted event is received" do check_result_projection( cluster_id: cluster_id = Faker.UUID.v4(), @@ -61,4 +95,36 @@ defmodule Trento.CheckResultProjectorTest do assert :critical == check_result_projections.result end + + test "should update a host execution when HostChecksExecutionCompleted event is received" do + host_checks_result_projection( + cluster_id: cluster_id = Faker.UUID.v4(), + host_id: host_id = Faker.UUID.v4(), + reachable: true, + msg: "" + ) + + event = + HostChecksExecutionCompleted.new!(%{ + cluster_id: cluster_id, + host_id: host_id, + reachable: true, + msg: "", + checks_results: [] + }) + + ProjectorTestHelper.project( + CheckResultProjector, + event, + "check_result_projector" + ) + + hosts_executions = Repo.all(HostChecksExecutionsReadModel) + + assert Enum.all?(hosts_executions, fn %HostChecksExecutionsReadModel{ + reachable: reachable + } -> + reachable == true + end) + end end diff --git a/test/trento/domain/cluster/cluster_test.exs b/test/trento/domain/cluster/cluster_test.exs index 5608339d53..3d18595af8 100644 --- a/test/trento/domain/cluster/cluster_test.exs +++ b/test/trento/domain/cluster/cluster_test.exs @@ -5,8 +5,6 @@ defmodule Trento.ClusterTest do alias Trento.Support.StructHelper - alias Trento.Domain.Cluster - alias Trento.Domain.Commands.{ CompleteChecksExecution, RegisterClusterHost, @@ -27,7 +25,11 @@ defmodule Trento.ClusterTest do HostChecksExecutionCompleted } - alias Trento.Domain.Cluster + alias Trento.Domain.{ + CheckResult, + Cluster, + HostExecution + } describe "cluster registration" do test "should register a cluster and add the node host to the cluster if the node is a DC" do @@ -221,6 +223,7 @@ defmodule Trento.ClusterTest do cluster_id = Faker.UUID.v4() host_id = Faker.UUID.v4() selected_checks = Enum.map(0..4, fn _ -> Faker.Cat.name() end) + checks_results = Enum.map(selected_checks, &%CheckResult{check_id: &1, result: :unknown}) assert_events_and_state( [ @@ -248,7 +251,14 @@ defmodule Trento.ClusterTest do fn cluster -> assert %Cluster{ health: :unknown, - checks_execution: :requested + checks_execution: :requested, + hosts_executions: %{ + ^host_id => %HostExecution{ + host_id: ^host_id, + reachable: true, + checks_results: ^checks_results + } + } } = cluster end ) @@ -289,6 +299,8 @@ defmodule Trento.ClusterTest do host_id = Faker.UUID.v4() selected_checks = Enum.map(0..4, fn _ -> Faker.Cat.name() end) checks_results = Enum.map(selected_checks, &%{check_id: &1, result: :critical}) + expected_results = Enum.map(checks_results, &CheckResult.new!(&1)) + msg = Faker.StarWars.planet() assert_events_and_state( [ @@ -304,6 +316,8 @@ defmodule Trento.ClusterTest do hosts_executions: [ %{ host_id: host_id, + reachable: true, + msg: msg, checks_results: checks_results } ] @@ -312,6 +326,8 @@ defmodule Trento.ClusterTest do HostChecksExecutionCompleted.new!(%{ cluster_id: cluster_id, host_id: host_id, + reachable: true, + msg: msg, checks_results: checks_results }), %ChecksExecutionCompleted{ @@ -324,7 +340,72 @@ defmodule Trento.ClusterTest do ], fn cluster -> assert %Cluster{ - checks_execution: :not_running + checks_execution: :not_running, + hosts_executions: %{ + ^host_id => %HostExecution{ + host_id: ^host_id, + reachable: true, + checks_results: ^expected_results + } + } + } = cluster + end + ) + end + + test "should complete a checks execution when reachable is false" do + cluster_id = Faker.UUID.v4() + host_id = Faker.UUID.v4() + selected_checks = Enum.map(0..4, fn _ -> Faker.Cat.name() end) + checks_results = Enum.map(selected_checks, &%{check_id: &1, result: :unknown}) + expected_results = Enum.map(checks_results, &CheckResult.new!(&1)) + msg = Faker.StarWars.planet() + + assert_events_and_state( + [ + cluster_registered_event(cluster_id: cluster_id), + host_added_to_cluster_event(cluster_id: cluster_id, host_id: host_id), + %ChecksSelected{ + cluster_id: cluster_id, + checks: selected_checks + }, + checks_execution_requested_event( + cluster_id: cluster_id, + hosts: [host_id], + checks: selected_checks + ) + ], + CompleteChecksExecution.new!(%{ + cluster_id: cluster_id, + hosts_executions: [ + %{ + host_id: host_id, + reachable: false, + msg: msg + } + ] + }), + [ + HostChecksExecutionCompleted.new!(%{ + cluster_id: cluster_id, + host_id: host_id, + reachable: false, + msg: msg, + checks_results: checks_results + }), + %ChecksExecutionCompleted{ + cluster_id: cluster_id + }, + %ClusterHealthChanged{cluster_id: cluster_id, health: :unknown} + ], + fn cluster -> + assert %Cluster{ + checks_execution: :not_running, + hosts_executions: %{ + ^host_id => %HostExecution{ + checks_results: ^expected_results + } + } } = cluster end )