lxdops

package module
v0.0.0-...-985a361 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 5, 2021 License: Apache-2.0 Imports: 26 Imported by: 0

README

lxdops

Go program that launches and configures LXD containers and manages ZFS filesystems attached to these containers as disk devices. It reads container and filesystem configuration from YAML files.

Examples

  • Create and configure a stopped container that we'll use to clone other containers from:
lxdops launch alp.yaml
  • Create a dev container by cloning alp:
lxdops launch dev.yaml
  • Create @test ZFS snapshots of dev's attached disk devices:
lxdops snapshot -s test dev.yaml
  • Make a clone of dev for testing
lxdops launch dev-test.yaml
  • Rebuild all three containers from the latest LXD image:
lxdops rebuild alp.yaml
lxdops rebuild dev-test.yaml
# test dev-test, to make sure all is well, and then rebuild *dev*:
lxdops rebuild dev.yaml

rebuild preserves the container ip address (since 2021-04-05).

Every container in these examples has its own /home filesystem as an attached disk device, independent from the container.

The examples are in demo/

cd ./demo

Before running them, specify a (zfsroot) global property:

lxdops property set zfsroot <zfs-filesystem>

Replace with an existing ZFS filesystem (preferably empty).

It's best to run the examples in a clean LXD project so they don't interfere with any other containers or profiles. See the LXD Project Support section below about how to create a clean project.

home.yaml (included by other examples)

filesystems:
  main:
    pattern: (zfsroot)/(project/)home/(instance)
devices:
  home:
    path: /home
    filesystem: main
  • specify a /home filesystem to attach to containers.

The location of the filesystem is parameterized by the name of the container (instance), the LXD project (project/) and a global property (zfsroot).

If the /home filesystem exists, it will be reused, otherwise it will be created, or cloned and/or copied from another container.

user.yaml (included by other examples)

users:
    - name: user1
      uid: 2001
      shell: /bin/bash
      sudo: true
      ssh: true
      groups:
        - wheel
        - adm
  • create a user
  • install ~/.ssh/authorized_keys by copying the calling user's ~/.ssh/authorized_keys'

alp.yaml - launch a container from scratch

os:
  name: alpine
  version: 3.13
packages:
- sudo
- bash
include:
- home.yaml
stop: true
snapshot: copy
  • create an alpine container
  • create a zfs filesystem and attach it to the container as /home
  • install the specified packages
  • stop the container after configuring
  • create a container snapshot named "copy"
  • we don't create any users in this container. We'll create users in its clones.

dev.yaml - clone a container

os:
  name: alpine
origin: alp/copy
device-template: alp
include:
- home.yaml
- user.yaml

  • clone the alp/copy container snapshot
  • attach a new /home directory
  • copy files form alp's /home
  • add a user

dev-test.yaml - clone a container and its attached filesystems

os:
  name: alpine
origin: alp/copy
device-origin: dev@test
include:
- home.yaml
- user.yaml

dev-test is created the same way as dev, except that its /home filesystem is cloned from the @test snapshot of dev's /home.

Description

lxdops launches, copies, and deletes lxdops instances.

An lxdops instance is:

  • An LXD container
  • A set of ZFS filesystems
  • A set of disk devices that are in these filesystems and are attached to the container (via a profile)
  • An LXD profile that specifies the attached disk devices

A Yaml instance configuration file specifies how to launch and configure an instance. It specifies:

  • packages to install in the container
  • LXD profiles to attach to the container
  • ZFS Filesystems to create and zfs properties for these filesystems
  • Disk Devices in these filesystems that are attached to the container
  • An LXD profile to create with the instance devices
  • scripts to run in the container
  • files to push to the container
  • users to create in the container, with optional sudo privileges and .ssh/authorized_keys

Several configuration elements can be parameterized with properties such as the instance name, project, and user-defined properties. This allows test instances to have their devices under a separate test filesystem, etc.

More detailed documentation of configuration elements is in the file Config.go

LXD Project Support

lxdops has support for LXD projects and can clone instances across projects. I find it simpler to keep all my instances in a single project.

If an instance does not specify a specific project, lxdops will use the current LXD project, as specified in ~/snap/lxd/current/.config/lxc/config.yml or ~/.config/lxc/config.yml

To create a new clean LXD project, t1, you can use an lxdops convenience command:

lxdops project create t1
lxc project switch t1

This creates a new project with:

  • Its own profiles. lxdops creates a profile for each container
  • Shared images. lxdops does not create, modify, or delete any images
  • The default profile copied from the default project

