From f03dd3bb938eb2522141debd8d766f1ff8f99efc Mon Sep 17 00:00:00 2001 From: Celia Amador Date: Wed, 30 Sep 2026 13:35:25 +0200 Subject: [PATCH] EDM-5239: Delta updates - part 2 Made-with: Cursor --- libs/i18n/locales/en/translation.json | 28 ++++++---- libs/types/models/ApplicationImageDigest.ts | 6 +- libs/types/models/DeviceApplicationStatus.ts | 4 +- libs/types/models/Event.ts | 4 ++ libs/types/models/ImageDeltaHint.ts | 4 ++ libs/types/models/ImageSpec.ts | 2 +- .../models/InlineApplicationProviderSpec.ts | 5 ++ libs/types/models/ResourceUpdatedDetails.ts | 2 +- .../ApplicationDeltaStatusFields.tsx | 20 +++---- .../DeviceDeltaUpdateDetails.tsx | 36 ------------ .../DeviceDetails/SidebarDescriptionList.tsx | 56 +++++++++++++------ .../DeviceDetails/VmAppExpandedDetails.tsx | 2 +- .../WorkloadAppExpandedDetails.tsx | 2 +- .../src/components/Events/useEvents.ts | 5 ++ .../DeltaGenerationHelpContent.tsx | 20 ++++--- .../steps/UpdateStepDeltaGeneration.tsx | 6 +- .../src/hooks/useDeviceSpecSystemInfo.tsx | 3 +- 17 files changed, 113 insertions(+), 92 deletions(-) delete mode 100644 libs/ui-components/src/components/Device/DeviceDetails/DeviceDeltaUpdateDetails.tsx diff --git a/libs/i18n/locales/en/translation.json b/libs/i18n/locales/en/translation.json index 7305c99a9d..9cfc8004b2 100644 --- a/libs/i18n/locales/en/translation.json +++ b/libs/i18n/locales/en/translation.json @@ -687,9 +687,6 @@ "{{appCount}} need attention": "{{appCount}} need attention", "Custom data": "Custom data", "No value reported": "No value reported", - "Not eligible": "Not eligible", - "Device eligible for delta updates, but oci-delta not found": "Device eligible for delta updates, but oci-delta not found", - "Eligible": "Eligible", "Delta update status": "Delta update status", "Update download size": "Update download size", "Estimated download size for the current update. When a delta artifact is available, this reflects the smaller incremental download instead of the full OS image.": "Estimated download size for the current update. When a delta artifact is available, this reflects the smaller incremental download instead of the full OS image.", @@ -805,8 +802,12 @@ "System status": "System status", "Resource status": "Resource status", "System services": "System services", - "Delta eligibility": "Delta eligibility", - "Whether this device can apply incremental OCI delta updates. Requires bootc and the oci-delta tool on the device": "Whether this device can apply incremental OCI delta updates. Requires bootc and the oci-delta tool on the device", + "Eligible": "Eligible", + "Not eligible": "Not eligible", + "Delta generation": "Delta generation", + "Delta updates download only the incremental changes between versions, reducing download size for updates. To receive delta updates, a device needs a compatible bootc version and the OCI delta package installed on its OS image.": "Delta updates download only the incremental changes between versions, reducing download size for updates. To receive delta updates, a device needs a compatible bootc version and the OCI delta package installed on its OS image.", + "Bootc version": "Bootc version", + "OCI delta version": "OCI delta version", "OS mode": "OS mode", "You do not have permission to access the device terminal.": "You do not have permission to access the device terminal.", "Console type": "Console type", @@ -1178,12 +1179,18 @@ "Preparing delta updates": "Preparing delta updates", "Delta updates are being generated": "Delta updates are being generated", "Delta updates generation is complete": "Delta updates generation is complete", + "Enrollment hook succeeded": "Enrollment hook succeeded", + "Enrollment hook failed": "Enrollment hook failed", + "Enrollment hook notify failed": "Enrollment hook notify failed", + "Enrollment hook has manual override": "Enrollment hook has manual override", "Resource": "Resource", "Edit fleet": "Edit fleet", "Create fleet": "Create fleet", - "Delta updates create smaller incremental artifacts for operating system and application rollouts. Your devices download only what changed instead of full images.": "Delta updates create smaller incremental artifacts for operating system and application rollouts. Your devices download only what changed instead of full images.", - "This reduces bandwidth use across large fleets and constrained networks.": "This reduces bandwidth use across large fleets and constrained networks.", - "Deltas are stored in an OCI registry marked as a delta repository. Your administrator may have configured one at deployment. If not, mark an existing OCI registry in <1>Repositories": "Deltas are stored in an OCI registry marked as a delta repository. Your administrator may have configured one at deployment. If not, mark an existing OCI registry in <1>Repositories", + "Delta generation runs during fleet rollouts. The platform creates OCI delta artifacts between successive image digests and stores them in a writable registry so devices can pull a smaller update instead of the full artifact.": "Delta generation runs during fleet rollouts. The platform creates OCI delta artifacts between successive image digests and stores them in a writable registry so devices can pull a smaller update instead of the full artifact.", + "To apply deltas": "To apply deltas", + "Devices must report a compatible bootc version and OCI delta support.": "Devices must report a compatible bootc version and OCI delta support.", + "Enable and configure delta artifact storage in an OCI registry in <2>Repositories": "Enable and configure delta artifact storage in an OCI registry in <2>Repositories", + "If a delta artifact cannot be applied, the device falls back to the full artifact.": "If a delta artifact cannot be applied, the device falls back to the full artifact.", "Failed to determine the number of selected devices": "Failed to determine the number of selected devices", "{{ count }} devices matching the labels were selected._one": "{{ count }} device matching the labels was selected.", "{{ count }} devices matching the labels were selected._other": "{{ count }} devices matching the labels were selected.", @@ -1206,9 +1213,9 @@ "Maximum time to wait for delta generation before rollout continues without unfinished artifacts. Leave empty to use the deployment default.": "Maximum time to wait for delta generation before rollout continues without unfinished artifacts. Leave empty to use the deployment default.", "Per-job generation timeout": "Per-job generation timeout", "Maximum time allowed for each delta generation job. Jobs that exceed this deadline are cancelled. Leave empty to use the deployment default.": "Maximum time allowed for each delta generation job. Jobs that exceed this deadline are cancelled. Leave empty to use the deployment default.", - "Delta generation": "Delta generation", "Generate deltas for this fleet": "Generate deltas for this fleet", - "Generates incremental update artifacts during rollouts so your devices download only what changed for operating systems and applications.": "Generates incremental update artifacts during rollouts so your devices download only what changed for operating systems and applications.", + "When delta generation is enabled for a fleet, the platform pushes only the incremental changes between versions, reducing download size and bandwidth demand for updates. Ensure your devices and OCI registry are configured to apply deltas.": "When delta generation is enabled for a fleet, the platform pushes only the incremental changes between versions, reducing download size and bandwidth demand for updates. Ensure your devices and OCI registry are configured to apply deltas.", + "Learn how delta updates work": "Learn how delta updates work", "Delta storage not configured": "Delta storage not configured", "Delta generation is enabled, but no repository is configured for delta artifact storage. Create one before this fleet rolls out updates.": "Delta generation is enabled, but no repository is configured for delta artifact storage. Create one before this fleet rolls out updates.", "Go to Repositories": "Go to Repositories", @@ -2258,7 +2265,6 @@ "TPM vendor info": "TPM vendor info", "Architecture": "Architecture", "Distro": "Distro", - "Bootc version": "Bootc version", "Boot ID": "Boot ID", "Kernel": "Kernel", "Net interface default": "Net interface default", diff --git a/libs/types/models/ApplicationImageDigest.ts b/libs/types/models/ApplicationImageDigest.ts index 19536c11b8..c72fd4009f 100644 --- a/libs/types/models/ApplicationImageDigest.ts +++ b/libs/types/models/ApplicationImageDigest.ts @@ -3,7 +3,7 @@ /* tslint:disable */ /* eslint-disable */ /** - * An image reference and its content digest in local storage. + * An image reference and, when known, its content digest in local storage. */ export type ApplicationImageDigest = { /** @@ -11,8 +11,8 @@ export type ApplicationImageDigest = { */ image: string; /** - * Content digest of the image in local storage (e.g. sha256:abc...). + * Content digest of the image in local storage (e.g. sha256:abc...). Omitted when the local digest is unknown. */ - digest: string; + digest?: string; }; diff --git a/libs/types/models/DeviceApplicationStatus.ts b/libs/types/models/DeviceApplicationStatus.ts index 661607a7f5..2f39fb2daa 100644 --- a/libs/types/models/DeviceApplicationStatus.ts +++ b/libs/types/models/DeviceApplicationStatus.ts @@ -36,11 +36,11 @@ export type DeviceApplicationStatus = { volumes?: Array; lastDelta?: DeviceDeltaApplyStatus; /** - * Image references this application uses and their digests in local storage. image is the ref from the current rendered spec (tag or digest). digest is what is in storage. When image is already a digest ref it matches digest. + * Image references this application uses and their known content digests in local storage. image is the ref from the current rendered spec (tag or digest). digest is omitted when the local digest is unknown. When image is already a digest ref, digest matches the ref's digest. */ imageDigests?: Array; /** - * Expected total download size for this application update in IEC units (e.g. "245.3 MiB", "1 GiB"). Computed as the sum of all image pair sizes (parent + nested + volumes), using delta size when available or full manifest size otherwise. Absent when no size information is available. + * Expected total download size for this application update in IEC units (e.g. "245.3 MiB", "1 GiB"). Computed as the sum of all required image pair sizes (parent + nested + volumes), using delta payload size when available or full image payload size otherwise. Absent when no image download is required or any required image size is unknown. */ size?: string; }; diff --git a/libs/types/models/Event.ts b/libs/types/models/Event.ts index 6143825da4..675ea1ea0a 100644 --- a/libs/types/models/Event.ts +++ b/libs/types/models/Event.ts @@ -111,6 +111,10 @@ export namespace Event { PREPARE_DELTAS = 'PrepareDeltas', DELTA_GENERATION_PROGRESS = 'DeltaGenerationProgress', DELTA_GENERATION_COMPLETED = 'DeltaGenerationCompleted', + ENROLLMENT_HOOK_SUCCEEDED = 'EnrollmentHookSucceeded', + ENROLLMENT_HOOK_FAILED = 'EnrollmentHookFailed', + ENROLLMENT_HOOK_NOTIFY_FAILED = 'EnrollmentHookNotifyFailed', + ENROLLMENT_HOOK_MANUAL_OVERRIDE = 'EnrollmentHookManualOverride', } /** * The type of the event. One of Normal, Warning. diff --git a/libs/types/models/ImageDeltaHint.ts b/libs/types/models/ImageDeltaHint.ts index c657f566da..5c28a06217 100644 --- a/libs/types/models/ImageDeltaHint.ts +++ b/libs/types/models/ImageDeltaHint.ts @@ -6,6 +6,10 @@ * A delta hint for a nested image within an application. */ export type ImageDeltaHint = { + /** + * The target image reference this delta applies to. + */ + targetImage: string; /** * The content digest of the target image. */ diff --git a/libs/types/models/ImageSpec.ts b/libs/types/models/ImageSpec.ts index 99266c6eda..fe7157564c 100644 --- a/libs/types/models/ImageSpec.ts +++ b/libs/types/models/ImageSpec.ts @@ -16,7 +16,7 @@ export type ImageSpec = { */ deltaImage?: string; /** - * Optional hints for nested images within this application (e.g. service images in a compose app, OCI volume images). Each entry maps a target digest to its delta artifact reference. + * Optional hints for nested images within this application (e.g. service images in a compose app, OCI volume images). Each entry identifies a target image reference and digest and names its delta artifact reference. */ deltaImages?: Array; }; diff --git a/libs/types/models/InlineApplicationProviderSpec.ts b/libs/types/models/InlineApplicationProviderSpec.ts index 63b58da0be..b3cdd7dc2b 100644 --- a/libs/types/models/InlineApplicationProviderSpec.ts +++ b/libs/types/models/InlineApplicationProviderSpec.ts @@ -3,10 +3,15 @@ /* tslint:disable */ /* eslint-disable */ import type { ApplicationContent } from './ApplicationContent'; +import type { ImageDeltaHint } from './ImageDeltaHint'; export type InlineApplicationProviderSpec = { /** * A list of application content. */ inline: Array; + /** + * Optional hints for nested OCI images referenced by this inline application. Each entry identifies a target image reference and digest and names its delta artifact. + */ + deltaImages?: Array; }; diff --git a/libs/types/models/ResourceUpdatedDetails.ts b/libs/types/models/ResourceUpdatedDetails.ts index 133d6302c5..c15c520e8a 100644 --- a/libs/types/models/ResourceUpdatedDetails.ts +++ b/libs/types/models/ResourceUpdatedDetails.ts @@ -10,7 +10,7 @@ export type ResourceUpdatedDetails = { /** * List of fields that were updated in the resource. */ - updatedFields: Array<'owner' | 'labels' | 'spec' | 'spec.selector' | 'spec.template'>; + updatedFields: Array<'owner' | 'labels' | 'spec' | 'spec.selector' | 'spec.template' | 'status.conditions.EnrollmentHooks'>; /** * The previous owner (if applicable). */ diff --git a/libs/ui-components/src/components/Application/ApplicationDeltaStatusFields.tsx b/libs/ui-components/src/components/Application/ApplicationDeltaStatusFields.tsx index f23a4f24a7..8cd3d2e653 100644 --- a/libs/ui-components/src/components/Application/ApplicationDeltaStatusFields.tsx +++ b/libs/ui-components/src/components/Application/ApplicationDeltaStatusFields.tsx @@ -1,23 +1,23 @@ import * as React from 'react'; import { DescriptionListDescription, DescriptionListGroup, DescriptionListTerm } from '@patternfly/react-core'; -import type { DeviceDeltaApplyStatus } from '@flightctl/types'; +import type { DeviceApplicationStatus } from '@flightctl/types'; import { useTranslation } from '../../hooks/useTranslation'; import LabelWithHelperText from '../common/WithHelperText'; -const ApplicationDeltaStatusFields = ({ lastDelta }: { lastDelta?: DeviceDeltaApplyStatus }) => { +const ApplicationDeltaStatusFields = ({ appStatus }: { appStatus: DeviceApplicationStatus }) => { const { t } = useTranslation(); - const lastDeltaSize = lastDelta?.size; - const lastReason = lastDelta?.fallbackReason; - - if (!lastDeltaSize && !lastReason) { + // Applications use the "status.applications.size" field + const deltaSize = appStatus.size; + const fallbackReason = appStatus.lastDelta?.fallbackReason; + if (!deltaSize && !fallbackReason) { return null; } return ( <> - {lastDeltaSize && ( + {deltaSize && ( - {lastDeltaSize} + {deltaSize} )} - {lastReason && ( + {fallbackReason && ( - {lastReason} + {fallbackReason} )} diff --git a/libs/ui-components/src/components/Device/DeviceDetails/DeviceDeltaUpdateDetails.tsx b/libs/ui-components/src/components/Device/DeviceDetails/DeviceDeltaUpdateDetails.tsx deleted file mode 100644 index 2bef1b4d51..0000000000 --- a/libs/ui-components/src/components/Device/DeviceDetails/DeviceDeltaUpdateDetails.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from 'react'; - -import { type DeviceStatus } from '@flightctl/types'; -import { useTranslation } from '../../../hooks/useTranslation'; -import { StatusDisplayContent } from '../../Status/StatusDisplay'; - -const getBooleanValue = (value: string | boolean | undefined | null): boolean => { - if (value === undefined || value === null) { - return false; - } - if (typeof value === 'boolean') { - return value; - } - return value === 'true'; -}; - -const DeviceDeltaUpdateDetails = ({ deviceStatus }: { deviceStatus?: DeviceStatus }) => { - const { t } = useTranslation(); - - const deltaEligible = getBooleanValue(deviceStatus?.systemInfo?.deltaEligible); - const ociDeltaVersion = deviceStatus?.systemInfo?.ociDeltaVersion; - - if (deltaEligible !== true) { - return ; - } - - if (!ociDeltaVersion) { - return ( - - ); - } - - return ; -}; - -export default DeviceDeltaUpdateDetails; diff --git a/libs/ui-components/src/components/Device/DeviceDetails/SidebarDescriptionList.tsx b/libs/ui-components/src/components/Device/DeviceDetails/SidebarDescriptionList.tsx index fd0c19751d..3fb1302365 100644 --- a/libs/ui-components/src/components/Device/DeviceDetails/SidebarDescriptionList.tsx +++ b/libs/ui-components/src/components/Device/DeviceDetails/SidebarDescriptionList.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import type { TFunction } from 'i18next'; import { DescriptionList, DescriptionListDescription, @@ -12,24 +13,47 @@ import { useTranslation } from '../../../hooks/useTranslation'; import { getDeviceCapability } from '../../../utils/capabilities'; import { OsModeLabel } from '../../common/OsModeContent'; import LabelWithHelperText from '../../common/WithHelperText'; -import DeviceDeltaUpdateDetails from './DeviceDeltaUpdateDetails'; -const DeltaDescriptionGroupItem = ({ deviceStatus }: { deviceStatus: DeviceStatus | undefined }) => { +const getEligibilityStatus = (t: TFunction, isDeltaEligible?: string) => { + if (isDeltaEligible === undefined) { + return t('Unknown'); + } + return Boolean(isDeltaEligible) ? t('Eligible') : t('Not eligible'); +}; + +const DeltaGenerationDescriptionGroups = ({ deviceStatus }: { deviceStatus: DeviceStatus | undefined }) => { const { t } = useTranslation(); + + const { bootcVersion, ociDeltaVersion, deltaEligible } = deviceStatus?.systemInfo || {}; + return ( - - - - - - - - + <> + + + + + + {getEligibilityStatus(t, deltaEligible)} + + + {bootcVersion && ( + + {t('Bootc version')} + {bootcVersion} + + )} + {ociDeltaVersion && ( + + {t('OCI delta version')} + {ociDeltaVersion} + + )} + ); }; @@ -46,7 +70,7 @@ export const CapabilitiesFieldsList = ({ deviceStatus }: { deviceStatus: DeviceS - + ); }; diff --git a/libs/ui-components/src/components/Device/DeviceDetails/VmAppExpandedDetails.tsx b/libs/ui-components/src/components/Device/DeviceDetails/VmAppExpandedDetails.tsx index d9e7ebca47..59e22accf1 100644 --- a/libs/ui-components/src/components/Device/DeviceDetails/VmAppExpandedDetails.tsx +++ b/libs/ui-components/src/components/Device/DeviceDetails/VmAppExpandedDetails.tsx @@ -67,7 +67,7 @@ const VmAppExpandedDetails = ({ - + ); diff --git a/libs/ui-components/src/components/Device/DeviceDetails/WorkloadAppExpandedDetails.tsx b/libs/ui-components/src/components/Device/DeviceDetails/WorkloadAppExpandedDetails.tsx index 888d5e25d0..55c66bfb49 100644 --- a/libs/ui-components/src/components/Device/DeviceDetails/WorkloadAppExpandedDetails.tsx +++ b/libs/ui-components/src/components/Device/DeviceDetails/WorkloadAppExpandedDetails.tsx @@ -73,7 +73,7 @@ const WorkloadAppExpandedDetails = ({ - + {volumes.length > 0 && } ); diff --git a/libs/ui-components/src/components/Events/useEvents.ts b/libs/ui-components/src/components/Events/useEvents.ts index 3c087fc363..37dc86eced 100644 --- a/libs/ui-components/src/components/Events/useEvents.ts +++ b/libs/ui-components/src/components/Events/useEvents.ts @@ -124,6 +124,11 @@ const getEventReasonTitles = (t: TFunction, kindType: string): Record { {t( - 'Delta updates create smaller incremental artifacts for operating system and application rollouts. Your devices download only what changed instead of full images.', + 'Delta generation runs during fleet rollouts. The platform creates OCI delta artifacts between successive image digests and stores them in a writable registry so devices can pull a smaller update instead of the full artifact.', )} - {t('This reduces bandwidth use across large fleets and constrained networks.')} + {t('To apply deltas')}: + + {t('Devices must report a compatible bootc version and OCI delta support.')} + + + Enable and configure delta artifact storage in an OCI registry in{' '} + Repositories + + + - - Deltas are stored in an OCI registry marked as a delta repository. Your administrator may have configured one - at deployment. If not, mark an existing OCI registry in Repositories - + {t('If a delta artifact cannot be applied, the device falls back to the full artifact.')} ); diff --git a/libs/ui-components/src/components/Fleet/CreateFleet/steps/UpdateStepDeltaGeneration.tsx b/libs/ui-components/src/components/Fleet/CreateFleet/steps/UpdateStepDeltaGeneration.tsx index 5f9bc88d93..8cb8788354 100644 --- a/libs/ui-components/src/components/Fleet/CreateFleet/steps/UpdateStepDeltaGeneration.tsx +++ b/libs/ui-components/src/components/Fleet/CreateFleet/steps/UpdateStepDeltaGeneration.tsx @@ -94,9 +94,11 @@ const UpdateStepDeltaGeneration = ({ isReadOnly }: { isReadOnly: boolean }) => { helperText={ {t( - 'Generates incremental update artifacts during rollouts so your devices download only what changed for operating systems and applications.', + 'When delta generation is enabled for a fleet, the platform pushes only the incremental changes between versions, reducing download size and bandwidth demand for updates. Ensure your devices and OCI registry are configured to apply deltas.', )}{' '} - {deltaUpdatesDocLink && } + {deltaUpdatesDocLink && ( + + )} } isDisabled={isReadOnly} diff --git a/libs/ui-components/src/hooks/useDeviceSpecSystemInfo.tsx b/libs/ui-components/src/hooks/useDeviceSpecSystemInfo.tsx index cf87af30e0..1ab8835d7d 100644 --- a/libs/ui-components/src/hooks/useDeviceSpecSystemInfo.tsx +++ b/libs/ui-components/src/hooks/useDeviceSpecSystemInfo.tsx @@ -19,8 +19,9 @@ const excludedKnownProps = [ 'distroVersion', // It's combined with "distroName" 'customInfo', // Custom properies are evaluated separately from the predefined, known properties 'attestation', // In Phase1 this includes only the raw data, without a report of success or failure. - // "deltaEligible" and "ociDeltaVersion" are shown in a separate section on the device details page + // "deltaEligible", "bootcVersion", and "ociDeltaVersion" are shown in a separate section on the device details page 'deltaEligible', + 'bootcVersion', 'ociDeltaVersion', ];