Fork me on GitHub

plash shrink


Usage

plash shrink

Description

Delete half of the older containers.
Containers with a lower build id will be deleted first.

Tested Behaviour

#!/bin/sh
set -eux

testshrink(){
  plash shrink
  plash clean --clean-index
  out=$(ls $PLASH_DATA/index | xargs)
  test "$out" = "$(echo $@)"
  plash purge --yes
  plash init
}

mknode(){
  tmp=$(mktemp -d)
  plash add-layer "$1" "$tmp"
}

# start from zero
plash purge --yes
plash init

• if called with one node delete it,

mknode 0
testshrink 0

• if called with no nodes, stay with no nodes

testshrink 0

• two simple nodes

mknode 0
mknode 0
testshrink 0 2

• create 2 containers with same parent

mknode 0
mknode 1
mknode 1
testshrink 0 1

• create 3 containers with same parent

mknode 0
mknode 1
mknode 1
mknode 1
testshrink 0 1 4

• create some containers, where each has one child - except the leave

mknode 0
mknode 1
mknode 2
mknode 3
testshrink 0 1 2

• create an older and a newer tree, ensure the older one gets deleted

tree_a=$(mknode 0)
child_a1=$(mknode "$tree_a")
child_a2=$(mknode "$tree_a")
tree_b=$(mknode 0)
child_b1=$(mknode "$tree_b")
child_b2=$(mknode "$tree_b")
testshrink 0 $tree_b $child_b1 $child_b2

• same as above but with an extra node in the second tree, this makes an extra node being deleted

tree_a=$(mknode 0)
child_a1=$(mknode "$tree_a")
child_a2=$(mknode "$tree_a")
tree_b=$(mknode 0)
child_b1=$(mknode "$tree_b")
child_b2=$(mknode "$tree_b")
child_b3=$(mknode "$tree_b")
testshrink 0 $tree_b $child_b2 $child_b3

• test with deferenced node

mknode 0
plash rm 1
testshrink 0

Source Code


#define USAGE "usage: plash shrink\n"

#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include <plash.h>

#define ITERDIR_BEGIN(path)                                                    \
  DIR *dirp;                                                                   \
  struct dirent *dir;                                                          \
  dirp = opendir(path);                                                        \
  if (dirp == NULL)                                                            \
    pl_fatal("opendir");                                                       \
  while ((dir = readdir(dirp)) != NULL) {

#define ITERDIR_CLOSE()                                                        \
  }                                                                            \
  closedir(dirp);

int is_leave(char *nodepath) {
  int is_leave = 1;
  ITERDIR_BEGIN(nodepath)
  if (atoi(dir->d_name)) {
    is_leave = 0;
    break;
  }
  ITERDIR_CLOSE();
  return is_leave;
}

int count_images() {
  int count = 0;
  ITERDIR_BEGIN(".")
  if (dir->d_type == DT_LNK) {
    char *nodepath = realpath(dir->d_name, NULL);
    if (nodepath == NULL && errno == ENOENT)
      continue;
    if (nodepath == NULL)
      pl_fatal("realpath");
    count++;
  }
  ITERDIR_CLOSE()
  return count;
}

char *get_oldest_leave() {
  char *oldest_leave = NULL;
  char *oldest_leave_dup;
  char *nodepath;
  ITERDIR_BEGIN(".")
  if ((dir->d_type != DT_LNK) ||                          // its' not a link
      ((nodepath = realpath(dir->d_name, NULL)) == NULL)) // or a broken link
    continue;
  if (is_leave(nodepath) &&
      (oldest_leave == NULL ||                 // this is the first item or
       atoi(oldest_leave) > atoi(dir->d_name)) // this item is even smaller
  )
    oldest_leave = dir->d_name;
  ITERDIR_CLOSE()
  oldest_leave_dup = strdup(oldest_leave);
  if (oldest_leave_dup == NULL)
    pl_fatal("dup");
  return oldest_leave_dup;
}

int shrink_main(int argc, char *argv[]) {
  char *image_id;
  char *plash_data = pl_call("data");
  if (chdir(plash_data) == -1)
    pl_fatal("chdir %s");
  if (chdir("index") == -1)
    pl_fatal("chdir %s");

  int images_count = count_images() - 1; // substract special root image
  if (!images_count) {
    printf("You have no images\n");
    return EXIT_SUCCESS;
  }
  printf("You have %d images.\n", images_count);
  for (int to_delete = ((images_count + 1) / 2); to_delete; to_delete--) {
    char *o = get_oldest_leave();
    if (o[0] == '0' && o[1] == '\0') {
      printf("Nothing to delete.\n");
      break;
    }
    printf("Deleting image id: %s\n", o);
    pl_call("rm", o);
  }
  printf("You have %d images.\n", count_images() - 1);
  return EXIT_SUCCESS;
}