Fork me on GitHub
  ▖     
▃▀▄▄▖▄▄▖
 ▀▖     
        


#!/usr/bin/env python3
# usage: plash shrink
# Delete half of the older containers.
# Containers with a lower build id will be deleted first.

import math
import os
import shutil
import subprocess
import sys
import tempfile
from collections import Counter

from plash import unshare, utils

# allows changing subuids in the fs
unshare.unshare_if_user()

DELETE_PERCENT = 50

index_dir = os.path.join(utils.get_plash_data(), 'index')
links = os.listdir(index_dir)

node_deletation_effect = Counter()
nodepaths = {}
for container_id in links:
    try:
        nodepath = os.readlink(os.path.join(index_dir, container_id))
        os.stat(
            nodepath)  # raises the file not found if containers was deleted
    except FileNotFoundError:
        # broken link, it's `plash clean`s responsability to clean that
        continue
    nodepaths[container_id] = nodepath
    components = nodepath.split('layer/')[-1].split('/')
    node_deletation_effect.update(components)

# don't mess around with the special root container
del node_deletation_effect['0']

nodes = list(node_deletation_effect.keys())
nodes.sort(key=int)
delete_quota = math.ceil(len(nodes) * DELETE_PERCENT / 100.0)
mastertmp = utils.mkdtemp()
already_deleted = 0
for container_id in nodes:
    affected = node_deletation_effect[container_id]

    # if we already fulfilled the delete quota, break
    if already_deleted >= delete_quota:
        break

    # delete this container if does not exceed the quota
    if already_deleted + affected <= delete_quota:
        tmp = tempfile.mkdtemp(dir=mastertmp)
        try:
            os.rename(nodepaths[container_id], tmp)
        except FileNotFoundError:
            continue  # this or another process already deleted it
        already_deleted += affected

print(
    'dereferenced {} of {} containers'.format(already_deleted, len(nodes)),
    file=sys.stderr)
print('cleaning up...', file=sys.stderr)
shutil.rmtree(mastertmp)