Provisioning: Allow viewers to view /status subresource and wait on the frontend before sync (#111576)

This commit is contained in:
Stephanie Hingtgen
2025-09-24 22:37:55 +00:00
committed by GitHub
parent 64c700e563
commit 9172daa3dd
4 changed files with 45 additions and 4 deletions
+5 -1
View File
@@ -355,7 +355,11 @@ func (b *APIBuilder) GetAuthorizer() authorizer.Authorizer {
} else { } else {
return authorizer.DecisionDeny, "editor role is required", nil return authorizer.DecisionDeny, "editor role is required", nil
} }
case "status":
if id.GetOrgRole().Includes(identity.RoleViewer) && a.GetVerb() == apiutils.VerbGet {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionDeny, "users cannot update the status of a repository", nil
default: default:
if id.GetIsGrafanaAdmin() { if id.GetIsGrafanaAdmin() {
return authorizer.DecisionAllow, "", nil return authorizer.DecisionAllow, "", nil
@@ -7,6 +7,7 @@ import {
useCreateRepositoryJobsMutation, useCreateRepositoryJobsMutation,
useGetFrontendSettingsQuery, useGetFrontendSettingsQuery,
useGetRepositoryFilesQuery, useGetRepositoryFilesQuery,
useGetRepositoryStatusQuery,
useGetResourceStatsQuery, useGetResourceStatsQuery,
} from 'app/api/clients/provisioning/v0alpha1'; } from 'app/api/clients/provisioning/v0alpha1';
@@ -29,6 +30,7 @@ jest.mock('app/api/clients/provisioning/v0alpha1', () => ({
...jest.requireActual('app/api/clients/provisioning/v0alpha1'), ...jest.requireActual('app/api/clients/provisioning/v0alpha1'),
useGetFrontendSettingsQuery: jest.fn(), useGetFrontendSettingsQuery: jest.fn(),
useGetRepositoryFilesQuery: jest.fn(), useGetRepositoryFilesQuery: jest.fn(),
useGetRepositoryStatusQuery: jest.fn(),
useGetResourceStatsQuery: jest.fn(), useGetResourceStatsQuery: jest.fn(),
useCreateRepositoryJobsMutation: jest.fn(), useCreateRepositoryJobsMutation: jest.fn(),
})); }));
@@ -43,6 +45,9 @@ const mockUseGetFrontendSettingsQuery = useGetFrontendSettingsQuery as jest.Mock
const mockUseGetRepositoryFilesQuery = useGetRepositoryFilesQuery as jest.MockedFunction< const mockUseGetRepositoryFilesQuery = useGetRepositoryFilesQuery as jest.MockedFunction<
typeof useGetRepositoryFilesQuery typeof useGetRepositoryFilesQuery
>; >;
const mockUseGetRepositoryStatusQuery = useGetRepositoryStatusQuery as jest.MockedFunction<
typeof useGetRepositoryStatusQuery
>;
const mockUseGetResourceStatsQuery = useGetResourceStatsQuery as jest.MockedFunction<typeof useGetResourceStatsQuery>; const mockUseGetResourceStatsQuery = useGetResourceStatsQuery as jest.MockedFunction<typeof useGetResourceStatsQuery>;
const mockUseCreateRepositoryJobsMutation = useCreateRepositoryJobsMutation as jest.MockedFunction< const mockUseCreateRepositoryJobsMutation = useCreateRepositoryJobsMutation as jest.MockedFunction<
typeof useCreateRepositoryJobsMutation typeof useCreateRepositoryJobsMutation
@@ -131,6 +136,23 @@ describe('ProvisioningWizard', () => {
refetch: jest.fn(), refetch: jest.fn(),
}); });
mockUseGetRepositoryStatusQuery.mockReturnValue({
data: {
status: {
health: {
healthy: true,
checked: true,
message: '',
},
},
},
isLoading: false,
isFetching: false,
isError: false,
error: null,
refetch: jest.fn(),
});
mockUseGetResourceStatsQuery.mockReturnValue({ mockUseGetResourceStatsQuery.mockReturnValue({
data: { data: {
instance: [], instance: [],
@@ -40,7 +40,9 @@ export function SynchronizeStep({ isLegacyStorage, onCancel, isCancelling }: Syn
message: repositoryHealthMessages, message: repositoryHealthMessages,
checked, checked,
} = repositoryStatusQuery?.data?.status?.health || {}; } = repositoryStatusQuery?.data?.status?.health || {};
const isButtonDisabled = checked !== undefined && isRepositoryHealthy === false;
const hasError = repositoryStatusQuery.isError;
const isButtonDisabled = hasError || (checked !== undefined && isRepositoryHealthy === false);
const startSynchronization = async () => { const startSynchronization = async () => {
const [history] = getValues(['migrate.history']); const [history] = getValues(['migrate.history']);
@@ -65,7 +67,18 @@ export function SynchronizeStep({ isLegacyStorage, onCancel, isCancelling }: Syn
to the repository and provisioned back into the instance. to the repository and provisioned back into the instance.
</Trans> </Trans>
</Text> </Text>
{repositoryHealthMessages && !isRepositoryHealthy && ( {hasError && (
<ProvisioningAlert
error={{
title: t('provisioning.synchronize-step.repository-error', 'Repository error'),
message: t(
'provisioning.synchronize-step.repository-error-message',
'Unable to check repository status. Please verify the repository configuration and try again.'
),
}}
/>
)}
{repositoryHealthMessages && !isRepositoryHealthy && !hasError && (
<ProvisioningAlert <ProvisioningAlert
error={{ error={{
title: t( title: t(
@@ -134,7 +147,7 @@ export function SynchronizeStep({ isLegacyStorage, onCancel, isCancelling }: Syn
)} )}
<Field noMargin> <Field noMargin>
{isRepositoryHealthy === false ? ( {hasError || isRepositoryHealthy === false ? (
<Button variant="destructive" onClick={() => onCancel?.(repoName)} disabled={isCancelling}> <Button variant="destructive" onClick={() => onCancel?.(repoName)} disabled={isCancelling}>
{isCancelling ? ( {isCancelling ? (
<Trans i18nKey="provisioning.wizard.button-cancelling">Cancelling...</Trans> <Trans i18nKey="provisioning.wizard.button-cancelling">Cancelling...</Trans>
+2
View File
@@ -11674,6 +11674,8 @@
"tooltip-unhealthy-repository": "Unable to pull an unhealthy repository" "tooltip-unhealthy-repository": "Unable to pull an unhealthy repository"
}, },
"synchronize-step": { "synchronize-step": {
"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.", "repository-unhealthy": "The repository cannot be synchronized. Cancel provisioning and try again once the issue has been resolved. See details below.",
"synchronization-description": "Include commits for each historical value", "synchronization-description": "Include commits for each historical value",
"synchronization-options": "Synchronization options" "synchronization-options": "Synchronization options"