Skip to content

Commit

Permalink
Add checks execution domain logic (#256)
Browse files Browse the repository at this point in the history
* Start MockRunner GenServer in dev environment

* Update cluster checks execution domain logic

* Wire-up command and events

* Make deftype changeset defoverridable

* Update MockRunner with callbacks

* Update checks event handler to use the Checks service

* Update check result projector and read model

* Update cluster projector and read model

* Remove unused :running result from enum

* Remove preload

* Add runner callback controller

* Update factory and test helper

* Update frontend
  • Loading branch information
fabriziosestito authored Mar 31, 2022
1 parent ee2b95a commit 7e52180
Show file tree
Hide file tree
Showing 36 changed files with 857 additions and 287 deletions.
83 changes: 28 additions & 55 deletions assets/js/components/ChecksResults.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,6 @@ const getChecksResults = (cluster) => {
return {};
};

const getDescriptionFromCatalog =
(catalog = []) =>
(check_id) => {
return catalog.reduce((acc, check) => {
if (check.id === check_id) {
return check.description;
}

return acc;
}, '');
};

const getGroupFromCatalog =
(catalog = []) =>
(checkId) => {
return catalog.reduce((acc, check) => {
if (check.id === checkId) {
return check.group;
}

return acc;
}, '');
};

const getHostname =
(hosts = []) =>
(hostId) => {
Expand All @@ -55,6 +31,12 @@ const getHostname =
}, '');
};

const getCatalogByProvider = (catalogProvider) => (state) => {
return state.catalog.catalog.find(
({ provider }) => provider === catalogProvider
);
};

