src: add custom repositories import feature

This allows sharing blueprints cross organizations even with custom
repositories.
This commit is contained in:
Anna Vítová 2024-11-26 15:10:02 +02:00 committed by Klara Simickova
parent 2bee0ef49b
commit e805c65f81
5 changed files with 158 additions and 3 deletions

View file

@ -16,6 +16,7 @@ const config: ConfigFile = {
'searchPackageGroup',
'listFeatures',
'listSnapshotsByDate',
'bulkImportRepositories',
],
};

View file

@ -23,9 +23,10 @@ import { mapOnPremToHosted } from './helpers/onPremToHostedBlueprintMapper';
import { useAppDispatch } from '../../store/hooks';
import { BlueprintExportResponse } from '../../store/imageBuilderApi';
import { wizardState } from '../../store/wizardSlice';
import { importCustomRepositories, wizardState } from '../../store/wizardSlice';
import { resolveRelPath } from '../../Utilities/path';
import { mapExportRequestToState } from '../CreateImageWizard/utilities/requestMapper';
import { ApiRepositoryRequest, useBulkImportRepositoriesMutation } from '../../store/contentSourcesApi';
interface ImportBlueprintModalProps {
setShowImportModal: React.Dispatch<React.SetStateAction<boolean>>;
@ -52,6 +53,8 @@ export const ImportBlueprintModal: React.FunctionComponent<
const [isRejected, setIsRejected] = React.useState(false);
const [isOnPrem, setIsOnPrem] = React.useState(false);
const dispatch = useAppDispatch();
const [importRepositories, repositoriesResult] = useBulkImportRepositoriesMutation();
const handleFileInputChange = (
_event: React.ChangeEvent<HTMLInputElement> | React.DragEvent<HTMLElement>,
file: File
@ -61,6 +64,7 @@ export const ImportBlueprintModal: React.FunctionComponent<
setIsRejected(false);
setIsInvalidFormat(false);
};
React.useEffect(() => {
if (filename && fileContent) {
try {
@ -84,11 +88,22 @@ export const ImportBlueprintModal: React.FunctionComponent<
distribution: blueprintFromFile.distribution,
customizations: blueprintFromFile.customizations,
metadata: blueprintFromFile.metadata,
content_sources: blueprintFromFile.content_sources,
};
const importBlueprintState = mapExportRequestToState(
blueprintExportedResponse,
blueprintFromFile.image_requests || []
);
if (blueprintExportedResponse.content_sources) {
const customRepositories: ApiRepositoryRequest[] = blueprintExportedResponse.content_sources.map(item => item as ApiRepositoryRequest);
const result = importRepositories({
body: customRepositories,
});
dispatch(
importCustomRepositories(blueprintExportedResponse.customizations.custom_repositories || [])
);
};
setIsOnPrem(false);
setImportedBlueprint(importBlueprintState);
} catch {
@ -114,6 +129,7 @@ export const ImportBlueprintModal: React.FunctionComponent<
}
}
}, [filename, fileContent]);
const handleClear = () => {
setFilename('');
setFileContent('');

View file

@ -75,7 +75,6 @@ import {
selectSnapshotDate,
selectUseLatest,
selectFirstBootScript,
selectMetadata,
initialState,
selectTimezone,
selectNtpServers,
@ -83,6 +82,7 @@ import {
selectKeyboard,
selectHostname,
selectUsers,
selectMetadata,
} from '../../../store/wizardSlice';
import { FileSystemConfigurationType } from '../steps/FileSystem';
import {
@ -370,6 +370,7 @@ export const mapExportRequestToState = (
customizations: request.customizations,
image_requests: image_requests,
};
return {
wizardMode,
metadata: {

View file

@ -48,6 +48,16 @@ const injectedRtkApi = api.injectEndpoints({
body: queryArg.apiRepositoryRequest,
}),
}),
bulkImportRepositories: build.mutation<
BulkImportRepositoriesApiResponse,
BulkImportRepositoriesApiArg
>({
query: (queryArg) => ({
url: `/repositories/bulk_import/`,
method: "POST",
body: queryArg.body,
}),
}),
listRepositoriesRpms: build.query<
ListRepositoriesRpmsApiResponse,
ListRepositoriesRpmsApiArg
@ -129,6 +139,12 @@ export type CreateRepositoryApiArg = {
/** request body */
apiRepositoryRequest: ApiRepositoryRequest;
};
export type BulkImportRepositoriesApiResponse =
/** status 201 Created */ ApiRepositoryImportResponseRead[];
export type BulkImportRepositoriesApiArg = {
/** request body */
body: ApiRepositoryRequest[];
};
export type ListRepositoriesRpmsApiResponse =
/** status 200 OK */ ApiRepositoryRpmCollectionResponse;
export type ListRepositoriesRpmsApiArg = {
@ -423,6 +439,116 @@ export type ApiRepositoryRequestRead = {
/** URL of the remote yum repository */
url?: string | undefined;
};
export type ApiRepositoryImportResponse = {
/** Content Type (rpm) of the repository */
content_type?: string;
/** Architecture to restrict client usage to */
distribution_arch?: string;
/** Versions to restrict client usage to */
distribution_versions?: string[];
/** Number of consecutive failed introspections */
failed_introspections_count?: number;
/** GPG key for repository */
gpg_key?: string;
/** Label used to configure the yum repository on clients */
label?: string;
/** Error of last attempted introspection */
last_introspection_error?: string;
/** Status of last introspection */
last_introspection_status?: string;
/** Timestamp of last attempted introspection */
last_introspection_time?: string;
last_snapshot?: ApiSnapshotResponse;
last_snapshot_task?: ApiTaskInfoResponse;
/** UUID of the last snapshot task */
last_snapshot_task_uuid?: string;
/** UUID of the last dao.Snapshot */
last_snapshot_uuid?: string;
/** Timestamp of last successful introspection */
last_success_introspection_time?: string;
/** Timestamp of last introspection that had updates */
last_update_introspection_time?: string;
/** Latest URL for the snapshot distribution */
latest_snapshot_url?: string;
/** Verify packages */
metadata_verification?: boolean;
/** Disable modularity filtering on this repository */
module_hotfixes?: boolean;
/** Name of the remote yum repository */
name?: string;
/** Origin of the repository */
origin?: string;
/** Number of packages last read in the repository */
package_count?: number;
/** Enable snapshotting and hosting of this repository */
snapshot?: boolean;
/** Combined status of last introspection and snapshot of repository (Valid, Invalid, Unavailable, Pending) */
status?: string;
/** URL of the remote yum repository */
url?: string;
/** Warnings to alert user of mismatched fields if there is an existing repo with the same URL */
warnings?: {
[key: string]: any;
}[];
};
export type ApiRepositoryImportResponseRead = {
/** Account ID of the owner */
account_id?: string;
/** Content Type (rpm) of the repository */
content_type?: string;
/** Architecture to restrict client usage to */
distribution_arch?: string;
/** Versions to restrict client usage to */
distribution_versions?: string[];
/** Number of consecutive failed introspections */
failed_introspections_count?: number;
/** GPG key for repository */
gpg_key?: string;
/** Label used to configure the yum repository on clients */
label?: string;
/** Error of last attempted introspection */
last_introspection_error?: string;
/** Status of last introspection */
last_introspection_status?: string;
/** Timestamp of last attempted introspection */
last_introspection_time?: string;
last_snapshot?: ApiSnapshotResponse;
last_snapshot_task?: ApiTaskInfoResponse;
/** UUID of the last snapshot task */
last_snapshot_task_uuid?: string;
/** UUID of the last dao.Snapshot */
last_snapshot_uuid?: string;
/** Timestamp of last successful introspection */
last_success_introspection_time?: string;
/** Timestamp of last introspection that had updates */
last_update_introspection_time?: string;
/** Latest URL for the snapshot distribution */
latest_snapshot_url?: string;
/** Verify packages */
metadata_verification?: boolean;
/** Disable modularity filtering on this repository */
module_hotfixes?: boolean;
/** Name of the remote yum repository */
name?: string;
/** Organization ID of the owner */
org_id?: string;
/** Origin of the repository */
origin?: string;
/** Number of packages last read in the repository */
package_count?: number;
/** Enable snapshotting and hosting of this repository */
snapshot?: boolean;
/** Combined status of last introspection and snapshot of repository (Valid, Invalid, Unavailable, Pending) */
status?: string;
/** URL of the remote yum repository */
url?: string;
/** UUID of the object */
uuid?: string;
/** Warnings to alert user of mismatched fields if there is an existing repo with the same URL */
warnings?: {
[key: string]: any;
}[];
};
export type ApiRepositoryRpm = {
/** The architecture of the rpm */
arch?: string | undefined;
@ -475,6 +601,7 @@ export const {
useSearchPackageGroupMutation,
useListRepositoriesQuery,
useCreateRepositoryMutation,
useBulkImportRepositoriesMutation,
useListRepositoriesRpmsQuery,
useSearchRpmMutation,
useListSnapshotsByDateMutation,

View file

@ -1,7 +1,7 @@
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { v4 as uuidv4 } from 'uuid';
import type { ApiRepositoryResponseRead } from './contentSourcesApi';
import type { ApiRepositoryImportResponseRead, ApiRepositoryRequest, ApiRepositoryResponseRead } from './contentSourcesApi';
import type {
CustomRepository,
Distributions,
@ -665,6 +665,15 @@ export const wizardSlice = createSlice({
changeSnapshotDate: (state, action: PayloadAction<string>) => {
state.snapshotting.snapshotDate = action.payload;
},
importCustomRepositories: (
state,
action: PayloadAction<CustomRepository[]>
) => {
state.repositories.customRepositories = [
...state.repositories.customRepositories,
...action.payload
]
},
changeCustomRepositories: (
state,
action: PayloadAction<CustomRepository[]>
@ -867,6 +876,7 @@ export const {
changeUseLatest,
changeSnapshotDate,
changeCustomRepositories,
importCustomRepositories,
changePayloadRepositories,
addRecommendedRepository,
removeRecommendedRepository,