More Examples

A more elaborate set of configuration files is provided in a separate repository: https://github.com/melato/lxdops.script

Build (requires go 1.16)

export GO111MODULE=auto
export GOPATH=~/go
export GOBIN=~/bin

go get melato.org/lxdops
# this will clone the lxdops repository from github and all dependencies to $GOPATH/src

If you prefer to not use my go get server, something like this also works:

mkdir -p $GOPATH/src/melato.org
cd $GOPATH/src/melato.org
git clone https://github.com/melato/command
git clone https://github.com/melato/script
git clone https://github.com/melato/table3
git clone https://github.com/melato/lxdops

go get gopkg.in/yaml.v2
go get github.com/lxc/lxd

Compile:

cd $GOPATH/src/melato.org/lxdops/main
go install lxdops.go

# check that it was built:
~/bin/lxdops version

Debian/Ubuntu

On a minimal Debian or Ubuntu system, you may need to do this, if you get build errors:

apt install git gcc libc6-dev

Alpine

On Alpine Linux, you may need to do this:

apk add git gcc libc-dev linux-headers

and compile with static linking, so you can use it on a Debian/Ubuntu LXD host:

go install -ldflags -extldflags "-static" lxdops.go

External Programs

lxdops calls these external programs, on the host, with sudo when necessary:

  • lxc (It mostly uses the LXD API, but uses the "lxc" command for launching and cloning containers)
  • zfs
  • rsync
  • chown
  • mkdir
  • mv

lxdops calls these external programs, in the container:

  • sh
  • chpasswd
  • chown
  • OS-specific commands for adding packages and creating users

Documentation

Index

Constants

View Source
const DefaultProject = "default"

Variables

View Source
var OSTypes map[string]OSType

Functions

func AnnotateLXDError

func AnnotateLXDError(name string, err error) error

func BaseName

func BaseName(file string) string

func FileExists

func FileExists(server lxd.InstanceServer, container string, file string) bool

func NopCloser

func NopCloser(r io.Writer) io.WriteCloser

NopCloser returns a ReadCloser with a no-op Close method wrapping the provided Reader r.

func PrintConfigYaml

func PrintConfigYaml(config *Config) error

func RandomDeviceName

func RandomDeviceName() string

func RootCommand

func RootCommand() *command.SimpleCommand

func RootPaths

func RootPaths(paths []string) []string

RootPaths returns a copy of paths , removing any path that is a descendant of another one

func SplitSnapshotName

func SplitSnapshotName(name string) (container, snapshot string)

func WaitForNetwork

func WaitForNetwork(server lxd.InstanceServer, container string) error

Types

type Config

type Config struct {
	// ConfigTop fields are not merged with included files
	ConfigTop `yaml:",inline"`
	// ConfigInherit fields are merged with all included files, depth first
	ConfigInherit `yaml:",inline"`
}

Config - Instance configuration An instance is a name that is used to launch a container or create LXD disk devices, typically both. The instance name, is the same as the base name of the config file, without the extension. It can be overridden by the -name flag Configuration sections are applied in the order that they are mentioned in the Config: - PreScripts - Packages - Users - Files - Scripts - Passwords

func ReadConfig

func ReadConfig(file string) (*Config, error)

func ReadConfigYaml

func ReadConfigYaml(file string) (*Config, error)

* Read raw config from yaml

func ReadRawConfig

func ReadRawConfig(file string) (*Config, error)

* Read config without includes

func (*Config) Filesystem

func (t *Config) Filesystem(id string) *Filesystem

Return the filesystem for the given id, or nil if it doesn't exist.

func (*Config) Print

func (t *Config) Print() error

func (*Config) ResolvePaths

func (t *Config) ResolvePaths(dir string)

func (*Config) Verify

func (config *Config) Verify() bool

func (*Config) VerifyFileExists

func (t *Config) VerifyFileExists(file HostPath) bool

* Check that the requirements are met

type ConfigInherit

