Sometimes, when you pack a lot of charms, you may end up with lots of wasted space in /var/snap/lxd
. For a single charm, you can solve this issue by running charmcraft clean
; but what if you have 10, 20, 100 charms?
With the original idea from @sed-i, I wrote a small bash script that cleans up all the LXD instances that are used to pack charms. Two cool features:
--last-used <date>
allows you to specify a cutoff time (i.e., “delete the instances that have not been used since<date>
”);--dry-run
lets you check what would be deleted, without touching any instance.
I use argc
to parse input arguments in my bash scripts, so I’ll include both my original script (you’ll need to download argc
, but it’s easier to read and modify to your liking) and the “compiled” version (no dependency, but more boilerplate).
Original script (argc
dependency)
#!/usr/bin/env bash
# charmcraft-purge
# @describe Delete all the LXC instances that charmcraft creates to pack charms
# @author Luca Bello
# @meta version 0.3
# @meta require-tools lxc,jq
# @option --last-used Delete instances that were last use before this date (format: anything parsed by 'date') (default: one month ago)
# @flag --dry-run Perform a dry run (don't delete instances)
eval "$(argc --argc-eval "$0" "$@")"
# Default --last-used to one week ago (if not specified)
if [[ -z $argc_last_used ]]; then
argc_last_used="$(date -d '-7 days' +'%F')";
else
argc_last_used="$(date -d "$argc_last_used" +'%F')"
fi
lxc_instances=$(lxc list --project=charmcraft --format=json | jq --arg cutoff "$argc_last_used" '[.[] | select(.last_used_at | . < $cutoff)]')
lxc_instances_count=$(echo "$lxc_instances" | jq -r '[ .[] | select(.name | startswith("charmcraft-")) | .name ] | length')
echo "$lxc_instances_count lxc containers will be removed (not used since $argc_last_used)"
for instance in $(echo $lxc_instances | jq -r '.[] | select(.name | startswith("charmcraft-")) | .name'); do
echo "Deleting $instance"
if [ "$argc_dry_run" = 0 ]; then
lxc --project=charmcraft delete "$instance"
fi
done
Compiled script (no dependency, lots of boilerplate)
#!/usr/bin/env bash
# charmcraft-purge
# @describe Delete all the LXC instances that charmcraft creates to pack charms
# @author Luca Bello
# @meta version 0.3
# @meta require-tools lxc,jq
# @option --last-used Delete instances that were last use before this date (format: anything parsed by 'date') (default: one month ago)
# @flag --dry-run Perform a dry run (don't delete instances)
# ARGC-BUILD {
# This block was generated by argc (https://github.com/sigoden/argc).
# Modifying it manually is not recommended
_argc_run() {
if [[ "${1:-}" == "___internal___" ]]; then
_argc_die "error: unsupported ___internal___ command"
fi
if [[ "${OS:-}" == "Windows_NT" ]] && [[ -n "${MSYSTEM:-}" ]]; then
set -o igncr
fi
argc__args=("$(basename "$0" .sh)" "$@")
argc__positionals=()
_argc_index=1
_argc_len="${#argc__args[@]}"
_argc_tools=()
_argc_parse
_argc_require_tools "${_argc_tools[@]}"
if [ -n "${argc__fn:-}" ]; then
$argc__fn "${argc__positionals[@]}"
fi
}
_argc_usage() {
cat <<-'EOF'
charmcraft-purge 0.3
Luca Bello
Delete all the LXC instances that charmcraft creates to pack charms
USAGE: charmcraft-purge [OPTIONS]
OPTIONS:
--last-used <LAST-USED> Delete instances that were last use before this date (format: anything parsed by 'date') (default: one month ago)
--dry-run Perform a dry run (don't delete instances)
-h, --help Print help
-V, --version Print version
EOF
exit
}
_argc_version() {
echo charmcraft-purge 0.3
exit
}
_argc_parse() {
local _argc_key _argc_action
local _argc_subcmds=""
while [[ $_argc_index -lt $_argc_len ]]; do
_argc_item="${argc__args[_argc_index]}"
_argc_key="${_argc_item%%=*}"
case "$_argc_key" in
--help | -help | -h)
_argc_usage
;;
--version | -version | -V)
_argc_version
;;
--)
_argc_dash="${#argc__positionals[@]}"
argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
_argc_index=$_argc_len
break
;;
--last-used)
_argc_take_args "--last-used <LAST-USED>" 1 1 "-" ""
_argc_index=$((_argc_index + _argc_take_args_len + 1))
if [[ -z "${argc_last_used:-}" ]]; then
argc_last_used="${_argc_take_args_values[0]:-}"
else
_argc_die "error: the argument \`--last-used\` cannot be used multiple times"
fi
;;
--dry-run)
if [[ "$_argc_item" == *=* ]]; then
_argc_die "error: flag \`--dry-run\` don't accept any value"
fi
_argc_index=$((_argc_index + 1))
if [[ -n "${argc_dry_run:-}" ]]; then
_argc_die "error: the argument \`--dry-run\` cannot be used multiple times"
else
argc_dry_run=1
fi
;;
*)
if _argc_maybe_flag_option "-" "$_argc_item"; then
_argc_die "error: unexpected argument \`$_argc_key\` found"
fi
argc__positionals+=("$_argc_item")
_argc_index=$((_argc_index + 1))
;;
esac
done
_argc_tools=(lxc jq)
if [[ -n "${_argc_action:-}" ]]; then
$_argc_action
else
if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then
_argc_usage
fi
fi
}
_argc_take_args() {
_argc_take_args_values=()
_argc_take_args_len=0
local param="$1" min="$2" max="$3" signs="$4" delimiter="$5"
if [[ "$min" -eq 0 ]] && [[ "$max" -eq 0 ]]; then
return
fi
local _argc_take_index=$((_argc_index + 1)) _argc_take_value
if [[ "$_argc_item" == *=* ]]; then
_argc_take_args_values=("${_argc_item##*=}")
else
while [[ $_argc_take_index -lt $_argc_len ]]; do
_argc_take_value="${argc__args[_argc_take_index]}"
if _argc_maybe_flag_option "$signs" "$_argc_take_value"; then
if [[ "${#_argc_take_value}" -gt 1 ]]; then
break
fi
fi
_argc_take_args_values+=("$_argc_take_value")
_argc_take_args_len=$((_argc_take_args_len + 1))
if [[ "$_argc_take_args_len" -ge "$max" ]]; then
break
fi
_argc_take_index=$((_argc_take_index + 1))
done
fi
if [[ "${#_argc_take_args_values[@]}" -lt "$min" ]]; then
_argc_die "error: incorrect number of values for \`$param\`"
fi
if [[ -n "$delimiter" ]] && [[ "${#_argc_take_args_values[@]}" -gt 0 ]]; then
local item values arr=()
for item in "${_argc_take_args_values[@]}"; do
IFS="$delimiter" read -r -a values <<<"$item"
arr+=("${values[@]}")
done
_argc_take_args_values=("${arr[@]}")
fi
}
_argc_maybe_flag_option() {
local signs="$1" arg="$2"
if [[ -z "$signs" ]]; then
return 1
fi
local cond=false
if [[ "$signs" == *"+"* ]]; then
if [[ "$arg" =~ ^\+[^+].* ]]; then
cond=true
fi
elif [[ "$arg" == -* ]]; then
if (( ${#arg} < 3 )) || [[ ! "$arg" =~ ^---.* ]]; then
cond=true
fi
fi
if [[ "$cond" == "false" ]]; then
return 1
fi
local value="${arg%%=*}"
if [[ "$value" =~ [[:space:]] ]]; then
return 1
fi
return 0
}
_argc_require_tools() {
local tool missing_tools=()
for tool in "$@"; do
if ! command -v "$tool" >/dev/null 2>&1; then
missing_tools+=("$tool")
fi
done
if [[ "${#missing_tools[@]}" -gt 0 ]]; then
echo "error: missing tools: ${missing_tools[*]}" >&2
exit 1
fi
}
_argc_die() {
if [[ $# -eq 0 ]]; then
cat
else
echo "$*" >&2
fi
exit 1
}
_argc_run "$@"
# ARGC-BUILD }
# Default --last-used to one week ago (if not specified)
if [[ -z $argc_last_used ]]; then
argc_last_used="$(date -d '-7 days' +'%F')";
else
argc_last_used="$(date -d "$argc_last_used" +'%F')"
fi
lxc_instances=$(lxc list --project=charmcraft --format=json | jq --arg cutoff "$argc_last_used" '[.[] | select(.last_used_at | . < $cutoff)]')
lxc_instances_count=$(echo "$lxc_instances" | jq -r '[ .[] | select(.name | startswith("charmcraft-")) | .name ] | length')
echo "$lxc_instances_count lxc containers will be removed (not used since $argc_last_used)"
for instance in $(echo $lxc_instances | jq -r '.[] | select(.name | startswith("charmcraft-")) | .name'); do
echo "Deleting $instance"
if [ "$argc_dry_run" = 0 ]; then
lxc --project=charmcraft delete "$instance"
fi
done
I hope that was useful, enjoy!