Dashboards: Show provisioned badge from annotations (#101625)

This commit is contained in:
Ryan McKinley
2025-03-05 20:08:20 +03:00
committed by GitHub
parent 627e8995c0
commit 5980c8efbb
4 changed files with 84 additions and 11 deletions
+17 -9
View File
@@ -43,11 +43,18 @@ export const AnnoKeyFolderUrl = 'grafana.app/folderUrl';
export const AnnoKeyMessage = 'grafana.app/message';
export const AnnoKeySlug = 'grafana.app/slug';
// Identify where values came from
export const AnnoKeyRepoName = 'grafana.app/repoName';
export const AnnoKeyRepoPath = 'grafana.app/repoPath';
export const AnnoKeyRepoHash = 'grafana.app/repoHash';
export const AnnoKeyRepoTimestamp = 'grafana.app/repoTimestamp';
export enum ManagerKind {
Repo = 'repo',
Terraform = 'terraform',
Kubectl = 'kubectl',
Plugin = 'plugin',
}
export const AnnoKeyManagerKind = 'grafana.app/managedBy';
export const AnnoKeyManagerIdentity = 'grafana.app/managerId';
export const AnnoKeySourcePath = 'grafana.app/sourcePath';
export const AnnoKeySourceChecksum = 'grafana.app/sourceChecksum';
export const AnnoKeySourceTimestamp = 'grafana.app/sourceTimestamp';
export const AnnoKeySavedFromUI = 'grafana.app/saved-from-ui';
export const AnnoKeyDashboardNotFound = 'grafana.app/dashboard-not-found';
@@ -66,10 +73,11 @@ type GrafanaAnnotations = {
[AnnoKeyFolder]?: string;
[AnnoKeySlug]?: string;
[AnnoKeyRepoName]?: string;
[AnnoKeyRepoPath]?: string;
[AnnoKeyRepoHash]?: string;
[AnnoKeyRepoTimestamp]?: string;
[AnnoKeyManagerKind]?: ManagerKind;
[AnnoKeyManagerIdentity]?: string;
[AnnoKeySourcePath]?: string;
[AnnoKeySourceChecksum]?: string;
[AnnoKeySourceTimestamp]?: string;
};
// Annotations provided by the front-end client
@@ -42,6 +42,7 @@ import { VariablesChanged } from 'app/features/variables/types';
import { DashboardDTO, DashboardMeta, KioskMode, SaveDashboardResponseDTO } from 'app/types';
import { ShowConfirmModalEvent } from 'app/types/events';
import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, AnnoKeySourcePath, ManagerKind } from '../../apiserver/types';
import { DashboardEditPane } from '../edit-pane/DashboardEditPane';
import { PanelEditor } from '../panel-edit/PanelEditor';
import { DashboardSceneChangeTracker } from '../saving/DashboardSceneChangeTracker';
@@ -741,6 +742,35 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> impleme
getDashboardChanges(saveTimeRange?: boolean, saveVariables?: boolean, saveRefresh?: boolean): DashboardChangeInfo {
return this._serializer.getDashboardChangesFromScene(this, { saveTimeRange, saveVariables, saveRefresh });
}
getManagerKind(): ManagerKind | undefined {
return this.state.meta.k8s?.annotations?.[AnnoKeyManagerKind];
}
isManaged() {
return Boolean(this.getManagerKind());
}
isManagedRepository() {
return Boolean(this.getManagerKind() === ManagerKind.Repo);
}
getPath() {
return this.state.meta.k8s?.annotations?.[AnnoKeySourcePath];
}
setManager(kind: ManagerKind, id: string) {
this.setState({
meta: {
k8s: {
annotations: {
[AnnoKeyManagerKind]: kind,
[AnnoKeyManagerIdentity]: id,
},
},
},
});
}
}
export class DashboardVariableDependency implements SceneVariableDependencyConfigLike {
@@ -0,0 +1,23 @@
import { Badge } from '@grafana/ui';
import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, ManagerKind } from 'app/features/apiserver/types';
import { DashboardMeta } from 'app/types';
export default function ManagedDashboardNavBarBadge({ meta }: { meta: DashboardMeta }) {
const obj = meta.k8s;
if (!obj?.annotations) {
return;
}
let text = 'Provisioned';
const kind = obj.annotations?.[AnnoKeyManagerKind];
const id = obj.annotations?.[AnnoKeyManagerIdentity];
switch (kind) {
case ManagerKind.Terraform:
text = 'Terraform';
case ManagerKind.Kubectl:
text = 'Kubectl';
case ManagerKind.Plugin:
text = `Plugin: ${id}`;
}
return <Badge color="darkgrey" icon="exchange-alt" text={text} key="provisioned-dashboard-button-badge" />;
}
@@ -37,6 +37,7 @@ import { isLibraryPanel } from '../utils/utils';
import { DashboardScene } from './DashboardScene';
import { GoToSnapshotOriginButton } from './GoToSnapshotOriginButton';
import ManagedDashboardNavBarBadge from './ManagedDashboardNavBarBadge';
interface Props {
dashboard: DashboardScene;
@@ -69,7 +70,7 @@ export function ToolbarActions({ dashboard }: Props) {
const isViewingPanel = Boolean(viewPanelScene);
const isEditedPanelDirty = usePanelEditDirty(editPanel);
const isEditingLibraryPanel = editPanel && isLibraryPanel(editPanel.state.panelRef.resolve());
const isNew = !Boolean(uid);
const isNew = !Boolean(uid || dashboard.isManaged());
const hasCopiedPanel = store.exists(LS_PANEL_COPY_KEY);
// Means we are not in settings view, fullscreen panel or edit panel
@@ -77,6 +78,7 @@ export function ToolbarActions({ dashboard }: Props) {
const isEditingAndShowingDashboard = isEditing && isShowingDashboard;
const showScopesSelector = config.featureToggles.scopeFilters && !isEditing;
const dashboardNewLayouts = config.featureToggles.dashboardNewLayouts;
const isManaged = Boolean(dashboard.isManaged());
if (!isEditingPanel) {
// This adds the presence indicators in enterprise
@@ -125,6 +127,16 @@ export function ToolbarActions({ dashboard }: Props) {
});
}
if (isManaged && meta.canEdit) {
toolbarActions.push({
group: 'icon-actions',
condition: true,
render: () => {
return <ManagedDashboardNavBarBadge meta={meta} />;
},
});
}
const isDevEnv = config.buildInfo.env === 'development';
toolbarActions.push({
@@ -548,7 +560,7 @@ export function ToolbarActions({ dashboard }: Props) {
}
// If we only can save as copy
if (canSaveAs && !meta.canSave && !meta.canMakeEditable) {
if (canSaveAs && !meta.canSave && !meta.canMakeEditable && !isManaged) {
return (
<Button
onClick={() => {