type ConfigInherit struct {
	// Properties provide key-value pairs used for pattern substitution.
	// They override built-in properties
	// Properties from all included files are merged before they are applied.  Properties cannot override non-empty properties,
	// in order to avoid unexpected behavior that depends on the order of included files.
	Properties map[string]string `yaml:"properties"`

	OS *OS

	// Project is the LXD project where the container is
	Project string `yaml:"project"`

	// Experimental: The name of the container. Defaults to (instance)
	Container Pattern `yaml:"container,omitempty"`

	// ProfilePattern specifies how the instance profile should be named.
	// It defaults to "(instance).lxdops"
	Profile Pattern `yaml:"profile-pattern"`

	// ProfileConfig specifies Config entries to be added to the instance profile.
	// This was meant for creating templates with boot.autostart: "false",
	// without needing to use profiles external to lxdops.
	ProfileConfig map[string]string `yaml:"profile-config"`

	// Source specifies where to copy or clone the instance from
	Source `yaml:",inline"`

	// Extra options passed to lxc launch.
	LxcOptions []string `yaml:"lxc-options,omitempty,flow"`

	// Include is a list of other configs that are to be included.
	// Include paths are either absolute or relative to the path of the including config.
	Include []HostPath `yaml:"include,omitempty"`

	// Filesystems are zfs filesystems or plain directories that are created
	// when an instance is created.  Devices are created inside filesystems.
	Filesystems map[string]*Filesystem `yaml:"filesystems"`
	// Devices are disk devices that are directories within the instance filesystems
	// They are created and attached to the container via the instance profile
	Devices map[string]*Device `yaml:"devices"`

	Profiles []string `yaml:"profiles"`

	// PreScripts are scripts that are executed early, before packages, users, files, or Scripts
	PreScripts []*Script `yaml:"pre-scripts"`

	// Packages are OS packages that are installed when the instance is launched
	Packages []string `yaml:"packages"`

	// Users are OS users that are created when the instance is launched
	Users []*User `yaml:"users"`
	// Files are files that are copied from the host to the container when the instance is launched (as with lxc file push).
	Files []*File `yaml:"files"`
	// Scripts are scripts that are executed in the container (as with lxc exec)
	Scripts []*Script `yaml:"scripts"`
	// Passwords are a list of OS accounts, whose password is set to a random password
	Passwords []string `yaml:"passwords"`
}

type ConfigOps

type ConfigOps struct {
}

func (*ConfigOps) Includes

func (t *ConfigOps) Includes(file ...string) error

func (*ConfigOps) Script

func (t *ConfigOps) Script(file string, script string) error

type ConfigOptions

type ConfigOptions struct {
	Project    string   `name:"project" usage:"the LXD project to use.  Overrides Config.Project"`
	Name       string   `` /* 154-byte string literal not displayed */
	Properties []string `` /* 132-byte string literal not displayed */

	PropertyOptions
	// contains filtered or unexported fields
}

func (*ConfigOptions) Configured

func (t *ConfigOptions) Configured() error

func (*ConfigOptions) CurrentProject

func (t *ConfigOptions) CurrentProject() string

func (*ConfigOptions) Init

func (t *ConfigOptions) Init() error

func (*ConfigOptions) Instance

func (t *ConfigOptions) Instance(file string) (*Instance, error)

func (*ConfigOptions) InstanceFunc

func (t *ConfigOptions) InstanceFunc(f func(*Instance) error, multiple bool) func(configs []string) error

func (*ConfigOptions) ReadConfig

func (t *ConfigOptions) ReadConfig(file string) (*Config, error)

func (*ConfigOptions) RunInstances

func (t *ConfigOptions) RunInstances(f func(*Instance) error, args ...string) error

func (*ConfigOptions) UpdateConfig

func (t *ConfigOptions) UpdateConfig(config *Config)

type ConfigReader

type ConfigReader struct {
	Warn    bool
	Verbose bool
	// contains filtered or unexported fields
}

func (*ConfigReader) Read

func (r *ConfigReader) Read(file string) (*Config, error)

type ConfigTop

type ConfigTop struct {
	// Description is provided for documentation
	Description string `yaml:"description"`

	// Stop specifies that the container should be stopped at the end of the configuration
	Stop bool `yaml:"stop"`

	// Snapshot specifies that that the container should be snapshoted with this name at the end of the configuration process.
	Snapshot string `yaml:"snapshot"`
}

type Configurer

type Configurer struct {
	Client *LxdClient `name:"-"`
	ConfigOptions
	Trace      bool     `name:"trace,t" usage:"print exec arguments"`
	DryRun     bool     `name:"dry-run" usage:"show the commands to run, but do not change anything"`
	Components []string `name:"components" usage:"which components to configure: packages, scripts, users"`
	All        bool     `` /* 142-byte string literal not displayed */
	Packages   bool     `name:"packages" usage:"whether to install packages"`
	Scripts    bool     `name:"scripts" usage:"whether to run scripts"`
	Files      bool     `name:"files" usage:"whether to copy files"`
	Users      bool     `name:"users" usage:"whether to create users and change passwords"`
}

