Skip to content

Commit

Permalink
feat(vault): Add incomplete setup warning to machine list (#4503)
Browse files Browse the repository at this point in the history
- Add warning message to machine list if not all region controllers are configured with Vault
- Add warning message to machine list if region controllers are configured with Vault but secrets are not migrated
  • Loading branch information
ndv99 authored Oct 21, 2022
1 parent aafbe78 commit 886d8ad
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
111 changes: 111 additions & 0 deletions src/app/machines/views/MachineList/MachineList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type { RootState } from "app/store/root/types";
import {
NodeStatus,
NodeStatusCode,
NodeType,
TestStatusStatus,
} from "app/store/types/node";
import {
Expand All @@ -33,6 +34,9 @@ import {
testStatus as testStatusFactory,
machineStateList as machineStateListFactory,
machineStateListGroup as machineStateListGroupFactory,
vaultEnabledState as vaultEnabledStateFactory,
controllerState as controllerStateFactory,
controller as controllerFactory,
} from "testing/factories";
import { renderWithBrowserRouter } from "testing/utils";

Expand Down Expand Up @@ -181,6 +185,7 @@ describe("MachineList", () => {
loaded: false,
loading: false,
},
vaultEnabled: vaultEnabledStateFactory({ data: false, loaded: true }),
osInfo: {
data: osInfoFactory({
osystems: [["ubuntu", "Ubuntu"]],
Expand All @@ -191,6 +196,10 @@ describe("MachineList", () => {
loading: false,
},
}),
controller: controllerStateFactory({
loaded: true,
items: [controllerFactory({ vault_configured: false })],
}),
machine: machineStateFactory({
items: machines,
lists: {
Expand Down Expand Up @@ -705,4 +714,106 @@ describe("MachineList", () => {
expect(fetches).toHaveLength(2);
expect(fetches[fetches.length - 1].payload.params.page_number).toBe(2);
});

it("shows a warning notification if not all controllers are configured with Vault", async () => {
const controllers = [
controllerFactory({
system_id: "abc123",
vault_configured: true,
node_type: NodeType.REGION_CONTROLLER,
}),
controllerFactory({
system_id: "def456",
vault_configured: false,
node_type: NodeType.REGION_AND_RACK_CONTROLLER,
}),
];
state.controller.items = controllers;

renderWithBrowserRouter(
<MachineList searchFilter="" setSearchFilter={jest.fn()} />,
{ wrapperProps: { state } }
);

expect(screen.getByTestId("vault-notification")).toHaveTextContent(
"Configure 1 other controller with Vault to complete this operation."
);
});

it("shows a warning notification if all controllers are configured with Vault but secrets are not migrated", async () => {
const controllers = [
controllerFactory({
system_id: "abc123",
vault_configured: true,
node_type: NodeType.REGION_CONTROLLER,
}),
controllerFactory({
system_id: "def456",
vault_configured: true,
node_type: NodeType.REGION_AND_RACK_CONTROLLER,
}),
];
state.controller.items = controllers;

renderWithBrowserRouter(
<MachineList searchFilter="" setSearchFilter={jest.fn()} />,
{ wrapperProps: { state } }
);

expect(screen.getByTestId("vault-notification")).toHaveTextContent(
"Migrate your secrets to Vault to complete this operation."
);
});

it("doesn't show a warning notification if Vault setup has not been started", async () => {
const controllers = [
controllerFactory({
system_id: "abc123",
vault_configured: false,
node_type: NodeType.REGION_CONTROLLER,
}),
controllerFactory({
system_id: "def456",
vault_configured: false,
node_type: NodeType.REGION_AND_RACK_CONTROLLER,
}),
];
state.controller.items = controllers;

renderWithBrowserRouter(
<MachineList searchFilter="" setSearchFilter={jest.fn()} />,
{ wrapperProps: { state } }
);

expect(screen.queryByTestId("vault-notification")).not.toBeInTheDocument();
});

it("doesn't show a warning notification if Vault is fully configured", async () => {
const controllers = [
controllerFactory({
system_id: "abc123",
vault_configured: true,
node_type: NodeType.REGION_CONTROLLER,
}),
controllerFactory({
system_id: "def456",
vault_configured: true,
node_type: NodeType.REGION_AND_RACK_CONTROLLER,
}),
];
state.controller.items = controllers;
state.general = generalStateFactory({
vaultEnabled: vaultEnabledStateFactory({
data: true,
loaded: true,
}),
});

renderWithBrowserRouter(
<MachineList searchFilter="" setSearchFilter={jest.fn()} />,
{ wrapperProps: { state } }
);

expect(screen.queryByTestId("vault-notification")).not.toBeInTheDocument();
});
});
44 changes: 44 additions & 0 deletions src/app/machines/views/MachineList/MachineList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";

import type { ValueOf } from "@canonical/react-components";
import { Link, Notification } from "@canonical/react-components";
import { useDispatch, useSelector } from "react-redux";
import { useStorageState } from "react-storage-hooks";

Expand All @@ -11,11 +12,16 @@ import { DEFAULTS } from "./MachineListTable/constants";

import { useWindowTitle } from "app/base/hooks";
import type { SetSearchFilter, SortDirection } from "app/base/types";
import { actions as controllerActions } from "app/store/controller";
import controllerSelectors from "app/store/controller/selectors";
import { actions as generalActions } from "app/store/general";
import { vaultEnabled as vaultEnabledSelectors } from "app/store/general/selectors";
import { actions as machineActions } from "app/store/machine";
import machineSelectors from "app/store/machine/selectors";
import { FetchGroupKey } from "app/store/machine/types";
import { mapSortDirection, FilterMachineItems } from "app/store/machine/utils";
import { useFetchMachines } from "app/store/machine/utils/hooks";
import type { RootState } from "app/store/root/types";

type Props = {
headerFormOpen?: boolean;
Expand Down Expand Up @@ -74,6 +80,12 @@ const MachineList = ({
[]
);

const { unconfiguredControllers, configuredControllers } = useSelector(
(state: RootState) =>
controllerSelectors.getVaultConfiguredControllers(state)
);
const vaultEnabled = useSelector(vaultEnabledSelectors.get);

const { callId, loading, machineCount, machines, machinesErrors } =
useFetchMachines({
collapsedGroups: hiddenGroups,
Expand All @@ -94,6 +106,12 @@ const MachineList = ({
[dispatch]
);

// Fetch vault enabled status and controllers on page load
useEffect(() => {
dispatch(controllerActions.fetch());
dispatch(generalActions.fetchVaultEnabled());
}, [dispatch]);

return (
<>
{errors && !headerFormOpen ? (
Expand All @@ -103,6 +121,32 @@ const MachineList = ({
/>
) : null}
{!headerFormOpen ? <ErrorsNotification errors={machinesErrors} /> : null}
{configuredControllers.length >= 1 &&
unconfiguredControllers.length >= 1 ? (
<Notification
data-testid="vault-notification"
severity="caution"
title="Incomplete Vault integration"
>
Configure {unconfiguredControllers.length} other{" "}
<Link href="/controllers">
{unconfiguredControllers.length > 1 ? "controllers" : "controller"}
</Link>{" "}
with Vault to complete this operation. Check the{" "}
<Link href="/settings/configuration/security">security settings</Link>{" "}
for more information.
</Notification>
) : unconfiguredControllers.length === 0 && vaultEnabled === false ? (
<Notification
data-testid="vault-notification"
severity="caution"
title="Incomplete Vault integration"
>
Migrate your secrets to Vault to complete this operation. Check the{" "}
<Link href="/settings/configuration/security">security settings</Link>{" "}
for more information.
</Notification>
) : null}
<MachineListControls
filter={searchFilter}
grouping={grouping}
Expand Down

0 comments on commit 886d8ad

Please sign in to comment.