Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pulp_file: Repositories & Remotes #137

Merged
merged 11 commits into from
Dec 30, 2024
9 changes: 9 additions & 0 deletions src/actions/file-remote-create.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { msg } from '@lingui/core/macro';
import { Paths, formatPath } from 'src/paths';
import { Action } from './action';

export const fileRemoteCreateAction = Action({
title: msg`Add remote`,
onClick: (item, { navigate }) =>
navigate(formatPath(Paths.file.remote.edit, { name: '_' })),
});
44 changes: 44 additions & 0 deletions src/actions/file-remote-delete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { msg, t } from '@lingui/core/macro';
import { FileRemoteAPI } from 'src/api';
import { DeleteRemoteModal } from 'src/components';
import {
handleHttpError,
parsePulpIDFromURL,
taskAlert,
waitForTaskUrl,
} from 'src/utilities';
import { Action } from './action';

export const fileRemoteDeleteAction = Action({
title: msg`Delete`,
modal: ({ addAlert, listQuery, setState, state }) =>
state.deleteModalOpen ? (
<DeleteRemoteModal
closeAction={() => setState({ deleteModalOpen: null })}
deleteAction={() =>
deleteRemote(state.deleteModalOpen, { addAlert, setState, listQuery })
}
name={state.deleteModalOpen.name}
/>
) : null,
onClick: (
{ name, id, pulp_href }: { name: string; id?: string; pulp_href?: string },
{ setState },
) =>
setState({
deleteModalOpen: { pulpId: id || parsePulpIDFromURL(pulp_href), name },
}),
});

function deleteRemote({ name, pulpId }, { addAlert, setState, listQuery }) {
return FileRemoteAPI.delete(pulpId)
.then(({ data }) => {
addAlert(taskAlert(data.task, t`Removal started for remote ${name}`));
setState({ deleteModalOpen: null });
return waitForTaskUrl(data.task);
})
.then(() => listQuery())
.catch(
handleHttpError(t`Failed to remove remote ${name}`, () => null, addAlert),
);
}
9 changes: 9 additions & 0 deletions src/actions/file-remote-edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { msg } from '@lingui/core/macro';
import { Paths, formatPath } from 'src/paths';
import { Action } from './action';

export const fileRemoteEditAction = Action({
title: msg`Edit`,
onClick: ({ name }, { navigate }) =>
navigate(formatPath(Paths.file.remote.edit, { name })),
});
9 changes: 9 additions & 0 deletions src/actions/file-repository-create.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { msg } from '@lingui/core/macro';
import { Paths, formatPath } from 'src/paths';
import { Action } from './action';

export const fileRepositoryCreateAction = Action({
title: msg`Add repository`,
onClick: (item, { navigate }) =>
navigate(formatPath(Paths.file.repository.edit, { name: '_' })),
});
99 changes: 99 additions & 0 deletions src/actions/file-repository-delete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { msg, t } from '@lingui/core/macro';
import { FileDistributionAPI, FileRepositoryAPI } from 'src/api';
import { DeleteRepositoryModal } from 'src/components';
import {
handleHttpError,
parsePulpIDFromURL,
taskAlert,
waitForTaskUrl,
} from 'src/utilities';
import { Action } from './action';

export const fileRepositoryDeleteAction = Action({
title: msg`Delete`,
modal: ({ addAlert, listQuery, setState, state }) =>
state.deleteModalOpen ? (
<DeleteRepositoryModal
closeAction={() => setState({ deleteModalOpen: null })}
deleteAction={() =>
deleteRepository(state.deleteModalOpen, {
addAlert,
listQuery,
setState,
})
}
name={state.deleteModalOpen.name}
/>
) : null,
onClick: (
{ name, id, pulp_href }: { name: string; id?: string; pulp_href?: string },
{ setState },
) =>
setState({
deleteModalOpen: {
pulpId: id || parsePulpIDFromURL(pulp_href),
name,
pulp_href,
},
}),
});

async function deleteRepository(
{ name, pulp_href, pulpId },
{ addAlert, setState, listQuery },
) {
// TODO: handle more pages
const distributionsToDelete = await FileDistributionAPI.list({
repository: pulp_href,
page: 1,
page_size: 100,
})
.then(({ data: { results } }) => results || [])
.catch((e) => {
handleHttpError(
t`Failed to list distributions, removing only the repository.`,
() => null,
addAlert,
)(e);
return [];
});

const deleteRepo = FileRepositoryAPI.delete(pulpId)
.then(({ data }) => {
addAlert(taskAlert(data.task, t`Removal started for repository ${name}`));
return waitForTaskUrl(data.task);
})
.catch(
handleHttpError(
t`Failed to remove repository ${name}`,
() => setState({ deleteModalOpen: null }),
addAlert,
),
);

const deleteDistribution = ({ name, pulp_href }) => {
const distribution_id = parsePulpIDFromURL(pulp_href);
return FileDistributionAPI.delete(distribution_id)
.then(({ data }) => {
addAlert(
taskAlert(data.task, t`Removal started for distribution ${name}`),
);
return waitForTaskUrl(data.task);
})
.catch(
handleHttpError(
t`Failed to remove distribution ${name}`,
() => null,
addAlert,
),
);
};

return Promise.all([
deleteRepo,
...distributionsToDelete.map(deleteDistribution),
]).then(() => {
setState({ deleteModalOpen: null });
listQuery();
});
}
9 changes: 9 additions & 0 deletions src/actions/file-repository-edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { msg } from '@lingui/core/macro';
import { Paths, formatPath } from 'src/paths';
import { Action } from './action';