func (*Configurer) ConfigureContainer

func (t *Configurer) ConfigureContainer(instance *Instance) error

* run things inside the container: install packages, create users, run scripts

func (*Configurer) Configured

func (t *Configurer) Configured() error

func (*Configurer) CurrentProject

func (t *Configurer) CurrentProject() string

func (*Configurer) Init

func (t *Configurer) Init() error

func (*Configurer) NewExec

func (t *Configurer) NewExec(project string, name string) *execRunner

func (*Configurer) NewScript

func (t *Configurer) NewScript() *script.Script

type ContainerDevicesOp

type ContainerDevicesOp struct {
	ContainerOps *ContainerOps `name:""`
	Yaml         bool
}

func (*ContainerDevicesOp) Devices

func (t *ContainerDevicesOp) Devices(container string) error

type ContainerOps

type ContainerOps struct {
	Client *LxdClient `name:"-"`
	// contains filtered or unexported fields
}

func (*ContainerOps) Config

func (t *ContainerOps) Config(container string) error

func (*ContainerOps) Configured

func (t *ContainerOps) Configured() error

func (*ContainerOps) Network

func (t *ContainerOps) Network(container string) error

func (*ContainerOps) Profiles

func (t *ContainerOps) Profiles(container string) error

func (*ContainerOps) State

func (t *ContainerOps) State(container string, action ...string) error

func (*ContainerOps) Wait

func (t *ContainerOps) Wait(args []string) error

type ContainerSource

type ContainerSource struct {
	Project   string
	Container string
	Snapshot  string
}

func (*ContainerSource) IsDefined

func (t *ContainerSource) IsDefined() bool

func (*ContainerSource) String

func (t *ContainerSource) String() string

type Device

type Device struct {
	// Path is the device "path" in the LXD disk device
	Path string

	// Filesystem is the Filesystem Id that this device belongs to
	Filesystem string `yaml:"filesystem"`

	// Dir is the subdirectory of the Device, relative to its Filesystem
	// If empty, it default to the device Name
	// If Dir == ".", the device source is the same as the Filesystem directory
	// Rarely used:
	// Dir goes through pattern substitution, using parenthesized tokens, for example (instance)
	// Dir may be absolute, but this is no longer necessary now that filesystems are specified, since one can define the "/" filesystem.
	Dir Pattern `yaml:""`
}

A Device is an LXD disk device that is attached to the instance profile, which in turn is attached to a container

type DeviceConfigurer

type DeviceConfigurer struct {
	Client  *LxdClient
	Config  *Config
	Trace   bool
	DryRun  bool
	FuncMap map[string]func() (string, error)
}

func NewDeviceConfigurer

func NewDeviceConfigurer(client *LxdClient, config *Config) *DeviceConfigurer

func (*DeviceConfigurer) ConfigureDevices

func (t *DeviceConfigurer) ConfigureDevices(instance *Instance) error

func (*DeviceConfigurer) CreateDir

func (t *DeviceConfigurer) CreateDir(dir string, chown bool) error

func (*DeviceConfigurer) CreateFilesystem

func (t *DeviceConfigurer) CreateFilesystem(fs *InstanceFS, originDataset string) error

func (*DeviceConfigurer) CreateFilesystems

func (t *DeviceConfigurer) CreateFilesystems(instance, origin *Instance, snapshot string) error

func (*DeviceConfigurer) CreateProfile

func (t *DeviceConfigurer) CreateProfile(instance *Instance) error

func (*DeviceConfigurer) NewScript

func (t *DeviceConfigurer) NewScript() *script.Script

func (*DeviceConfigurer) RenameFilesystems

func (t *DeviceConfigurer) RenameFilesystems(oldInstance, newInstance *Instance) error

type DeviceSource

type DeviceSource struct {
	Instance *Instance
	Snapshot string
	Clone    bool
}

func (*DeviceSource) IsDefined

func (t *DeviceSource) IsDefined() bool

func (*DeviceSource) String

func (t *DeviceSource) String() string

type Doas

type Doas struct {
	// contains filtered or unexported fields
}

func (*Doas) Configure

func (t *Doas) Configure(user string) []string

type DryRun

type DryRun struct {
	DryRun bool `name:"dry-run" usage:"show the commands to run, but do not change anything"`
}

type File

