@flightctl/ui-components 1.1.0-rc1 → 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/src/components/Repository/CreateRepository/CreateRepositoryForm.css +5 -1
- package/dist/types/imagebuilder/index.d.ts +2 -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 +28 -0
- package/dist/types/imagebuilder/models/Status.d.ts.map +1 -0
- package/dist/types/imagebuilder/models/Status.js +3 -0
- package/dist/types/imagebuilder/models/Status.js.map +1 -0
- package/dist/types/index.d.ts +8 -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/ApplicationProviderBase.d.ts +12 -0
- package/dist/types/models/ApplicationProviderBase.d.ts.map +1 -0
- package/dist/types/models/ApplicationProviderBase.js +3 -0
- package/dist/types/models/ApplicationProviderBase.js.map +1 -0
- package/dist/types/models/ApplicationProviderSpec.d.ts +5 -15
- package/dist/types/models/ApplicationProviderSpec.d.ts.map +1 -1
- package/dist/types/models/ApplicationUser.d.ts +7 -0
- package/dist/types/models/ApplicationUser.d.ts.map +1 -0
- package/dist/types/models/ApplicationUser.js +3 -0
- package/dist/types/models/ApplicationUser.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/ComposeApplication.d.ts +7 -0
- package/dist/types/models/ComposeApplication.d.ts.map +1 -0
- package/dist/types/models/ComposeApplication.js +3 -0
- package/dist/types/models/ComposeApplication.js.map +1 -0
- package/dist/types/models/ContainerApplication.d.ts +18 -0
- package/dist/types/models/ContainerApplication.d.ts.map +1 -0
- package/dist/types/models/ContainerApplication.js +3 -0
- package/dist/types/models/ContainerApplication.js.map +1 -0
- package/dist/types/models/ContainerApplicationProperties.d.ts +13 -0
- package/dist/types/models/ContainerApplicationProperties.d.ts.map +1 -0
- package/dist/types/models/ContainerApplicationProperties.js +3 -0
- package/dist/types/models/ContainerApplicationProperties.js.map +1 -0
- 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/HelmApplication.d.ts +20 -0
- package/dist/types/models/HelmApplication.d.ts.map +1 -0
- package/dist/types/models/HelmApplication.js +3 -0
- package/dist/types/models/HelmApplication.js.map +1 -0
- package/dist/types/models/ImageApplicationProviderSpec.d.ts +2 -22
- package/dist/types/models/ImageApplicationProviderSpec.d.ts.map +1 -1
- package/dist/types/models/InlineApplicationProviderSpec.d.ts +2 -3
- package/dist/types/models/InlineApplicationProviderSpec.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/QuadletApplication.d.ts +8 -0
- package/dist/types/models/QuadletApplication.d.ts.map +1 -0
- package/dist/types/models/QuadletApplication.js +3 -0
- package/dist/types/models/QuadletApplication.js.map +1 -0
- 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/DetailsPage/Tables/ApplicationsTable.js +1 -1
- package/dist/ui-components/src/components/DetailsPage/Tables/ApplicationsTable.js.map +1 -1
- package/dist/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage.js +5 -4
- package/dist/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage.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 +3 -3
- package/dist/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.js +310 -363
- package/dist/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationContainerForm.d.ts +1 -3
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationContainerForm.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationContainerForm.js +18 -19
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationContainerForm.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationHelmForm.d.ts +1 -3
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationHelmForm.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationHelmForm.js +5 -4
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationHelmForm.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationImageForm.d.ts +1 -3
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationImageForm.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationImageForm.js +2 -2
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationImageForm.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationInlineForm.d.ts +3 -3
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationInlineForm.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationInlineForm.js +20 -23
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationInlineForm.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationIntegritySettings.js +3 -3
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationIntegritySettings.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.js +25 -45
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationVariablesForm.d.ts +8 -0
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationVariablesForm.d.ts.map +1 -0
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationVariablesForm.js +37 -0
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationVariablesForm.js.map +1 -0
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationVolumeForm.d.ts +1 -3
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationVolumeForm.d.ts.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationVolumeForm.js +5 -8
- package/dist/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationVolumeForm.js.map +1 -1
- package/dist/ui-components/src/components/Device/EditDeviceWizard/utils.d.ts +18 -18
- 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 +3 -3
- package/dist/ui-components/src/components/Fleet/CreateFleet/utils.js.map +1 -1
- package/dist/ui-components/src/components/Fleet/ImportFleetWizard/steps/RepositoryStep.d.ts.map +1 -1
- package/dist/ui-components/src/components/Fleet/ImportFleetWizard/steps/RepositoryStep.js +3 -1
- package/dist/ui-components/src/components/Fleet/ImportFleetWizard/steps/RepositoryStep.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/CancelImageBuildModal/CancelImageBuildModal.d.ts +7 -0
- package/dist/ui-components/src/components/ImageBuilds/CancelImageBuildModal/CancelImageBuildModal.d.ts.map +1 -0
- package/dist/ui-components/src/components/ImageBuilds/CancelImageBuildModal/CancelImageBuildModal.js +40 -0
- package/dist/ui-components/src/components/ImageBuilds/CancelImageBuildModal/CancelImageBuildModal.js.map +1 -0
- package/dist/ui-components/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.d.ts +8 -0
- package/dist/ui-components/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.d.ts.map +1 -0
- package/dist/ui-components/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.js +37 -0
- package/dist/ui-components/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.js.map +1 -0
- 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.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildDetails/ImageBuildDetailsPage.js +23 -12
- 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 +115 -39
- 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/ImageBuildRow.d.ts +5 -2
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildRow.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildRow.js +22 -12
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildRow.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildsPage.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildsPage.js +17 -8
- package/dist/ui-components/src/components/ImageBuilds/ImageBuildsPage.js.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageExportCards.d.ts +10 -9
- package/dist/ui-components/src/components/ImageBuilds/ImageExportCards.d.ts.map +1 -1
- package/dist/ui-components/src/components/ImageBuilds/ImageExportCards.js +122 -26
- 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 +10 -4
- 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 +25 -18
- package/dist/ui-components/src/components/form/validations.d.ts.map +1 -1
- package/dist/ui-components/src/components/form/validations.js +79 -33
- 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 +5 -6
- package/dist/ui-components/src/constants.d.ts.map +1 -1
- package/dist/ui-components/src/constants.js +19 -11
- 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/types/deviceSpec.d.ts +44 -76
- package/dist/ui-components/src/types/deviceSpec.d.ts.map +1 -1
- package/dist/ui-components/src/types/deviceSpec.js +13 -26
- package/dist/ui-components/src/types/deviceSpec.js.map +1 -1
- package/dist/ui-components/src/types/extraTypes.d.ts +1 -7
- package/dist/ui-components/src/types/extraTypes.d.ts.map +1 -1
- package/dist/ui-components/src/types/extraTypes.js.map +1 -1
- package/dist/ui-components/src/types/rbac.d.ts +7 -1
- package/dist/ui-components/src/types/rbac.d.ts.map +1 -1
- package/dist/ui-components/src/types/rbac.js +6 -0
- package/dist/ui-components/src/types/rbac.js.map +1 -1
- package/dist/ui-components/src/utils/imageBuilds.d.ts +1 -0
- package/dist/ui-components/src/utils/imageBuilds.d.ts.map +1 -1
- package/dist/ui-components/src/utils/imageBuilds.js +20 -29
- 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/DetailsPage/Tables/ApplicationsTable.tsx +2 -2
- package/src/components/Device/DeviceDetails/DeviceDetailsPage.tsx +10 -4
- package/src/components/Device/DeviceDetails/TerminalTab.tsx +9 -1
- package/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts +361 -425
- package/src/components/Device/EditDeviceWizard/steps/ApplicationContainerForm.tsx +19 -29
- package/src/components/Device/EditDeviceWizard/steps/ApplicationHelmForm.tsx +5 -13
- package/src/components/Device/EditDeviceWizard/steps/ApplicationImageForm.tsx +2 -16
- package/src/components/Device/EditDeviceWizard/steps/ApplicationInlineForm.tsx +8 -7
- package/src/components/Device/EditDeviceWizard/steps/ApplicationIntegritySettings.tsx +5 -5
- package/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx +29 -101
- package/src/components/Device/EditDeviceWizard/steps/ApplicationVariablesForm.tsx +87 -0
- package/src/components/Device/EditDeviceWizard/steps/ApplicationVolumeForm.tsx +5 -10
- package/src/components/ErrorAlert/ErrorAlert.tsx +13 -3
- package/src/components/Fleet/CreateFleet/utils.ts +4 -5
- package/src/components/Fleet/ImportFleetWizard/steps/RepositoryStep.tsx +11 -8
- package/src/components/ImageBuilds/CancelImageBuildModal/CancelImageBuildModal.tsx +81 -0
- package/src/components/ImageBuilds/ConfirmImageExportModal/ConfirmImageExportModal.tsx +61 -0
- 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 +36 -17
- package/src/components/ImageBuilds/ImageBuildDetails/ImageBuildExportsGallery.tsx +131 -44
- package/src/components/ImageBuilds/ImageBuildDetails/ImageBuildLogsTab.tsx +22 -26
- package/src/components/ImageBuilds/ImageBuildRow.tsx +41 -20
- package/src/components/ImageBuilds/ImageBuildsPage.tsx +34 -15
- package/src/components/ImageBuilds/ImageExportCards.tsx +198 -80
- package/src/components/Repository/CreateRepository/CreateRepositoryForm.css +5 -1
- package/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx +14 -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 +156 -106
- package/src/components/modals/CreateRepositoryModal/CreateRepositoryModal.tsx +3 -1
- package/src/constants.ts +19 -6
- package/src/hooks/useWebSocket.ts +25 -3
- package/src/types/deviceSpec.ts +68 -108
- package/src/types/extraTypes.ts +2 -12
- package/src/types/rbac.ts +6 -0
- package/src/utils/imageBuilds.ts +22 -32
- package/src/utils/search.ts +2 -2
|
@@ -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,
|
|
@@ -8,7 +8,7 @@ import { useTranslation } from '../../../hooks/useTranslation';
|
|
|
8
8
|
import { ROUTE, useNavigate } from '../../../hooks/useNavigate';
|
|
9
9
|
import { usePermissionsContext } from '../../common/PermissionsContext';
|
|
10
10
|
import { useAppContext } from '../../../hooks/useAppContext';
|
|
11
|
-
import { getImageBuildStatusReason } from '../../../utils/imageBuilds';
|
|
11
|
+
import { getImageBuildStatusReason, isImageBuildCancelable } from '../../../utils/imageBuilds';
|
|
12
12
|
import DetailsPage from '../../DetailsPage/DetailsPage';
|
|
13
13
|
import DetailsPageActions from '../../DetailsPage/DetailsPageActions';
|
|
14
14
|
import DeleteImageBuildModal from '../DeleteImageBuildModal/DeleteImageBuildModal';
|
|
@@ -19,10 +19,14 @@ import ImageBuildYaml from './ImageBuildYaml';
|
|
|
19
19
|
import ImageBuildDetailsTab from './ImageBuildDetailsTab';
|
|
20
20
|
import ImageBuildExportsGallery from './ImageBuildExportsGallery';
|
|
21
21
|
import ImageBuildLogsTab from './ImageBuildLogsTab';
|
|
22
|
+
import CancelImageBuildModal from '../CancelImageBuildModal/CancelImageBuildModal';
|
|
22
23
|
|
|
23
24
|
const imageBuildDetailsPermissions = [
|
|
24
25
|
{ kind: RESOURCE.IMAGE_BUILD, verb: VERB.CREATE },
|
|
26
|
+
{ kind: RESOURCE.IMAGE_BUILD_CANCEL, verb: VERB.CREATE },
|
|
25
27
|
{ kind: RESOURCE.IMAGE_BUILD, verb: VERB.DELETE },
|
|
28
|
+
// Users that can view logs for imagebuilds also can view logs for imageexports
|
|
29
|
+
{ kind: RESOURCE.IMAGE_BUILD_LOG, verb: VERB.GET },
|
|
26
30
|
];
|
|
27
31
|
|
|
28
32
|
const ImageBuildDetailsPageContent = () => {
|
|
@@ -34,10 +38,17 @@ const ImageBuildDetailsPageContent = () => {
|
|
|
34
38
|
|
|
35
39
|
const { imageBuildId } = useParams() as { imageBuildId: string };
|
|
36
40
|
const [imageBuild, isLoading, error, refetch] = useImageBuild(imageBuildId);
|
|
37
|
-
const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState<boolean>();
|
|
38
41
|
const { checkPermissions } = usePermissionsContext();
|
|
39
|
-
const [canCreate, canDelete] = checkPermissions(imageBuildDetailsPermissions);
|
|
40
42
|
const buildReason = imageBuild ? getImageBuildStatusReason(imageBuild) : undefined;
|
|
43
|
+
const [canCreate, hasCancelPermission, canDelete, canViewLogs] = checkPermissions(imageBuildDetailsPermissions);
|
|
44
|
+
const canCancel = hasCancelPermission && buildReason && isImageBuildCancelable(buildReason);
|
|
45
|
+
|
|
46
|
+
const tabKeys = React.useMemo(
|
|
47
|
+
() => (canViewLogs ? ['details', 'exports', 'yaml', 'logs'] : ['details', 'exports', 'yaml']),
|
|
48
|
+
[canViewLogs],
|
|
49
|
+
);
|
|
50
|
+
const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState<boolean>();
|
|
51
|
+
const [isCancelModalOpen, setIsCancelModalOpen] = React.useState<boolean>();
|
|
41
52
|
|
|
42
53
|
return (
|
|
43
54
|
<DetailsPage
|
|
@@ -48,11 +59,11 @@ const ImageBuildDetailsPageContent = () => {
|
|
|
48
59
|
resourceType="Image builds"
|
|
49
60
|
resourceTypeLabel={t('Image builds')}
|
|
50
61
|
nav={
|
|
51
|
-
<TabsNav aria-label="Image build details tabs" tabKeys={
|
|
52
|
-
<Tab eventKey="details" title={t('
|
|
62
|
+
<TabsNav aria-label="Image build details tabs" tabKeys={tabKeys}>
|
|
63
|
+
<Tab eventKey="details" title={t('Base image')} />
|
|
53
64
|
<Tab eventKey="exports" title={t('Export images')} />
|
|
54
65
|
<Tab eventKey="yaml" title={t('YAML')} />
|
|
55
|
-
<Tab eventKey="logs" title={t('Logs')} />
|
|
66
|
+
{canViewLogs && <Tab eventKey="logs" title={t('Logs')} />}
|
|
56
67
|
</TabsNav>
|
|
57
68
|
}
|
|
58
69
|
actions={
|
|
@@ -66,7 +77,10 @@ const ImageBuildDetailsPageContent = () => {
|
|
|
66
77
|
: t('Duplicate')}
|
|
67
78
|
</DropdownItem>
|
|
68
79
|
)}
|
|
69
|
-
{
|
|
80
|
+
{canCancel && (
|
|
81
|
+
<DropdownItem onClick={() => setIsCancelModalOpen(true)}>{t('Cancel image build')}</DropdownItem>
|
|
82
|
+
)}
|
|
83
|
+
{canDelete && !canCancel && (
|
|
70
84
|
<DropdownItem onClick={() => setIsDeleteModalOpen(true)}>{t('Delete image build')}</DropdownItem>
|
|
71
85
|
)}
|
|
72
86
|
</DropdownList>
|
|
@@ -81,7 +95,7 @@ const ImageBuildDetailsPageContent = () => {
|
|
|
81
95
|
<Route path="details" element={<ImageBuildDetailsTab imageBuild={imageBuild} />} />
|
|
82
96
|
<Route path="exports" element={<ImageBuildExportsGallery imageBuild={imageBuild} refetch={refetch} />} />
|
|
83
97
|
<Route path="yaml" element={<ImageBuildYaml imageBuild={imageBuild} refetch={refetch} />} />
|
|
84
|
-
<Route path="logs" element={<ImageBuildLogsTab imageBuild={imageBuild} />} />
|
|
98
|
+
{canViewLogs && <Route path="logs" element={<ImageBuildLogsTab imageBuild={imageBuild} />} />}
|
|
85
99
|
</Routes>
|
|
86
100
|
{isDeleteModalOpen && (
|
|
87
101
|
<DeleteImageBuildModal
|
|
@@ -94,26 +108,31 @@ const ImageBuildDetailsPageContent = () => {
|
|
|
94
108
|
}}
|
|
95
109
|
/>
|
|
96
110
|
)}
|
|
111
|
+
{isCancelModalOpen && (
|
|
112
|
+
<CancelImageBuildModal
|
|
113
|
+
imageBuildId={imageBuildId}
|
|
114
|
+
onClose={(confirmed) => {
|
|
115
|
+
setIsCancelModalOpen(false);
|
|
116
|
+
if (confirmed) {
|
|
117
|
+
refetch();
|
|
118
|
+
}
|
|
119
|
+
}}
|
|
120
|
+
/>
|
|
121
|
+
)}
|
|
97
122
|
</>
|
|
98
123
|
)}
|
|
99
124
|
</DetailsPage>
|
|
100
125
|
);
|
|
101
126
|
};
|
|
102
127
|
|
|
103
|
-
const ImageBuildDetailsPage = () => {
|
|
104
|
-
return (
|
|
105
|
-
<OciRegistriesContextProvider>
|
|
106
|
-
<ImageBuildDetailsPageContent />
|
|
107
|
-
</OciRegistriesContextProvider>
|
|
108
|
-
);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
128
|
const ImageBuildDetailsWithPermissions = () => {
|
|
112
129
|
const { checkPermissions, loading } = usePermissionsContext();
|
|
113
130
|
const [allowed] = checkPermissions([{ kind: RESOURCE.IMAGE_BUILD, verb: VERB.GET }]);
|
|
114
131
|
return (
|
|
115
132
|
<PageWithPermissions allowed={allowed} loading={loading}>
|
|
116
|
-
<
|
|
133
|
+
<OciRegistriesContextProvider>
|
|
134
|
+
<ImageBuildDetailsPageContent />
|
|
135
|
+
</OciRegistriesContextProvider>
|
|
117
136
|
</PageWithPermissions>
|
|
118
137
|
);
|
|
119
138
|
};
|
|
@@ -2,15 +2,20 @@ import * as React from 'react';
|
|
|
2
2
|
import { Gallery } from '@patternfly/react-core';
|
|
3
3
|
import { saveAs } from 'file-saver';
|
|
4
4
|
|
|
5
|
-
import { ExportFormatType, ImageExport } from '@flightctl/types/imagebuilder';
|
|
5
|
+
import { ExportFormatType, ImageBuildConditionReason, ImageExport } from '@flightctl/types/imagebuilder';
|
|
6
6
|
import { ImageBuildWithExports } from '../../../types/extraTypes';
|
|
7
|
+
import { getImageBuildStatusReason } from '../../../utils/imageBuilds';
|
|
8
|
+
import { RESOURCE, VERB } from '../../../types/rbac';
|
|
7
9
|
import { useFetch } from '../../../hooks/useFetch';
|
|
10
|
+
import { usePermissionsContext } from '../../common/PermissionsContext';
|
|
8
11
|
import { getErrorMessage } from '../../../utils/error';
|
|
9
12
|
import { getImageExportResource } from '../CreateImageBuildWizard/utils';
|
|
10
|
-
import { ViewImageBuildExportCard } from '../ImageExportCards';
|
|
13
|
+
import { ImageExportAction, ViewImageBuildExportCard } from '../ImageExportCards';
|
|
11
14
|
import { useOciRegistriesContext } from '../OciRegistriesContext';
|
|
12
15
|
import { showSpinnerBriefly } from '../../../utils/time';
|
|
13
16
|
import { getAllExportFormats, getExportDownloadResult, getImageReference } from '../../../utils/imageBuilds';
|
|
17
|
+
import { ROUTE, useNavigate } from '../../../hooks/useNavigate';
|
|
18
|
+
import { IMAGE_EXPORT_ID_PARAM } from './ImageBuildLogsTab';
|
|
14
19
|
|
|
15
20
|
type ImageBuildExportsGalleryProps = {
|
|
16
21
|
imageBuild: ImageBuildWithExports;
|
|
@@ -21,6 +26,8 @@ const REFRESH_IMAGE_BUILD_DELAY = 450;
|
|
|
21
26
|
// Delay to keep loading state while browser processes redirect
|
|
22
27
|
const DOWNLOAD_REDIRECT_DELAY = 1000;
|
|
23
28
|
|
|
29
|
+
const createExportAliases = ['retry', 'rebuild', 'createExport'];
|
|
30
|
+
|
|
24
31
|
const createDownloadLink = (url: string) => {
|
|
25
32
|
const link = document.createElement('a');
|
|
26
33
|
link.href = url;
|
|
@@ -30,21 +37,69 @@ const createDownloadLink = (url: string) => {
|
|
|
30
37
|
document.body.removeChild(link);
|
|
31
38
|
};
|
|
32
39
|
|
|
40
|
+
const imageBuildExportsPermissions = [
|
|
41
|
+
{ kind: RESOURCE.IMAGE_EXPORT, verb: VERB.CREATE },
|
|
42
|
+
{ kind: RESOURCE.IMAGE_EXPORT, verb: VERB.DELETE },
|
|
43
|
+
{ kind: RESOURCE.IMAGE_EXPORT_LOG, verb: VERB.GET },
|
|
44
|
+
{ kind: RESOURCE.IMAGE_EXPORT_DOWNLOAD, verb: VERB.GET },
|
|
45
|
+
{ kind: RESOURCE.IMAGE_EXPORT_CANCEL, verb: VERB.CREATE },
|
|
46
|
+
];
|
|
47
|
+
|
|
33
48
|
const ImageBuildExportsGallery = ({ imageBuild, refetch }: ImageBuildExportsGalleryProps) => {
|
|
34
|
-
const { post, proxyFetch } = useFetch();
|
|
49
|
+
const { post, proxyFetch, remove } = useFetch();
|
|
50
|
+
const { checkPermissions } = usePermissionsContext();
|
|
51
|
+
const [canCreateExport, canDelete, canViewLogs, canDownload, canCancel] =
|
|
52
|
+
checkPermissions(imageBuildExportsPermissions);
|
|
53
|
+
|
|
54
|
+
const buildReason = getImageBuildStatusReason(imageBuild);
|
|
55
|
+
|
|
56
|
+
const actionPermissions = React.useMemo(() => {
|
|
57
|
+
const actions: ImageExportAction[] = [];
|
|
58
|
+
if (
|
|
59
|
+
buildReason === ImageBuildConditionReason.ImageBuildConditionReasonFailed ||
|
|
60
|
+
buildReason === ImageBuildConditionReason.ImageBuildConditionReasonCanceled ||
|
|
61
|
+
buildReason === ImageBuildConditionReason.ImageBuildConditionReasonCanceling
|
|
62
|
+
) {
|
|
63
|
+
if (canDelete) {
|
|
64
|
+
actions.push('delete');
|
|
65
|
+
}
|
|
66
|
+
return actions;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (canCreateExport) {
|
|
70
|
+
actions.push('createExport');
|
|
71
|
+
actions.push('rebuild');
|
|
72
|
+
actions.push('retry');
|
|
73
|
+
}
|
|
74
|
+
if (canDelete) {
|
|
75
|
+
actions.push('delete');
|
|
76
|
+
}
|
|
77
|
+
if (canViewLogs) {
|
|
78
|
+
actions.push('viewLogs');
|
|
79
|
+
}
|
|
80
|
+
if (canDownload) {
|
|
81
|
+
actions.push('download');
|
|
82
|
+
}
|
|
83
|
+
if (canCancel) {
|
|
84
|
+
actions.push('cancel');
|
|
85
|
+
}
|
|
86
|
+
return actions;
|
|
87
|
+
}, [buildReason, canCreateExport, canDelete, canViewLogs, canDownload, canCancel]);
|
|
88
|
+
|
|
89
|
+
const navigate = useNavigate();
|
|
35
90
|
const [error, setError] = React.useState<{
|
|
36
91
|
format: ExportFormatType;
|
|
92
|
+
action: ImageExportAction;
|
|
37
93
|
message: string;
|
|
38
|
-
mode: 'export' | 'download';
|
|
39
94
|
}>();
|
|
40
95
|
const { ociRegistries } = useOciRegistriesContext();
|
|
41
|
-
|
|
42
|
-
const [
|
|
96
|
+
|
|
97
|
+
const [activeFormatAction, setActiveFormatAction] = React.useState<
|
|
98
|
+
{ format: ExportFormatType; action: ImageExportAction } | undefined
|
|
99
|
+
>();
|
|
43
100
|
const imageBuildId = imageBuild.metadata.name as string;
|
|
44
101
|
|
|
45
|
-
const
|
|
46
|
-
setExportingFormat(format);
|
|
47
|
-
setError(undefined);
|
|
102
|
+
const handleCreateNewExport = async (format: ExportFormatType) => {
|
|
48
103
|
try {
|
|
49
104
|
const imageExport = getImageExportResource(imageBuildId, format);
|
|
50
105
|
await post<ImageExport>('imageexports', imageExport);
|
|
@@ -54,44 +109,79 @@ const ImageBuildExportsGallery = ({ imageBuild, refetch }: ImageBuildExportsGall
|
|
|
54
109
|
} catch (error) {
|
|
55
110
|
// If process failed, it was likely very fast, so we also add the delay in this case.
|
|
56
111
|
await showSpinnerBriefly(REFRESH_IMAGE_BUILD_DELAY);
|
|
57
|
-
|
|
58
|
-
setError({ format, message: getErrorMessage(error), mode: 'export' });
|
|
59
|
-
} finally {
|
|
60
|
-
setExportingFormat(undefined);
|
|
112
|
+
throw error;
|
|
61
113
|
}
|
|
62
114
|
};
|
|
115
|
+
const handleDownload = async (ieName: string, format: ExportFormatType) => {
|
|
116
|
+
const response = await proxyFetch(`imagebuilder/api/v1/imageexports/${ieName}/download`, {
|
|
117
|
+
method: 'GET',
|
|
118
|
+
credentials: 'include',
|
|
119
|
+
});
|
|
120
|
+
const downloadResult = await getExportDownloadResult(response);
|
|
121
|
+
if (downloadResult === null) {
|
|
122
|
+
await showSpinnerBriefly(DOWNLOAD_REDIRECT_DELAY);
|
|
123
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
124
|
+
}
|
|
125
|
+
if (downloadResult.type === 'redirect') {
|
|
126
|
+
createDownloadLink(downloadResult.url);
|
|
127
|
+
await showSpinnerBriefly(DOWNLOAD_REDIRECT_DELAY);
|
|
128
|
+
} else {
|
|
129
|
+
const defaultFilename = `image-export-${ieName}.${format}`;
|
|
130
|
+
saveAs(downloadResult.blob, downloadResult.filename || defaultFilename);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const handleCancel = async (ieName: string) => {
|
|
135
|
+
await post(`imageexports/${ieName}/cancel`, {});
|
|
136
|
+
await showSpinnerBriefly(REFRESH_IMAGE_BUILD_DELAY);
|
|
137
|
+
refetch();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const handleDelete = async (ieName: string) => {
|
|
141
|
+
await remove(`imageexports/${ieName}`);
|
|
142
|
+
await showSpinnerBriefly(REFRESH_IMAGE_BUILD_DELAY);
|
|
143
|
+
refetch();
|
|
144
|
+
};
|
|
63
145
|
|
|
64
|
-
const
|
|
146
|
+
const handleCardAction = async ({ format, action }: { format: ExportFormatType; action: ImageExportAction }) => {
|
|
65
147
|
const imageExport = imageBuild.imageExports.find((ie) => ie?.spec.format === format);
|
|
66
|
-
if (!imageExport) {
|
|
148
|
+
if (!imageExport && !createExportAliases.includes(action)) {
|
|
67
149
|
return;
|
|
68
150
|
}
|
|
69
151
|
|
|
70
|
-
|
|
152
|
+
setActiveFormatAction({ format, action });
|
|
153
|
+
setError(undefined);
|
|
154
|
+
|
|
71
155
|
try {
|
|
72
|
-
const ieName = imageExport
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
156
|
+
const ieName = imageExport?.metadata.name as string;
|
|
157
|
+
switch (action) {
|
|
158
|
+
case 'createExport':
|
|
159
|
+
case 'retry':
|
|
160
|
+
case 'rebuild':
|
|
161
|
+
await handleCreateNewExport(format);
|
|
162
|
+
break;
|
|
163
|
+
case 'download':
|
|
164
|
+
await handleDownload(ieName, format);
|
|
165
|
+
break;
|
|
166
|
+
case 'cancel':
|
|
167
|
+
await handleCancel(ieName);
|
|
168
|
+
break;
|
|
169
|
+
case 'delete':
|
|
170
|
+
await handleDelete(ieName);
|
|
171
|
+
break;
|
|
172
|
+
case 'viewLogs': {
|
|
173
|
+
const searchParams = new URLSearchParams({
|
|
174
|
+
[IMAGE_EXPORT_ID_PARAM]: ieName,
|
|
175
|
+
});
|
|
176
|
+
navigate({ route: ROUTE.IMAGE_BUILD_DETAILS, postfix: `${imageBuildId}/logs?${searchParams.toString()}` });
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
90
179
|
}
|
|
91
|
-
} catch (
|
|
92
|
-
setError({ format, message: getErrorMessage(
|
|
180
|
+
} catch (error) {
|
|
181
|
+
setError({ format, message: getErrorMessage(error), action });
|
|
182
|
+
refetch();
|
|
93
183
|
} finally {
|
|
94
|
-
|
|
184
|
+
setActiveFormatAction(undefined);
|
|
95
185
|
}
|
|
96
186
|
};
|
|
97
187
|
|
|
@@ -99,26 +189,23 @@ const ImageBuildExportsGallery = ({ imageBuild, refetch }: ImageBuildExportsGall
|
|
|
99
189
|
<Gallery hasGutter minWidths={{ default: '350px' }}>
|
|
100
190
|
{getAllExportFormats().map((format) => {
|
|
101
191
|
const imageExport = imageBuild.imageExports.find((imageExport) => imageExport?.spec.format === format);
|
|
102
|
-
const isDisabled = exportingFormat && exportingFormat !== format;
|
|
103
192
|
// We can only link to the generic destination for the image build.
|
|
104
193
|
// The individual export artifacts are references to this generic output image.
|
|
105
194
|
const imageReference = getImageReference(ociRegistries, imageBuild.spec.destination);
|
|
106
195
|
|
|
107
196
|
const hasError = error?.format === format;
|
|
197
|
+
const activeAction = activeFormatAction?.format === format ? activeFormatAction.action : undefined;
|
|
108
198
|
return (
|
|
109
199
|
<ViewImageBuildExportCard
|
|
110
|
-
imageBuildId={imageBuildId}
|
|
111
200
|
key={format}
|
|
112
201
|
imageReference={imageReference}
|
|
113
202
|
format={format}
|
|
114
203
|
error={hasError ? error : null}
|
|
115
204
|
imageExport={imageExport}
|
|
116
|
-
|
|
117
|
-
isDownloading={downloadingFormat === format}
|
|
118
|
-
isDisabled={isDisabled}
|
|
205
|
+
activeAction={activeAction}
|
|
119
206
|
onDismissError={() => setError(undefined)}
|
|
120
|
-
|
|
121
|
-
|
|
207
|
+
onCardAction={handleCardAction}
|
|
208
|
+
actionPermissions={actionPermissions}
|
|
122
209
|
/>
|
|
123
210
|
);
|
|
124
211
|
})}
|