Provisioning: Add Git Sync limitations warning and migrate resources checkbox (#115532)

* Provisioning: Add Git Sync limitations warning and migrate resources checkbox

- Update SynchronizeStep alert to use warning severity with comprehensive Git Sync limitations
- Add conditional warnings for instance sync (permissions loss, alerts/library panels loss)
- Add conditional warnings for folder sync (folder structure changes, manual cleanup needed)
- Add "Migrate existing resources" checkbox for folder sync mode
- Update useCreateSyncJob hook to handle migrateResources option for folder sync
- Extract i18n translations for new strings

* Simplify createSyncJob: calculate requiresMigration in caller

- Remove syncTarget and migrateResources parameters from useCreateSyncJob hook
- Calculate requiresMigration in SynchronizeStep based on sync target and checkbox value
- Pass requiresMigration as parameter to createSyncJob function

* Revert: Pass requiresMigration as hook parameter

- Calculate requiresMigration in SynchronizeStep using useMemo
- Pass requiresMigration to useCreateSyncJob hook
- Remove parameter from createSyncJob function call

* Revert "Revert: Pass requiresMigration as hook parameter"

This reverts commit 97e3b7107d.

* Fix TypeScript errors in ProvisioningWizard

- Remove requiresMigration from useCreateSyncJob call
- Pass requiresMigration parameter to createSyncJob call
- Remove unused Target import from SynchronizeStep

* Show migrate resources checkbox for instance sync (checked and disabled)

- Display checkbox for both instance and folder sync
- For instance sync: checkbox is checked and disabled with explanation
- For instance sync: automatically set migrateResources to true via useEffect
- Update description to explain instance sync requires all resources to be managed

* Extract i18n translations for instance-migrate-resources-description

* Rename 'Synchronization options' to 'Options'

* Update i18n translations: rename synchronization-options to options

* Remove unnecessary conditional check for sync target

* Add bodySmall variant to announcement banner TextLink

* Move requiresMigration calculation logic into useResourceStats hook

- Add migrateResources parameter to useResourceStats hook
- Calculate final requiresMigration in hook based on sync target and checkbox value
- Use watch instead of getValues to reactively get migrateResources value
- Simplify startSynchronization to use requiresMigration from hook
This commit is contained in:
Roberto Jiménez Sánchez
2025-12-18 12:16:57 +00:00
committed by GitHub
parent 5bedcc7bd7
commit e29bb47e95
6 changed files with 135 additions and 45 deletions
@@ -117,14 +117,9 @@ export const ProvisioningWizard = memo(function ProvisioningWizard({
const [repoName = '', repoType, syncTarget] = watch(['repositoryName', 'repository.type', 'repository.sync.target']);
const [submitData] = useCreateOrUpdateRepository(repoName);
const [deleteRepository] = useDeleteRepositoryMutation();
const {
shouldSkipSync,
requiresMigration,
isLoading: isResourceStatsLoading,
} = useResourceStats(repoName, syncTarget);
const { shouldSkipSync, isLoading: isResourceStatsLoading } = useResourceStats(repoName, syncTarget);
const { createSyncJob, isLoading: isCreatingSkipJob } = useCreateSyncJob({
repoName: repoName,
requiresMigration,
setStepStatusInfo,
});
@@ -274,8 +269,8 @@ export const ProvisioningWizard = memo(function ProvisioningWizard({
if (activeStep === 'bootstrap' && canSkipSync) {
nextStepIndex = currentStepIndex + 2; // Skip to finish step
// Create a pull job to initialize the repository
const job = await createSyncJob();
// No migration needed when skipping sync
const job = await createSyncJob(false);
if (!job) {
return; // Don't proceed if job creation fails
}
@@ -3,7 +3,7 @@ import { memo, useEffect, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { Trans, t } from '@grafana/i18n';
import { Alert, Button, Field, Spinner, Stack, Text, TextLink } from '@grafana/ui';
import { Alert, Button, Checkbox, Field, Spinner, Stack, Text, TextLink } from '@grafana/ui';
import { Job, useGetRepositoryStatusQuery } from 'app/api/clients/provisioning/v0alpha1';
import { JobStatus } from '../Job/JobStatus';
@@ -20,13 +20,17 @@ export interface SynchronizeStepProps {
}
export const SynchronizeStep = memo(function SynchronizeStep({ onCancel, isCancelling }: SynchronizeStepProps) {
const { watch } = useFormContext<WizardFormData>();
const { watch, register } = useFormContext<WizardFormData>();
const { setStepStatusInfo } = useStepStatus();
const [repoName = '', syncTarget] = watch(['repositoryName', 'repository.sync.target']);
const { requiresMigration } = useResourceStats(repoName, syncTarget);
const [repoName = '', syncTarget, migrateResources] = watch([
'repositoryName',
'repository.sync.target',
'migrate.migrateResources',
]);
const { requiresMigration } = useResourceStats(repoName, syncTarget, migrateResources);
const { createSyncJob } = useCreateSyncJob({
repoName,
requiresMigration,
setStepStatusInfo,
});
const [job, setJob] = useState<Job>();
@@ -63,7 +67,7 @@ export const SynchronizeStep = memo(function SynchronizeStep({ onCancel, isCance
const isButtonDisabled = hasError || (checked !== undefined && isRepositoryHealthy === false) || healthStatusNotReady;
const startSynchronization = async () => {
const response = await createSyncJob();
const response = await createSyncJob(requiresMigration);
if (response) {
setJob(response);
}
@@ -108,41 +112,108 @@ export const SynchronizeStep = memo(function SynchronizeStep({ onCancel, isCance
)}
{isRepositoryHealthy && (
<Alert
title={t(
'provisioning.wizard.alert-title',
'Important: No data or configuration will be lost. Dashboards remain accessible during migration, but changes made during this process may not be exported.'
)}
severity={'info'}
title={t('provisioning.wizard.alert-title', 'Important: Review Git Sync limitations before proceeding')}
severity={'warning'}
>
<ul style={{ marginLeft: '16px' }}>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-1">
Resources can still be created, edited, or deleted during this process, but changes may not be exported.
<Stack direction="column" gap={2}>
<Text>
<Trans i18nKey="provisioning.wizard.alert-intro">
Please be aware of the following limitations. For more details, see the{' '}
<TextLink
external
href="https://grafana.com/docs/grafana/latest/as-code/observability-as-code/provision-resources/intro-git-sync/"
>
Git Sync documentation
</TextLink>
.
</Trans>
</li>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-2">
Once provisioning is complete, resources will be marked as managed through external storage.
</Trans>
</li>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-3">
The duration of this process depends on the number of resources involved.
</Trans>
</li>
<li>
</Text>
<ul style={{ marginLeft: '16px', marginTop: 0, marginBottom: 0 }}>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-1">
Resources can still be created, edited, or deleted during this process, but changes may not be
exported.
</Trans>
</li>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-unsupported">
Alerts and library panels are not supported in provisioned folders.
</Trans>
</li>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-permissions">
Fine-grained permissions are not supported. Default permissions apply: Admin, Editor, and Viewer roles
are preserved with their standard access levels.
</Trans>
</li>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-3">
The duration of this process depends on the number of resources involved.
</Trans>
</li>
{syncTarget === 'instance' && (
<li>
<Trans i18nKey="provisioning.wizard.alert-point-instance-alerts">
Existing alerts and library panels will be lost and will not be usable after migration.
</Trans>
</li>
)}
{syncTarget === 'folder' && (
<>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-folder-structure">
When migrating existing dashboards, the folder structure will be replicated in the repository.
Original folders will be emptied of dashboards but may still contain alerts or library panels.
</Trans>
</li>
<li>
<Trans i18nKey="provisioning.wizard.alert-point-folder-cleanup">
You may need to manually remove or manage original folders after migration.
</Trans>
</li>
</>
)}
</ul>
<Text color="secondary" variant="bodySmall">
<Trans i18nKey="provisioning.wizard.alert-point-4">
Enterprise instance administrators can display an announcement banner to notify users that migration is
in progress. See{' '}
<TextLink external href="https://grafana.com/docs/grafana/latest/administration/announcement-banner/">
<TextLink
external
variant="bodySmall"
href="https://grafana.com/docs/grafana/latest/administration/announcement-banner/"
>
this guide
</TextLink>{' '}
for step-by-step instructions.
</Trans>
</li>
</ul>
</Text>
</Stack>
</Alert>
)}
<Text element="h3">
<Trans i18nKey="provisioning.synchronize-step.options">Options</Trans>
</Text>
<Field noMargin>
<Checkbox
{...register('migrate.migrateResources')}
id="migrate-resources"
label={t('provisioning.wizard.sync-option-migrate-resources', 'Migrate existing resources')}
checked={syncTarget === 'instance' ? true : undefined}
disabled={syncTarget === 'instance'}
description={
syncTarget === 'instance' ? (
<Trans i18nKey="provisioning.synchronize-step.instance-migrate-resources-description">
Instance sync requires all resources to be managed. Existing resources will be migrated automatically.
</Trans>
) : (
<Trans i18nKey="provisioning.synchronize-step.migrate-resources-description">
Import existing dashboards from all folders into the new provisioned folder
</Trans>
)
}
/>
</Field>
{healthStatusNotReady ? (
<>
<Stack>
@@ -5,14 +5,13 @@ import { StepStatusInfo } from '../types';
export interface UseCreateSyncJobParams {
repoName: string;
requiresMigration: boolean;
setStepStatusInfo?: (info: StepStatusInfo) => void;
}
export function useCreateSyncJob({ repoName, requiresMigration, setStepStatusInfo }: UseCreateSyncJobParams) {
export function useCreateSyncJob({ repoName, setStepStatusInfo }: UseCreateSyncJobParams) {
const [createJob, { isLoading }] = useCreateRepositoryJobsMutation();
const createSyncJob = async () => {
const createSyncJob = async (requiresMigration: boolean) => {
if (!repoName) {
setStepStatusInfo?.({
status: 'error',
@@ -100,7 +100,7 @@ function getResourceStats(files?: GetRepositoryFilesApiResponse, stats?: GetReso
/**
* Hook that provides resource statistics and sync logic
*/
export function useResourceStats(repoName?: string, syncTarget?: RepositoryView['target']) {
export function useResourceStats(repoName?: string, syncTarget?: RepositoryView['target'], migrateResources?: boolean) {
const resourceStatsQuery = useGetResourceStatsQuery(repoName ? undefined : skipToken);
const filesQuery = useGetRepositoryFilesQuery(repoName ? { name: repoName } : skipToken);
@@ -121,7 +121,22 @@ export function useResourceStats(repoName?: string, syncTarget?: RepositoryView[
};
}, [resourceStatsQuery.data]);
const requiresMigration = resourceCount > 0 && syncTarget === 'instance';
// Calculate base requiresMigration: true if there are resources to migrate
const baseRequiresMigration = resourceCount > 0;
// Calculate final requiresMigration based on sync target and user selection
// For instance sync: always use baseRequiresMigration (checkbox is disabled and always true)
// For folder sync: only migrate if user explicitly opts in via checkbox
const requiresMigration = useMemo(() => {
if (syncTarget === 'instance') {
return baseRequiresMigration;
}
if (syncTarget === 'folder') {
return migrateResources ?? false;
}
return baseRequiresMigration;
}, [syncTarget, baseRequiresMigration, migrateResources]);
const shouldSkipSync = (resourceCount === 0 || syncTarget === 'folder') && fileCount === 0;
// Format display strings
@@ -9,6 +9,7 @@ export type RepoType = RepositorySpec['type'];
export interface MigrateFormData {
history: boolean;
identifier: boolean;
migrateResources?: boolean;
}
export interface WizardFormData {
+11 -2
View File
@@ -12182,6 +12182,9 @@
"tooltip-unhealthy-repository": "Unable to pull an unhealthy repository"
},
"synchronize-step": {
"instance-migrate-resources-description": "Instance sync requires all resources to be managed. Existing resources will be migrated automatically.",
"migrate-resources-description": "Import existing dashboards from all folders into the new provisioned folder",
"options": "Options",
"repository-error": "Repository error",
"repository-error-message": "Unable to check repository status. Please verify the repository configuration and try again.",
"repository-unhealthy": "The repository cannot be synchronized. Cancel provisioning and try again once the issue has been resolved. See details below."
@@ -12201,11 +12204,16 @@
},
"warning-title-default": "Warning",
"wizard": {
"alert-intro": "Please be aware of the following limitations. For more details, see the <2>Git Sync documentation</2>.",
"alert-point-1": "Resources can still be created, edited, or deleted during this process, but changes may not be exported.",
"alert-point-2": "Once provisioning is complete, resources will be marked as managed through external storage.",
"alert-point-3": "The duration of this process depends on the number of resources involved.",
"alert-point-4": "Enterprise instance administrators can display an announcement banner to notify users that migration is in progress. See <2>this guide</2> for step-by-step instructions.",
"alert-title": "Important: No data or configuration will be lost. Dashboards remain accessible during migration, but changes made during this process may not be exported.",
"alert-point-folder-cleanup": "You may need to manually remove or manage original folders after migration.",
"alert-point-folder-structure": "When migrating existing dashboards, the folder structure will be replicated in the repository. Original folders will be emptied of dashboards but may still contain alerts or library panels.",
"alert-point-instance-alerts": "Existing alerts and library panels will be lost and will not be usable after migration.",
"alert-point-permissions": "Fine-grained permissions are not supported. Default permissions apply: Admin, Editor, and Viewer roles are preserved with their standard access levels.",
"alert-point-unsupported": "Alerts and library panels are not supported in provisioned folders.",
"alert-title": "Important: Review Git Sync limitations before proceeding",
"button-cancel": "Cancel",
"button-cancelling": "Cancelling...",
"button-next": "Finish",
@@ -12223,6 +12231,7 @@
"step-finish": "Choose additional settings",
"step-synchronize": "Synchronize with external storage",
"sync-description": "Sync resources with external storage. After this one-time step, all future updates will be automatically saved to the repository and provisioned back into the instance.",
"sync-option-migrate-resources": "Migrate existing resources",
"title-bootstrap": "Choose what to synchronize",
"title-connect": "Connect to external storage",
"title-finish": "Choose additional settings",