type File struct {
	// Path is the file path in the container
	Path string

	// Source is the file path on the host
	Source HostPath

	// The file's mode as a string, e.g. 0775
	Mode string

	// Uid is the file's numeric uid in the container
	Uid int

	// Gid is the file's numeric gid in the container
	Gid int

	// User is the file's owner name in the container.  It is an error if both uid and user are set.
	User string

	// Group is the file's group owner name in the container.  It is an error if both gid and group are set.
	Group string
}

File specifies a file that is copied from the host to the container

type Filesystem

type Filesystem struct {
	// Pattern is a pattern that is used to produce the directory or zfs filesystem
	// If the pattern begins with '/', it is a directory
	// If it does not begin with '/', it is a zfs filesystem name
	Pattern Pattern
	// Zfsproperties is a list of properties that are set when a zfs filesystem is created or cloned
	Zfsproperties map[string]string `yaml:""`
}

Filesystem is a ZFS filesystem or a plain directory that is created when an instance is created The disk devices of an instance are created as subdirectories of a Filesystem

type HostPath

type HostPath string

HostPath is a file path on the host, which is either absolute or relative to a base directory When a config file includes another config file, the base directory is the directory of the including file

func (HostPath) Resolve

func (path HostPath) Resolve(dir string) HostPath

type Ids

type Ids struct {
	Exec *execRunner
	// contains filtered or unexported fields
}

func (*Ids) Gid

func (t *Ids) Gid(group string) (int64, error)

func (*Ids) Uid

func (t *Ids) Uid(user string) (int64, error)

type Instance

type Instance struct {
	GlobalProperties map[string]string
	Config           *Config
	Name             string

	Properties *util.PatternProperties
	// contains filtered or unexported fields
}

func NewInstance

func NewInstance(globalProperties map[string]string, config *Config, name string) (*Instance, error)

func (*Instance) Container

func (t *Instance) Container() string

func (*Instance) ContainerSource

func (t *Instance) ContainerSource() *ContainerSource

func (*Instance) DeviceDir

func (t *Instance) DeviceDir(deviceId string, device *Device) (string, error)

func (*Instance) DeviceList

func (t *Instance) DeviceList() ([]InstanceDevice, error)

func (*Instance) DeviceSource

func (t *Instance) DeviceSource() *DeviceSource

func (*Instance) FilesystemList

func (t *Instance) FilesystemList() ([]*InstanceFS, error)

func (*Instance) Filesystems

func (t *Instance) Filesystems() (map[string]*InstanceFS, error)

func (*Instance) GetSourceConfig

func (t *Instance) GetSourceConfig() (*Config, error)

GetSourceConfig returns the parsed configuration specified by Config.SourceConfig If there is no Config.SourceConfig, it returns this instance's config It returns a non nil *Config or an error.

func (*Instance) NewDeviceMap

func (t *Instance) NewDeviceMap() (map[string]map[string]string, error)

func (*Instance) NewInstance

func (t *Instance) NewInstance(name string) (*Instance, error)

func (*Instance) PrintDevices

func (instance *Instance) PrintDevices() error

func (*Instance) ProfileName

func (t *Instance) ProfileName() string

func (*Instance) Snapshot

func (instance *Instance) Snapshot(name string) error

Snapshot creates a snapshot of all ZFS filesystems of the instance

type InstanceDevice

type InstanceDevice struct {
	Device *Device
	Name   string
	Source string
}

type InstanceDeviceList

type InstanceDeviceList []InstanceDevice

func (InstanceDeviceList) Len

func (t InstanceDeviceList) Len() int

func (InstanceDeviceList) Less

func (t InstanceDeviceList) Less(i, j int) bool

func (InstanceDeviceList) Sort

func (t InstanceDeviceList) Sort()

func (InstanceDeviceList) Swap

func (t InstanceDeviceList) Swap(i, j int)

type InstanceFS

type InstanceFS struct {
	Id         string
	Path       string
	IsNew      bool
	Filesystem *Filesystem
}

func (*InstanceFS) Dir

func (t *InstanceFS) Dir() string

func (*InstanceFS) IsDir

func (t *InstanceFS) IsDir() bool

func (*InstanceFS) IsZfs

func (t *InstanceFS) IsZfs() bool

type InstanceFSList

type InstanceFSList []*InstanceFS

func (InstanceFSList) Len

func (t InstanceFSList) Len() int

func (InstanceFSList) Less

func (t InstanceFSList) Less(i, j int) bool

func (InstanceFSList) Roots

func (t InstanceFSList) Roots() []*InstanceFS

Roots returns the FSPaths that are not children of other FSPaths, within this set.

func (InstanceFSList) Sort

func (t InstanceFSList) Sort()

func (InstanceFSList) Swap

