100 lines
3 KiB
Go
100 lines
3 KiB
Go
package incus
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
type IncusDriver struct{}
|
|
|
|
func NewIncusDriver() *IncusDriver {
|
|
return &IncusDriver{}
|
|
}
|
|
|
|
func (d *IncusDriver) GetInstances(target string) ([]Instance, error) {
|
|
// Command: incus list -f json
|
|
|
|
var cmd *exec.Cmd
|
|
if target == "" {
|
|
cmd = exec.Command("incus", "list", "--format", "json")
|
|
} else {
|
|
cmd = exec.Command("incus", "list", target+":", "--format", "json")
|
|
}
|
|
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to execute incus list: %w", err)
|
|
}
|
|
|
|
var instances []Instance
|
|
err = json.Unmarshal(output, &instances)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return instances, nil
|
|
}
|
|
|
|
func (d *IncusDriver) Sync(sourceInstance string, targetInstance string, targetHost string, targetPool string) error {
|
|
// incus copy edge0 racker1:edge0-cold -s pool0 --mode push -p default -p net_edge0 --stateless --refresh
|
|
// incus copy edge0 racker1:edge0-cold -s pool0 --mode push -p default -p net_edge0 --stateless
|
|
|
|
instances, err := d.GetInstances(targetHost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var cmd *exec.Cmd
|
|
if len(instances) == 0 {
|
|
cmd = exec.Command("incus", "copy", sourceInstance, targetHost+":"+targetInstance, "-s", targetPool, "--mode", "push", "--stateless", "-c", "user.backup=false", "-c", "user.sync=false")
|
|
} else {
|
|
cmd = exec.Command("incus", "copy", sourceInstance, targetHost+":"+targetInstance, "-s", targetPool, "--mode", "push", "--stateless", "-c", "user.backup=false", "-c", "user.sync=false", "--refresh")
|
|
}
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute incus copy: %w (%s)", err, string(out))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *IncusDriver) Backup(instance string, tags []string) error {
|
|
// incus export ups - -q --compression=zstd --instance-only --optimized-storage | restic backup --stdin --stdin-filename ups.btrfs.zstd --tag instance
|
|
|
|
// Create the incus export command
|
|
incusCmd := exec.Command("incus", "export", instance, "-", "-q", "--compression=zstd", "--instance-only", "--optimized-storage")
|
|
|
|
// Create the restic backup command
|
|
resticCmd := exec.Command("restic", "backup", "--host", instance, "--stdin", "--stdin-filename", fmt.Sprintf("%s.btrfs.zstd", instance), "--tag", strings.Join(tags, ","))
|
|
|
|
// Connect the output of incusCmd to the input of resticCmd
|
|
pipe, err := incusCmd.StdoutPipe()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create pipe: %w", err)
|
|
}
|
|
resticCmd.Stdin = pipe
|
|
|
|
// Start the incus export command
|
|
if err := incusCmd.Start(); err != nil {
|
|
return fmt.Errorf("failed to start incus export: %w", err)
|
|
}
|
|
|
|
// Start the restic backup command
|
|
if err := resticCmd.Start(); err != nil {
|
|
return fmt.Errorf("failed to start restic backup: %w", err)
|
|
}
|
|
|
|
// Wait for the incus export command to finish
|
|
if err := incusCmd.Wait(); err != nil {
|
|
return fmt.Errorf("incus export command failed: %w", err)
|
|
}
|
|
|
|
// Wait for the restic backup command to finish
|
|
if err := resticCmd.Wait(); err != nil {
|
|
return fmt.Errorf("restic backup command failed: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|