From 2f8afb3dd22e4a846c64f6ee5d12946c0376c145 Mon Sep 17 00:00:00 2001 From: arbulu89 Date: Wed, 13 Sep 2023 13:37:35 +0200 Subject: [PATCH 1/3] Add saptune_status to host read model --- lib/trento/application/read_models/host_read_model.ex | 1 + ...20230905141817_add_saptune_status_host_read_model.exs | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 priv/repo/migrations/20230905141817_add_saptune_status_host_read_model.exs diff --git a/lib/trento/application/read_models/host_read_model.ex b/lib/trento/application/read_models/host_read_model.ex index f596daaa89..0222d6abb6 100644 --- a/lib/trento/application/read_models/host_read_model.ex +++ b/lib/trento/application/read_models/host_read_model.ex @@ -24,6 +24,7 @@ defmodule Trento.HostReadModel do field :selected_checks, {:array, :string}, default: [] field :provider, Ecto.Enum, values: Provider.values() field :provider_data, :map + field :saptune_status, :map has_many :tags, Trento.Tag, foreign_key: :resource_id diff --git a/priv/repo/migrations/20230905141817_add_saptune_status_host_read_model.exs b/priv/repo/migrations/20230905141817_add_saptune_status_host_read_model.exs new file mode 100644 index 0000000000..df2f3bf690 --- /dev/null +++ b/priv/repo/migrations/20230905141817_add_saptune_status_host_read_model.exs @@ -0,0 +1,9 @@ +defmodule Trento.Repo.Migrations.AddSaptuneStatusHostReadModel do + use Ecto.Migration + + def change do + alter table(:hosts) do + add :saptune_status, :map + end + end +end From 1efddb5bf3f08edc1ef5b71061b21580c21ba20e Mon Sep 17 00:00:00 2001 From: arbulu89 Date: Wed, 13 Sep 2023 13:38:07 +0200 Subject: [PATCH 2/3] Add saptune status projection --- .../application/projectors/host_projector.ex | 39 ++++++++++++++++--- lib/trento_web/views/v1/host_view.ex | 6 +++ test/support/factory.ex | 3 +- .../projectors/host_projector_test.exs | 33 +++++++++++++++- 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/lib/trento/application/projectors/host_projector.ex b/lib/trento/application/projectors/host_projector.ex index c52835c952..41c6e57586 100644 --- a/lib/trento/application/projectors/host_projector.ex +++ b/lib/trento/application/projectors/host_projector.ex @@ -22,7 +22,8 @@ defmodule Trento.HostProjector do HostRegistered, HostRemovedFromCluster, HostRestored, - ProviderUpdated + ProviderUpdated, + SaptuneStatusUpdated } alias Trento.HostReadModel @@ -201,18 +202,32 @@ defmodule Trento.HostProjector do |> Repo.get!(id) |> HostReadModel.changeset(%{ provider: provider, - provider_data: handle_provider_data(provider_data) + provider_data: map_from_struct(provider_data) }) Ecto.Multi.update(multi, :host, changeset) end ) - def handle_provider_data(provider_data) when is_map(provider_data) do - Map.from_struct(provider_data) + project( + %SaptuneStatusUpdated{host_id: id, status: status}, + fn multi -> + changeset = + HostReadModel + |> Repo.get!(id) + |> HostReadModel.changeset(%{ + saptune_status: map_from_struct(status) + }) + + Ecto.Multi.update(multi, :host, changeset) + end + ) + + def map_from_struct(struct) when is_struct(struct) do + Map.from_struct(struct) end - def handle_provider_data(_), do: nil + def map_from_struct(_), do: nil @impl true @spec after_update(any, any, any) :: :ok | {:error, any} @@ -363,5 +378,19 @@ defmodule Trento.HostProjector do TrentoWeb.Endpoint.broadcast("monitoring:hosts", "host_details_updated", message) end + def after_update( + %SaptuneStatusUpdated{}, + _, + %{host: %HostReadModel{} = host} + ) do + message = + HostView.render( + "saptune_status_updated.json", + %{host: host} + ) + + TrentoWeb.Endpoint.broadcast("monitoring:hosts", "saptune_status_updated", message) + end + def after_update(_, _, _), do: :ok end diff --git a/lib/trento_web/views/v1/host_view.ex b/lib/trento_web/views/v1/host_view.ex index 1e1d171dec..44bc53e274 100644 --- a/lib/trento_web/views/v1/host_view.ex +++ b/lib/trento_web/views/v1/host_view.ex @@ -33,4 +33,10 @@ defmodule TrentoWeb.V1.HostView do def render("heartbeat_result.json", %{host: %{id: id, hostname: hostname}}) do %{id: id, hostname: hostname} end + + def render("saptune_status_updated.json", %{ + host: %{id: id, saptune_status: status, hostname: hostname} + }) do + %{id: id, status: status, hostname: hostname} + end end diff --git a/test/support/factory.ex b/test/support/factory.ex index 8af3c579e9..787b5ff419 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -117,7 +117,8 @@ defmodule Trento.Factory do provider: Enum.random(Provider.values()), provider_data: nil, deregistered_at: nil, - selected_checks: Enum.map(0..4, fn _ -> Faker.StarWars.planet() end) + selected_checks: Enum.map(0..4, fn _ -> Faker.StarWars.planet() end), + saptune_status: nil } end diff --git a/test/trento/application/projectors/host_projector_test.exs b/test/trento/application/projectors/host_projector_test.exs index 7b3747f072..a9941d8262 100644 --- a/test/trento/application/projectors/host_projector_test.exs +++ b/test/trento/application/projectors/host_projector_test.exs @@ -29,11 +29,13 @@ defmodule Trento.HostProjectorTest do HostDetailsUpdated, HostRemovedFromCluster, HostRestored, - ProviderUpdated + ProviderUpdated, + SaptuneStatusUpdated } alias Trento.ProjectorTestHelper alias Trento.Repo + alias Trento.Support.StructHelper @moduletag :integration @@ -511,4 +513,33 @@ defmodule Trento.HostProjectorTest do }, 1000 end + + test "should update the saptune_status field when SaptuneStatusUpdated is received", + %{ + host_id: host_id, + hostname: hostname + } do + saptune_status = build(:saptune_status) + + event = %SaptuneStatusUpdated{ + host_id: host_id, + status: saptune_status + } + + expected_status = StructHelper.to_map(saptune_status) + expected_broadcast_status = Map.from_struct(saptune_status) + + ProjectorTestHelper.project(HostProjector, event, "host_projector") + host_projection = Repo.get!(HostReadModel, host_id) + + assert expected_status == host_projection.saptune_status + + assert_broadcast "saptune_status_updated", + %{ + id: ^host_id, + hostname: ^hostname, + status: ^expected_broadcast_status + }, + 1000 + end end From 6b0f6362219685763c818f48c8037603e1ff2cff Mon Sep 17 00:00:00 2001 From: arbulu89 Date: Wed, 13 Sep 2023 13:38:24 +0200 Subject: [PATCH 3/3] Add saptune status openapi schema --- lib/trento_web/openapi/v1/schema/host.ex | 3 +- .../openapi/v1/schema/saptune_status.ex | 136 ++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 lib/trento_web/openapi/v1/schema/saptune_status.ex diff --git a/lib/trento_web/openapi/v1/schema/host.ex b/lib/trento_web/openapi/v1/schema/host.ex index b800d6f6c4..095202928a 100644 --- a/lib/trento_web/openapi/v1/schema/host.ex +++ b/lib/trento_web/openapi/v1/schema/host.ex @@ -4,7 +4,7 @@ defmodule TrentoWeb.OpenApi.V1.Schema.Host do require OpenApiSpex alias OpenApiSpex.Schema - alias TrentoWeb.OpenApi.V1.Schema.{Provider, SlesSubscription, Tags} + alias TrentoWeb.OpenApi.V1.Schema.{Provider, SaptuneStatus, SlesSubscription, Tags} defmodule IPv4 do @moduledoc false @@ -70,6 +70,7 @@ defmodule TrentoWeb.OpenApi.V1.Schema.Host do type: :array, items: SlesSubscription }, + saptune_status: SaptuneStatus, deregistered_at: %Schema{ title: "DeregisteredAt", description: "Timestamp of the last deregistration of the host", diff --git a/lib/trento_web/openapi/v1/schema/saptune_status.ex b/lib/trento_web/openapi/v1/schema/saptune_status.ex new file mode 100644 index 0000000000..43547ce93f --- /dev/null +++ b/lib/trento_web/openapi/v1/schema/saptune_status.ex @@ -0,0 +1,136 @@ +defmodule TrentoWeb.OpenApi.V1.Schema.SaptuneStatus do + @moduledoc false + + require OpenApiSpex + + alias OpenApiSpex.Schema + + defmodule Service do + @moduledoc false + + OpenApiSpex.schema(%{ + title: "Saptune service", + description: "Saptune service", + type: :object, + properties: %{ + name: %Schema{ + type: :string, + description: "Saptune service name" + }, + enabled: %Schema{ + type: :string, + description: "Enabled state as string" + }, + active: %Schema{ + type: :string, + description: "Active state as string" + } + } + }) + end + + defmodule Note do + @moduledoc false + + OpenApiSpex.schema(%{ + title: "Saptune note", + description: "Saptune note", + type: :object, + properties: %{ + id: %Schema{ + type: :string, + description: "Saptune note ID" + }, + additionally_enabled: %Schema{ + type: :boolean, + description: "Note is additionally enabled" + } + } + }) + end + + defmodule Solution do + @moduledoc false + + OpenApiSpex.schema(%{ + title: "Saptune solution", + description: "Saptune solution", + type: :object, + properties: %{ + id: %Schema{ + type: :boolean, + description: "Saptune solution ID" + }, + notes: %Schema{ + type: :array, + description: "Solution note IDs", + items: %Schema{type: :string} + }, + partial: %Schema{ + type: :boolean, + description: "Solution is partially applied" + } + } + }) + end + + defmodule Staging do + @moduledoc false + + OpenApiSpex.schema(%{ + title: "Saptune staging", + description: "Saptune staging data", + type: :object, + properties: %{ + enabled: %Schema{ + type: :boolean, + description: "Saptune staging is enabled" + }, + notes: %Schema{ + type: :array, + description: "Staged saptune note IDs", + items: %Schema{type: :string} + }, + solutions_ids: %Schema{ + type: :array, + description: "Staged saptune solution IDs", + items: %Schema{type: :string} + } + } + }) + end + + OpenApiSpex.schema(%{ + title: "Saptune status", + description: "Saptune status output on the host", + type: :object, + nullable: true, + properties: %{ + package_version: %Schema{type: :string, description: "Saptune package version"}, + configured_version: %Schema{type: :string, description: "Saptune configure version"}, + tuning_state: %Schema{type: :string, description: "Saptune tuning state"}, + services: %Schema{ + title: "Saptune services", + description: "A list of saptune services", + type: :array, + items: Service + }, + enabled_nodes: %Schema{ + title: "Enabled notes", + description: "A list of enabled notes", + type: :array, + items: Note + }, + applied_notes: %Schema{ + title: "Applied notes", + description: "A list of applied notes", + type: :array, + items: Note + }, + enabled_solution: Solution, + applied_solution: Solution, + staging: Staging + }, + required: [:package_version] + }) +end