incus-sentinel/incus/main.go
2025-01-05 10:34:14 +01:00

100 lines
2.9 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")
} else {
cmd = exec.Command("incus", "copy", sourceInstance, targetHost+":"+targetInstance, "-s", targetPool, "--mode", "push", "--stateless", "--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
}