Skip to content

Commit

Permalink
Merge pull request #424 from rvykydal/home-reuse-poc
Browse files Browse the repository at this point in the history
Home reuse
  • Loading branch information
KKoukiou authored Oct 25, 2024
2 parents c8c0686 + de613e5 commit 9f628dd
Show file tree
Hide file tree
Showing 14 changed files with 367 additions and 27 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ payload: bots

.PHONY: images
images: bots
bots/image-download $(TEST_OS) debian-stable ubuntu-stable
bots/image-download $(TEST_OS) debian-stable ubuntu-stable fedora-41

$(UPDATES_IMG): prepare-test-deps
test/prepare-updates-img
Expand Down
29 changes: 29 additions & 0 deletions src/apis/storage_partitioning.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,35 @@ export const partitioningSetEncrypt = ({ encrypt, partitioning }) => {
});
};

/**
* @param {string} partitioning DBus path to a partitioning
* @param {string} scheme autopartitioning scheme
*/
export const partitioningSetHomeReuse = async ({ partitioning, scheme }) => {
const request = await getPartitioningRequest({ partitioning });

const configurationSchemeToDBus = {
BTRFS: cockpit.variant("i", 1),
LVM: cockpit.variant("i", 2),
LVM_THINP: cockpit.variant("i", 3),
PLAIN: cockpit.variant("i", 0),
};
request["partitioning-scheme"] = configurationSchemeToDBus?.[scheme];

request["reused-mount-points"] = cockpit.variant("as", ["/home"]);
if (scheme === "PLAIN") {
// "/" will be reallocated by autopartitioning
request["removed-mount-points"] = cockpit.variant("as", ["/", "/boot", "bootloader"]);
} else {
// "LVM", "BTRFS", "LVM_THINP"
// "/" can't be reallocated by autopartitioing as it is sharing container device with /home
request["removed-mount-points"] = cockpit.variant("as", ["/boot", "bootloader"]);
request["reformatted-mount-points"] = cockpit.variant("as", ["/"]);
}

await setPartitioningRequest({ partitioning, request });
};

