Usage
plash create CONTAINER [ CMD1 [ CMD2 ... ] ]
Description
Creates a new container from a command. If no command is passed, a shell is
started. The new container is printed to stdout, all other output goes to
stderr. Note that a new container is only returned if the build command
returns 0 (success) as exit code. For most cases use `plash build` for a
higher level interface.
Example
$ plash create 7 ./buildscript.sh
42
$ sudo plash create 3
home/fulano # echo 'hello' > /file
home/fulano # exit 0
71
$ plash b create -f ubuntu -- touch /myfile
44
Tested Behaviour
#!/bin/bash
set -eux
• creating a new container succeedes
plash create 1 true
• filesystem changes are persisted
new=$(plash create 1 touch /myfileah)
plash with-mount $new ls ./myfileah
• shell is started when no build arguments specified
echo 'echo hi' | plash create 1
• multiple containers can be created on top of each other
plash create $(plash create $(plash create $(plash create 1 true) true) true) true
• hierarchical creations have a parent relationship
layer1=$(plash create 1 touch /a)
layer2=$(plash create $layer1 touch /b)
test $(plash parent $layer2) = $layer1
• hierarchical containers are hierarchically represented in the filesystem
np1=$(plash nodepath $layer1)
np2=$(plash nodepath $layer2)
test $(basename $np1) = $(basename $(dirname $np2))
• working directory when building is current working directory from caller
cd /home
plash build -f 1 --invalidate-layer -x 'test $(pwd) == /home'
cd -
• home is mounted when building
cont=$(plash build -f 1 -x 'ls -1 /home > /tmp/out')
np=$(plash nodepath $cont)
cmp $np/_data/root/tmp/out <(ls -1 /home)
• tmp is not mounted when building
mktemp /tmp/XXXXXXXXXXX
cont=$(plash build -f 1 -x 'test -z $(ls /tmp)')
• any build error returns exit code 1
set +e
plash create exit 42
test 1 -eq $? || exit 1
set -e
• build error does not return any container
stdout=$(mktemp)
(! plash create 1 false > $stdout)
(! test -s $stdout) # its empty
• PLASH_EXPORT is ignored
test "$(PLASH_EXPORT=A A=a plash create 1 sh -c 'echo MARKER$A' 2>&1 | grep MARKER | grep -v +)" = 'MARKER'
• HOME is exported
test "$(HOME=a plash create 1 sh -c 'echo MARKER$HOME' --invalidate-layer 2>&1 | grep MARKER | grep -v +)" = MARKERa
Source Code
#define USAGE "usage: plash create CONTAINER [ CMD1 [ CMD2 ... ] ]\n"
#define _GNU_SOURCE
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <plash.h>
int create_main(int argc, char *argv[]) {
char *plash_data = pl_call("data");
char *image_id = argv[1];
if (image_id == NULL) {
fputs(USAGE, stderr);
return EXIT_FAILURE;
}
// validate image exists
pl_call("nodepath", image_id);
argv++; // chop argv[0]
argv++; // chop image_id
char *changesdir = pl_call("mkdtemp");
pl_exec_add("/proc/self/exe");
pl_exec_add("runb");
pl_exec_add(image_id);
pl_exec_add(changesdir);
if (*argv) {
// Use login shell
pl_exec_add("/bin/sh");
pl_exec_add("-lc");
pl_exec_add("exec env \"$@\"");
pl_exec_add("--");
while (*argv) {
pl_exec_add(*argv);
argv++;
}
} else {
pl_exec_add("/bin/sh");
}
pid_t pid = fork();
if (pid == 0) {
// redirect stdout to stderr
if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1)
pl_fatal("dup2");
// exec away
pl_exec_add(NULL);
}
int exit;
if ((exit = pl_wait(pid))) {
errno = 0;
pl_fatal("build failed with exit status %d", exit);
}
char *changesdir_data = NULL;
asprintf(&changesdir_data, "%s/data", changesdir) != -1 ||
pl_fatal("asprintf");
execlp("/proc/self/exe", "plash", "add-layer", image_id, changesdir_data,
NULL);
pl_fatal("execlp");
return EXIT_SUCCESS;
}