From 593e52196abc5adaca68b075e477283dc1937e18 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Fri, 29 Nov 2024 13:10:32 +0000 Subject: [PATCH] Try to not crash in auto... with storage section The UI is not ready for the new storage configuration loaded from the profile. As the problem is reported just for AutoYaST and the installation finished page we are trying to fix only there until the new storage features PR is merged. --- web/src/api/storage.ts | 6 +- web/src/api/storage/types.ts | 4 + web/src/api/storage/types/config.ts | 369 ++++++++++++++++++ .../components/core/InstallationFinished.tsx | 30 +- web/src/queries/storage.ts | 13 + 5 files changed, 413 insertions(+), 9 deletions(-) create mode 100644 web/src/api/storage/types/config.ts diff --git a/web/src/api/storage.ts b/web/src/api/storage.ts index d67166e828..ffa26f375b 100644 --- a/web/src/api/storage.ts +++ b/web/src/api/storage.ts @@ -23,6 +23,7 @@ import { get, post } from "~/api/http"; import { Job } from "~/types/job"; import { calculate, fetchSettings } from "~/api/storage/proposal"; +import { config } from "~/api/storage/types"; /** * Starts the storage probing process. @@ -33,6 +34,9 @@ const probe = (): Promise => post("/api/storage/probe"); export { probe }; +const fetchConfig = (): Promise => + get("/api/storage/config").then((config) => config.storage); + /** * Returns the list of jobs */ @@ -57,4 +61,4 @@ const refresh = async (): Promise => { await calculate(settings); }; -export { fetchStorageJobs, findStorageJob, refresh }; +export { fetchConfig, fetchStorageJobs, findStorageJob, refresh }; diff --git a/web/src/api/storage/types.ts b/web/src/api/storage/types.ts index aee104ea76..44db12a9b7 100644 --- a/web/src/api/storage/types.ts +++ b/web/src/api/storage/types.ts @@ -436,3 +436,7 @@ export type SetProposalSettingsResponse = boolean; export type UsableDevicesResponse = Array; export type PingResponse2 = PingResponse; + +import * as config from "./types/config"; + +export { config }; diff --git a/web/src/api/storage/types/config.ts b/web/src/api/storage/types/config.ts new file mode 100644 index 0000000000..7a7864a01d --- /dev/null +++ b/web/src/api/storage/types/config.ts @@ -0,0 +1,369 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +export type DriveElement = FormattedDrive | PartitionedDrive; +export type SearchElement = SimpleSearchAll | SimpleSearchByName | AdvancedSearch; +/** + * Shortcut to match all devices if there is any (equivalent to specify no conditions and to skip the entry if no device is found). + */ +export type SimpleSearchAll = "*"; +export type SimpleSearchByName = string; +/** + * How to handle the section if the device is not found. + */ +export type SearchAction = "skip" | "error"; +/** + * Alias used to reference a device. + */ +export type Alias = string; +export type Encryption = + | EncryptionLuks1 + | EncryptionLuks2 + | EncryptionPervasiveLuks2 + | EncryptionTPM + | EncryptionSwap; +/** + * Password to use when creating a new encryption device. + */ +export type EncryptionPassword = string; +/** + * The value must be compatible with the --cipher argument of the command cryptsetup. + */ +export type EncryptionCipher = string; +/** + * The value (in bits) has to be a multiple of 8. The possible key sizes are limited by the used cipher. + */ +export type EncryptionKeySize = number; +export type EncryptionPbkdFunction = "pbkdf2" | "argon2i" | "argon2id"; +/** + * Swap encryptions. + */ +export type EncryptionSwap = "protected_swap" | "secure_swap" | "random_swap"; +export type FilesystemType = FilesystemTypeAny | FilesystemTypeBtrfs; +export type FilesystemTypeAny = + | "bcachefs" + | "btrfs" + | "exfat" + | "ext2" + | "ext3" + | "ext4" + | "f2fs" + | "jfs" + | "nfs" + | "nilfs2" + | "ntfs" + | "reiserfs" + | "swap" + | "tmpfs" + | "vfat" + | "xfs"; +/** + * How to mount the device. + */ +export type MountBy = "device" | "id" | "label" | "path" | "uuid"; +/** + * Partition table type. + */ +export type PtableType = "gpt" | "msdos" | "dasd"; +export type PartitionElement = + | SimpleVolumesGenerator + | AdvancedPartitionsGenerator + | RegularPartition + | PartitionToDelete + | PartitionToDeleteIfNeeded; +export type PartitionId = "linux" | "swap" | "lvm" | "raid" | "esp" | "prep" | "bios_boot"; +export type Size = SizeValue | SizeTuple | SizeRange; +export type SizeValue = SizeString | SizeBytes; +/** + * Human readable size. + */ +export type SizeString = string; +/** + * Size in bytes. + */ +export type SizeBytes = number; +/** + * Lower size limit and optionally upper size limit. + * + * @minItems 1 + * @maxItems 2 + */ +export type SizeTuple = [SizeValueWithCurrent] | [SizeValueWithCurrent, SizeValueWithCurrent]; +export type SizeValueWithCurrent = SizeValue | SizeCurrent; +/** + * The current size of the device. + */ +export type SizeCurrent = "current"; +export type PhysicalVolumeElement = + | Alias + | SimplePhysicalVolumesGenerator + | AdvancedPhysicalVolumesGenerator; +export type LogicalVolumeElement = + | SimpleVolumesGenerator + | AdvancedLogicalVolumesGenerator + | LogicalVolume + | ThinPoolLogicalVolume + | ThinLogicalVolume; +/** + * Number of stripes. + */ +export type LogicalVolumeStripes = number; + +/** + * Storage config. + */ +export interface Config { + boot?: Boot; + /** + * Drives (disks, BIOS RAIDs and multipath devices). + */ + drives?: DriveElement[]; + /** + * LVM volume groups. + */ + volumeGroups?: VolumeGroup[]; +} +/** + * Allows configuring boot partitions automatically. + */ +export interface Boot { + /** + * Whether to configure partitions for booting. + */ + configure: boolean; + /** + * The target installation device is used by default. + */ + device?: string; +} +/** + * Drive without a partition table (e.g., directly formatted). + */ +export interface FormattedDrive { + search?: SearchElement; + alias?: Alias; + encryption?: Encryption; + filesystem: Filesystem; +} +/** + * Advanced options for searching devices. + */ +export interface AdvancedSearch { + condition?: SearchCondition; + /** + * Maximum devices to match. + */ + max?: number; + ifNotFound?: SearchAction; +} +export interface SearchCondition { + name: SimpleSearchByName; +} +/** + * LUKS1 encryption. + */ +export interface EncryptionLuks1 { + luks1: { + password: EncryptionPassword; + cipher?: EncryptionCipher; + keySize?: EncryptionKeySize; + }; +} +/** + * LUKS2 encryption. + */ +export interface EncryptionLuks2 { + luks2: { + password: EncryptionPassword; + cipher?: EncryptionCipher; + keySize?: EncryptionKeySize; + pbkdFunction?: EncryptionPbkdFunction; + /** + * LUKS2 label. + */ + label?: string; + }; +} +/** + * LUKS2 pervasive encryption. + */ +export interface EncryptionPervasiveLuks2 { + pervasiveLuks2: { + password: EncryptionPassword; + }; +} +/** + * TPM-Based Full Disk Encrytion. + */ +export interface EncryptionTPM { + tpmFde: { + password: EncryptionPassword; + }; +} +export interface Filesystem { + /** + * Try to reuse the existing file system. In some cases the file system could not be reused, for example, if the device is re-encrypted. + */ + reuseIfPossible?: boolean; + type?: FilesystemType; + /** + * File system label. + */ + label?: string; + /** + * Mount path. + */ + path?: string; + mountBy?: MountBy; + /** + * Options for creating the file system. + */ + mkfsOptions?: string[]; + /** + * Options to add to the fourth field of fstab. + */ + mountOptions?: string[]; +} +/** + * Btrfs file system. + */ +export interface FilesystemTypeBtrfs { + btrfs: { + /** + * Whether to configrue Btrfs snapshots. + */ + snapshots?: boolean; + }; +} +export interface PartitionedDrive { + search?: SearchElement; + alias?: Alias; + ptableType?: PtableType; + partitions?: PartitionElement[]; +} +/** + * Automatically creates the default or mandatory volumes configured by the selected product. + */ +export interface SimpleVolumesGenerator { + generate: "default" | "mandatory"; +} +/** + * Creates the default or mandatory partitions configured by the selected product. + */ +export interface AdvancedPartitionsGenerator { + generate: { + partitions: "default" | "mandatory"; + encryption?: Encryption; + }; +} +export interface RegularPartition { + search?: SearchElement; + alias?: Alias; + id?: PartitionId; + size?: Size; + encryption?: Encryption; + filesystem?: Filesystem; +} +/** + * Size range. + */ +export interface SizeRange { + min: SizeValueWithCurrent; + max?: SizeValueWithCurrent; +} +export interface PartitionToDelete { + search: SearchElement; + /** + * Delete the partition. + */ + delete: true; +} +export interface PartitionToDeleteIfNeeded { + search: SearchElement; + /** + * Delete the partition if needed to make space. + */ + deleteIfNeeded: true; + size?: Size; +} +/** + * LVM volume group. + */ +export interface VolumeGroup { + /** + * Volume group name. + */ + name?: string; + extentSize?: SizeValue; + /** + * Devices to use as physical volumes. + */ + physicalVolumes?: PhysicalVolumeElement[]; + logicalVolumes?: LogicalVolumeElement[]; +} +/** + * Automatically creates the needed physical volumes in the indicated devices. + */ +export interface SimplePhysicalVolumesGenerator { + generate: Alias[]; +} +/** + * Automatically creates the needed physical volumes in the indicated devices. + */ +export interface AdvancedPhysicalVolumesGenerator { + generate: { + targetDevices: Alias[]; + encryption?: Encryption; + }; +} +/** + * Automatically creates the default or mandatory logical volumes configured by the selected product. + */ +export interface AdvancedLogicalVolumesGenerator { + generate: { + logicalVolumes: "default" | "mandatory"; + encryption?: Encryption; + stripes?: LogicalVolumeStripes; + stripeSize?: SizeValue; + }; +} +export interface LogicalVolume { + /** + * Logical volume name. + */ + name?: string; + size?: Size; + stripes?: LogicalVolumeStripes; + stripeSize?: SizeValue; + encryption?: Encryption; + filesystem?: Filesystem; +} +export interface ThinPoolLogicalVolume { + /** + * LVM thin pool. + */ + pool: true; + alias?: Alias; + /** + * Logical volume name. + */ + name?: string; + size?: Size; + stripes?: LogicalVolumeStripes; + stripeSize?: SizeValue; + encryption?: Encryption; +} +export interface ThinLogicalVolume { + /** + * Thin logical volume name. + */ + name?: string; + size?: Size; + usedPool: Alias; + encryption?: Encryption; + filesystem?: Filesystem; +} diff --git a/web/src/components/core/InstallationFinished.tsx b/web/src/components/core/InstallationFinished.tsx index d61e5f5e96..598ed705ca 100644 --- a/web/src/components/core/InstallationFinished.tsx +++ b/web/src/components/core/InstallationFinished.tsx @@ -38,11 +38,10 @@ import { Text, } from "@patternfly/react-core"; import { Center, Icon } from "~/components/layout"; -import { EncryptionMethods } from "~/types/storage"; import { _ } from "~/i18n"; import alignmentStyles from "@patternfly/react-styles/css/utilities/Alignment/alignment"; import { useInstallerStatus } from "~/queries/status"; -import { useProposalResult } from "~/queries/storage"; +import { useConfig } from "~/queries/storage"; import { finishInstallation } from "~/api/manager"; import { InstallationPhase } from "~/types/status"; import { Navigate } from "react-router-dom"; @@ -77,11 +76,28 @@ the machine needs to boot directly to the new boot loader.", const SuccessIcon = () => ; +// TODO: define some utility method to get the device used as root (drive, partition, logical volume). +// TODO: use type checking for config. +function usingTpm(config): boolean { + const { guided, drives = [], volumeGroups = [] } = config; + + if (guided !== undefined) { + return guided.encryption?.method === "tpm_fde"; + } + const devices = [ + ...drives, + ...drives.flatMap((d) => d.partitions || []), + ...volumeGroups.flatMap((v) => v.logicalVolumes || []), + ]; + + const root = devices.find((d) => d.filesystem?.path === "/"); + + return root?.encryption?.tpmFde !== undefined; +} + function InstallationFinished() { const { phase, isBusy, useIguana } = useInstallerStatus({ suspense: true }); - const { - settings: { encryptionPassword, encryptionMethod }, - } = useProposalResult(); + const config = useConfig(); if (phase !== InstallationPhase.Install) { return ; @@ -91,8 +107,6 @@ function InstallationFinished() { return ; } - const usingTpm = encryptionPassword?.length > 0 && encryptionMethod === EncryptionMethods.TPM; - return (
@@ -119,7 +133,7 @@ function InstallationFinished() { "At this point you can reboot the machine to log in to the new system.", )} - {usingTpm && } + {usingTpm(config) && } diff --git a/web/src/queries/storage.ts b/web/src/queries/storage.ts index ee58f1c77c..10814d6b80 100644 --- a/web/src/queries/storage.ts +++ b/web/src/queries/storage.ts @@ -29,6 +29,7 @@ import { } from "@tanstack/react-query"; import React from "react"; import { fetchDevices, fetchDevicesDirty } from "~/api/storage/devices"; +import { fetchConfig } from "~/api/storage"; import { calculate, fetchActions, @@ -123,6 +124,17 @@ const useDevices = ( return data; }; +const configQuery = { + queryKey: ["storage", "config"], + queryFn: fetchConfig, +}; + +const useConfig = () => { + const { data } = useSuspenseQuery(configQuery); + console.log(data); + return data; +}; + /** * Hook that returns the list of available devices for installation. */ @@ -349,6 +361,7 @@ const useDeprecatedChanges = () => { }; export { + useConfig, useDevices, useAvailableDevices, useProductParams,