@flightctl/ui-components 1.1.0-rc2 → 1.1.0-rc3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/imagebuilder/index.d.ts +1 -0
- package/dist/types/imagebuilder/index.d.ts.map +1 -1
- package/dist/types/imagebuilder/index.js +3 -1
- package/dist/types/imagebuilder/index.js.map +1 -1
- package/dist/types/imagebuilder/models/ApiVersion.d.ts +8 -0
- package/dist/types/imagebuilder/models/ApiVersion.d.ts.map +1 -0
- package/dist/types/imagebuilder/models/ApiVersion.js +16 -0
- package/dist/types/imagebuilder/models/ApiVersion.js.map +1 -0
- package/dist/types/imagebuilder/models/ImageBuild.d.ts +2 -4
- package/dist/types/imagebuilder/models/ImageBuild.d.ts.map +1 -1
- package/dist/types/imagebuilder/models/ImageBuildList.d.ts +2 -4
- package/dist/types/imagebuilder/models/ImageBuildList.d.ts.map +1 -1
- package/dist/types/imagebuilder/models/ImageExport.d.ts +2 -4
- package/dist/types/imagebuilder/models/ImageExport.d.ts.map +1 -1
- package/dist/types/imagebuilder/models/ImageExportList.d.ts +2 -4
- package/dist/types/imagebuilder/models/ImageExportList.d.ts.map +1 -1
- package/dist/types/imagebuilder/models/Status.d.ts +2 -4
- package/dist/types/imagebuilder/models/Status.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/models/ApiVersion.d.ts +9 -0
- package/dist/types/models/ApiVersion.d.ts.map +1 -0
- package/dist/types/models/ApiVersion.js +17 -0
- package/dist/types/models/ApiVersion.js.map +1 -0
- package/dist/types/models/AuthConfig.d.ts +2 -4
- package/dist/types/models/AuthConfig.d.ts.map +1 -1
- package/dist/types/models/AuthProvider.d.ts +2 -4
- package/dist/types/models/AuthProvider.d.ts.map +1 -1
- package/dist/types/models/AuthProviderList.d.ts +2 -4
- package/dist/types/models/AuthProviderList.d.ts.map +1 -1
- package/dist/types/models/CertificateSigningRequest.d.ts +2 -4
- package/dist/types/models/CertificateSigningRequest.d.ts.map +1 -1
- package/dist/types/models/CertificateSigningRequestList.d.ts +2 -4
- package/dist/types/models/CertificateSigningRequestList.d.ts.map +1 -1
- package/dist/types/models/Device.d.ts +2 -4
- package/dist/types/models/Device.d.ts.map +1 -1
- package/dist/types/models/DeviceList.d.ts +2 -4
- package/dist/types/models/DeviceList.d.ts.map +1 -1
- package/dist/types/models/EnrollmentRequest.d.ts +2 -4
- package/dist/types/models/EnrollmentRequest.d.ts.map +1 -1
- package/dist/types/models/EnrollmentRequestList.d.ts +2 -4
- package/dist/types/models/EnrollmentRequestList.d.ts.map +1 -1
- package/dist/types/models/Event.d.ts +2 -4
- package/dist/types/models/Event.d.ts.map +1 -1
- package/dist/types/models/Event.js.map +1 -1
- package/dist/types/models/EventList.d.ts +2 -4
- package/dist/types/models/EventList.d.ts.map +1 -1
- package/dist/types/models/Fleet.d.ts +2 -4
- package/dist/types/models/Fleet.d.ts.map +1 -1
- package/dist/types/models/FleetList.d.ts +2 -4
- package/dist/types/models/FleetList.d.ts.map +1 -1
- package/dist/types/models/Organization.d.ts +2 -4
- package/dist/types/models/Organization.d.ts.map +1 -1
- package/dist/types/models/OrganizationList.d.ts +2 -4
- package/dist/types/models/OrganizationList.d.ts.map +1 -1
- package/dist/types/models/Repository.d.ts +2 -4
- package/dist/types/models/Repository.d.ts.map +1 -1
- package/dist/types/models/RepositoryList.d.ts +2 -4
- package/dist/types/models/RepositoryList.d.ts.map +1 -1
- package/dist/types/models/ResourceSync.d.ts +2 -4
- package/dist/types/models/ResourceSync.d.ts.map +1 -1
- package/dist/types/models/ResourceSyncList.d.ts +2 -4
- package/dist/types/models/ResourceSyncList.d.ts.map +1 -1
- package/dist/types/models/Status.d.ts +2 -4
- package/dist/types/models/Status.d.ts.map +1 -1
- package/dist/types/models/TemplateVersion.d.ts +2 -4
- package/dist/types/models/TemplateVersion.d.ts.map +1 -1
- package/dist/types/models/TemplateVersionList.d.ts +2 -4
- package/dist/types/models/TemplateVersionList.d.ts.map +1 -1
- package/dist/ui-components/src/components/AuthProvider/CreateAuthProvider/utils.d.ts.map +1 -1
- package/dist/ui-components/src/components/AuthProvider/CreateAuthProvider/utils.js +51 -51
- package/dist/ui-components/src/components/AuthProvider/CreateAuthProvider/utils.js.map +1 -1
- package/dist/ui-components/src/components/Device/DeviceDetails/TerminalTab.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/DeviceDetails/TerminalTab.js +5 -1
- package/dist/ui-components/src/components/Device/DeviceDetails/TerminalTab.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.js +3 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationHelmForm.js +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationHelmForm.js.map +1 -1
- package/dist/ui-components/src/components/ErrorAlert/ErrorAlert.d.ts +4 -2
- package/dist/ui-components/src/components/ErrorAlert/ErrorAlert.d.ts.map +1 -1
- package/dist/ui-components/src/components/ErrorAlert/ErrorAlert.js +2 -2
- package/dist/ui-components/src/components/ErrorAlert/ErrorAlert.js.map +1 -1
- package/dist/ui-components/src/components/Fleet/CreateFleet/utils.d.ts +1 -1
- package/dist/ui-components/src/components/Fleet/CreateFleet/utils.d.ts.map +1 -1
- package/dist/ui-components/src/components/Fleet/CreateFleet/utils.js +2 -2
- package/dist/ui-components/src/components/Fleet/CreateFleet/utils.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.d.ts +3 -3
- package/dist/ui-components/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.js +20 -13
- package/dist/ui-components/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/CreateImageBuildWizard.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/CreateImageBuildWizard.js +4 -3
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/CreateImageBuildWizard.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/OutputImageStep.js +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/OutputImageStep.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/RegistrationStep.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/RegistrationStep.js +13 -10
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/RegistrationStep.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/ReviewStep.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/ReviewStep.js +4 -2
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/ReviewStep.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/SourceImageStep.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/SourceImageStep.js +7 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/SourceImageStep.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/types.d.ts +3 -5
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/types.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/utils.d.ts +3 -2
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/utils.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/utils.js +139 -34
- package/dist/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/utils.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildDetailsPage.js +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildDetailsPage.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildExportsGallery.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildExportsGallery.js +25 -16
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildExportsGallery.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildLogsTab.d.ts +1 -0
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildLogsTab.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildLogsTab.js +17 -18
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildLogsTab.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageExportCards.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageExportCards.js +22 -10
- package/dist/ui-components/src/components/ImageBuilds/ImageExportCards.js.map +1 -1
- package/dist/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.d.ts +2 -1
- package/dist/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.d.ts.map +1 -1
- package/dist/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.js +9 -3
- package/dist/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.js.map +1 -1
- package/dist/ui-components/src/components/Repository/CreateRepository/utils.d.ts.map +1 -1
- package/dist/ui-components/src/components/Repository/CreateRepository/utils.js +3 -4
- package/dist/ui-components/src/components/Repository/CreateRepository/utils.js.map +1 -1
- package/dist/ui-components/src/components/form/RepositorySelect.d.ts.map +1 -1
- package/dist/ui-components/src/components/form/RepositorySelect.js +1 -1
- package/dist/ui-components/src/components/form/RepositorySelect.js.map +1 -1
- package/dist/ui-components/src/components/form/UploadField.d.ts.map +1 -1
- package/dist/ui-components/src/components/form/UploadField.js +25 -16
- package/dist/ui-components/src/components/form/UploadField.js.map +1 -1
- package/dist/ui-components/src/components/form/validations.d.ts +5 -0
- package/dist/ui-components/src/components/form/validations.d.ts.map +1 -1
- package/dist/ui-components/src/components/form/validations.js +40 -23
- package/dist/ui-components/src/components/form/validations.js.map +1 -1
- package/dist/ui-components/src/components/modals/CreateRepositoryModal/CreateRepositoryModal.d.ts +2 -1
- package/dist/ui-components/src/components/modals/CreateRepositoryModal/CreateRepositoryModal.d.ts.map +1 -1
- package/dist/ui-components/src/components/modals/CreateRepositoryModal/CreateRepositoryModal.js +2 -2
- package/dist/ui-components/src/components/modals/CreateRepositoryModal/CreateRepositoryModal.js.map +1 -1
- package/dist/ui-components/src/constants.d.ts +0 -2
- package/dist/ui-components/src/constants.d.ts.map +1 -1
- package/dist/ui-components/src/constants.js +5 -5
- package/dist/ui-components/src/constants.js.map +1 -1
- package/dist/ui-components/src/hooks/useWebSocket.d.ts.map +1 -1
- package/dist/ui-components/src/hooks/useWebSocket.js +25 -4
- package/dist/ui-components/src/hooks/useWebSocket.js.map +1 -1
- package/dist/ui-components/src/utils/imageBuilds.d.ts.map +1 -1
- package/dist/ui-components/src/utils/imageBuilds.js +13 -28
- package/dist/ui-components/src/utils/imageBuilds.js.map +1 -1
- package/dist/ui-components/src/utils/search.d.ts +2 -1
- package/dist/ui-components/src/utils/search.d.ts.map +1 -1
- package/dist/ui-components/src/utils/search.js +2 -2
- package/dist/ui-components/src/utils/search.js.map +1 -1
- package/package.json +2 -2
- package/src/components/AuthProvider/CreateAuthProvider/utils.ts +2 -2
- package/src/components/Device/DeviceDetails/TerminalTab.tsx +9 -1
- package/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts +3 -1
- package/src/components/Device/EditDeviceWizard/steps/ApplicationHelmForm.tsx +1 -1
- package/src/components/ErrorAlert/ErrorAlert.tsx +13 -3
- package/src/components/Fleet/CreateFleet/utils.ts +2 -3
- package/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.tsx +28 -15
- package/src/components/ImageBuilds/CreateImageBuildWizard/CreateImageBuildWizard.tsx +8 -3
- package/src/components/ImageBuilds/CreateImageBuildWizard/steps/OutputImageStep.tsx +1 -1
- package/src/components/ImageBuilds/CreateImageBuildWizard/steps/RegistrationStep.tsx +18 -10
- package/src/components/ImageBuilds/CreateImageBuildWizard/steps/ReviewStep.tsx +5 -1
- package/src/components/ImageBuilds/CreateImageBuildWizard/steps/SourceImageStep.tsx +13 -1
- package/src/components/ImageBuilds/CreateImageBuildWizard/types.ts +3 -6
- package/src/components/ImageBuilds/CreateImageBuildWizard/utils.ts +161 -37
- package/src/components/ImageBuilds/ImageBuildDetails/ImageBuildDetailsPage.tsx +1 -1
- package/src/components/ImageBuilds/ImageBuildDetails/ImageBuildExportsGallery.tsx +27 -18
- package/src/components/ImageBuilds/ImageBuildDetails/ImageBuildLogsTab.tsx +22 -26
- package/src/components/ImageBuilds/ImageExportCards.tsx +31 -12
- package/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx +13 -4
- package/src/components/Repository/CreateRepository/utils.ts +4 -4
- package/src/components/form/RepositorySelect.tsx +1 -0
- package/src/components/form/UploadField.tsx +29 -30
- package/src/components/form/validations.ts +44 -24
- package/src/components/modals/CreateRepositoryModal/CreateRepositoryModal.tsx +3 -1
- package/src/constants.ts +5 -5
- package/src/hooks/useWebSocket.ts +25 -3
- package/src/utils/imageBuilds.ts +14 -32
- package/src/utils/search.ts +2 -2
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { Alert } from '@patternfly/react-core';
|
|
2
|
+
import { Alert, AlertActionLink } from '@patternfly/react-core';
|
|
3
3
|
import { useTranslation } from '../../hooks/useTranslation';
|
|
4
4
|
import { getErrorMessage } from '../../utils/error';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type ErrorAlertProps = {
|
|
7
|
+
error: unknown;
|
|
8
|
+
onRetry?: VoidFunction;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const ErrorAlert = ({ error, onRetry }: ErrorAlertProps) => {
|
|
7
12
|
const { t } = useTranslation();
|
|
8
13
|
return (
|
|
9
|
-
<Alert
|
|
14
|
+
<Alert
|
|
15
|
+
isInline
|
|
16
|
+
variant="danger"
|
|
17
|
+
title={t('Unexpected error occurred')}
|
|
18
|
+
actionLinks={onRetry ? <AlertActionLink onClick={onRetry}>{t('Try again')}</AlertActionLink> : undefined}
|
|
19
|
+
>
|
|
10
20
|
{getErrorMessage(error)}
|
|
11
21
|
</Alert>
|
|
12
22
|
);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Fleet, PatchRequest } from '@flightctl/types';
|
|
2
1
|
import { TFunction } from 'i18next';
|
|
3
2
|
import * as Yup from 'yup';
|
|
4
|
-
import {
|
|
3
|
+
import { ApiVersion, Fleet, PatchRequest } from '@flightctl/types';
|
|
5
4
|
import { toAPILabel } from '../../../utils/labels';
|
|
6
5
|
import {
|
|
7
6
|
systemdUnitListValidationSchema,
|
|
@@ -178,7 +177,7 @@ export const getFleetResource = (values: FleetFormValues): Fleet => {
|
|
|
178
177
|
},
|
|
179
178
|
};
|
|
180
179
|
const fleet: Fleet = {
|
|
181
|
-
apiVersion:
|
|
180
|
+
apiVersion: ApiVersion.ApiVersionV1beta1,
|
|
182
181
|
kind: 'Fleet',
|
|
183
182
|
metadata: {
|
|
184
183
|
name: values.name,
|
|
@@ -3,9 +3,9 @@ import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from '@patternfly/
|
|
|
3
3
|
|
|
4
4
|
import { useTranslation } from '../../../hooks/useTranslation';
|
|
5
5
|
|
|
6
|
-
export type ConfirmImageExportAction = 'cancel' | 'delete';
|
|
6
|
+
export type ConfirmImageExportAction = 'cancel' | 'delete' | 'rebuild';
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const ConfirmImageExportActionModal = ({
|
|
9
9
|
action,
|
|
10
10
|
onClose,
|
|
11
11
|
}: {
|
|
@@ -17,16 +17,29 @@ const ConfirmDeleteOrCancelImageExportModal = ({
|
|
|
17
17
|
let title = '';
|
|
18
18
|
let message = '';
|
|
19
19
|
let confirmButtonTitle = '';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
|
|
21
|
+
switch (action) {
|
|
22
|
+
case 'cancel':
|
|
23
|
+
title = t('Cancel image export?');
|
|
24
|
+
message = t(
|
|
25
|
+
'This will immediately stop the current process. As a result, the image will not be exported in this format.',
|
|
26
|
+
);
|
|
27
|
+
confirmButtonTitle = t('Cancel image export');
|
|
28
|
+
break;
|
|
29
|
+
case 'delete':
|
|
30
|
+
title = t('Delete image export?');
|
|
31
|
+
message = t(
|
|
32
|
+
'This image export will be permanently removed. The actual image files in your storage will not be deleted.',
|
|
33
|
+
);
|
|
34
|
+
confirmButtonTitle = t('Delete');
|
|
35
|
+
break;
|
|
36
|
+
case 'rebuild':
|
|
37
|
+
title = t('Rebuild image export?');
|
|
38
|
+
message = t(
|
|
39
|
+
'Rebuilding updates the image currently displayed in the console. Previous versions remain accessible via the flightctl CLI.',
|
|
40
|
+
);
|
|
41
|
+
confirmButtonTitle = t('Rebuild');
|
|
42
|
+
break;
|
|
30
43
|
}
|
|
31
44
|
|
|
32
45
|
return (
|
|
@@ -34,15 +47,15 @@ const ConfirmDeleteOrCancelImageExportModal = ({
|
|
|
34
47
|
<ModalHeader title={title} />
|
|
35
48
|
<ModalBody>{message}</ModalBody>
|
|
36
49
|
<ModalFooter>
|
|
37
|
-
<Button key="confirm" variant=
|
|
50
|
+
<Button key="confirm" variant={action === 'rebuild' ? 'primary' : 'danger'} onClick={() => onClose(true)}>
|
|
38
51
|
{confirmButtonTitle}
|
|
39
52
|
</Button>
|
|
40
53
|
<Button key="cancel" variant="link" onClick={() => onClose(false)}>
|
|
41
|
-
{t('Close')}
|
|
54
|
+
{action === 'cancel' ? t('Close') : t('Cancel')}
|
|
42
55
|
</Button>
|
|
43
56
|
</ModalFooter>
|
|
44
57
|
</Modal>
|
|
45
58
|
);
|
|
46
59
|
};
|
|
47
60
|
|
|
48
|
-
export default
|
|
61
|
+
export default ConfirmImageExportActionModal;
|
|
@@ -70,11 +70,16 @@ const CreateImageBuildWizard = () => {
|
|
|
70
70
|
const [error, setError] = React.useState<ImageBuildWizardError>();
|
|
71
71
|
const [currentStep, setCurrentStep] = React.useState<WizardStepType>();
|
|
72
72
|
const [imageBuildId, imageBuild, imageBuildLoading, editError] = useEditImageBuild();
|
|
73
|
-
const { isLoading: registriesLoading, error: registriesError } = useOciRegistriesContext();
|
|
73
|
+
const { ociRegistries, isLoading: registriesLoading, error: registriesError } = useOciRegistriesContext();
|
|
74
74
|
|
|
75
75
|
const isEdit = !!imageBuildId;
|
|
76
76
|
const buildReason = imageBuild ? getImageBuildStatusReason(imageBuild) : undefined;
|
|
77
77
|
|
|
78
|
+
const availableRepositoryIds = React.useMemo(
|
|
79
|
+
() => new Set(ociRegistries.map((r) => r.metadata?.name as string)),
|
|
80
|
+
[ociRegistries],
|
|
81
|
+
);
|
|
82
|
+
|
|
78
83
|
let title: string;
|
|
79
84
|
if (isEdit) {
|
|
80
85
|
title =
|
|
@@ -117,7 +122,7 @@ const CreateImageBuildWizard = () => {
|
|
|
117
122
|
</Alert>
|
|
118
123
|
) : (
|
|
119
124
|
<Formik<ImageBuildFormValues>
|
|
120
|
-
initialValues={getInitialValues(imageBuild)}
|
|
125
|
+
initialValues={getInitialValues(imageBuild, availableRepositoryIds)}
|
|
121
126
|
validationSchema={getValidationSchema(t)}
|
|
122
127
|
validateOnMount
|
|
123
128
|
onSubmit={async (values) => {
|
|
@@ -184,7 +189,7 @@ const CreateImageBuildWizard = () => {
|
|
|
184
189
|
setCurrentStep(step);
|
|
185
190
|
}}
|
|
186
191
|
>
|
|
187
|
-
<WizardStep name={t('
|
|
192
|
+
<WizardStep name={t('Base image')} id={sourceImageStepId}>
|
|
188
193
|
{(!currentStep || currentStep?.id === sourceImageStepId) && <SourceImageStep />}
|
|
189
194
|
</WizardStep>
|
|
190
195
|
<WizardStep
|
|
@@ -37,7 +37,7 @@ const OutputImageStep = () => {
|
|
|
37
37
|
const writableRepoValidation = React.useCallback(
|
|
38
38
|
(repo: Repository) => {
|
|
39
39
|
if (isOciRepoSpec(repo.spec) && repo.spec.accessMode === OciRepoSpec.accessMode.READ) {
|
|
40
|
-
return t('
|
|
40
|
+
return t('Repositories used for output images must be writable.');
|
|
41
41
|
}
|
|
42
42
|
return undefined;
|
|
43
43
|
},
|
|
@@ -22,6 +22,7 @@ import { FormikErrors, useFormikContext } from 'formik';
|
|
|
22
22
|
|
|
23
23
|
import { BindingType } from '@flightctl/types/imagebuilder';
|
|
24
24
|
import { ImageBuildFormValues } from '../types';
|
|
25
|
+
import { PUBLIC_KEY_MAX_LENGTH } from '../utils';
|
|
25
26
|
import { useTranslation } from '../../../../hooks/useTranslation';
|
|
26
27
|
import FlightCtlForm from '../../../form/FlightCtlForm';
|
|
27
28
|
import TextField from '../../../form/TextField';
|
|
@@ -31,7 +32,14 @@ import { CERTIFICATE_VALIDITY_IN_YEARS } from '../../../../constants';
|
|
|
31
32
|
|
|
32
33
|
export const registrationStepId = 'registration';
|
|
33
34
|
|
|
34
|
-
export const isRegistrationStepValid = (errors: FormikErrors<ImageBuildFormValues>) =>
|
|
35
|
+
export const isRegistrationStepValid = (errors: FormikErrors<ImageBuildFormValues>) => {
|
|
36
|
+
const { userConfiguration } = errors;
|
|
37
|
+
if (!userConfiguration) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return !userConfiguration.username && !userConfiguration.publickey;
|
|
42
|
+
};
|
|
35
43
|
|
|
36
44
|
const RegistrationStep = () => {
|
|
37
45
|
const { t } = useTranslation();
|
|
@@ -52,11 +60,7 @@ const RegistrationStep = () => {
|
|
|
52
60
|
};
|
|
53
61
|
|
|
54
62
|
const handleRemoteAccessToggle = (enabled: boolean) => {
|
|
55
|
-
|
|
56
|
-
setFieldValue('userConfiguration', { username: '', publickey: '', enabled: true });
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
setFieldValue('userConfiguration.enabled', enabled);
|
|
63
|
+
setFieldValue('remoteAccessEnabled', enabled);
|
|
60
64
|
};
|
|
61
65
|
|
|
62
66
|
return (
|
|
@@ -158,19 +162,23 @@ const RegistrationStep = () => {
|
|
|
158
162
|
<Grid lg={5} span={8}>
|
|
159
163
|
<FormSection title={t('Remote access')}>
|
|
160
164
|
<CheckboxField
|
|
161
|
-
name="
|
|
165
|
+
name="remoteAccessEnabled"
|
|
162
166
|
label={t('Provide an SSH public key to enable passwordless login once your image is deployed.')}
|
|
163
167
|
onChangeCustom={handleRemoteAccessToggle}
|
|
164
168
|
>
|
|
165
|
-
<FormGroup label={t('Username')} fieldId="user-config-username">
|
|
169
|
+
<FormGroup label={t('Username')} fieldId="user-config-username" isRequired>
|
|
166
170
|
<TextField
|
|
167
171
|
name="userConfiguration.username"
|
|
168
172
|
aria-label={t('Username')}
|
|
169
173
|
helperText={t('The username for the user account')}
|
|
170
174
|
/>
|
|
171
175
|
</FormGroup>
|
|
172
|
-
<FormGroup label={t('SSH public key')} fieldId="user-config-publickey">
|
|
173
|
-
<UploadField
|
|
176
|
+
<FormGroup label={t('SSH public key')} fieldId="user-config-publickey" isRequired>
|
|
177
|
+
<UploadField
|
|
178
|
+
name="userConfiguration.publickey"
|
|
179
|
+
ariaLabel={t('SSH public key')}
|
|
180
|
+
maxFileBytes={PUBLIC_KEY_MAX_LENGTH}
|
|
181
|
+
/>
|
|
174
182
|
</FormGroup>
|
|
175
183
|
</CheckboxField>
|
|
176
184
|
<Content component="small">
|
|
@@ -54,7 +54,7 @@ const ReviewStep = ({ error }: ReviewStepProps) => {
|
|
|
54
54
|
);
|
|
55
55
|
|
|
56
56
|
const isEarlyBinding = values.bindingType === BindingType.BindingTypeEarly;
|
|
57
|
-
const remoteAccessUsername = values.
|
|
57
|
+
const remoteAccessUsername = values.remoteAccessEnabled ? values.userConfiguration.username || '' : '';
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
60
|
<Stack hasGutter>
|
|
@@ -63,6 +63,10 @@ const ReviewStep = ({ error }: ReviewStepProps) => {
|
|
|
63
63
|
<CardTitle>{t('Base image')}</CardTitle>
|
|
64
64
|
<CardBody>
|
|
65
65
|
<FlightCtlDescriptionList isHorizontal isCompact>
|
|
66
|
+
<DescriptionListGroup>
|
|
67
|
+
<DescriptionListTerm>{t('Build name')}</DescriptionListTerm>
|
|
68
|
+
<DescriptionListDescription>{values.buildName}</DescriptionListDescription>
|
|
69
|
+
</DescriptionListGroup>
|
|
66
70
|
<DescriptionListGroup>
|
|
67
71
|
<DescriptionListTerm>{t('Source repository')}</DescriptionListTerm>
|
|
68
72
|
<DescriptionListDescription>{values.source.repository}</DescriptionListDescription>
|
|
@@ -6,6 +6,7 @@ import { RepoSpecType } from '@flightctl/types';
|
|
|
6
6
|
import { ImageBuildFormValues } from '../types';
|
|
7
7
|
import { useTranslation } from '../../../../hooks/useTranslation';
|
|
8
8
|
import FlightCtlForm from '../../../form/FlightCtlForm';
|
|
9
|
+
import NameField from '../../../form/NameField';
|
|
9
10
|
import TextField from '../../../form/TextField';
|
|
10
11
|
import RepositorySelect from '../../../form/RepositorySelect';
|
|
11
12
|
import { usePermissionsContext } from '../../../common/PermissionsContext';
|
|
@@ -13,11 +14,15 @@ import { RESOURCE, VERB } from '../../../../types/rbac';
|
|
|
13
14
|
import { getImageReference } from '../../../../utils/imageBuilds';
|
|
14
15
|
import ImageUrlCard from '../../ImageUrlCard';
|
|
15
16
|
import { useOciRegistriesContext } from '../../OciRegistriesContext';
|
|
17
|
+
import { getBuildNameValidations } from '../../../form/validations';
|
|
16
18
|
|
|
17
19
|
export const sourceImageStepId = 'source-image';
|
|
18
20
|
|
|
19
21
|
export const isSourceImageStepValid = (errors: FormikErrors<ImageBuildFormValues>) => {
|
|
20
|
-
const { source } = errors;
|
|
22
|
+
const { buildName, source } = errors;
|
|
23
|
+
if (buildName) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
21
26
|
if (!source) {
|
|
22
27
|
return true;
|
|
23
28
|
}
|
|
@@ -39,6 +44,13 @@ const SourceImageStep = () => {
|
|
|
39
44
|
<FlightCtlForm>
|
|
40
45
|
<Grid lg={5} span={8}>
|
|
41
46
|
<FormSection>
|
|
47
|
+
<NameField
|
|
48
|
+
name="buildName"
|
|
49
|
+
aria-label={t('Build name')}
|
|
50
|
+
isRequired
|
|
51
|
+
resourceType="imagebuilds"
|
|
52
|
+
validations={getBuildNameValidations(t)}
|
|
53
|
+
/>
|
|
42
54
|
<RepositorySelect
|
|
43
55
|
name="source.repository"
|
|
44
56
|
label={t('Source repository')}
|
|
@@ -6,17 +6,14 @@ import {
|
|
|
6
6
|
} from '@flightctl/types/imagebuilder';
|
|
7
7
|
import { ExportFormatType } from '@flightctl/types/imagebuilder';
|
|
8
8
|
|
|
9
|
-
type ImageBuildUserConfigurationForm = ImageBuildUserConfiguration & {
|
|
10
|
-
enabled?: boolean;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
9
|
export type ImageBuildFormValues = {
|
|
14
|
-
|
|
10
|
+
buildName: string;
|
|
15
11
|
source: ImageBuildSource;
|
|
16
12
|
destination: ImageBuildDestination;
|
|
17
13
|
bindingType: BindingType;
|
|
18
14
|
exportFormats: ExportFormatType[];
|
|
19
|
-
|
|
15
|
+
remoteAccessEnabled: boolean;
|
|
16
|
+
userConfiguration: ImageBuildUserConfiguration;
|
|
20
17
|
};
|
|
21
18
|
|
|
22
19
|
export type ImageBuildWizardError =
|
|
@@ -2,32 +2,148 @@ import { TFunction } from 'i18next';
|
|
|
2
2
|
import * as Yup from 'yup';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
ApiVersion,
|
|
5
6
|
BindingType,
|
|
6
7
|
ExportFormatType,
|
|
7
8
|
ImageBuild,
|
|
8
9
|
ImageBuildDestination,
|
|
9
10
|
ImageBuildSource,
|
|
11
|
+
ImageBuildUserConfiguration,
|
|
10
12
|
ImageExport,
|
|
11
13
|
ResourceKind,
|
|
12
14
|
} from '@flightctl/types/imagebuilder';
|
|
13
|
-
import {
|
|
15
|
+
import { validImageBuildName } from '../../form/validations';
|
|
14
16
|
import { ImageBuildFormValues } from './types';
|
|
15
17
|
import { ImageBuildWithExports } from '../../../types/extraTypes';
|
|
16
18
|
|
|
17
|
-
export const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
export const PUBLIC_KEY_MAX_LENGTH = 8 * 1024; // (8 KB)
|
|
20
|
+
const VALID_SSH_PUBLIC_KEY_TYPES = [
|
|
21
|
+
'ssh-rsa',
|
|
22
|
+
'ssh-ed25519',
|
|
23
|
+
'ecdsa-sha2-nistp256',
|
|
24
|
+
'ecdsa-sha2-nistp384',
|
|
25
|
+
'ecdsa-sha2-nistp521',
|
|
26
|
+
'ssh-dss',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const SSH_PUBLIC_KEY_BASE64_DATA_REGEX = /^(?=.{50,}$)[A-Za-z0-9+/]+=*$/;
|
|
30
|
+
// Characters that could be used for injection attacks
|
|
31
|
+
const MALICIOUS_PUBLIC_KEY_CHARACTERS = /[;|&`()[\]{}<>"'\\\t$]/;
|
|
32
|
+
|
|
33
|
+
const OCI_IMAGE_NAME_MAX_LENGTH = 255;
|
|
34
|
+
const OCI_IMAGE_NAME_REGEX = /^[a-z0-9]+(?:[._-]+[a-z0-9]+)*(?:\/[a-z0-9]+(?:[._-]+[a-z0-9]+)*)*$/;
|
|
35
|
+
const OCI_IMAGE_NAME_VALID_CHARS = /^[a-z0-9._/-]+$/;
|
|
36
|
+
|
|
37
|
+
const OCI_IMAGE_TAG_MAX_LENGTH = 128;
|
|
38
|
+
const OCI_IMAGE_TAG_REGEX = /^[\w][\w.-]{0,127}$/;
|
|
39
|
+
const OCI_IMAGE_TAG_VALID_CHARS = /^[\w.-]+$/;
|
|
40
|
+
|
|
41
|
+
/** Returns an error message for image name: invalid chars, or invalid format. */
|
|
42
|
+
const getImageNameValidationError = (value: string, t: TFunction): string | undefined => {
|
|
43
|
+
if (!value) return undefined;
|
|
44
|
+
if (value.length > OCI_IMAGE_NAME_MAX_LENGTH) {
|
|
45
|
+
return t('Image name must not exceed {{max}} characters.', { max: OCI_IMAGE_NAME_MAX_LENGTH });
|
|
46
|
+
}
|
|
47
|
+
if (!OCI_IMAGE_NAME_VALID_CHARS.test(value)) {
|
|
48
|
+
return t('Image name may only contain alphanumeric characters, dots, underscores, hyphens, and slashes.');
|
|
49
|
+
}
|
|
50
|
+
if (!OCI_IMAGE_NAME_REGEX.test(value)) {
|
|
51
|
+
return t('Only alphanumeric characters are allowed at the start and end of each path component.');
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const getImageTagValidationError = (value: string, t: TFunction): string | undefined => {
|
|
57
|
+
if (!value) return undefined;
|
|
58
|
+
if (value.length > OCI_IMAGE_TAG_MAX_LENGTH) {
|
|
59
|
+
return t('Image tag must not exceed {{max}} characters.', { max: OCI_IMAGE_TAG_MAX_LENGTH });
|
|
60
|
+
}
|
|
61
|
+
if (!OCI_IMAGE_TAG_VALID_CHARS.test(value)) {
|
|
62
|
+
return t('Image tag may only contain letters, numbers, underscores, dots, and hyphens.');
|
|
63
|
+
}
|
|
64
|
+
if (!OCI_IMAGE_TAG_REGEX.test(value)) {
|
|
65
|
+
return t('Image tag must start with a letter, number, or underscore.');
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getPublicKeyValidationError = (publicKey: string, t: TFunction): string | undefined => {
|
|
71
|
+
if (publicKey.length > PUBLIC_KEY_MAX_LENGTH) {
|
|
72
|
+
return t('SSH public key is too long');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Allow newlines only at the end
|
|
76
|
+
const trimmedKey = publicKey.replace(/[\r\n]+$/g, '');
|
|
77
|
+
if (/[\r\n]/.test(trimmedKey)) {
|
|
78
|
+
return t('A single public key can be provided only');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (MALICIOUS_PUBLIC_KEY_CHARACTERS.test(trimmedKey)) {
|
|
82
|
+
return t('Invalid SSH public key');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const parts = trimmedKey.trim().split(/\s+/);
|
|
86
|
+
if (parts.length < 2) {
|
|
87
|
+
return t('Invalid SSH public key format. Expected: "[TYPE] key [comment]"');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const keyType = parts[0];
|
|
91
|
+
if (!VALID_SSH_PUBLIC_KEY_TYPES.includes(keyType)) {
|
|
92
|
+
return t('Unsupported SSH public key type. Supported types: {{supportedTypes}}', {
|
|
93
|
+
supportedTypes: VALID_SSH_PUBLIC_KEY_TYPES.join(', '),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const base64Data = parts[1];
|
|
98
|
+
if (!SSH_PUBLIC_KEY_BASE64_DATA_REGEX.test(base64Data)) {
|
|
99
|
+
return t('Invalid SSH public key data');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return undefined;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const validImageBuildImageFields = (t: TFunction) =>
|
|
106
|
+
Yup.object<ImageBuildSource | ImageBuildDestination>({
|
|
107
|
+
repository: Yup.string().required(t('Repository is required')),
|
|
108
|
+
imageName: Yup.string()
|
|
109
|
+
.required(t('Image name is required'))
|
|
110
|
+
.test('oci-image-name', function (value) {
|
|
111
|
+
if (!value) return true;
|
|
112
|
+
const error = getImageNameValidationError(value, t);
|
|
113
|
+
return error ? this.createError({ message: error }) : true;
|
|
114
|
+
}),
|
|
115
|
+
imageTag: Yup.string()
|
|
116
|
+
.required(t('Image tag is required'))
|
|
117
|
+
.test('oci-image-tag', function (value) {
|
|
118
|
+
if (!value) return true;
|
|
119
|
+
const error = getImageTagValidationError(value, t);
|
|
120
|
+
return error ? this.createError({ message: error }) : true;
|
|
121
|
+
}),
|
|
30
122
|
});
|
|
123
|
+
|
|
124
|
+
export const getValidationSchema = (t: TFunction) => {
|
|
125
|
+
return Yup.lazy((values: ImageBuildFormValues) =>
|
|
126
|
+
Yup.object<ImageBuildFormValues>({
|
|
127
|
+
buildName: validImageBuildName(t),
|
|
128
|
+
source: validImageBuildImageFields(t).required(t('Source image is required')),
|
|
129
|
+
destination: validImageBuildImageFields(t).required(t('Target image is required')),
|
|
130
|
+
bindingType: Yup.string<BindingType>().required(t('Binding type is required')),
|
|
131
|
+
userConfiguration: Yup.object<ImageBuildUserConfiguration>({
|
|
132
|
+
username: values.remoteAccessEnabled ? Yup.string().required(t('Username is required')) : Yup.string(),
|
|
133
|
+
publickey: values.remoteAccessEnabled
|
|
134
|
+
? Yup.string()
|
|
135
|
+
.required(t('SSH public key is required'))
|
|
136
|
+
.test('flightctl-ssh-public-key', function (publicKey) {
|
|
137
|
+
if (!publicKey) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
const error = getPublicKeyValidationError(publicKey, t);
|
|
141
|
+
return error ? this.createError({ message: error }) : true;
|
|
142
|
+
})
|
|
143
|
+
: Yup.string(),
|
|
144
|
+
}),
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
31
147
|
};
|
|
32
148
|
|
|
33
149
|
// Returns an array with one item per format (VMDK, QCOW2, ISO), where each item is either
|
|
@@ -84,32 +200,40 @@ export const toImageBuildWithExports = (imageBuild: ImageBuild): ImageBuildWithE
|
|
|
84
200
|
};
|
|
85
201
|
};
|
|
86
202
|
|
|
87
|
-
|
|
203
|
+
const getExistingImageData = (image: ImageBuildSource | ImageBuildDestination, repoIds: Set<string>) => {
|
|
204
|
+
if (repoIds.has(image.repository)) {
|
|
205
|
+
return image;
|
|
206
|
+
}
|
|
207
|
+
// When copying the image build, drop the reference to the repository if it doesn't exist anymore
|
|
208
|
+
return {
|
|
209
|
+
...image,
|
|
210
|
+
repository: '',
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export const getInitialValues = (
|
|
215
|
+
imageBuild: ImageBuildWithExports | undefined,
|
|
216
|
+
repoIds: Set<string>,
|
|
217
|
+
): ImageBuildFormValues => {
|
|
88
218
|
if (imageBuild) {
|
|
89
219
|
const exportFormats = imageBuild.imageExports
|
|
90
220
|
.filter((ie): ie is ImageExport => ie !== undefined)
|
|
91
221
|
.map((imageExport) => imageExport.spec.format);
|
|
92
222
|
const userConfig = imageBuild.spec.userConfiguration;
|
|
93
|
-
const userConfiguration = userConfig
|
|
94
|
-
? {
|
|
95
|
-
...userConfig,
|
|
96
|
-
enabled: !!(userConfig.username || userConfig.publickey),
|
|
97
|
-
}
|
|
98
|
-
: {
|
|
99
|
-
username: '',
|
|
100
|
-
publickey: '',
|
|
101
|
-
enabled: false,
|
|
102
|
-
};
|
|
103
223
|
return {
|
|
104
|
-
|
|
105
|
-
|
|
224
|
+
// Since we're always creating new imageBuilds, we don't copy the current name
|
|
225
|
+
buildName: '',
|
|
226
|
+
source: getExistingImageData(imageBuild.spec.source, repoIds),
|
|
227
|
+
destination: getExistingImageData(imageBuild.spec.destination, repoIds),
|
|
106
228
|
bindingType: imageBuild.spec.binding.type as BindingType,
|
|
107
229
|
exportFormats: exportFormats || [],
|
|
108
|
-
|
|
230
|
+
remoteAccessEnabled: !!(userConfig?.username || userConfig?.publickey),
|
|
231
|
+
userConfiguration: userConfig || { username: '', publickey: '' },
|
|
109
232
|
};
|
|
110
233
|
}
|
|
111
234
|
|
|
112
235
|
return {
|
|
236
|
+
buildName: '',
|
|
113
237
|
source: {
|
|
114
238
|
repository: '',
|
|
115
239
|
imageName: '',
|
|
@@ -122,10 +246,10 @@ export const getInitialValues = (imageBuild?: ImageBuildWithExports): ImageBuild
|
|
|
122
246
|
},
|
|
123
247
|
bindingType: BindingType.BindingTypeEarly,
|
|
124
248
|
exportFormats: [],
|
|
249
|
+
remoteAccessEnabled: false,
|
|
125
250
|
userConfiguration: {
|
|
126
251
|
username: '',
|
|
127
252
|
publickey: '',
|
|
128
|
-
enabled: false,
|
|
129
253
|
},
|
|
130
254
|
};
|
|
131
255
|
};
|
|
@@ -136,13 +260,13 @@ const getHash = () =>
|
|
|
136
260
|
.toString(16)
|
|
137
261
|
.padStart(6, '0');
|
|
138
262
|
|
|
139
|
-
const generateBuildName = () => `imagebuild-${getHash()}`;
|
|
140
263
|
const generateExportName = (imageBuildName: string, format: ExportFormatType) => {
|
|
141
|
-
|
|
264
|
+
const formatKey = format === ExportFormatType.ExportFormatTypeQCOW2DiskContainer ? 'qcow2-disk' : format;
|
|
265
|
+
return `${imageBuildName}-${formatKey}-${getHash()}`;
|
|
142
266
|
};
|
|
143
267
|
|
|
144
268
|
export const getImageBuildResource = (values: ImageBuildFormValues): ImageBuild => {
|
|
145
|
-
const name =
|
|
269
|
+
const name = values.buildName;
|
|
146
270
|
const spec: ImageBuild['spec'] = {
|
|
147
271
|
source: values.source,
|
|
148
272
|
destination: values.destination,
|
|
@@ -152,9 +276,9 @@ export const getImageBuildResource = (values: ImageBuildFormValues): ImageBuild
|
|
|
152
276
|
};
|
|
153
277
|
|
|
154
278
|
// Allow the user to uncheck the toggle without having cleared the fields
|
|
155
|
-
const username = values.userConfiguration
|
|
156
|
-
const publickey = values.userConfiguration
|
|
157
|
-
if (values.
|
|
279
|
+
const username = values.userConfiguration.username || '';
|
|
280
|
+
const publickey = values.userConfiguration.publickey || '';
|
|
281
|
+
if (values.remoteAccessEnabled && username && publickey) {
|
|
158
282
|
spec.userConfiguration = {
|
|
159
283
|
username,
|
|
160
284
|
publickey,
|
|
@@ -162,7 +286,7 @@ export const getImageBuildResource = (values: ImageBuildFormValues): ImageBuild
|
|
|
162
286
|
}
|
|
163
287
|
|
|
164
288
|
return {
|
|
165
|
-
apiVersion:
|
|
289
|
+
apiVersion: ApiVersion.ApiVersionV1alpha1,
|
|
166
290
|
kind: ResourceKind.IMAGE_BUILD,
|
|
167
291
|
metadata: {
|
|
168
292
|
name,
|
|
@@ -175,7 +299,7 @@ export const getImageExportResource = (imageBuildName: string, format: ExportFor
|
|
|
175
299
|
const exportName = generateExportName(imageBuildName, format);
|
|
176
300
|
|
|
177
301
|
return {
|
|
178
|
-
apiVersion:
|
|
302
|
+
apiVersion: ApiVersion.ApiVersionV1alpha1,
|
|
179
303
|
kind: ResourceKind.IMAGE_EXPORT,
|
|
180
304
|
metadata: {
|
|
181
305
|
name: exportName,
|
|
@@ -60,7 +60,7 @@ const ImageBuildDetailsPageContent = () => {
|
|
|
60
60
|
resourceTypeLabel={t('Image builds')}
|
|
61
61
|
nav={
|
|
62
62
|
<TabsNav aria-label="Image build details tabs" tabKeys={tabKeys}>
|
|
63
|
-
<Tab eventKey="details" title={t('
|
|
63
|
+
<Tab eventKey="details" title={t('Base image')} />
|
|
64
64
|
<Tab eventKey="exports" title={t('Export images')} />
|
|
65
65
|
<Tab eventKey="yaml" title={t('YAML')} />
|
|
66
66
|
{canViewLogs && <Tab eventKey="logs" title={t('Logs')} />}
|