195 lines
3.6 KiB
Go
195 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
incus "github.com/lxc/incus/v6/client"
|
|
"github.com/lxc/incus/v6/shared/api"
|
|
)
|
|
|
|
var (
|
|
timeout = 120
|
|
killTimeout = 5
|
|
)
|
|
|
|
type BytesBuffer struct {
|
|
*bytes.Buffer
|
|
}
|
|
|
|
func (*BytesBuffer) Close() error {
|
|
return nil
|
|
}
|
|
|
|
type IncusClient struct {
|
|
Client incus.InstanceServer
|
|
}
|
|
|
|
// Connect to the LXD socket.
|
|
func (c *IncusClient) Connect() error {
|
|
client, err := incus.ConnectIncusUnix("", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.Client = client
|
|
return nil
|
|
}
|
|
|
|
// Create a container using a specific remote and alias.
|
|
func (c *IncusClient) Create(name, remote, alias string) error {
|
|
fmt.Printf("creating %s with %s:%s\n", name, remote, alias)
|
|
|
|
if c.Client == nil {
|
|
err := c.Connect()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
server := ""
|
|
switch remote {
|
|
case "images":
|
|
server = "https://images.linuxcontainers.org"
|
|
case "ubuntu":
|
|
server = "https://cloud-images.ubuntu.com/releases"
|
|
case "ubuntu-daily":
|
|
server = "https://cloud-images.ubuntu.com/daily"
|
|
default:
|
|
return fmt.Errorf("unknown remote: %s", remote)
|
|
}
|
|
|
|
req := api.InstancesPost{
|
|
Name: name,
|
|
Source: api.InstanceSource{
|
|
Type: "image",
|
|
Mode: "pull",
|
|
Protocol: "simplestreams",
|
|
Server: server,
|
|
Alias: alias,
|
|
},
|
|
}
|
|
|
|
// Get LXD to create the container (background operation)
|
|
op, err := c.Client.CreateInstance(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Wait for the operation to complete
|
|
err = op.Wait()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Delete the given container.
|
|
func (c *IncusClient) Delete(name string) error {
|
|
fmt.Println("deleting", name)
|
|
op, err := c.Client.DeleteInstance(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return op.Wait()
|
|
}
|
|
|
|
// Run a command returning result struct. Will kill commands that take longer
|
|
// than 120 seconds.
|
|
func (c *IncusClient) Exec(name string, command ...string) error {
|
|
fmt.Printf("$ %s\n", strings.Join(command, " "))
|
|
|
|
cmd := []string{"/usr/bin/timeout", "-k", strconv.Itoa(killTimeout), strconv.Itoa(timeout)}
|
|
cmd = append(cmd, command...)
|
|
req := api.InstanceExecPost{
|
|
Command: cmd,
|
|
WaitForWS: true,
|
|
}
|
|
|
|
output := &BytesBuffer{bytes.NewBuffer(nil)}
|
|
|
|
args := incus.InstanceExecArgs{
|
|
Stdout: output,
|
|
Stderr: output,
|
|
DataDone: make(chan bool),
|
|
}
|
|
|
|
op, err := c.Client.ExecInstance(name, req, &args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = op.Wait()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Wait for any remaining I/O to be flushed
|
|
<-args.DataDone
|
|
|
|
// get the return code
|
|
opAPI := op.Get()
|
|
rc := int(opAPI.Metadata["return"].(float64))
|
|
|
|
if rc != 0 {
|
|
return errors.New(output.String())
|
|
}
|
|
|
|
fmt.Println(output.String())
|
|
|
|
return nil
|
|
}
|
|
|
|
// Push file to container.
|
|
func (c *IncusClient) Push(name, src, dst string) error {
|
|
fmt.Printf("cp %s %s%s\n", src, name, dst)
|
|
f, err := os.Open(src)
|
|
if err != nil {
|
|
return fmt.Errorf("error reading %s: %w", src, err)
|
|
}
|
|
defer f.Close()
|
|
|
|
return c.Client.CreateInstanceFile(name, dst, incus.InstanceFileArgs{
|
|
Content: f,
|
|
Mode: 0644,
|
|
})
|
|
}
|
|
|
|
// Start the given container.
|
|
func (c *IncusClient) Start(name string) error {
|
|
fmt.Println("starting", name)
|
|
reqState := api.InstanceStatePut{
|
|
Action: "start",
|
|
Timeout: -1,
|
|
}
|
|
|
|
op, err := c.Client.UpdateInstanceState(name, reqState, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return op.Wait()
|
|
}
|
|
|
|
// Stop the given container.
|
|
func (c *IncusClient) Stop(name string) error {
|
|
fmt.Println("stopping", name)
|
|
reqState := api.InstanceStatePut{
|
|
Action: "stop",
|
|
Force: true,
|
|
Timeout: 10,
|
|
}
|
|
|
|
op, err := c.Client.UpdateInstanceState(name, reqState, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return op.Wait()
|
|
}
|