mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-15 16:42:15 +00:00
feat(alerts): expose docker update-alert delay with an off state in the UI
Users hit by unwanted container image update alerts (#1545) had no UI lever short of disabling all container alerts; updateAlertDelayHours was backend-only. Add a Container update alerts card to the Docker thresholds tab with an on/off toggle (off saves -1) and an hours delay input. The save path normalizes the value so 0 (which the backend treats as unset and resets to 24) is never emitted, and the snapshot builder carries the field through unrelated saves. Also fix the docker-specific-controls gate: it required a legacy 'docker-host' typed resource, which hid the ignored-prefixes and swarm service cards (and would have hidden this one) for unified-agent Docker hosts that only appear in the runtime list via their app-containers. The gate now also accepts any non-TrueNAS container runtime.
This commit is contained in:
@@ -4,6 +4,7 @@ import { ThresholdsTableDockerContainersSection } from './ThresholdsTableDockerC
|
|||||||
import { ThresholdsTableDockerHostsSection } from './ThresholdsTableDockerHostsSection';
|
import { ThresholdsTableDockerHostsSection } from './ThresholdsTableDockerHostsSection';
|
||||||
import { ThresholdsTableDockerIgnoredPrefixesSection } from './ThresholdsTableDockerIgnoredPrefixesSection';
|
import { ThresholdsTableDockerIgnoredPrefixesSection } from './ThresholdsTableDockerIgnoredPrefixesSection';
|
||||||
import { ThresholdsTableDockerServiceGapSection } from './ThresholdsTableDockerServiceGapSection';
|
import { ThresholdsTableDockerServiceGapSection } from './ThresholdsTableDockerServiceGapSection';
|
||||||
|
import { ThresholdsTableDockerUpdateAlertsSection } from './ThresholdsTableDockerUpdateAlertsSection';
|
||||||
import type { ThresholdsTableSectionProps } from '@/features/alerts/thresholds/thresholdsTableSectionProps';
|
import type { ThresholdsTableSectionProps } from '@/features/alerts/thresholds/thresholdsTableSectionProps';
|
||||||
|
|
||||||
export function ThresholdsTableDockerTab(props: ThresholdsTableSectionProps) {
|
export function ThresholdsTableDockerTab(props: ThresholdsTableSectionProps) {
|
||||||
@@ -11,6 +12,7 @@ export function ThresholdsTableDockerTab(props: ThresholdsTableSectionProps) {
|
|||||||
<>
|
<>
|
||||||
<Show when={props.state.hasDockerSpecificControls()}>
|
<Show when={props.state.hasDockerSpecificControls()}>
|
||||||
<ThresholdsTableDockerIgnoredPrefixesSection {...props} />
|
<ThresholdsTableDockerIgnoredPrefixesSection {...props} />
|
||||||
|
<ThresholdsTableDockerUpdateAlertsSection {...props} />
|
||||||
<ThresholdsTableDockerServiceGapSection {...props} />
|
<ThresholdsTableDockerServiceGapSection {...props} />
|
||||||
</Show>
|
</Show>
|
||||||
<ThresholdsTableDockerHostsSection {...props} />
|
<ThresholdsTableDockerHostsSection {...props} />
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import { Show, createSignal } from 'solid-js';
|
||||||
|
|
||||||
|
import { Card } from '@/components/shared/Card';
|
||||||
|
import Toggle from '@/components/shared/Toggle';
|
||||||
|
import { FACTORY_DOCKER_DEFAULTS } from '@/utils/alertThresholdDefaults';
|
||||||
|
import type { ThresholdsTableSectionProps } from '@/features/alerts/thresholds/thresholdsTableSectionProps';
|
||||||
|
|
||||||
|
export function ThresholdsTableDockerUpdateAlertsSection(props: ThresholdsTableSectionProps) {
|
||||||
|
const { state, tableProps } = props;
|
||||||
|
|
||||||
|
const updateAlertsEnabled = () => tableProps.dockerDefaults.updateAlertDelayHours > 0;
|
||||||
|
// Remember the last positive delay so toggling off and back on restores it.
|
||||||
|
const [lastDelayHours, setLastDelayHours] = createSignal(
|
||||||
|
updateAlertsEnabled()
|
||||||
|
? tableProps.dockerDefaults.updateAlertDelayHours
|
||||||
|
: FACTORY_DOCKER_DEFAULTS.updateAlertDelayHours,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card padding="md" tone="card" class="mb-6">
|
||||||
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-sm font-semibold text-base-content">
|
||||||
|
{state.dockerUpdatePresentation.title}
|
||||||
|
</h3>
|
||||||
|
<p class="mt-1 text-xs text-muted">{state.dockerUpdatePresentation.description}</p>
|
||||||
|
</div>
|
||||||
|
<Toggle
|
||||||
|
checked={updateAlertsEnabled()}
|
||||||
|
onToggle={() => {
|
||||||
|
if (updateAlertsEnabled()) {
|
||||||
|
setLastDelayHours(tableProps.dockerDefaults.updateAlertDelayHours);
|
||||||
|
tableProps.setDockerDefaults((prev) => ({ ...prev, updateAlertDelayHours: -1 }));
|
||||||
|
} else {
|
||||||
|
tableProps.setDockerDefaults((prev) => ({
|
||||||
|
...prev,
|
||||||
|
updateAlertDelayHours: lastDelayHours(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
tableProps.setHasUnsavedChanges(true);
|
||||||
|
}}
|
||||||
|
label={
|
||||||
|
<span class="text-sm font-medium text-base-content">
|
||||||
|
{state.dockerUpdatePresentation.toggleLabel}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
<span class="text-xs text-muted">
|
||||||
|
{state.dockerUpdatePresentation.toggleDescription}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={updateAlertsEnabled()}>
|
||||||
|
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
for={state.dockerUpdateDelayInputId}
|
||||||
|
class="text-xs font-medium uppercase tracking-wide text-muted"
|
||||||
|
>
|
||||||
|
{state.dockerUpdatePresentation.delayLabel}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
id={state.dockerUpdateDelayInputId}
|
||||||
|
value={tableProps.dockerDefaults.updateAlertDelayHours}
|
||||||
|
onInput={(event) => {
|
||||||
|
const value = Number(event.currentTarget.value);
|
||||||
|
const normalized = Number.isFinite(value)
|
||||||
|
? Math.max(1, Math.round(value))
|
||||||
|
: FACTORY_DOCKER_DEFAULTS.updateAlertDelayHours;
|
||||||
|
setLastDelayHours(normalized);
|
||||||
|
tableProps.setDockerDefaults((prev) => ({
|
||||||
|
...prev,
|
||||||
|
updateAlertDelayHours: normalized,
|
||||||
|
}));
|
||||||
|
tableProps.setHasUnsavedChanges(true);
|
||||||
|
}}
|
||||||
|
class="mt-1 w-full rounded-md border border-border bg-surface p-2 text-sm text-base-content focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200 dark:focus:border-sky-400 dark:focus:ring-sky-600"
|
||||||
|
/>
|
||||||
|
<p class="mt-1 text-xs text-muted">{state.dockerUpdatePresentation.delayDescription}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
+73
@@ -1406,3 +1406,76 @@ describe('cloneBackupDefaults (via createDefaultAlertsConfigurationSnapshot)', (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// dockerDefaults.updateAlertDelayHours (read + save round-trip)
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
describe('dockerDefaults.updateAlertDelayHours', () => {
|
||||||
|
it('defaults to 24 when the config omits the field', () => {
|
||||||
|
const snapshot = readAlertsConfigurationSnapshot(cfg({ dockerDefaults: {} }));
|
||||||
|
expect(snapshot.dockerDefaults.updateAlertDelayHours).toBe(24);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves a custom positive delay and the -1 off state on read', () => {
|
||||||
|
const custom = readAlertsConfigurationSnapshot(
|
||||||
|
cfg({ dockerDefaults: { updateAlertDelayHours: 48 } }),
|
||||||
|
);
|
||||||
|
expect(custom.dockerDefaults.updateAlertDelayHours).toBe(48);
|
||||||
|
|
||||||
|
const off = readAlertsConfigurationSnapshot(
|
||||||
|
cfg({ dockerDefaults: { updateAlertDelayHours: -1 } }),
|
||||||
|
);
|
||||||
|
expect(off.dockerDefaults.updateAlertDelayHours).toBe(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maps 0 (backend "unset") to the factory 24 on read', () => {
|
||||||
|
const snapshot = readAlertsConfigurationSnapshot(
|
||||||
|
cfg({ dockerDefaults: { updateAlertDelayHours: 0 } }),
|
||||||
|
);
|
||||||
|
expect(snapshot.dockerDefaults.updateAlertDelayHours).toBe(24);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('round-trips -1 and custom delays through buildAlertsConfigurationPayload', () => {
|
||||||
|
for (const value of [-1, 6, 24, 72]) {
|
||||||
|
const snapshot = createDefaultAlertsConfigurationSnapshot();
|
||||||
|
snapshot.dockerDefaults.updateAlertDelayHours = value;
|
||||||
|
|
||||||
|
const { alertConfig } = buildAlertsConfigurationPayload({
|
||||||
|
snapshot,
|
||||||
|
rawOverridesConfig: {},
|
||||||
|
alertsActivationState: null,
|
||||||
|
alertsActivationConfig: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(alertConfig?.dockerDefaults?.updateAlertDelayHours).toBe(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('never emits 0 on save (the backend would reset it to 24)', () => {
|
||||||
|
const snapshot = createDefaultAlertsConfigurationSnapshot();
|
||||||
|
snapshot.dockerDefaults.updateAlertDelayHours = 0;
|
||||||
|
|
||||||
|
const { alertConfig } = buildAlertsConfigurationPayload({
|
||||||
|
snapshot,
|
||||||
|
rawOverridesConfig: {},
|
||||||
|
alertsActivationState: null,
|
||||||
|
alertsActivationConfig: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(alertConfig?.dockerDefaults?.updateAlertDelayHours).toBe(24);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the field when saving unrelated settings (default snapshot save)', () => {
|
||||||
|
const snapshot = createDefaultAlertsConfigurationSnapshot();
|
||||||
|
|
||||||
|
const { alertConfig } = buildAlertsConfigurationPayload({
|
||||||
|
snapshot,
|
||||||
|
rawOverridesConfig: {},
|
||||||
|
alertsActivationState: null,
|
||||||
|
alertsActivationConfig: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(alertConfig?.dockerDefaults?.updateAlertDelayHours).toBe(24);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -186,6 +186,20 @@ const normalizeGap = (value: unknown, fallback: number) => {
|
|||||||
return Math.max(0, Math.min(100, numeric));
|
return Math.max(0, Math.min(100, numeric));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// -1 disables update alerts; the backend treats 0 as unset and resets it to 24,
|
||||||
|
// so never emit 0. Anything non-finite or non-positive falls back to the factory delay.
|
||||||
|
const normalizeUpdateAlertDelayHours = (value: unknown): number => {
|
||||||
|
const numeric = Number(value);
|
||||||
|
if (!Number.isFinite(numeric)) {
|
||||||
|
return FACTORY_DOCKER_DEFAULTS.updateAlertDelayHours;
|
||||||
|
}
|
||||||
|
if (numeric < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const rounded = Math.round(numeric);
|
||||||
|
return rounded > 0 ? rounded : FACTORY_DOCKER_DEFAULTS.updateAlertDelayHours;
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeWarningCriticalPair = (
|
const normalizeWarningCriticalPair = (
|
||||||
warning: number | undefined,
|
warning: number | undefined,
|
||||||
critical: number | undefined,
|
critical: number | undefined,
|
||||||
@@ -445,6 +459,9 @@ export function readAlertsConfigurationSnapshot(config: AlertConfig): AlertsConf
|
|||||||
config.dockerDefaults.memoryCriticalPct ?? FACTORY_DOCKER_DEFAULTS.memoryCriticalPct,
|
config.dockerDefaults.memoryCriticalPct ?? FACTORY_DOCKER_DEFAULTS.memoryCriticalPct,
|
||||||
serviceWarnGapPercent: serviceWarnGap,
|
serviceWarnGapPercent: serviceWarnGap,
|
||||||
serviceCriticalGapPercent: serviceCriticalGap,
|
serviceCriticalGapPercent: serviceCriticalGap,
|
||||||
|
updateAlertDelayHours: normalizeUpdateAlertDelayHours(
|
||||||
|
config.dockerDefaults.updateAlertDelayHours,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
snapshot.dockerDisableConnectivity = Boolean(config.dockerDefaults.stateDisableConnectivity);
|
snapshot.dockerDisableConnectivity = Boolean(config.dockerDefaults.stateDisableConnectivity);
|
||||||
snapshot.dockerPoweredOffSeverity =
|
snapshot.dockerPoweredOffSeverity =
|
||||||
@@ -772,6 +789,9 @@ export function buildAlertsConfigurationPayload({
|
|||||||
memoryCriticalPct: snapshot.dockerDefaults.memoryCriticalPct,
|
memoryCriticalPct: snapshot.dockerDefaults.memoryCriticalPct,
|
||||||
serviceWarnGapPercent: snapshot.dockerDefaults.serviceWarnGapPercent,
|
serviceWarnGapPercent: snapshot.dockerDefaults.serviceWarnGapPercent,
|
||||||
serviceCriticalGapPercent: snapshot.dockerDefaults.serviceCriticalGapPercent,
|
serviceCriticalGapPercent: snapshot.dockerDefaults.serviceCriticalGapPercent,
|
||||||
|
updateAlertDelayHours: normalizeUpdateAlertDelayHours(
|
||||||
|
snapshot.dockerDefaults.updateAlertDelayHours,
|
||||||
|
),
|
||||||
stateDisableConnectivity: snapshot.dockerDisableConnectivity,
|
stateDisableConnectivity: snapshot.dockerDisableConnectivity,
|
||||||
statePoweredOffSeverity: snapshot.dockerPoweredOffSeverity,
|
statePoweredOffSeverity: snapshot.dockerPoweredOffSeverity,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useLocation, useNavigate } from '@solidjs/router';
|
|||||||
|
|
||||||
import type { PMGThresholdDefaults, SnapshotAlertConfig, BackupAlertConfig } from '@/types/alerts';
|
import type { PMGThresholdDefaults, SnapshotAlertConfig, BackupAlertConfig } from '@/types/alerts';
|
||||||
import { useAlertsActivation } from '@/stores/alertsActivation';
|
import { useAlertsActivation } from '@/stores/alertsActivation';
|
||||||
|
import { isTrueNASSystemResource } from '@/utils/agentResources';
|
||||||
import { logger } from '@/utils/logger';
|
import { logger } from '@/utils/logger';
|
||||||
import {
|
import {
|
||||||
AGENT_DISKS_EMPTY_STATE,
|
AGENT_DISKS_EMPTY_STATE,
|
||||||
@@ -13,6 +14,7 @@ import {
|
|||||||
getAlertThresholdsHelpDismissLabel,
|
getAlertThresholdsHelpDismissLabel,
|
||||||
getAlertThresholdsBackupOrphanedPresentation,
|
getAlertThresholdsBackupOrphanedPresentation,
|
||||||
getAlertThresholdsDockerServicePresentation,
|
getAlertThresholdsDockerServicePresentation,
|
||||||
|
getAlertThresholdsDockerUpdatePresentation,
|
||||||
getAlertThresholdsDockerIgnoredPrefixesPresentation,
|
getAlertThresholdsDockerIgnoredPrefixesPresentation,
|
||||||
getAlertThresholdsSearchPlaceholder,
|
getAlertThresholdsSearchPlaceholder,
|
||||||
getAlertThresholdsSectionTitles,
|
getAlertThresholdsSectionTitles,
|
||||||
@@ -87,6 +89,7 @@ export function useThresholdsTableState(props: ThresholdsTableProps) {
|
|||||||
const guestFilterPresentation = getAlertThresholdsGuestFilterPresentation();
|
const guestFilterPresentation = getAlertThresholdsGuestFilterPresentation();
|
||||||
const backupOrphanedPresentation = getAlertThresholdsBackupOrphanedPresentation();
|
const backupOrphanedPresentation = getAlertThresholdsBackupOrphanedPresentation();
|
||||||
const dockerServicePresentation = getAlertThresholdsDockerServicePresentation();
|
const dockerServicePresentation = getAlertThresholdsDockerServicePresentation();
|
||||||
|
const dockerUpdatePresentation = getAlertThresholdsDockerUpdatePresentation();
|
||||||
const dockerIgnoredPrefixesPresentation = getAlertThresholdsDockerIgnoredPrefixesPresentation();
|
const dockerIgnoredPrefixesPresentation = getAlertThresholdsDockerIgnoredPrefixesPresentation();
|
||||||
|
|
||||||
const { isCollapsed, toggleSection, setCollapsed, expandAll, collapseAll } =
|
const { isCollapsed, toggleSection, setCollapsed, expandAll, collapseAll } =
|
||||||
@@ -120,6 +123,7 @@ export function useThresholdsTableState(props: ThresholdsTableProps) {
|
|||||||
|
|
||||||
const serviceWarnInputId = 'docker-service-warn-gap';
|
const serviceWarnInputId = 'docker-service-warn-gap';
|
||||||
const serviceCriticalInputId = 'docker-service-critical-gap';
|
const serviceCriticalInputId = 'docker-service-critical-gap';
|
||||||
|
const dockerUpdateDelayInputId = 'docker-update-alert-delay';
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const remote = props.dockerIgnoredPrefixes();
|
const remote = props.dockerIgnoredPrefixes();
|
||||||
@@ -143,7 +147,14 @@ export function useThresholdsTableState(props: ThresholdsTableProps) {
|
|||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasDockerSpecificControls = createMemo(() => (props.dockerHosts?.length ?? 0) > 0);
|
// Docker-only cards (ignored prefixes, update alerts, swarm gaps) stay hidden for
|
||||||
|
// TrueNAS-only setups, but must show for unified-agent Docker hosts, which are not
|
||||||
|
// typed 'docker-host' and only appear in the runtime list via their app-containers.
|
||||||
|
const hasDockerSpecificControls = createMemo(
|
||||||
|
() =>
|
||||||
|
(props.dockerHosts?.length ?? 0) > 0 ||
|
||||||
|
(props.containerRuntimes ?? []).some((runtime) => !isTrueNASSystemResource(runtime)),
|
||||||
|
);
|
||||||
|
|
||||||
const getActiveTabFromRoute = (): ThresholdsActiveTab => {
|
const getActiveTabFromRoute = (): ThresholdsActiveTab => {
|
||||||
const path = location.pathname;
|
const path = location.pathname;
|
||||||
@@ -848,6 +859,8 @@ export function useThresholdsTableState(props: ThresholdsTableProps) {
|
|||||||
dockerIgnoredInput,
|
dockerIgnoredInput,
|
||||||
dockerIgnoredPrefixesPresentation,
|
dockerIgnoredPrefixesPresentation,
|
||||||
dockerServicePresentation,
|
dockerServicePresentation,
|
||||||
|
dockerUpdatePresentation,
|
||||||
|
dockerUpdateDelayInputId,
|
||||||
editingId,
|
editingId,
|
||||||
editingNote,
|
editingNote,
|
||||||
editingThresholds,
|
editingThresholds,
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ export interface ThresholdsTableProps {
|
|||||||
memoryCriticalPct: number;
|
memoryCriticalPct: number;
|
||||||
serviceWarnGapPercent: number;
|
serviceWarnGapPercent: number;
|
||||||
serviceCriticalGapPercent: number;
|
serviceCriticalGapPercent: number;
|
||||||
|
updateAlertDelayHours: number;
|
||||||
};
|
};
|
||||||
dockerDisableConnectivity: () => boolean;
|
dockerDisableConnectivity: () => boolean;
|
||||||
setDockerDisableConnectivity: (value: boolean) => void;
|
setDockerDisableConnectivity: (value: boolean) => void;
|
||||||
@@ -173,6 +174,7 @@ export interface ThresholdsTableProps {
|
|||||||
memoryCriticalPct: number;
|
memoryCriticalPct: number;
|
||||||
serviceWarnGapPercent: number;
|
serviceWarnGapPercent: number;
|
||||||
serviceCriticalGapPercent: number;
|
serviceCriticalGapPercent: number;
|
||||||
|
updateAlertDelayHours: number;
|
||||||
}
|
}
|
||||||
| ((prev: {
|
| ((prev: {
|
||||||
cpu: number;
|
cpu: number;
|
||||||
@@ -184,6 +186,7 @@ export interface ThresholdsTableProps {
|
|||||||
memoryCriticalPct: number;
|
memoryCriticalPct: number;
|
||||||
serviceWarnGapPercent: number;
|
serviceWarnGapPercent: number;
|
||||||
serviceCriticalGapPercent: number;
|
serviceCriticalGapPercent: number;
|
||||||
|
updateAlertDelayHours: number;
|
||||||
}) => {
|
}) => {
|
||||||
cpu: number;
|
cpu: number;
|
||||||
memory: number;
|
memory: number;
|
||||||
@@ -194,6 +197,7 @@ export interface ThresholdsTableProps {
|
|||||||
memoryCriticalPct: number;
|
memoryCriticalPct: number;
|
||||||
serviceWarnGapPercent: number;
|
serviceWarnGapPercent: number;
|
||||||
serviceCriticalGapPercent: number;
|
serviceCriticalGapPercent: number;
|
||||||
|
updateAlertDelayHours: number;
|
||||||
}),
|
}),
|
||||||
) => void;
|
) => void;
|
||||||
dockerIgnoredPrefixes: () => string[];
|
dockerIgnoredPrefixes: () => string[];
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export interface DockerThresholdConfig {
|
|||||||
memoryCriticalPct?: number;
|
memoryCriticalPct?: number;
|
||||||
serviceWarnGapPercent?: number;
|
serviceWarnGapPercent?: number;
|
||||||
serviceCriticalGapPercent?: number;
|
serviceCriticalGapPercent?: number;
|
||||||
|
updateAlertDelayHours?: number;
|
||||||
stateDisableConnectivity?: boolean;
|
stateDisableConnectivity?: boolean;
|
||||||
statePoweredOffSeverity?: 'warning' | 'critical';
|
statePoweredOffSeverity?: 'warning' | 'critical';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ describe('alertThresholdDefaults', () => {
|
|||||||
memoryCriticalPct: 95,
|
memoryCriticalPct: 95,
|
||||||
serviceWarnGapPercent: 10,
|
serviceWarnGapPercent: 10,
|
||||||
serviceCriticalGapPercent: 50,
|
serviceCriticalGapPercent: 50,
|
||||||
|
updateAlertDelayHours: 24,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ export const FACTORY_DOCKER_DEFAULTS = {
|
|||||||
memoryCriticalPct: 95,
|
memoryCriticalPct: 95,
|
||||||
serviceWarnGapPercent: 10,
|
serviceWarnGapPercent: 10,
|
||||||
serviceCriticalGapPercent: 50,
|
serviceCriticalGapPercent: 50,
|
||||||
|
// Hours before an available image update raises an alert; -1 disables update alerts.
|
||||||
|
updateAlertDelayHours: 24,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FACTORY_DOCKER_STATE_DISABLE_CONNECTIVITY = false;
|
export const FACTORY_DOCKER_STATE_DISABLE_CONNECTIVITY = false;
|
||||||
|
|||||||
@@ -63,6 +63,15 @@ export const ALERT_THRESHOLDS_DOCKER_SERVICES_CRITICAL_GAP_DESCRIPTION =
|
|||||||
'Raise a critical alert when the missing replica gap meets or exceeds this value.';
|
'Raise a critical alert when the missing replica gap meets or exceeds this value.';
|
||||||
export const ALERT_THRESHOLDS_DOCKER_SERVICES_GAP_VALIDATION_MESSAGE =
|
export const ALERT_THRESHOLDS_DOCKER_SERVICES_GAP_VALIDATION_MESSAGE =
|
||||||
'Critical gap must be greater than or equal to the warning gap when enabled.';
|
'Critical gap must be greater than or equal to the warning gap when enabled.';
|
||||||
|
export const ALERT_THRESHOLDS_DOCKER_UPDATES_TITLE = 'Container update alerts';
|
||||||
|
export const ALERT_THRESHOLDS_DOCKER_UPDATES_DESCRIPTION =
|
||||||
|
'Pulse raises a warning when a container image update has been available for longer than the delay below. Turn this off to stop update alerts without affecting other container alerts.';
|
||||||
|
export const ALERT_THRESHOLDS_DOCKER_UPDATES_TOGGLE_LABEL = 'Alerts';
|
||||||
|
export const ALERT_THRESHOLDS_DOCKER_UPDATES_TOGGLE_DESCRIPTION =
|
||||||
|
'Toggle container image update alerts';
|
||||||
|
export const ALERT_THRESHOLDS_DOCKER_UPDATES_DELAY_LABEL = 'Delay (hours)';
|
||||||
|
export const ALERT_THRESHOLDS_DOCKER_UPDATES_DELAY_DESCRIPTION =
|
||||||
|
'Wait this many hours after an update is first seen before alerting.';
|
||||||
export const ALERT_THRESHOLDS_SECTION_TITLE_NODES = 'Virtualization Hosts';
|
export const ALERT_THRESHOLDS_SECTION_TITLE_NODES = 'Virtualization Hosts';
|
||||||
export const ALERT_THRESHOLDS_SECTION_TITLE_PBS = 'PBS Servers';
|
export const ALERT_THRESHOLDS_SECTION_TITLE_PBS = 'PBS Servers';
|
||||||
export const ALERT_THRESHOLDS_SECTION_TITLE_GUESTS = 'VMs & Containers';
|
export const ALERT_THRESHOLDS_SECTION_TITLE_GUESTS = 'VMs & Containers';
|
||||||
@@ -149,6 +158,17 @@ export function getAlertThresholdsDockerServicePresentation() {
|
|||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAlertThresholdsDockerUpdatePresentation() {
|
||||||
|
return {
|
||||||
|
title: ALERT_THRESHOLDS_DOCKER_UPDATES_TITLE,
|
||||||
|
description: ALERT_THRESHOLDS_DOCKER_UPDATES_DESCRIPTION,
|
||||||
|
toggleLabel: ALERT_THRESHOLDS_DOCKER_UPDATES_TOGGLE_LABEL,
|
||||||
|
toggleDescription: ALERT_THRESHOLDS_DOCKER_UPDATES_TOGGLE_DESCRIPTION,
|
||||||
|
delayLabel: ALERT_THRESHOLDS_DOCKER_UPDATES_DELAY_LABEL,
|
||||||
|
delayDescription: ALERT_THRESHOLDS_DOCKER_UPDATES_DELAY_DESCRIPTION,
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
|
||||||
export function getAlertThresholdsSectionTitles() {
|
export function getAlertThresholdsSectionTitles() {
|
||||||
return {
|
return {
|
||||||
nodes: ALERT_THRESHOLDS_SECTION_TITLE_NODES,
|
nodes: ALERT_THRESHOLDS_SECTION_TITLE_NODES,
|
||||||
|
|||||||
Reference in New Issue
Block a user