const sortChecksResults = (checksResults = [], group) => {
return checksResults.sort((a, b) => {
if (a.check_id === b.check_id) {
Expand Down Expand Up @@ -83,16 +65,18 @@ const ChecksResults = () => {
state.clustersList.clusters.find((cluster) => cluster.id === clusterID)
);

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

const catalog = useSelector((state) => state.catalog.catalog).flatMap(
({ checks }) => checks
);
const hostname = getHostname(useSelector((state) => state.hostsList.hosts));

const checksResults = getChecksResults(cluster);
// FIXME: Check the provider by cluster
const catalog = useSelector(getCatalogByProvider('azure'));

const description = getDescriptionFromCatalog(catalog);
const group = getGroupFromCatalog(catalog);
const description = (checkId) => {
return catalog?.groups
?.flatMap(({ checks }) => checks)
.find(({ id }) => id === checkId).description;
};

return (
<div>
Expand All @@ -109,12 +93,6 @@ const ChecksResults = () => {
<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"
>
Group
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
Expand All @@ -136,24 +114,19 @@ const ChecksResults = () => {
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{sortChecksResults(checksResults[c], group).map(
(checkResult) => (
<tr key={checkResult.check_id} className="animate-fade">
<td className="px-6 py-4 whitespace-nowrap">
{group(checkResult.check_id)}
</td>
<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>
</tr>
)
)}
{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>
</tr>
))}
</tbody>
</table>
</div>
Expand Down
110 changes: 57 additions & 53 deletions assets/js/components/ChecksSelection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,65 +37,69 @@ const ChecksSelection = () => {

return (
<div>
{catalog.map(({ group, checks }) => (
<div
key={group.id}
className="bg-white shadow overflow-hidden sm:rounded-md mb-8"
>
<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">
{group}
</h3>
</div>
<ul role="list" className="divide-y divide-gray-200">
{checks.map((check) => (
<li key={check.id}>
<a href="#" className="block hover:bg-gray-50">
<div className="px-4 py-4 sm:px-6">
<div className="flex items-center">
<p className="text-sm font-medium">{check.name}</p>
<p className="ml-2 px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
{check.id}
</p>
</div>
<div className="mt-2 sm:flex sm:justify-between">
<div className="sm:flex">
<p className="flex items-center text-sm text-gray-500">
{check.description}
{catalog
.flatMap(({ _provider, groups }) => groups)
.map(({ group, checks }) => (
<div
key={group.id}
className="bg-white shadow overflow-hidden sm:rounded-md mb-8"
>
<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">
{group}
</h3>
</div>
<ul role="list" className="divide-y divide-gray-200">
{checks.map((check) => (
<li key={check.id}>
<a href="#" className="block hover:bg-gray-50">
<div className="px-4 py-4 sm:px-6">
<div className="flex items-center">
<p className="text-sm font-medium">{check.name}</p>
<p className="ml-2 px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
{check.id}
</p>
</div>
<Switch.Group as="div" className="flex items-center">
<Switch
checked={isSelected(check.id)}
className={classNames(
isSelected(check.id)
? 'bg-jungle-green-500'
: 'bg-gray-200',
'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer focus:outline-none transition-colors ease-in-out duration-200'
)}
onChange={() => {
setSelectedChecks(toggle(selectedChecks, check.id));
}}
>
<span
aria-hidden="true"
<div className="mt-2 sm:flex sm:justify-between">
<div className="sm:flex">
<p className="flex items-center text-sm text-gray-500">
{check.description}
</p>
</div>
<Switch.Group as="div" className="flex items-center">
<Switch
checked={isSelected(check.id)}
className={classNames(
isSelected(check.id)
? 'translate-x-5'
: 'translate-x-0',
'inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200'
? 'bg-jungle-green-500'
: 'bg-gray-200',
'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer focus:outline-none transition-colors ease-in-out duration-200'
)}
/>
</Switch>
</Switch.Group>
onChange={() => {
setSelectedChecks(
toggle(selectedChecks, check.id)
);
}}
>
<span
aria-hidden="true"
className={classNames(
isSelected(check.id)
? 'translate-x-5'
: 'translate-x-0',
'inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200'
)}
/>
</Switch>
</Switch.Group>
</div>
</div>
</div>
</a>
</li>
))}
</ul>
</div>
))}
</a>
</li>
))}
</ul>
</div>
))}
<div className="place-items-end">
<button
className="bg-jungle-green-500 hover:opacity-75 text-white font-bold py-2 px-4 rounded"
Expand Down
25 changes: 19 additions & 6 deletions assets/js/components/ClustersList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { EOS_EDIT, EOS_RUN_CIRCLE, EOS_MORE_HORIZ } from 'eos-icons-react';

import { logError } from '@lib/log';

import HealthIcon from './Health';
import HealthIcon from '@components/Health';
import Spinner from '@components/Spinner';

const getClusterTypeLabel = (type) => {
switch (type) {
Expand Down Expand Up @@ -65,11 +66,21 @@ const ClustersList = () => {
title: 'Health',
key: 'health',
filter: true,
render: (content) => (
<div className="ml-4">
<HealthIcon health={content} />
</div>
),
render: (content, { checks_execution }) => {
if (checks_execution === 'not_running') {
return (
<div className="ml-4">
<HealthIcon health={content} />
</div>
);
} else {
return (
<div className="ml-4">
<Spinner></Spinner>
</div>
);
}
},
},
{
title: 'Name',
Expand Down Expand Up @@ -206,6 +217,8 @@ const ClustersList = () => {
sid: cluster.sid,
type: cluster.type,
hasDetails: cluster.details != null,
checks_execution: cluster.checks_execution,
selected_checks: cluster.selected_checks,
tags: (cluster.tags && cluster.tags.map((tag) => tag.value)) || [],
};
});
Expand Down
2 changes: 2 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ config :trento, Trento.Scheduler,
config :trento, Trento.Integration.Telemetry, adapter: Trento.Integration.Telemetry.ToLogger
config :trento, Trento.Integration.Checks, adapter: Trento.Integration.Checks.MockRunner

config :trento, :extra_children, [Trento.Integration.Checks.MockRunner]

# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"

Expand Down
34 changes: 18 additions & 16 deletions lib/trento/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@ defmodule Trento.Application do

@impl true
def start(_type, _args) do
children = [
# Start the Ecto repository
Trento.Repo,
# Start the Telemetry supervisor
TrentoWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: Trento.PubSub},
# Start the Endpoint (http/https)
TrentoWeb.Endpoint,
Trento.Commanded,
Trento.Scheduler,
Trento.EventHandlersSupervisor,
Trento.ProjectorsSupervisor
# Start a worker by calling: Trento.Worker.start_link(arg)
# {Trento.Worker, arg}
]
children =
[
# Start the Ecto repository
Trento.Repo,
# Start the Telemetry supervisor
TrentoWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: Trento.PubSub},
# Start the Endpoint (http/https)
TrentoWeb.Endpoint,
Trento.Commanded,
Trento.Scheduler,
Trento.EventHandlersSupervisor,
Trento.ProjectorsSupervisor
# Start a worker by calling: Trento.Worker.start_link(arg)
# {Trento.Worker, arg}
] ++
Application.get_env(:trento, :extra_children, [])

# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
Expand Down
27 changes: 16 additions & 11 deletions lib/trento/application/event_handlers/checks_event_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@ defmodule Trento.ChecksEventHandler do
name: "checks_event_handler"

alias Trento.Domain.Events.ChecksExecutionRequested
alias Trento.Integration.Checks

require Logger

def handle(
%ChecksExecutionRequested{cluster_id: cluster_id, hosts: hosts, checks: checks},
_metadata
%{correlation_id: execution_id}
) do
Enum.each(hosts, fn host ->
TrentoWeb.Endpoint.broadcast("monitoring:agent_" <> host, "checks_execution_requested", %{
host_id: host,
cluster_id: cluster_id,
checks: checks
})
end)
case Checks.request_execution(execution_id, cluster_id, hosts, checks) do
:ok ->
TrentoWeb.Endpoint.broadcast("monitoring:clusters", "checks_execution_requested", %{
cluster_id: cluster_id
})

{:error, reason} = error ->
Logger.error("Failed to request checks execution for cluster #{cluster_id}: #{reason}",
error: reason
)

TrentoWeb.Endpoint.broadcast("monitoring:clusters", "checks_execution_started", %{
cluster_id: cluster_id
})
error
end
end
end
2 changes: 1 addition & 1 deletion lib/trento/application/integration/checks/adapter/gen.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule Trento.Integration.Checks.Gen do
execution_id :: String.t(),
cluster_id :: [String.t()],
hosts :: String.t(),
selected_checks :: [String.t()]
checks :: [String.t()]
) ::
:ok | {:error, any}

Expand Down
Loading

0 comments on commit 7e52180

Please sign in to comment.