func (t InstanceFSList) Swap(i, j int)

type InstanceOps

type InstanceOps struct {
	ConfigOptions
	Trace  bool
	DryRun bool `name:"dry-run" usage:"show the commands to run, but do not change anything"`
}

func (*InstanceOps) Configured

func (t *InstanceOps) Configured() error

func (*InstanceOps) CurrentProject

func (t *InstanceOps) CurrentProject() string

func (*InstanceOps) Description

func (t *InstanceOps) Description(instance *Instance) error

Description prints the description of the instance

func (*InstanceOps) Devices

func (t *InstanceOps) Devices(instance *Instance) error

func (*InstanceOps) Filesystems

func (t *InstanceOps) Filesystems(instance *Instance) error

func (*InstanceOps) Init

func (t *InstanceOps) Init() error

func (*InstanceOps) Packages

func (t *InstanceOps) Packages(instance *Instance) error

Packages prints the packages installed by the instance

func (*InstanceOps) Project

func (t *InstanceOps) Project(instance *Instance) error

func (*InstanceOps) Properties

func (t *InstanceOps) Properties(instance *Instance) error

Properties prints the instance properties

func (*InstanceOps) Verify

func (t *InstanceOps) Verify(instance *Instance) error

type InstanceServer

type InstanceServer struct {
	Server lxd.InstanceServer
}

func (InstanceServer) ProfileExists

func (t InstanceServer) ProfileExists(profile string) bool

func (InstanceServer) StartContainer

func (t InstanceServer) StartContainer(container string) error

func (InstanceServer) StopContainer

func (t InstanceServer) StopContainer(container string) error

type Launcher

type Launcher struct {
	Client *LxdClient `name:"-"`
	ConfigOptions
	Trace  bool `name:"t" usage:"trace print what is happening"`
	DryRun bool `name:"dry-run" usage:"show the commands to run, but do not change anything"`
}

func (*Launcher) Configured

func (t *Launcher) Configured() error

func (*Launcher) CreateDevices

func (t *Launcher) CreateDevices(instance *Instance) error

func (*Launcher) CreateProfile

func (t *Launcher) CreateProfile(instance *Instance) error

func (*Launcher) CurrentProject

func (t *Launcher) CurrentProject() string

func (*Launcher) DeleteContainer

func (t *Launcher) DeleteContainer(instance *Instance) error

func (*Launcher) Init

func (t *Launcher) Init() error

func (*Launcher) LaunchContainer

func (t *Launcher) LaunchContainer(instance *Instance) error

func (*Launcher) NewConfigurer

func (t *Launcher) NewConfigurer() *Configurer

func (*Launcher) NewScript

func (t *Launcher) NewScript() *script.Script

func (*Launcher) Rebuild

func (t *Launcher) Rebuild(instance *Instance) error

func (*Launcher) Rename

func (t *Launcher) Rename(configFile string, newname string) error

type LxdClient

type LxdClient struct {
	Socket string
	// contains filtered or unexported fields
}

func (*LxdClient) CurrentProject

func (t *LxdClient) CurrentProject() string

func (*LxdClient) Init

func (t *LxdClient) Init() error

func (*LxdClient) ProjectServer

func (t *LxdClient) ProjectServer(project string) (lxd.InstanceServer, error)

func (*LxdClient) RootServer

func (t *LxdClient) RootServer() (lxd.InstanceServer, error)

type LxdOps

type LxdOps struct {
	Client *LxdClient `name:"-"`
	Trace  bool       `name:"trace,t" usage:"print exec arguments"`
}

func (*LxdOps) AddDiskDevice

func (t *LxdOps) AddDiskDevice(profile, source, path string) error

func (*LxdOps) ProfileExists

func (t *LxdOps) ProfileExists(profile string) error

type OS

type OS struct {
	// Name if the name of the container image, without the version number.
	// All included configuration files should have the same OS Name.
	// Supported OS names are "alpine", "debian", "ubuntu".
	// Support for an OS is the ability to determine the LXD image, install packages, create users, set passwords
	Name string `yaml:"name"`
	// Version is the image version, e.g. 3.13, 10.04.  The image name is composed of Name/Version
	// Version is optional in configuration files, but the final assembled configuration file should have a OS Version.
	// It should typically be specified in one configuration file that is included by all other configuration files that use use this OS
	Version string `yaml:"version"`
	// contains filtered or unexported fields
}

OS specifies the container OS

func (*OS) Equals

func (t *OS) Equals(x *OS) bool