/**
* @returns {Promise} The request of automatic partitioning
*/
Expand Down
10 changes: 7 additions & 3 deletions src/components/Common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const LanguageContext = createContext(null);
export const OsReleaseContext = createContext(null);
export const RuntimeContext = createContext(null);
export const StorageContext = createContext(null);
export const StorageDefaultsContext = createContext(null);
export const SystemTypeContext = createContext(null);
export const TargetSystemRootContext = createContext(null);
export const UsersContext = createContext(null);
Expand Down Expand Up @@ -66,13 +67,16 @@ const ModuleContextWrapper = ({ children, state }) => {

const SystemInfoContextWrapper = ({ children, conf, osRelease }) => {
const systemType = conf?.["Installation System"].type;
const defaultScheme = conf?.Storage.default_scheme;

return (
<OsReleaseContext.Provider value={osRelease}>
<SystemTypeContext.Provider value={systemType}>
<TargetSystemRootContext.Provider value={conf["Installation Target"].system_root}>
{children}
</TargetSystemRootContext.Provider>
<StorageDefaultsContext.Provider value={{ defaultScheme }}>
<TargetSystemRootContext.Provider value={conf["Installation Target"].system_root}>
{children}
</TargetSystemRootContext.Provider>
</StorageDefaultsContext.Provider>
</SystemTypeContext.Provider>
</OsReleaseContext.Provider>
);
Expand Down
18 changes: 13 additions & 5 deletions src/components/review/StorageReview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const DeviceRow = ({ disk }) => {

const requests = partitioning.requests;
const deviceData = devices?.[disk];
const reuseHomeRequest = requests.find(request => request["reused-mount-points"]);

if (!deviceData) {
return null;
Expand All @@ -86,11 +87,18 @@ const DeviceRow = ({ disk }) => {
const size = cockpit.format_bytes(devices[device].size.v);
const request = requests.find(request => request["device-spec"] === device);
const format = devices[device].formatData.type.v;
const action = (
request === undefined || request.reformat
? (format ? cockpit.format(_("format as $0"), format) : null)
: ((format === "biosboot") ? format : _("mount"))
);

let action = null;
if (reuseHomeRequest?.["reused-mount-points"].includes(mount)) {
action = _("mount");
} else if ((request === undefined || request.reformat) && format) {
action = cockpit.format(_("format as $0"), format);
} else if (format === "biosboot") {
action = format;
} else {
action = _("mount");
}

const parents = getParentPartitions(devices, device);
const showMaybeType = () => {
if (checkDeviceOnStorageType(devices, device, "lvmvg")) {
Expand Down
10 changes: 6 additions & 4 deletions src/components/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ import { Page as PageAccounts } from "./users/Accounts.jsx";
const _ = cockpit.gettext;

export const getSteps = (...args) => {
const diskConfigurationSteps = [
new PageMountPointMapping(...args),
new PageDiskEncryption(...args),
];
const stepsOrder = [
new PageInstallationLanguage(...args),
new PageInstallationMethod(...args),
{
id: "disk-configuration",
isHidden: diskConfigurationSteps.every(step => step.isHidden),
label: _("Disk configuration"),
steps: [
new PageMountPointMapping(...args),
new PageDiskEncryption(...args),
]
steps: diskConfigurationSteps,
},
new PageAccounts(...args),
new PageReviewConfiguration(...args),
Expand Down
11 changes: 10 additions & 1 deletion src/components/storage/Common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
createPartitioning,
getDeviceTree,
partitioningSetEncrypt,
partitioningSetHomeReuse,
partitioningSetPassphrase,
} from "../../apis/storage_partitioning.js";

Expand Down Expand Up @@ -129,6 +130,7 @@ export const useMountPointConstraints = () => {
};

export const getNewPartitioning = async ({
autopartScheme,
currentPartitioning,
method = "AUTOMATIC",
storageScenarioId,
Expand All @@ -143,7 +145,14 @@ export const getNewPartitioning = async ({

const part = await createPartitioning({ method });

if (currentPartitioning?.method === method && method === "AUTOMATIC" && currentPartitioning.requests[0].encrypted) {
if (storageScenarioId === "home-reuse") {
await partitioningSetHomeReuse({ partitioning: part, scheme: autopartScheme });
}

if (currentPartitioning?.method === method &&
method === "AUTOMATIC" &&
storageScenarioId !== "home-reuse" &&
currentPartitioning.requests[0].encrypted) {
await partitioningSetEncrypt({ encrypt: true, partitioning: part });
await partitioningSetPassphrase({ partitioning: part, passphrase: currentPartitioning.requests[0].passphrase });
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/storage/DiskEncryption.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export class Page {
constructor (isBootIso, storageScenarioId) {
this.component = DiskEncryption;
this.id = "disk-encryption";
this.isHidden = ["mount-point-mapping", "use-configured-storage"].includes(storageScenarioId);
this.isHidden = ["mount-point-mapping", "use-configured-storage", "home-reuse"].includes(storageScenarioId);
this.label = _("Disk encryption");
this.title = _("Encrypt the selected devices?");
this.usePageInit = usePageInit;
Expand Down
2 changes: 2 additions & 0 deletions src/components/storage/HelpAutopartOptions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ export const helpUseFreeSpace = _("Keep current disk layout and use available sp

export const helpMountPointMapping = _("Assign partitions to mount points. Useful for pre-configured custom layouts.");

export const helpHomeReuse = _("Replace current installation, but keep files in home.");

export const helpConfiguredStorage = _("Storage is based on the configuration from 'Modify storage'.");
58 changes: 49 additions & 9 deletions src/components/storage/InstallationMethod.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,19 @@ import {
useWizardFooter,
} from "@patternfly/react-core";

import { resetPartitioning } from "../../apis/storage_partitioning.js";
import {
applyStorage,
resetPartitioning,
} from "../../apis/storage_partitioning.js";

import { AnacondaWizardFooter } from "../AnacondaWizardFooter.jsx";
import { DialogsContext, FooterContext, OsReleaseContext, StorageContext } from "../Common.jsx";
import {
DialogsContext,
FooterContext,
OsReleaseContext,
StorageContext,
StorageDefaultsContext,
} from "../Common.jsx";
import { getNewPartitioning } from "./Common.jsx";
import { InstallationDestination } from "./InstallationDestination.jsx";
import { InstallationScenario, scenarios } from "./InstallationScenario.jsx";
Expand All @@ -44,6 +53,7 @@ const InstallationMethod = ({
onCritFail,
setIsFormDisabled,
setIsFormValid,
setStepNotification,
showStorage,
}) => {
const [isReclaimSpaceCheckboxChecked, setIsReclaimSpaceCheckboxChecked] = useState();
Expand All @@ -53,8 +63,9 @@ const InstallationMethod = ({
<CustomFooter
isFormDisabled={isFormDisabled}
isReclaimSpaceCheckboxChecked={isReclaimSpaceCheckboxChecked}
setStepNotification={setStepNotification}
/>
), [isFormDisabled, isReclaimSpaceCheckboxChecked]);
), [isFormDisabled, isReclaimSpaceCheckboxChecked, setStepNotification]);
useWizardFooter(getFooter);

return (
Expand Down Expand Up @@ -86,13 +97,14 @@ const InstallationMethod = ({
);
};

const CustomFooter = ({ isFormDisabled, isReclaimSpaceCheckboxChecked }) => {
const CustomFooter = ({ isFormDisabled, isReclaimSpaceCheckboxChecked, setStepNotification }) => {
const [isReclaimSpaceModalOpen, setIsReclaimSpaceModalOpen] = useState(false);
const [isNextClicked, setIsNextClicked] = useState(false);
const { goToNextStep } = useWizardContext();
const [newPartitioning, setNewPartitioning] = useState(-1);
const nextRef = useRef();
const { partitioning, storageScenarioId } = useContext(StorageContext);
const { defaultScheme } = useContext(StorageDefaultsContext);
const method = ["mount-point-mapping", "use-configured-storage"].includes(storageScenarioId) ? "MANUAL" : "AUTOMATIC";

useEffect(() => {
Expand All @@ -102,21 +114,45 @@ const CustomFooter = ({ isFormDisabled, isReclaimSpaceCheckboxChecked }) => {
}
}, [isNextClicked, goToNextStep, newPartitioning, partitioning.path]);

const onNext = async () => {
const onNext = async ({ setIsFormDisabled }) => {
if (method === "MANUAL") {
setNewPartitioning(partitioning.path);
setIsNextClicked(true);
} else {
const part = await getNewPartitioning({ currentPartitioning: partitioning, method, storageScenarioId });
const part = await getNewPartitioning({
autopartScheme: defaultScheme,
currentPartitioning: partitioning,
method,
storageScenarioId,
});
setNewPartitioning(part);

const scenarioSupportsReclaimSpace = scenarios.find(sc => sc.id === storageScenarioId)?.canReclaimSpace;
const willShowReclaimSpaceModal = scenarioSupportsReclaimSpace && isReclaimSpaceCheckboxChecked;

if (willShowReclaimSpaceModal) {
setIsReclaimSpaceModalOpen(true);
} else {
} else if (storageScenarioId !== "home-reuse") {
setIsNextClicked(true);
} else {
setIsFormDisabled(true);
const step = new Page().id;
await applyStorage({
onFail: ex => {
console.error(ex);
setIsFormDisabled(false);
setStepNotification({ step, ...ex });
},
onSuccess: () => {
goToNextStep();

// Reset the state after the onNext call. Otherwise,
// React will try to render the current step again.
setIsFormDisabled(false);
setStepNotification();
},
partitioning: part,
});
}
}
};
Expand Down Expand Up @@ -165,12 +201,16 @@ const InstallationMethodFooterHelper = () => {
const usePageInit = () => {
const { setIsFormDisabled } = useContext(FooterContext);
const { appliedPartitioning, partitioning } = useContext(StorageContext);
const pageHasMounted = useRef(false);
// Always reset the partitioning when entering the installation destination page
// If the last partitioning applied was from the cockpit storage integration
// we should not reset it, as this option does apply the partitioning onNext
const needsReset = partitioning.storageScenarioId !== "use-configured-storage" && appliedPartitioning;
const needsReset = partitioning.storageScenarioId !== "use-configured-storage" &&
appliedPartitioning &&
pageHasMounted.current !== true;

useEffect(() => {
// Always reset the partitioning when entering the installation destination page
pageHasMounted.current = true;
if (needsReset) {
resetPartitioning();
} else {
Expand Down
Loading

0 comments on commit 9f628dd

Please sign in to comment.