Skip to content

Commit

Permalink
Add options to LXC builder for influencing for how containers are bui…
Browse files Browse the repository at this point in the history
…lt and started

via

- create_options: a list of options passed to lxc-create
- start_options: a list of options passed to lxc-start
- attach_options: a list of options passed to lxc-attach

Also extended existing LXC builder BATS tests to exercise the new builder
options, and added website docs.
  • Loading branch information
Matt Schreiber authored and Matt Schreiber committed Oct 31, 2017
1 parent fe4d464 commit 1f2135f
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 20 deletions.
8 changes: 7 additions & 1 deletion builder/lxc/communicator.go
Expand Up @@ -16,6 +16,7 @@ import (
type LxcAttachCommunicator struct {
RootFs string
ContainerName string
AttachOptions []string
CmdWrapper CommandWrapper
}

Expand Down Expand Up @@ -110,8 +111,13 @@ func (c *LxcAttachCommunicator) DownloadDir(src string, dst string, exclude []st

func (c *LxcAttachCommunicator) Execute(commandString string) (*exec.Cmd, error) {
log.Printf("Executing with lxc-attach in container: %s %s %s", c.ContainerName, c.RootFs, commandString)

attachCommand := []string{"sudo", "lxc-attach"}
attachCommand = append(attachCommand, c.AttachOptions...)
attachCommand = append(attachCommand, []string{"--name", "%s", "--", "/bin/sh -c \"%s\""}...)

command, err := c.CmdWrapper(
fmt.Sprintf("sudo lxc-attach --name %s -- /bin/sh -c \"%s\"", c.ContainerName, commandString))
fmt.Sprintf(strings.Join(attachCommand, " "), c.ContainerName, commandString))
if err != nil {
return nil, err
}
Expand Down
3 changes: 3 additions & 0 deletions builder/lxc/config.go
Expand Up @@ -18,6 +18,9 @@ type Config struct {
ContainerName string `mapstructure:"container_name"`
CommandWrapper string `mapstructure:"command_wrapper"`
RawInitTimeout string `mapstructure:"init_timeout"`
CreateOptions []string `mapstructure:"create_options"`
StartOptions []string `mapstructure:"start_options"`
AttachOptions []string `mapstructure:"attach_options"`
Name string `mapstructure:"template_name"`
Parameters []string `mapstructure:"template_parameters"`
EnvVars []string `mapstructure:"template_environment_vars"`
Expand Down
7 changes: 5 additions & 2 deletions builder/lxc/step_lxc_create.go
Expand Up @@ -28,12 +28,15 @@ func (s *stepLxcCreate) Run(state multistep.StateBag) multistep.StepAction {
}

commands := make([][]string, 3)
commands[0] = append(config.EnvVars, []string{"lxc-create", "-n", name, "-t", config.Name, "--"}...)
commands[0] = append(config.EnvVars, "lxc-create")
commands[0] = append(commands[0], config.CreateOptions...)
commands[0] = append(commands[0], []string{"-n", name, "-t", config.Name, "--"}...)
commands[0] = append(commands[0], config.Parameters...)
// prevent tmp from being cleaned on boot, we put provisioning scripts there
// todo: wait for init to finish before moving on to provisioning instead of this
commands[1] = []string{"touch", filepath.Join(rootfs, "tmp", ".tmpfs")}
commands[2] = []string{"lxc-start", "-d", "--name", name}
commands[2] = append([]string{"lxc-start"}, config.StartOptions...)
commands[2] = append(commands[2], []string{"-d", "--name", name}...)

ui.Say("Creating container...")
for _, command := range commands {
Expand Down
1 change: 1 addition & 0 deletions builder/lxc/step_provision.go
Expand Up @@ -19,6 +19,7 @@ func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction {
// Create our communicator
comm := &LxcAttachCommunicator{
ContainerName: config.ContainerName,
AttachOptions: config.AttachOptions,
RootFs: mountPath,
CmdWrapper: wrappedCommand,
}
Expand Down
1 change: 1 addition & 0 deletions builder/lxc/step_wait_init.go
Expand Up @@ -76,6 +76,7 @@ func (s *StepWaitInit) waitForInit(state multistep.StateBag, cancel <-chan struc

comm := &LxcAttachCommunicator{
ContainerName: config.ContainerName,
AttachOptions: config.AttachOptions,
RootFs: mountPath,
CmdWrapper: wrappedCommand,
}
Expand Down
98 changes: 82 additions & 16 deletions test/builder_lxc.bats
@@ -1,40 +1,106 @@
#!/usr/bin/env bats
#
# This tests the lxc builder. The teardown function will
# delete any images in the output-lxc-* folders.
# This tests the lxc builder by creating minimal containers and checking that
# custom lxc container configuration files are successfully applied. The
# teardown function will delete any images in the output-lxc-* folders along
# with the auto-generated lxc container configuration files and hook scripts.

#load test_helper
#fixtures builder-lxc
FIXTURE_ROOT="$BATS_TEST_DIRNAME/fixtures/builder-lxc"

have_command() {
command -v "$1" >/dev/null 2>&1
}

# Required parameters
command -v lxc-create >/dev/null 2>&1 || {
have_command lxc-create || {
echo "'lxc-create' must be installed via the lxc (or lxc1 for ubuntu >=16.04) package" >&2
exit 1
}

DESTROY_HOOK_SCRIPT=$FIXTURE_ROOT/destroy-hook.sh
DESTROY_HOOK_LOG=$FIXTURE_ROOT/destroy-hook.log
printf > "$DESTROY_HOOK_SCRIPT" '
echo "$LXC_NAME" > "%s"
' "$DESTROY_HOOK_LOG"
chmod +x "$DESTROY_HOOK_SCRIPT"

INIT_CONFIG=$FIXTURE_ROOT/lxc.custom.conf
printf > "$INIT_CONFIG" '
lxc.hook.destroy = %s
' "$DESTROY_HOOK_SCRIPT"

teardown() {
for f in "$INIT_CONFIG" "$DESTROY_HOOK_SCRIPT" "$DESTROY_HOOK_LOG"; do
[ -e "$f" ] && rm -f "$f"
done

rm -rf output-lxc-*
}

@test "lxc: build centos minimal.json" {
run packer build -var template_name=centos $FIXTURE_ROOT/minimal.json
[ "$status" -eq 0 ]
[ -f output-lxc-centos/rootfs.tar.gz ]
[ -f output-lxc-centos/lxc-config ]
assert_build() {
local template_name="$1"
shift

local build_status=0

run packer build -var template_name="$template_name" "$@"

[ "$status" -eq 0 ] || {
echo "${template_name} build exited badly: $status" >&2
echo "$output" >&2
build_status="$status"
}

for expected in "output-lxc-${template_name}"/{rootfs.tar.gz,lxc-config}; do
[ -f "$expected" ] || {
echo "missing expected artifact '${expected}'" >&2
build_status=1
}
done

return $build_status
}

assert_container_name() {
local container_name="$1"

[ -f "$DESTROY_HOOK_LOG" ] || {
echo "missing expected lxc.hook.destroy logfile '$DESTROY_HOOK_LOG'"
return 1
}

read -r lxc_name < "$DESTROY_HOOK_LOG"

[ "$lxc_name" = "$container_name" ]
}

@test "lxc: build centos minimal.json" {
have_command yum || skip "'yum' must be installed to build centos containers"
local container_name=packer-lxc-centos
assert_build centos -var init_config="$INIT_CONFIG" \
-var container_name="$container_name" \
$FIXTURE_ROOT/minimal.json
assert_container_name "$container_name"
}

@test "lxc: build trusty minimal.json" {
run packer build -var template_name=ubuntu -var template_parameters="SUITE=trusty" $FIXTURE_ROOT/minimal.json
[ "$status" -eq 0 ]
[ -f output-lxc-ubuntu/rootfs.tar.gz ]
[ -f output-lxc-ubuntu/lxc-config ]
have_command debootstrap || skip "'debootstrap' must be installed to build ubuntu containers"
local container_name=packer-lxc-ubuntu
assert_build ubuntu -var init_config="$INIT_CONFIG" \
-var container_name="$container_name" \
-var template_parameters="SUITE=trusty" \
$FIXTURE_ROOT/minimal.json
assert_container_name "$container_name"
}

@test "lxc: build debian minimal.json" {
run packer build -var template_name=debian -var template_parameters="SUITE=jessie" $FIXTURE_ROOT/minimal.json
[ "$status" -eq 0 ]
[ -f output-lxc-debian/rootfs.tar.gz ]
[ -f output-lxc-debian/lxc-config ]
have_command debootstrap || skip "'debootstrap' must be installed to build debian containers"
local container_name=packer-lxc-debian
assert_build debian -var init_config="$INIT_CONFIG" \
-var container_name="$container_name" \
-var template_parameters="SUITE=jessie" \
$FIXTURE_ROOT/minimal.json
assert_container_name "$container_name"
}
18 changes: 17 additions & 1 deletion test/fixtures/builder-lxc/minimal.json
@@ -1,13 +1,29 @@
{
"variables": {
"template_name": "debian",
"template_parameters": "SUITE=jessie"
"template_parameters": "SUITE=jessie",
"container_name": "packer-lxc",
"set_var": "hello"
},
"provisioners": [
{
"type": "shell",
"inline": [
"if [ \"$SET_VAR\" != \"{{user `set_var`}}\" ]; then",
" echo \"Got unexpected value '$SET_VAR' for SET_VAR\" 1>&2",
" exit 1",
"fi"
]
}
],
"builders": [
{
"type": "lxc",
"name": "lxc-{{user `template_name`}}",
"template_name": "{{user `template_name`}}",
"container_name": "{{user `container_name`}}",
"create_options": [ "-f", "{{user `init_config`}}" ],
"attach_options": [ "--clear-env", "--set-var", "SET_VAR={{user `set_var`}}" ],
"config_file": "/usr/share/lxc/config/{{user `template_name`}}.common.conf",
"template_environment_vars": [ "{{user `template_parameters`}}" ]
}
Expand Down
15 changes: 15 additions & 0 deletions website/source/docs/builders/lxc.html.md
Expand Up @@ -110,3 +110,18 @@ Below is a fully functioning example.
`/usr/share/lxc/templates/lxc-<template_name>`. Note: This gets passed as
ARGV to the template command. Ensure you have an array of strings, as
a single string with spaces probably won't work. Defaults to `[]`.

- `create_options` (array of strings) - Options to pass to `lxc-create`. For
instance, you can specify a custom LXC container configuration file with
`["-f", "/path/to/lxc.conf"]`. Defaults to `[]`. See `man 1 lxc-create` for
available options.

- `start_options` (array of strings) - Options to pass to `lxc-start`. For
instance, you can override parameters from the LXC container configuration
file via `["--define", "KEY=VALUE"]`. Defaults to `[]`. See `man 1
lxc-start` for available options.

- `attach_options` (array of strings) - Options to pass to `lxc-attach`. For
instance, you can prevent the container from inheriting the host machine's
environment by specifying `["--clear-env"]`. Defaults to `[]`. See `man 1
lxc-attach` for available options.

0 comments on commit 1f2135f

Please sign in to comment.