Skip to content

Commit 049b1d8

Browse files
committed
incus: fix recovering from existing runtime disk
Signed-off-by: Abiola Ibrahim <git@abiosoft.com>
1 parent 27d2f89 commit 049b1d8

File tree

3 files changed

+105
-9
lines changed

3 files changed

+105
-9
lines changed

environment/container/incus/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ networks:
77
name: {{.Interface}}
88
type: ""
99
project: default
10+
{{ if .SetStorage }}
1011
storage_pools:
1112
- config:
1213
source: {{.Disk}}
1314
description: ""
1415
name: default
1516
driver: zfs
17+
{{ end }}
1618
profiles:
1719
- config: {}
1820
description: ""

environment/container/incus/incus.go

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ func (c *incusRuntime) Dependencies() []string {
5353

5454
// Provision implements environment.Container.
5555
func (c *incusRuntime) Provision(ctx context.Context) error {
56+
log := c.Logger(ctx)
57+
5658
// ensure that the systemd socket file is created
5759
if err := c.guest.RunQuiet("sudo", "systemctl", "start", "incus.socket"); err != nil {
5860
return fmt.Errorf("error starting incus socket: %w", err)
@@ -67,17 +69,23 @@ func (c *incusRuntime) Provision(ctx context.Context) error {
6769
return nil
6870
}
6971

72+
emptyDisk := true
73+
recoverStorage := false
7074
if limautil.DiskProvisioned(Name) {
71-
// disk already provisioned
72-
return nil
75+
emptyDisk = false
76+
// previous disk exist
77+
// ignore storage, recovery would be attempted later
78+
recoverStorage = cli.Prompt("existing Incus data found, would you like to recover the storage pool")
7379
}
7480

7581
var value struct {
76-
Disk string
77-
Interface string
82+
Disk string
83+
Interface string
84+
SetStorage bool
7885
}
7986
value.Disk = "/dev/vdb"
8087
value.Interface = incusBridgeInterface
88+
value.SetStorage = emptyDisk // set only when the disk is empty
8189

8290
buf, err := util.ParseTemplate(configYaml, value)
8391
if err != nil {
@@ -89,6 +97,40 @@ func (c *incusRuntime) Provision(ctx context.Context) error {
8997
return fmt.Errorf("error setting up incus: %w", err)
9098
}
9199

100+
// provision successful
101+
if emptyDisk {
102+
return nil
103+
}
104+
105+
if !recoverStorage {
106+
log.Warnln("discarding data, creating new storage pool")
107+
return c.wipeDisk(false)
108+
}
109+
110+
if !c.hasExistingPool() {
111+
log.Warnln("discarding corrupted disk, creating new storage pool")
112+
return c.wipeDisk(false)
113+
}
114+
115+
if err := c.importExistingPool(); err != nil {
116+
log.Warnln(fmt.Errorf("cannot recover disk: %w, creating new storage pool", err))
117+
return c.wipeDisk(false)
118+
}
119+
120+
for {
121+
if err := c.recoverDisk(ctx); err != nil {
122+
log.Warnln(err)
123+
124+
if cli.Prompt("recovery failed, try again") {
125+
continue
126+
}
127+
128+
log.Warnln("discarding disk, creating new storage pool")
129+
return c.wipeDisk(true)
130+
}
131+
break
132+
}
133+
92134
return nil
93135
}
94136

@@ -315,9 +357,61 @@ func DataDisk() environment.DataDisk {
315357
}
316358
}
317359

318-
func SystemdServices() []string {
319-
return []string{
320-
"incus.service",
321-
"incus.socket",
360+
func (c *incusRuntime) hasExistingPool() bool {
361+
return c.guest.RunQuiet("sh", "-c", "sudo zpool import | grep -A 2 'pool: default' | grep 'state: ONLINE'") == nil
362+
}
363+
364+
func (c *incusRuntime) importExistingPool() error {
365+
if err := c.guest.RunQuiet("sudo", "zpool", "import", "default"); err != nil {
366+
return fmt.Errorf("error importing existing zpool: %w", err)
322367
}
368+
369+
return nil
370+
}
371+
372+
func (c *incusRuntime) recoverDisk(ctx context.Context) error {
373+
log := c.Logger(ctx)
374+
375+
log.Println()
376+
log.Println("Running 'incus admin recover' ...")
377+
log.Println()
378+
log.Println("Use the following values for the prompts")
379+
log.Println(" name of storage pool: default")
380+
log.Println(" name of storage backend: zfs")
381+
log.Println(" source of storage pool: /dev/vdb")
382+
log.Println()
383+
384+
if err := c.guest.RunInteractive("sudo", "incus", "admin", "recover"); err != nil {
385+
return fmt.Errorf("error recovering storage pool: %w", err)
386+
}
387+
388+
out, err := c.guest.RunOutput("sudo", "incus", "storage", "list", "name=default", "-c", "n", "--format", "compact,noheader")
389+
if err != nil {
390+
return err
391+
}
392+
393+
if out != "default" {
394+
return fmt.Errorf("storage pool recovery failure")
395+
}
396+
397+
return nil
398+
}
399+
400+
func (c *incusRuntime) wipeDisk(wipeZpool bool) error {
401+
if wipeZpool {
402+
if err := c.guest.RunQuiet("sudo", "zpool", "destroy", "default"); err != nil {
403+
return fmt.Errorf("cannot resetting pool data: %w", err)
404+
}
405+
} else {
406+
if err := c.guest.RunQuiet("sudo", "sfdisk", "--delete", "/dev/vdb", "1"); err != nil {
407+
return fmt.Errorf("error resetting pool data: %w", err)
408+
}
409+
}
410+
411+
// prepare directory
412+
if err := c.guest.RunQuiet("sudo", "rm", "-rf", "/var/lib/incus/storage-pools/default"); err != nil {
413+
return fmt.Errorf("error preparing storage pools directory: %w", err)
414+
}
415+
416+
return c.guest.RunQuiet("sudo", "incus", "storage", "create", "default", "zfs", "source=/dev/vdb")
323417
}

environment/vm/lima/limaconfig/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type Mount struct {
4848

4949
type Disk struct {
5050
Name string `yaml:"name" json:"name"` // REQUIRED
51-
Format bool `yaml:"format,omitempty" json:"format,omitempty"`
51+
Format bool `yaml:"format" json:"format"`
5252
FSType string `yaml:"fsType,omitempty" json:"fsType,omitempty"`
5353
FSArgs []string `yaml:"fsArgs,omitempty" json:"fsArgs,omitempty"`
5454
}

0 commit comments

Comments
 (0)