Fork me on GitHub

plash create


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;
}