func (*OS) IsAlpine

func (t *OS) IsAlpine() bool

func (*OS) Merge

func (t *OS) Merge(c *OS) error

func (*OS) String

func (t *OS) String() string

func (*OS) Type

func (t *OS) Type() OSType

type OSType

type OSType interface {
	NeedPasswords() bool
	ImageName(version string) string
	InstallPackageCommand(pkg string) string
	AddUserCommand(u *User) []string
}

type ParseOp

type ParseOp struct {
	Raw     bool `usage:"do not process includes"`
	Verbose bool `name:"v" usage:"verbose"`
}

func (*ParseOp) Parse

func (t *ParseOp) Parse(file ...string) error

func (*ParseOp) Print

func (t *ParseOp) Print(file string) error

type Path

type Path string

func (Path) IsDescendantOf

func (c Path) IsDescendantOf(p string) bool

IsDescendantOf returns true if c is under p's filesystem hierarchy

type Pattern

type Pattern string

Pattern is a string that is converted via property substitution, before it is used Properties are denoted in the pattern via (<key>), where <key> is the property key There are built-in properties like instance, project. Custom properties are defined in Config.Properties, and override built-in properties

func (Pattern) Substitute

func (pattern Pattern) Substitute(properties *util.PatternProperties) (string, error)

type ProfileConfigurer

type ProfileConfigurer struct {
	Client *LxdClient
	ConfigOptions
	Trace  bool
	DryRun bool `name:"dry-run" usage:"show the commands to run, but do not change anything"`
}

func (*ProfileConfigurer) Apply

func (t *ProfileConfigurer) Apply(instance *Instance) error

func (*ProfileConfigurer) Configured

func (t *ProfileConfigurer) Configured() error

func (*ProfileConfigurer) CurrentProject

func (t *ProfileConfigurer) CurrentProject() string

func (*ProfileConfigurer) Diff

func (t *ProfileConfigurer) Diff(instance *Instance) error

func (*ProfileConfigurer) Init

func (t *ProfileConfigurer) Init() error

func (*ProfileConfigurer) List

func (t *ProfileConfigurer) List(instance *Instance) error

func (*ProfileConfigurer) NewScript

func (t *ProfileConfigurer) NewScript() *script.Script

func (*ProfileConfigurer) Profiles

func (t *ProfileConfigurer) Profiles(instance *Instance) ([]string, error)

func (*ProfileConfigurer) Reorder

func (t *ProfileConfigurer) Reorder(instance *Instance) error

type ProjectCopyProfiles

type ProjectCopyProfiles struct {
	Client        *LxdClient `name:"-"`
	SourceProject string     `name:"source-project" usage:"project to copy profiles from"`
	TargetProject string     `name:"target-project" usage:"project to copy profiles to"`
}

func (*ProjectCopyProfiles) CopyProfiles

func (t *ProjectCopyProfiles) CopyProfiles(profiles []string) error

type ProjectCreate

type ProjectCreate struct {
	Client *LxdClient `name:"-"`
}

func (*ProjectCreate) Create

func (t *ProjectCreate) Create(projects ...string) error

type PropertyOptions

type PropertyOptions struct {
	PropertiesFile   string            `name:"properties" usage:"a file containing global config properties.  Instance properties override global properties"`
	GlobalProperties map[string]string `name:"-"`
}

func (*PropertyOptions) Configured

func (t *PropertyOptions) Configured() error

func (*PropertyOptions) File

func (t *PropertyOptions) File()

func (*PropertyOptions) Init

func (t *PropertyOptions) Init() error

func (*PropertyOptions) List

func (t *PropertyOptions) List()

func (*PropertyOptions) Set

func (t *PropertyOptions) Set(key, value string) error

type RebuildOptions

type RebuildOptions struct {
	Hwaddresses map[string]string
}

type Script

type Script struct {
	// An optional name that identifies the script, useful for debugging/testing
	Name string `yaml:"name"`

	// File is an optional host file that contains the script content.
	// It should be an executable.  It is copied to the container in /root/ and run there.
	File HostPath `yaml:"file"`

	// Reboot specifies that the container should be rebooted after running this script
	// This may be needed when replacing /etc files
	// Reboot may be slow, so avoid it, if possible
	Reboot bool `yaml:"reboot"`

	// Body is the content of the script
	// It is passed as the stdin to sh
	Body string `yaml:"body"`

	// Dir is the directory in the container to set as the working directory when running the script
	Dir string `yaml:"dir"`

	// Uid is the container uid to run the script as
	Uid uint32 `yaml:"uid"`

	// Gid is the container gid to run the script as
	Gid uint32 `yaml:"gid"`
}

