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:
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user