diff --git a/pkg/ui/workspaces/db-console/src/redux/alerts.ts b/pkg/ui/workspaces/db-console/src/redux/alerts.ts
index 22b24c796a45..a6c7b9f0e6f6 100644
--- a/pkg/ui/workspaces/db-console/src/redux/alerts.ts
+++ b/pkg/ui/workspaces/db-console/src/redux/alerts.ts
@@ -47,6 +47,7 @@ import {
import { LocalSetting } from "./localsettings";
import { AdminUIState, AppDispatch } from "./state";
import {
+ LICENSE_UPDATE_DISMISSED_KEY,
VERSION_DISMISSED_KEY,
INSTRUCTIONS_BOX_COLLAPSED_KEY,
saveUIData,
@@ -61,6 +62,7 @@ export enum AlertLevel {
WARNING,
CRITICAL,
SUCCESS,
+ INFORMATION,
}
export interface AlertInfo {
@@ -635,20 +637,6 @@ export const upgradeNotFinalizedWarningSelector = createSelector(
},
);
-/**
- * Selector which returns an array of all active alerts which should be
- * displayed in the overview list page, these should be non-critical alerts.
- */
-
-export const overviewListAlertsSelector = createSelector(
- staggeredVersionWarningSelector,
- clusterPreserveDowngradeOptionOvertimeSelector,
- upgradeNotFinalizedWarningSelector,
- (...alerts: Alert[]): Alert[] => {
- return without(alerts, null, undefined);
- },
-);
-
/**
* Selector which returns an array of all active alerts which should be
* displayed in the alerts panel, which is embedded within the cluster overview
@@ -707,6 +695,105 @@ export const licenseTypeSelector = createSelector(
data => licenseTypeNames.get(data.LicenseType) || "None",
);
+export const licenseUpdateDismissedLocalSetting = new LocalSetting(
+ "license_update_dismissed",
+ localSettingsSelector,
+ moment(0),
+);
+
+const licenseUpdateDismissedPersistentLoadedSelector = createSelector(
+ (state: AdminUIState) => state.uiData,
+ uiData => uiData && has(uiData, LICENSE_UPDATE_DISMISSED_KEY),
+);
+
+const licenseUpdateDismissedPersistentSelector = createSelector(
+ (state: AdminUIState) => state.uiData,
+ uiData => {
+ return (
+ (uiData &&
+ uiData[LICENSE_UPDATE_DISMISSED_KEY] &&
+ uiData[LICENSE_UPDATE_DISMISSED_KEY].data &&
+ moment(uiData[LICENSE_UPDATE_DISMISSED_KEY].data)) ||
+ moment(0)
+ );
+ },
+);
+
+export const licenseUpdateNotificationSelector = createSelector(
+ licenseTypeSelector,
+ licenseUpdateDismissedLocalSetting.selector,
+ licenseUpdateDismissedPersistentSelector,
+ licenseUpdateDismissedPersistentLoadedSelector,
+ (
+ licenseType,
+ licenseUpdateDismissed,
+ licenseUpdateDismissedPersistent,
+ licenseUpdateDismissedPersistentLoaded,
+ ): Alert => {
+ // If customer has Enterprise license they don't need to worry about this.
+ if (licenseType === "Enterprise") {
+ return undefined;
+ }
+
+ // If the notification has been dismissed based on the session storage
+ // timestamp, don't show it.
+ if (licenseUpdateDismissed.isAfter(moment(0))) {
+ return undefined;
+ }
+
+ // If the notification has been dismissed based on the uiData
+ // storage in the cluster, don't show it. Note that this is
+ // different from how version upgrade notifications work, this one
+ // is dismissed forever and won't return even if you upgrade
+ // further or time passes.
+ if (
+ licenseUpdateDismissedPersistentLoaded &&
+ licenseUpdateDismissedPersistent ) {
+ return undefined;
+ }
+
+ return {
+ level: AlertLevel.INFORMATION,
+ title: "Coming November 18, 2024",
+ text: "Important changes to CockroachDB’s licensing model.",
+ link: docsURL.enterpriseLicenseUpdate,
+ dismiss: (dispatch: any) => {
+ const dismissedAt = moment();
+ // Note(davidh): I haven't been able to find historical context
+ // for why some alerts have both a "local" and a "persistent"
+ // dismissal. My thinking is that just the persistent dismissal
+ // should be adequate, but I'm preserving that behavior here to
+ // match the version upgrade notification.
+
+ // Dismiss locally.
+ dispatch(licenseUpdateDismissedLocalSetting.set(dismissedAt));
+ // Dismiss persistently.
+ return dispatch(
+ saveUIData({
+ key: LICENSE_UPDATE_DISMISSED_KEY,
+ value: dismissedAt.valueOf(),
+ })
+ );
+ },
+ };
+ },
+);
+
+/**
+ * Selector which returns an array of all active alerts which should be
+ * displayed in the overview list page, these should be non-critical alerts.
+ */
+
+export const overviewListAlertsSelector = createSelector(
+ staggeredVersionWarningSelector,
+ clusterPreserveDowngradeOptionOvertimeSelector,
+ upgradeNotFinalizedWarningSelector,
+ licenseUpdateNotificationSelector,
+ (...alerts: Alert[]): Alert[] => {
+ return without(alerts, null, undefined);
+ },
+);
+
// daysUntilLicenseExpiresSelector returns number of days remaining before license expires.
export const daysUntilLicenseExpiresSelector = createSelector(
getDataFromServer,
diff --git a/pkg/ui/workspaces/db-console/src/redux/uiData.ts b/pkg/ui/workspaces/db-console/src/redux/uiData.ts
index 7286006a19fc..f3b7c76f5cbc 100644
--- a/pkg/ui/workspaces/db-console/src/redux/uiData.ts
+++ b/pkg/ui/workspaces/db-console/src/redux/uiData.ts
@@ -62,6 +62,11 @@ export class OptInAttributes {
// was last dismissed.
export const VERSION_DISMISSED_KEY = "version_dismissed";
+// LICENSE_UPDATE_DISMISSED_KEY is the uiData key on the server that tracks when the licence
+// update banner was last dismissed. This banner notifies the user that we've changed our
+// licensing if they're deployed without an active license.
+export const LICENSE_UPDATE_DISMISSED_KEY = "license_update_dismissed";
+
// INSTRUCTIONS_BOX_COLLAPSED_KEY is the uiData key on the server that tracks whether the
// instructions box on the cluster viz has been collapsed or not.
export const INSTRUCTIONS_BOX_COLLAPSED_KEY =
diff --git a/pkg/ui/workspaces/db-console/src/util/docs.ts b/pkg/ui/workspaces/db-console/src/util/docs.ts
index f1ace7749197..c9df508f7c27 100644
--- a/pkg/ui/workspaces/db-console/src/util/docs.ts
+++ b/pkg/ui/workspaces/db-console/src/util/docs.ts
@@ -63,6 +63,7 @@ export let upgradeTroubleshooting: string;
export let licensingFaqs: string;
// Note that these explicitly don't use the current version, since we want to
// link to the most up-to-date documentation available.
+export const enterpriseLicenseUpdate = "https://www.cockroachlabs.com/enterprise-license-update/"
export const upgradeCockroachVersion =
"https://www.cockroachlabs.com/docs/stable/upgrade-cockroach-version.html";
export const enterpriseLicensing =
diff --git a/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/alertbox.styl b/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/alertbox.styl
index 3b80a4e2bd34..d0773a910572 100644
--- a/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/alertbox.styl
+++ b/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/alertbox.styl
@@ -46,6 +46,13 @@
border-color $notification-info-border-color
background-color $notification-info-fill-color
+ &--information
+ color $body-color
+ border-color $notification-info-border-color
+ background-color $notification-info-fill-color
+ path
+ fill $colors--primary-blue-3
+
&--warning
color $body-color
border-color $notification-warning-border-color
diff --git a/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/index.tsx b/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/index.tsx
index 2974edf9aa59..c14cd7fbb016 100644
--- a/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/index.tsx
+++ b/pkg/ui/workspaces/db-console/src/views/shared/components/alertBox/index.tsx
@@ -17,6 +17,7 @@ import {
warningIcon,
notificationIcon,
criticalIcon,
+ informationIcon,
} from "src/views/shared/components/icons";
import "./alertbox.styl";
@@ -27,6 +28,8 @@ function alertIcon(level: AlertLevel) {
return trustIcon(criticalIcon);
case AlertLevel.WARNING:
return trustIcon(warningIcon);
+ case AlertLevel.INFORMATION:
+ return trustIcon(informationIcon);
default:
return trustIcon(notificationIcon);
}
diff --git a/pkg/ui/workspaces/db-console/src/views/shared/components/icons/index.tsx b/pkg/ui/workspaces/db-console/src/views/shared/components/icons/index.tsx
index 6b56105e646b..e44910770229 100644
--- a/pkg/ui/workspaces/db-console/src/views/shared/components/icons/index.tsx
+++ b/pkg/ui/workspaces/db-console/src/views/shared/components/icons/index.tsx
@@ -98,6 +98,19 @@ export const warningIcon = ``;
+export const informationIcon = `
+
+`
+
export const notificationIcon = `