Enhance provisioning wizard with migration options

- Added a checkbox for migrating existing resources in the BootstrapStep component.
- Updated the form context to track the new migration option.
- Adjusted the SynchronizeStep and useCreateSyncJob hook to incorporate the migration logic.
- Enhanced localization with new descriptions and labels for migration features.
This commit is contained in:
Roberto Jimenez Sanchez committed 2025-12-10 16:17:43 +01:00
1 parent 86bc69e5d7
commit d9bd00953a
7 files changed
+67 -28

No files matched your search

@@ -14,7 +14,7 @@ import (
//go:generate mockery --name FullSyncFn --structname MockFullSyncFn --inpackage --filename full_sync_fn_mock.go --with-expecter
type FullSyncFn func(ctx context.Context, repo repository.Reader, compare CompareFn, clients resources.ResourceClients, currentRef string, repositoryResources resources.RepositoryResources, progress jobs.JobProgressRecorder, tracer tracing.Tracer, maxSyncWorkers int, metrics jobs.JobMetrics) error
//go:generate mockery --name CompareFn --structname MockCompareFn --inpackage --filename compare_fn_mock.go --with-expecter
//go:generate mockery -name CompareFn --structname MockCompareFn --inpackage --filename compare_fn_mock.go --with-expecter
type CompareFn func(ctx context.Context, repo repository.Reader, repositoryResources resources.RepositoryResources, ref string) ([]ResourceFileChange, error)
//go:generate mockery --name IncrementalSyncFn --structname MockIncrementalSyncFn --inpackage --filename incremental_sync_fn_mock.go --with-expecter
@@ -1,10 +1,10 @@
import { css } from '@emotion/css';
import { memo, useEffect } from 'react';
import React, { memo, useEffect } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { GrafanaTheme2 } from '@grafana/data';
import { t } from '@grafana/i18n';
import { Box, Card, Field, Icon, Input, LoadingPlaceholder, Stack, Text, useStyles2 } from '@grafana/ui';
import { Box, Card, Checkbox, Field, Icon, Input, LoadingPlaceholder, Stack, Text, useStyles2 } from '@grafana/ui';
import { RepositoryViewList } from 'app/api/clients/provisioning/v0alpha1';
import { generateRepositoryTitle } from 'app/features/provisioning/utils/data';
@@ -116,30 +116,56 @@ export const BootstrapStep = memo(function BootstrapStep({ settingsData, repoNam
{/* Only show title field if folder sync */}
{selectedTarget === 'folder' && (
<Field
label={t('provisioning.bootstrap-step.label-display-name', 'Display name')}
description={t(
'provisioning.bootstrap-step.description-clear-repository-connection',
'Add a clear name for this repository connection'
)}
error={errors.repository?.title?.message}
invalid={!!errors.repository?.title}
required
noMargin
>
<Input
id="repository-title"
{...register('repository.title', {
required: t('provisioning.bootstrap-step.error-field-required', 'This field is required.'),
})}
placeholder={t(
'provisioning.bootstrap-step.placeholder-my-repository-connection',
'My repository connection'
<>
<Field
label={t('provisioning.bootstrap-step.label-display-name', 'Display name')}
description={t(
'provisioning.bootstrap-step.description-clear-repository-connection',
'Add a clear name for this repository connection'
)}
error={errors.repository?.title?.message}
invalid={!!errors.repository?.title}
required
noMargin
>
<Input
id="repository-title"
{...register('repository.title', {
required: t('provisioning.bootstrap-step.error-field-required', 'This field is required.'),
})}
placeholder={t(
'provisioning.bootstrap-step.placeholder-my-repository-connection',
'My repository connection'
)}
// Autofocus the title field if it's the only available option
autoFocus={enabledOptions?.length === 1 && enabledOptions[0]?.target === 'folder'}
/>
</Field>
<Controller
name="migrate.migrateExistingResources"
control={control}
render={({ field: { onChange, value, ...field } }) => (
<Field
description={t(
'provisioning.bootstrap-step.description-migrate-existing-resources',
'Migrate all existing resources into the repository and pull everything from there.'
)}
noMargin
>
<Checkbox
id="migrate-existing-resources"
{...field}
value={value || false}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.checked)}
label={t(
'provisioning.bootstrap-step.label-migrate-existing-resources',
'Migrate all existing resources into this folder'
)}
/>
</Field>
)}
// Autofocus the title field if it's the only available option
autoFocus={enabledOptions?.length === 1 && enabledOptions[0]?.target === 'folder'}
/>
</Field>
</>
)}
{disabledOptions?.length > 0 && (
@@ -114,7 +114,12 @@ export const ProvisioningWizard = memo(function ProvisioningWizard({
handleSubmit,
} = methods;
const [repoName = '', repoType, syncTarget] = watch(['repositoryName', 'repository.type', 'repository.sync.target']);
const [repoName = '', repoType, syncTarget, migrateExistingResources] = watch([
'repositoryName',
'repository.type',
'repository.sync.target',
'migrate.migrateExistingResources',
]);
const [submitData] = useCreateOrUpdateRepository(repoName);
const [deleteRepository] = useDeleteRepositoryMutation();
const {
@@ -125,6 +130,7 @@ export const ProvisioningWizard = memo(function ProvisioningWizard({
const { createSyncJob, isLoading: isCreatingSkipJob } = useCreateSyncJob({
repoName: repoName,
requiresMigration,
migrateExistingResources,
setStepStatusInfo,
});
@@ -25,11 +25,12 @@ export const SynchronizeStep = memo(function SynchronizeStep({
}: SynchronizeStepProps) {
const { watch } = useFormContext<WizardFormData>();
const { setStepStatusInfo } = useStepStatus();
const [repoName = ''] = watch(['repositoryName']);
const [repoName = '', migrateExistingResources] = watch(['repositoryName', 'migrate.migrateExistingResources']);
const { requiresMigration } = useResourceStats(repoName);
const { createSyncJob } = useCreateSyncJob({
repoName,
requiresMigration,
migrateExistingResources,
setStepStatusInfo,
});
const [job, setJob] = useState<Job>();
@@ -6,12 +6,14 @@ import { StepStatusInfo } from '../types';
export interface UseCreateSyncJobParams {
repoName: string;
requiresMigration: boolean;
migrateExistingResources?: boolean;
setStepStatusInfo?: (info: StepStatusInfo) => void;
}
export function useCreateSyncJob({
repoName,
requiresMigration,
migrateExistingResources,
setStepStatusInfo,
}: UseCreateSyncJobParams) {
const [createJob, { isLoading }] = useCreateRepositoryJobsMutation();
@@ -28,7 +30,8 @@ export function useCreateSyncJob({
try {
setStepStatusInfo?.({ status: 'running' });
const jobSpec = requiresMigration
const shouldMigrate = requiresMigration || migrateExistingResources === true;
const jobSpec = shouldMigrate
? {
migrate: {},
}
@@ -9,6 +9,7 @@ export type RepoType = RepositorySpec['type'];
export interface MigrateFormData {
history: boolean;
identifier: boolean;
migrateExistingResources?: boolean;
}
export interface WizardFormData {
+2
View File
@@ -11668,6 +11668,7 @@
"dashboards-count_one": "{{count}} dashboard",
"dashboards-count_other": "{{count}} dashboard",
"description-clear-repository-connection": "Add a clear name for this repository connection",
"description-migrate-existing-resources": "Migrate all existing resources into the repository and pull everything from there.",
"empty": "Empty",
"error-field-required": "This field is required.",
"external-storage-label": "External storage",
@@ -11676,6 +11677,7 @@
"folders-count_one": "{{count}} folder",
"folders-count_other": "{{count}} folder",
"label-display-name": "Display name",
"label-migrate-existing-resources": "Migrate all existing resources into this folder",
"placeholder-my-repository-connection": "My repository connection",
"text-loading-resource-information": "Loading resource information...",
"unavailable-options": {