mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-01 21:13:09 +00:00
feat: preserve volumes on failed install + migrate ext4 to btrfs
- COW snapshot (cp --reflink=always) of package volumes before install/update; restore on failure, remove on success - Automatic ext4→btrfs conversion via btrfs-convert during disk attach with e2fsck pre-check and post-conversion defrag - Probe package-data filesystem during setup.disk.list (on both disk and partition level) so the UI can warn about ext4 conversion - Setup wizard preserve-overwrite dialog shows ext4 warning with backup acknowledgment checkbox before allowing preserve
This commit is contained in:
@@ -1,12 +1,30 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { i18nPipe } from '@start9labs/shared'
|
||||
import { TuiButton, TuiTitle } from '@taiga-ui/core'
|
||||
import { TuiDialogContext } from '@taiga-ui/core'
|
||||
import {
|
||||
TuiButton,
|
||||
TuiCheckbox,
|
||||
TuiDialogContext,
|
||||
TuiNotification,
|
||||
TuiTitle,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiHeader } from '@taiga-ui/layout'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
|
||||
export interface PreserveOverwriteData {
|
||||
isExt4: boolean
|
||||
}
|
||||
|
||||
@Component({
|
||||
imports: [TuiButton, TuiHeader, TuiTitle, i18nPipe],
|
||||
imports: [
|
||||
FormsModule,
|
||||
TuiButton,
|
||||
TuiCheckbox,
|
||||
TuiHeader,
|
||||
TuiNotification,
|
||||
TuiTitle,
|
||||
i18nPipe,
|
||||
],
|
||||
template: `
|
||||
<header tuiHeader>
|
||||
<hgroup tuiTitle>
|
||||
@@ -24,6 +42,18 @@ import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
{{ 'to discard' | i18n }}
|
||||
</li>
|
||||
</ul>
|
||||
@if (context.data.isExt4) {
|
||||
<p tuiNotification appearance="warning" size="m">
|
||||
{{
|
||||
'This drive uses ext4 and will be automatically converted to btrfs. A backup is strongly recommended before proceeding.'
|
||||
| i18n
|
||||
}}
|
||||
</p>
|
||||
<label>
|
||||
<input tuiCheckbox type="checkbox" [(ngModel)]="backupAck" />
|
||||
{{ 'I have a backup of my data' | i18n }}
|
||||
</label>
|
||||
}
|
||||
<footer>
|
||||
<button
|
||||
tuiButton
|
||||
@@ -36,6 +66,7 @@ import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
tuiButton
|
||||
appearance=""
|
||||
[style.background]="'var(--tui-status-positive)'"
|
||||
[disabled]="context.data.isExt4 && !backupAck"
|
||||
(click)="context.completeWith(true)"
|
||||
>
|
||||
{{ 'Preserve' | i18n }}
|
||||
@@ -44,7 +75,9 @@ import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
`,
|
||||
})
|
||||
export class PreserveOverwriteDialog {
|
||||
protected readonly context = injectContext<TuiDialogContext<boolean>>()
|
||||
protected readonly context =
|
||||
injectContext<TuiDialogContext<boolean, PreserveOverwriteData>>()
|
||||
protected backupAck = false
|
||||
}
|
||||
|
||||
export const PRESERVE_OVERWRITE = new PolymorpheusComponent(
|
||||
|
||||
@@ -292,8 +292,18 @@ export default class DrivesPage {
|
||||
|
||||
private showPreserveOverwriteDialog() {
|
||||
let selectionMade = false
|
||||
const drive = this.selectedDataDrive
|
||||
const filesystem =
|
||||
drive?.filesystem ||
|
||||
drive?.partitions.find(p => p.guid)?.filesystem ||
|
||||
null
|
||||
const isExt4 = filesystem === 'ext2'
|
||||
|
||||
this.dialogs.openComponent<boolean>(PRESERVE_OVERWRITE).subscribe({
|
||||
this.dialogs
|
||||
.openComponent<boolean>(PRESERVE_OVERWRITE, {
|
||||
data: { isExt4 },
|
||||
})
|
||||
.subscribe({
|
||||
next: preserve => {
|
||||
selectionMade = true
|
||||
this.preserveData = preserve
|
||||
|
||||
@@ -203,6 +203,7 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
partitions: [],
|
||||
capacity: 0,
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
// 10 GiB - too small for OS and data; also tests both vendor+model null
|
||||
{
|
||||
@@ -217,10 +218,12 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
used: null,
|
||||
startOs: {},
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
],
|
||||
capacity: 10 * GiB,
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
// 18 GiB - exact OS boundary; tests vendor null with model present
|
||||
{
|
||||
@@ -235,10 +238,12 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
used: null,
|
||||
startOs: {},
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
],
|
||||
capacity: 18 * GiB,
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
// 20 GiB - exact data boundary; tests vendor present with model null
|
||||
{
|
||||
@@ -253,10 +258,12 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
used: null,
|
||||
startOs: {},
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
],
|
||||
capacity: 20 * GiB,
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
// 30 GiB - OK for OS or data alone, too small for both (< 38 GiB)
|
||||
{
|
||||
@@ -271,10 +278,12 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
used: null,
|
||||
startOs: {},
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
],
|
||||
capacity: 30 * GiB,
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
// 30 GiB with existing StartOS data - tests preserve/overwrite + capacity constraint
|
||||
{
|
||||
@@ -298,10 +307,12 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
},
|
||||
},
|
||||
guid: 'small-existing-guid',
|
||||
filesystem: 'ext2',
|
||||
},
|
||||
],
|
||||
capacity: 30 * GiB,
|
||||
guid: 'small-existing-guid',
|
||||
filesystem: 'ext2',
|
||||
},
|
||||
// 500 GB - large, always OK
|
||||
{
|
||||
@@ -316,10 +327,12 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
used: null,
|
||||
startOs: {},
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
],
|
||||
capacity: 500000000000,
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
// 1 TB with existing StartOS data
|
||||
{
|
||||
@@ -343,10 +356,12 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
},
|
||||
},
|
||||
guid: 'existing-guid',
|
||||
filesystem: 'btrfs',
|
||||
},
|
||||
],
|
||||
capacity: 1000000000000,
|
||||
guid: 'existing-guid',
|
||||
filesystem: 'btrfs',
|
||||
},
|
||||
// 2 TB
|
||||
{
|
||||
@@ -370,10 +385,12 @@ const MOCK_DISKS: DiskInfo[] = [
|
||||
},
|
||||
},
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
],
|
||||
capacity: 2000000000000,
|
||||
guid: null,
|
||||
filesystem: null,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user