Skip to content

Commit

Permalink
Handle unreachable checks execution scenario (#304)
Browse files Browse the repository at this point in the history
  • Loading branch information
arbulu89 authored Apr 14, 2022
1 parent db6e3fd commit ab82dac
Show file tree
Hide file tree
Showing 18 changed files with 596 additions and 133 deletions.
163 changes: 89 additions & 74 deletions assets/js/components/ChecksResults.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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 <EOS_SCHEDULE className="fill-gray-500" />;
case 'running':
return <Spinner></Spinner>;
}

switch (result) {
case 'passing':
return <EOS_LENS_FILLED className="fill-jungle-green-500" />;
Expand All @@ -54,7 +48,7 @@ const getResultIcon = (result) => {
case 'critical':
return <EOS_LENS_FILLED className="fill-red-500" />;
default:
return <Spinner></Spinner>;
return <EOS_LENS_FILLED className="fill-gray-500" />;
}
};

Expand All @@ -78,8 +72,6 @@ const ChecksResults = () => {
});
};

const checksResults = getChecksResults(cluster);

const hostname = getHostname(useSelector((state) => state.hostsList.hosts));

useEffect(() => {
Expand Down Expand Up @@ -107,60 +99,83 @@ const ChecksResults = () => {

return (
<div>
{Object.keys(checksResults).map((c) => (
<div key="c" className="flex flex-col">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
<div className="bg-white px-4 py-5 border-b border-gray-200 sm:px-6">
<h3 className="text-lg leading-6 font-medium text-gray-900">
{hostname(c)}
</h3>
</div>
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{sortHosts(cluster?.hosts_executions.slice()).map(
({ _cluster_id, host_id, reachable, msg }) => (
<div key="c" className="flex flex-col">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
<div className="bg-white px-4 py-5 border-b border-gray-200 sm:px-6">
<h3 className="text-lg leading-6 font-medium text-gray-900">
{hostname(host_id)}
</h3>
{reachable == false && (
<div
className="bg-yellow-200 border-yellow-600 text-yellow-600 border-l-4 p-4"
role="alert"
>
ID
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Description
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Result
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{sortChecksResults(checksResults[c]).map((checkResult) => (
<tr key={checkResult.check_id} className="animate-fade">
<td className="px-6 py-4 whitespace-nowrap">
{checkResult.check_id}
</td>
<td className="px-6 py-4 whitespace-nowrap">
{description(checkResult.check_id)}
</td>
<td className="px-6 py-4 whitespace-nowrap content-center">
{getResultIcon(checkResult.result)}
</td>
<p>{msg}</p>
</div>
)}
</div>
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
ID
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Description
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Result
</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{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) => (
<tr
key={checkResult.check_id}
className="animate-fade"
>
<td className="px-6 py-4 whitespace-nowrap">
{checkResult.check_id}
</td>
<td className="px-6 py-4 whitespace-nowrap">
{description(checkResult.check_id)}
</td>
<td className="px-6 py-4 whitespace-nowrap content-center">
{getResultIcon(
cluster.checks_execution,
checkResult.result
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
))}
)
)}
</div>
);
};
Expand Down
7 changes: 7 additions & 0 deletions assets/js/state/clusters.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
Expand Down
4 changes: 3 additions & 1 deletion lib/trento/application/integration/checks/checks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
49 changes: 47 additions & 2 deletions lib/trento/application/projectors/check_result_projector.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ defmodule Trento.CheckResultProjector do
HostChecksExecutionCompleted
}

alias Trento.CheckResultReadModel
alias Trento.{
CheckResultReadModel,
HostChecksExecutionsReadModel
}

project(
%ChecksExecutionRequested{
Expand All @@ -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 ->
Expand All @@ -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,
Expand Down Expand Up @@ -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}
Expand All @@ -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
},
_,
Expand All @@ -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}
Expand Down
6 changes: 5 additions & 1 deletion lib/trento/application/read_models/cluster_read_model.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ defmodule Trento.ClusterReadModel do

import Ecto.Changeset

alias Trento.CheckResultReadModel
alias Trento.{
CheckResultReadModel,
HostChecksExecutionsReadModel
}

@type t :: %__MODULE__{}

Expand All @@ -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
Expand Down
Loading

0 comments on commit ab82dac

Please sign in to comment.