Script specifies a sh script that is run in the container

type Snapshot

type Snapshot struct {
	ConfigOptions
	SnapshotParams
}

func (*Snapshot) Configured

func (t *Snapshot) Configured() error

func (*Snapshot) CurrentProject

func (t *Snapshot) CurrentProject() string

func (*Snapshot) DestroySnapshot

func (t *Snapshot) DestroySnapshot(instance *Instance) error

func (*Snapshot) Init

func (t *Snapshot) Init() error

func (*Snapshot) Run

func (t *Snapshot) Run(instance *Instance) error

type SnapshotParams

type SnapshotParams struct {
	DryRun    bool   `name:"dry-run" usage:"show the commands to run, but do not change anything"`
	Snapshot  string `name:"s" usage:"short snapshot name"`
	Destroy   bool   `name:"d" usage:"destroy snapshots"`
	Recursive bool   `name:"R" usage:"zfs destroy -R: Recursively destroy all dependents, including cloned datasets"`
}

type Source

type Source struct {
	// origin is the name of a container and a snapshot to clone from.
	// It has the form [<project>_]<container>[/<snapshot>]
	// It overrides SourceConfig
	Origin Pattern `yaml:"origin"`

	// device-template is the name of an instance, whose devices are copied (using rsync)
	// to a new instance with launch.
	// The devices are copied from the filesystems specified in SourceConfig, or this config.
	DeviceTemplate Pattern `yaml:"device-template"`

	// device-origin is the name an instance and s short snapshot name.
	// It has the form <instance>@<snapshot> where <instance> is an instance name,
	// and @<snapshot> is a the short snapshot name of the instance filesystems.
	// Each device zfs filesystem is cloned from @<snapshot>
	// The filesytems are those specified in SourceConfig, if any, otherwise this config.
	DeviceOrigin Pattern `yaml:"device-origin"`

	// Experimental: source-config specifies a config file that is used to determine:
	//   - The LXD project, container, and snapshot to clone when launching the instance.
	//   - The source filesystems used for cloning filesystems or copying device directories.
	// The name of the instance used for the source filesystems
	// is the base name of the filename, without the extension.
	// Various parts of these items can be overriden by other source properties above
	SourceConfig HostPath `yaml:"source-config,omitempty"`
}

Source specifies how to copy or clone the instance container, filesystem, and device directories. When DeviceTemplate is specified, the filesystems are copied with rsync. When DeviceOrigin is specified, the filesystems are cloned with zfs-clone The filesystems that are copied are determined by applying the source instance name to the filesystems of this config, or to the filesystems of a source config.

When basing an instance on a template with few skeleton files, it is preferable to copy with a DeviceTemplate, so the container's disk devices are not tied to the template.

Example: suppose test-a.yaml has:

origin: a/copy
filesystems: "default": "z/test/(instance)"
device-origin: a@copy
source-filesystems "default": "z/prod/(instance)"
devices: home, path=/home, filesystem=default

This would do something like:

zfs clone z/prod/a@copy z/test/test-a
lxc copy --container-only a/copy test-a
lxc profile create test-a.lxdops
lxc profile device add test-a.lxdops home disk path=/home source=/z/test/test-a/home
lxc profile add test-a test-a.lxdops

type Sudo

type Sudo struct {
	// contains filtered or unexported fields
}

func (*Sudo) Configure

func (t *Sudo) Configure(user string) []string

type Trace

type Trace struct {
	Trace bool `name:"trace,t" usage:"print exec arguments"`
}

type User

type User struct {
	// Name is the user name.  If missing, the user takes the name of current host user
	Name string `yaml:"name"`
	// Uid is an optional uid for the user
	Uid string `yaml:"uid"`
	// Sudo gives full passwordless sudo privileges to the user
	Sudo bool `yaml:"sudo"`
	// Ssh specifies that the current user's ~.ssh/authorized_keys should be copied from the host to this user
	Ssh bool `yaml:"ssh"`
	// Shell is the user shell
	Shell string `yaml:"shell"`
	// Home is the user home directory, optional
	Home string `yaml:"home"`
	// Groups is a list of groups that the user is added to
	Groups []string `yaml:"groups"`
}

An OS user

func (*User) EffectiveUser

func (u *User) EffectiveUser() *User

func (*User) HomeDir

func (user *User) HomeDir() string

func (*User) IsValidName

func (u *User) IsValidName() bool

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL