diff --git a/.betterer.results b/.betterer.results
index b889576f7fe..51766dc80c4 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -3308,9 +3308,6 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx:5381": [
[0, 0, 0, "\'@grafana/data/src/text/sanitize\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
],
- "public/app/features/dashboard-scene/scene/NavToolbarActions.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
"public/app/features/dashboard-scene/scene/PanelLinks.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts
index 3c04564f8dd..6df2e903ca0 100644
--- a/public/app/features/apiserver/types.ts
+++ b/public/app/features/apiserver/types.ts
@@ -43,6 +43,13 @@ export const AnnoKeyFolderUrl = 'grafana.app/folderUrl';
export const AnnoKeyMessage = 'grafana.app/message';
export const AnnoKeySlug = 'grafana.app/slug';
+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';
@@ -66,7 +73,7 @@ type GrafanaAnnotations = {
[AnnoKeyFolder]?: string;
[AnnoKeySlug]?: string;
- [AnnoKeyManagerKind]?: string;
+ [AnnoKeyManagerKind]?: ManagerKind;
[AnnoKeyManagerIdentity]?: string;
[AnnoKeySourcePath]?: string;
[AnnoKeySourceChecksum]?: string;
diff --git a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx
index 4de554e648f..30f99b64de0 100644
--- a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx
+++ b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx
@@ -59,7 +59,7 @@ export class SaveDashboardDrawer extends SceneObjectBase
diff --git a/public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx b/public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx
index 66327fdc54d..56fcac4528c 100644
--- a/public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx
+++ b/public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx
@@ -7,7 +7,7 @@ import { getAppEvents, locationService } from '@grafana/runtime';
import { Alert, Button, Field, Input, RadioButtonGroup, Stack, TextArea } from '@grafana/ui';
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
import { useUrlParams } from 'app/core/navigation/hooks';
-import { AnnoKeyManagerIdentity } from 'app/features/apiserver/types';
+import { AnnoKeyManagerIdentity, ManagerKind } from 'app/features/apiserver/types';
import { validationSrv } from 'app/features/manage-dashboards/services/ValidationSrv';
import { RepositoryView } from 'app/features/provisioning/api';
import { PROVISIONING_URL } from 'app/features/provisioning/constants';
@@ -73,7 +73,7 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
if (request.isSuccess) {
dashboard.setState({ isDirty: false });
if (isNew) {
- dashboard.setRepoName(defaultValues.repo);
+ dashboard.setManager(ManagerKind.Repo, defaultValues.repo);
}
if (workflow === 'branch' && ref !== '' && path !== '') {
// Redirect to the provisioning preview pages
diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx
index 463d0027b70..81c0acacd8f 100644
--- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx
+++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx
@@ -42,7 +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, AnnoKeySourcePath } from '../../apiserver/types';
+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';
@@ -743,25 +743,31 @@ export class DashboardScene extends SceneObjectBase impleme
return this._serializer.getDashboardChangesFromScene(this, { saveTimeRange, saveVariables, saveRefresh });
}
- /**
- * Provisioned dashboards helpers
- */
- getRepoName() {
- return this.state.meta.k8s?.annotations?.[AnnoKeyManagerIdentity];
+ getManagerKind(): ManagerKind | undefined {
+ return this.state.meta.k8s?.annotations?.[AnnoKeyManagerKind];
}
- isProvisioned() {
- return Boolean(this.getRepoName());
+ isManaged() {
+ return Boolean(this.getManagerKind());
+ }
+
+ isManagedRepository() {
+ return Boolean(this.getManagerKind() === ManagerKind.Repo);
}
getPath() {
return this.state.meta.k8s?.annotations?.[AnnoKeySourcePath];
}
- setRepoName(name: string) {
+ setManager(kind: ManagerKind, id: string) {
this.setState({
meta: {
- k8s: { annotations: { [AnnoKeyManagerIdentity]: name } },
+ k8s: {
+ annotations: {
+ [AnnoKeyManagerKind]: kind,
+ [AnnoKeyManagerIdentity]: id,
+ },
+ },
},
});
}
diff --git a/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx b/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx
new file mode 100644
index 00000000000..9594e1c1eb3
--- /dev/null
+++ b/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx
@@ -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 ;
+}
diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx
index c8358904793..5c509293dcb 100644
--- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx
+++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx
@@ -23,6 +23,7 @@ import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbar/Na
import { LS_PANEL_COPY_KEY } from 'app/core/constants';
import { contextSrv } from 'app/core/core';
import { Trans, t } from 'app/core/internationalization';
+import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, ManagerKind } from 'app/features/apiserver/types';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
import { ScopesSelector } from 'app/features/scopes';
@@ -39,6 +40,7 @@ import { isLibraryPanel } from '../utils/utils';
import { DashboardScene } from './DashboardScene';
import { GoToSnapshotOriginButton } from './GoToSnapshotOriginButton';
+import ManagedDashboardNavBarBadge from './ManagedDashboardNavBarBadge';
interface Props {
dashboard: DashboardScene;
@@ -71,7 +73,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 || dashboard.getRepoName());
+ 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
@@ -80,7 +82,7 @@ export function ToolbarActions({ dashboard }: Props) {
const showScopesSelector = config.featureToggles.scopeFilters && !isEditing;
const dashboardNewLayouts = config.featureToggles.dashboardNewLayouts;
const folderRepo = useSelector((state) => selectFolderRepository(state, meta.folderUid));
- const isProvisionedNG = Boolean(dashboard.isProvisioned() || folderRepo);
+ const isManaged = Boolean(dashboard.isManaged() || folderRepo);
if (!isEditingPanel) {
// This adds the presence indicators in enterprise
@@ -129,14 +131,12 @@ export function ToolbarActions({ dashboard }: Props) {
});
}
- if (isProvisionedNG) {
+ if (isManaged && meta.canEdit) {
toolbarActions.push({
group: 'icon-actions',
condition: true,
render: () => {
- return (
-
- );
+ return ;
},
});
}
@@ -564,7 +564,7 @@ export function ToolbarActions({ dashboard }: Props) {
}
// If we only can save as copy
- if (canSaveAs && !meta.canSave && !meta.canMakeEditable && !isProvisionedNG) {
+ if (canSaveAs && !meta.canSave && !meta.canMakeEditable && !isManaged) {
return (