export const fileRepositoryEditAction = Action({
title: msg`Edit`,
onClick: ({ name }, { navigate }) =>
navigate(formatPath(Paths.file.repository.edit, { name })),
});
148 changes: 148 additions & 0 deletions src/actions/file-repository-sync.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { msg, t } from '@lingui/core/macro';
import { Button, FormGroup, Modal, Switch } from '@patternfly/react-core';
import { useEffect, useState } from 'react';
import { FileRepositoryAPI } from 'src/api';
import { HelpButton, Spinner } from 'src/components';
import { handleHttpError, parsePulpIDFromURL, taskAlert } from 'src/utilities';
import { Action } from './action';

const SyncModal = ({
closeAction,
syncAction,
name,
}: {
closeAction: () => null;
syncAction: (syncParams) => Promise<void>;
name: string;
}) => {
const [pending, setPending] = useState(false);
const [syncParams, setSyncParams] = useState({
mirror: true,
optimize: true,
});

useEffect(() => {
setPending(false);
setSyncParams({ mirror: true, optimize: true });
}, [name]);

if (!name) {
return null;
}

return (
<Modal
actions={[
<div data-cy='sync-button' key='sync'>
<Button
key='sync'
onClick={() => {
setPending(true);
syncAction(syncParams)
.then(closeAction)
.finally(() => setPending(false));
}}
variant='primary'
isDisabled={pending}
>
{t`Sync`}
{pending && <Spinner size='sm' />}
</Button>
</div>,
<Button key='close' onClick={closeAction} variant='link'>
{t`Close`}
</Button>,
]}
isOpen
onClose={closeAction}
title={t`Sync repository "${name}"`}
variant='medium'
>
<FormGroup
label={t`Mirror`}
labelIcon={
<HelpButton
content={t`If selected, all content that is not present in the remote repository will be removed from the local repository; otherwise, sync will add missing content.`}
/>
}
>
<Switch
isChecked={syncParams.mirror}
onChange={(_event, mirror) =>
setSyncParams({ ...syncParams, mirror })
}
label={t`Content not present in remote repository will be removed from the local repository`}
labelOff={t`Sync will only add missing content`}
/>
</FormGroup>
<br />
<FormGroup
label={t`Optimize`}
labelIcon={
<HelpButton
content={t`Only perform the sync if changes are reported by the remote server. To force a sync to happen, deselect this option.`}
/>
}
>
<Switch
isChecked={syncParams.optimize}
onChange={(_event, optimize) =>
setSyncParams({ ...syncParams, optimize })
}
label={t`Only perform the sync if changes are reported by the remote server.`}
labelOff={t`Force a sync to happen.`}
/>
</FormGroup>
<br />
</Modal>
);
};

export const fileRepositorySyncAction = Action({
title: msg`Sync`,
modal: ({ addAlert, query, setState, state }) =>
state.syncModalOpen ? (
<SyncModal
closeAction={() => setState({ syncModalOpen: null })}
syncAction={(syncParams) =>
syncRepository(state.syncModalOpen, { addAlert, query }, syncParams)
}
name={state.syncModalOpen.name}
/>
) : null,
onClick: ({ name, pulp_href }, { setState }) =>
setState({
syncModalOpen: { name, pulp_href },
}),
visible: (_item, { hasPermission }) =>
hasPermission('file.change_collectionremote'),
disabled: ({ remote, last_sync_task }) => {
if (!remote) {
return t`There are no remotes associated with this repository.`;
}

if (
last_sync_task &&
['running', 'waiting'].includes(last_sync_task.state)
) {
return t`Sync task is already queued.`;
}
},
});

function syncRepository({ name, pulp_href }, { addAlert, query }, syncParams) {
const pulpId = parsePulpIDFromURL(pulp_href);
return FileRepositoryAPI.sync(pulpId, syncParams || { mirror: true })
.then(({ data }) => {
addAlert(taskAlert(data.task, t`Sync started for repository "${name}".`));

query();
})
.catch(
handleHttpError(
t`Failed to sync repository "${name}"`,
() => null,
addAlert,
),
);
}
7 changes: 7 additions & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ export { ansibleRepositoryDeleteAction } from './ansible-repository-delete';
export { ansibleRepositoryEditAction } from './ansible-repository-edit';
export { ansibleRepositorySyncAction } from './ansible-repository-sync';
export { ansibleRepositoryVersionRevertAction } from './ansible-repository-version-revert';
export { fileRemoteCreateAction } from './file-remote-create';
export { fileRemoteDeleteAction } from './file-remote-delete';
export { fileRemoteEditAction } from './file-remote-edit';
export { fileRepositoryCreateAction } from './file-repository-create';
export { fileRepositoryDeleteAction } from './file-repository-delete';
export { fileRepositoryEditAction } from './file-repository-edit';
export { fileRepositorySyncAction } from './file-repository-sync';
35 changes: 34 additions & 1 deletion src/api/ansible-remote.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
import { PulpAPI } from './pulp';
import { type AnsibleRemoteType } from './response-types/ansible-remote';

export class AnsibleRemoteType {
auth_url: string;
ca_cert: string;
client_cert: string;
download_concurrency: number;
name: string;
proxy_url: string;
pulp_href?: string;
rate_limit: number;
requirements_file: string;
tls_validation: boolean;
url: string;
signed_only: boolean;
sync_dependencies?: boolean;

// connect_timeout
// headers
// max_retries
// policy
// pulp_created
// pulp_labels
// pulp_last_updated
// sock_connect_timeout
// sock_read_timeout
// total_timeout

hidden_fields: {
is_set: boolean;
name: string;
}[];

my_permissions?: string[];
}

// simplified version of smartUpdate from execution-environment-registry
function smartUpdate(
Expand Down
Loading
Loading