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