21 Commits

Author SHA1 Message Date
Valentin Lab e9d4d833ad new: [compose-core] upgrade version of generated ``docker-compose.yml`` to ``2.1`` 2 months ago
Valentin Lab b4c455b8a4 new: update to docker-client version ``25.0.2`` 2 months ago
Valentin Lab 51cef58ed1 fix: [compose-core] make image local cache also fetched before looking on ``$COMPOSE_DOCKER_REGISTRY`` 2 months ago
Valentin Lab 85d4d0dce3 fix: [compose-core] add ``build`` support again, after the cache introduced a regression 2 months ago
Valentin Lab 2ea9386817 new: dev: [compose-core] refactoring out the ``if`` condition 2 months ago
Valentin Lab a6bca36900 new: [compose-core] do not ignore ``get_compose_relations`` errors 2 months ago
Valentin Lab 95a0e62ed5 new: [compose-core] move partial docker-compose to a file 2 months ago
Valentin Lab 1d82babd8e new: [compose] check remnant directories more seriously 2 months ago
Valentin Lab e949bf5659 fix: dev: [compose] typos !minor 2 months ago
Valentin Lab 9545d32b03 new: [compose] propagate ``$PROJECT_NAME`` 2 months ago
Valentin Lab a2dcb91512 new: [compose-core] cache images on ``$COMPOSE_DOCKER_REGISTRY`` 2 months ago
Valentin Lab 8ea4310945 new: dev: [compose-core] refactor ``service_base_docker_image()`` 3 months ago
Valentin Lab 9dbc151f78 fix: dev: [compose-core] declare all local variables in ``get_relation_data_file()`` 3 months ago
Valentin Lab 83108d4dc1 fix: [compose-core] improve protection of relation data file !minor 3 months ago
Valentin Lab 1a559ff7fb fix: [compose-core] don't ignore relation constraint if providers are more than one 3 months ago
Valentin Lab 0c4868ee5c fix: [compose-core] exit after errors in relation config retrieval 3 months ago
Valentin Lab 0db85daa48 new: [compose-core] provide full set of ``p-err`` and ``read-0-err`` for charms 3 months ago
Valentin Lab 8c0d97facb new: [compose-core] provide ``get_service_incoming_relations`` to list services linked to a service 3 months ago
Valentin Lab 8b71a39e3b new: replace ``shyaml`` python implementation with rust implementation 3 months ago
Valentin Lab 3a3a102459 fix: [compose-core] prevent re-building images already present 3 months ago
Valentin Lab 9313ca1432 new: [compose-core] ensure compatibility with new shyaml rust implementation 3 months ago
  1. 13
      Dockerfile
  2. 53
      bin/compose
  3. 527
      bin/compose-core

13
Dockerfile

@ -23,7 +23,7 @@ ENV KAL_SHLIB_ARRAY_VERSION="0.2.0" \
KAL_SHLIB_OTHER_VERSION="0.2.2" \
KAL_SHLIB_PRETTY_VERSION="0.4.3"
ARG DOCKER_CLI_VERSION="17.06.2-ce"
ARG DOCKER_CLI_VERSION="25.0.2"
ARG DOCKER_COMPOSE_VERSION="1.24.0"
## install docker
@ -57,8 +57,7 @@ RUN apk --update add binutils && \
RUN apk add python-dev build-base
RUN apk add yaml-dev cython cython-dev && \
pip install "cython<3.0.0" wheel && \
pip install pyyaml==5.4.1 --no-build-isolation && \
pip install shyaml
pip install pyyaml==5.4.1 --no-build-isolation
RUN pip install crudini
@ -150,6 +149,14 @@ RUN apk add patch
RUN sed -ri 's%^(mozilla/DST_Root_CA_X3.crt)%!\\1%g' /etc/ca-certificates.conf && \
update-ca-certificates
## New shyaml rust implementation
RUN apk add libgcc && \
wget https://docker.0k.io/downloads/shyaml-rs-musl-0.1.0.xz -O /tmp/shyaml-musl.xz && \
unxz /tmp/shyaml-musl.xz && \
mv /tmp/shyaml-musl /usr/local/bin/shyaml && \
chmod +x /usr/local/bin/shyaml
## install compose
COPY ./bin/ /usr/local/bin/

53
bin/compose

@ -359,11 +359,13 @@ get_volumes_for_container() {
is_volume_used() {
local volume="$1"
local volume="$1" container_id src dst
while read container_id; do
while read-0 src dst; do
[ "$src" == "$volume" ] && return 0
[ "$src" == "$volume" ] && {
return 0
}
done < <(get_volumes_for_container "$container_id")
done < <(get_running_compose_containers)
return 1
@ -384,8 +386,18 @@ clean_unused_sessions() {
[ -e "$f" ] || continue
is_volume_used "$f" && continue
## XXXvlab: the second rmdir should not be useful
rm -f "$f" >/dev/null || rmdir "$f" >/dev/null || {
debug "Unexpected session remnants $f"
[ -d "$f" ] && {
err "Unexpected directory as session remnant $(printf "%q" "$f")" >&2
echo " - can you contact support to report this issue ?" >&2
echo " - as a workaround, you can remove it manually using:" >&2
echo "" >&2
echo " rm -rf $(printf "%q" "$f")" >&2
echo "" >&2
return 1
}
rm -f "$f" >/dev/null || {
err "Couldn't delete $(printf "%q" "$f")" >&2
return 1
}
done
}
@ -961,11 +973,16 @@ mk_docker_run_options() {
)
fi
clean_unused_sessions
clean_unused_sessions || return 1
filename=$(mktemp -p /tmp/ -t launch_opts-XXXXXXXXXXXXXXXX)
p0 "${docker_run_opts[@]}" > "$filename"
hash=$(hash_get < "$filename") || return 1
src="$SESSION_DIR/$UID-$hash"
if [ -d "$src" ]; then
err "Unexpected directory found in '$src'."
return 1
fi
dest="/var/lib/compose/sessions/$UID-$hash"
additional_docker_run_opts=(
"-v" "$SESSION_DIR/$UID-$hash:$dest:ro"
@ -982,8 +999,17 @@ mk_docker_run_options() {
p0 "!env:$var=${!var}"
done >> "$filename"
if [ -e "$src" ]; then
## compare content of $src and $filename
if ! diff -q "$src" "$filename" >/dev/null; then
return 0
fi
warn "Session already exists but content is different. Squashing."
fi
mkdir -p "$SESSION_DIR" || return 1
mv -f "$filename" "$src" || return 1
cat "$filename" > "$src" || return 1
}
@ -993,6 +1019,10 @@ load_env() {
if [ -z "$COMPOSE_LAUNCHER_OPTS" ]; then
mk_docker_run_options "$@" || return 1
else
[ -d "$COMPOSE_LAUNCHER_OPTS" ] && {
err "Variable \$COMPOSE_LAUNCHER_OPTS provided but it points on a directory."
return 1
}
set_os || return 1
while read-0 opt; do
if [[ "$opt" == "!env:"* ]]; then
@ -1021,6 +1051,9 @@ load_env() {
fi
docker_run_opts+=("-e" "ALL_RELATIONS=$NEW_ALL_RELATIONS")
fi
if [ -n "$PROJECT_NAME" ]; then
docker_run_opts+=("-e" "PROJECT_NAME=$PROJECT_NAME")
fi
}
@ -1035,9 +1068,11 @@ show_env() {
echo " COMPOSE_LAUNCHER_CACHE: $COMPOSE_LAUNCHER_CACHE"
echo " SESSION_DIR: $SESSION_DIR"
echo " TZ_PATH: $TZ_PATH"
if [ -n "$ALL_RELATIONS" ]; then
[ -n "$ALL_RELATIONS" ] &&
echo " ALL_RELATIONS: $ALL_RELATIONS"
fi
[ -n "$PROJECT_NAME" ] &&
echo " PROJECT_NAME: $PROJECT_NAME"
}
@ -1054,7 +1089,7 @@ run() {
"$COMPOSE_LAUNCHER_BIN_OVERRIDE" \
"$@")
if [ "$E" != "0" ]; then
err "Unexpecte failure while trying to replace compose file option."
err "Unexpected failure while trying to replace compose file option."
return 1
fi
set -- "${cmd_args[@]}"

527
bin/compose-core

@ -136,6 +136,13 @@ read-0-err() {
false
}
}
export -f read-0-err
p-err() {
"$@"
echo "$?"
}
export -f p-err
wyq() {
@ -355,7 +362,7 @@ export -f merge_yaml_str
yaml_get_values() {
local sep=${1:-$'\n'} value input type first elt
input=$(cat -)
if [ -z "$input" ] || [ "$input" == "None" ]; then
if [ -z "$input" ] || [[ "$input" =~ ^None|null$ ]]; then
return 0
fi
type=$(e "$input" | shyaml get-type)
@ -459,7 +466,7 @@ export -f cached_cmd_on_image
cmd_on_base_image() {
local service="$1" base_image
shift
base_image=$(service_base_docker_image "$service") || return 1
base_image=$(service_ensure_image_ready "$service") || return 1
docker run -i --rm --entrypoint /bin/bash "$base_image" -c "$*"
}
export -f cmd_on_base_image
@ -473,10 +480,7 @@ cached_cmd_on_base_image() {
quick_cat_stdin < "$cache_file"
return 0
fi
base_image=$(service_base_docker_image "$service") || return 1
if ! docker_has_image "$base_image"; then
docker pull "$base_image" 1>&2
fi
base_image=$(service_ensure_image_ready "$service") || return 1
result=$(cached_cmd_on_image "$base_image" "$@") || return 1
echo "$result" | tee "$cache_file"
}
@ -493,7 +497,7 @@ docker_update() {
shift
shift
## this will build it if necessary
base_image=$(service_base_docker_image "$service") || return 1
base_image=$(service_ensure_image_ready "$service") || return 1
## XXXvlab: there are probably ways to avoid rebuilding that each time
image_id="$(docker_image_id "$base_image")" || return 1
@ -552,10 +556,7 @@ export -f docker_image_export_dir
service_base_image_export_dir() {
local service="$1" src="$2" dst="$3" base_image
shift
base_image=$(service_base_docker_image "$service") || return 1
if ! docker_has_image "$base_image"; then
docker pull "$base_image"
fi
base_image=$(service_ensure_image_ready "$service") || return 1
docker_image_export_dir "$base_image" "$src" "$dst"
}
export -f service_base_image_export_dir
@ -564,10 +565,7 @@ export -f service_base_image_export_dir
service_base_image_id() {
local service="$1" src="$2" dst="$3" base_image
shift
base_image=$(service_base_docker_image "$service") || return 1
if ! docker_has_image "$base_image"; then
docker pull "$base_image"
fi
base_image=$(service_ensure_image_ready "$service") || return 1
docker inspect "$base_image" --format="{{ .Id }}"
}
export -f service_base_image_id
@ -823,7 +821,8 @@ ensure_db_docker_running () {
else
verb "Database is not locked."
if ! docker_has_image "$DOCKER_BASE_IMAGE"; then
docker pull "$DOCKER_BASE_IMAGE"
err "Unexpected missing docker image $DOCKER_BASE_IMAGE."
return 1
fi
_set_server_db_params || return 1
@ -1122,7 +1121,7 @@ get_docker_compose_links() {
[ "$type" == "run-once" ] && continue
if [ "$tech_dep" == "reversed" ]; then
deps+=("$(echo -en "$master_target_service:\n links:\n - $master_service")")
elif [ "$tech_dep" == "True" ]; then
elif [[ "$tech_dep" =~ ^(True|true)$ ]]; then
deps+=("$(echo -en "$master_service:\n links:\n - $master_target_service")")
fi
## XXXvlab: an attempt to add depends_on, but this doesn't work well actually
@ -1284,12 +1283,12 @@ get_docker_compose () {
return 1
}
base_v2="version: '2.0'"
base_v2="version: '2.1'"
merge_yaml_str "$(yaml_key_val_str "services" "$docker_compose_services")" \
"$base_v2" > "$cache_file" || return 1
export _current_docker_compose="$(cat "$cache_file")"
echo "$_current_docker_compose"
export _CURRENT_DOCKER_COMPOSE="$cache_file"
cat "$_CURRENT_DOCKER_COMPOSE"
debug " ..compilation of base 'docker-compose.yml' done $GRAY(in $((SECONDS - start_compilation))s)$NORMAL" || true
# debug " ** ${WHITE}docker-compose.yml${NORMAL}:"
# debug "$_current_docker_compose"
@ -1393,12 +1392,15 @@ export -f get_service_charm
## full docker-compose.yml to be already built.
get_service_def () {
local service="$1" def
if [ -z "$_current_docker_compose" ]; then
print_syntax_error "$FUNCNAME is meant to be called after"\
"\$_current_docker_compose has been calculated."
if [ -z "$_CURRENT_DOCKER_COMPOSE" ]; then
err "${FUNCNAME[0]} is meant to be called after"\
"\$_CURRENT_DOCKER_COMPOSE has been calculated."
echo " Called by:" >&2
printf " - %s\n" "${FUNCNAME[@]:1}" >&2
return 1
fi
def=$(echo "$_current_docker_compose" | shyaml get-value "services.${service//./\\.}" 2>/dev/null)
def=$(cat "$_CURRENT_DOCKER_COMPOSE" | shyaml get-value "services.${service//./\\.}" 2>/dev/null)
if [ -z "$def" ]; then
err "No definition for service $DARKYELLOW$service$NORMAL in compiled 'docker-compose.yml'."
return 1
@ -1407,54 +1409,285 @@ get_service_def () {
}
export -f get_service_def
get_build_hash() {
local dir="$1" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$1")" hash
if [ -e "$cache_file" ]; then
# debug "$FUNCNAME: cache hit ($*)"
cat "$cache_file"
return 0
fi
## Check that there's a Dockerfile in this directory
if [ ! -e "$dir/Dockerfile" ]; then
err "No 'Dockerfile' found in '$dir'."
return 1
fi
## use find to md5sum all files in the directory and make a final hash
hash=$(set -o pipefail; cd "$dir"; env -i find "." -type f -exec md5sum {} \; |
sort | md5sum | awk '{print $1}') || {
err "Failed to get hash for '$dir'."
return 1
}
printf "%s" "$hash" | tee "$cache_file"
return $?
}
export -f get_build_hash
### Query/Get cached image from registry
##
## Returns on stdout the name of the image if found, or an empty string if not
cache:image:registry:get() {
local charm="$1" hash="$2" service="$3"
local charm_image_name="cache/charm/$charm"
local charm_image="$charm_image_name:$hash"
Elt "pulling ${DARKPINK}$charm${NORMAL} image from $COMPOSE_DOCKER_REGISTRY" >&2
if out=$(docker pull "$COMPOSE_DOCKER_REGISTRY/$charm_image" 2>&1); then
docker tag "$COMPOSE_DOCKER_REGISTRY/$charm_image" "$charm_image" || {
err "Failed set image '$COMPOSE_DOCKER_REGISTRY/$charm_image' as '$charm_image'" \
"for ${DARKYELLOW}$service${NORMAL}."
return 1
}
print_info "found" >&2
print_status success >&2
Feed >&2
printf "%s" "$charm_image" | tee "$cache_file"
return $?
fi
if [[ "$out" != *"manifest unknown"* ]] && [[ "$out" != *"manifest"*"not found"* ]]; then
print_status failure >&2
Feed >&2
err "Failed to pull image '$COMPOSE_DOCKER_REGISTRY/$charm_image'" \
"for ${DARKYELLOW}$service${NORMAL}:"
e "$out"$'\n' | prefix " ${GRAY}|${NORMAL} " >&2
return 1
fi
print_info "not found" >&2
if test "$type_method" = "long"; then
__status="[${NOOP}ABSENT${NORMAL}]"
else
echo -n "${NOOP}"
shift; shift;
echo -n "$*${NORMAL}"
fi >&2
Feed >&2
}
export -f cache:image:registry:get
### Store cached image on registry
##
## Returns nothing
cache:image:registry:put() {
if [ -n "$COMPOSE_DOCKER_REGISTRY" ] && [ -n "$COMPOSE_PUSH_TO_REGISTRY" ]; then
local charm="$1" hash="$2" service="$3"
local charm_image_name="cache/charm/$charm"
local charm_image="$charm_image_name:$hash"
Wrap -d "pushing ${DARKPINK}$charm${NORMAL} image to $COMPOSE_DOCKER_REGISTRY" <<EOF || return 1
docker tag "$charm_image" "$COMPOSE_DOCKER_REGISTRY/$charm_image" &&
docker push "$COMPOSE_DOCKER_REGISTRY/$charm_image"
EOF
fi >&2
}
export -f cache:image:registry:put
### Produce docker cached charm image 'cache/charm/$charm:$hash'
##
## Either by fetching it from a registry or by building it from a
## Dockerfile.
cache:image:produce() {
local type="$1" src="$2" charm="$3" hash="$4" service="$5"
local charm_image_name="cache/charm/$charm"
local charm_image="$charm_image_name:$hash"
case "$type" in
fetch)
local specified_image="$src"
## will not pull upstream image if already present locally
if ! docker_has_image "${specified_image}"; then
if ! out=$(docker pull "${specified_image}" 2>&1); then
err "Failed to pull image '$specified_image' for ${DARKYELLOW}$service${NORMAL}:"
echo "$out" | prefix " | " >&2
return 1
fi
fi
# specified_image_id=$(docker_image_id "$specified_image") || return 1
# charm_image_id=
# if docker_has_image "${image_dst}"; then
# charm_image_id=$(docker_image_id "${image_dst}") || return 1
# fi
# if [ "$specified_image_id" != "$charm_image_id" ]; then
docker tag "$specified_image" "${charm_image}" || return 1
# fi
;;
build)
local service_build="$src"
build_opts=()
if [ "$COMPOSE_ACTION" == "build" ]; then
while read-0 arg; do
case "$arg" in
-t|--tag)
## XXXvlab: doesn't seem to be actually a valid option
if [ -n "$COMPOSE_PUSH_TO_REGISTRY" ]; then
err "You can't use -t|--tag option when pushing to a registry."
exit 1
fi
has_named_image=true
read-0 val ## should always be okay because already checked
build_opts+=("$arg" "$val")
;;
--help|-h)
docker-compose "$action" --help |
filter_docker_compose_help_message >&2
exit 0
;;
--*|-*)
if str_pattern_matches "$arg" $DC_MATCH_MULTI; then
read-0 value
build_opts+=("$arg" "$value")
shift
elif str_pattern_matches "$arg" $DC_MATCH_SINGLE; then
build_opts+=("$arg")
else
err "Unexpected error while parsing a second time the build arguments."
fi
;;
*)
## Already parsed
build_opts+=("$arg")
;;
esac
done < <(cla.normalize "${action_opts[@]}")
fi
if [ -z "$has_named_image" ]; then
build_opts+=(-t "${charm_image}")
fi
Wrap -v -d "Building ${DARKPINK}$charm${NORMAL}:$hash image" -- \
docker build "$service_build" -t "${charm_image}" "${build_opts[@]}" >&2 || {
err "Failed to build image '${charm_image}' for ${DARKYELLOW}$service${NORMAL}."
return 1
}
if [ -n "$has_named_image" ]; then
exit 0
fi
;;
*)
err "Unknown type '$type'."
return 1
;;
esac
}
export -f cache:image:produce
## Return the base docker image name of a service
service_base_docker_image() {
service_ensure_image_ready() {
local service="$1" cache_file="$state_tmpdir/$FUNCNAME.cache.$1" \
master_service service_def service_image service_build service_dockerfile
master_service service_def service_image service_build service_dockerfile image \
specified_image specified_image_id charm_image_name hash \
service_quoted
if [ -e "$cache_file" ]; then
# debug "$FUNCNAME: cache hit ($*)"
#debug "$FUNCNAME: cache hit ($*)"
cat "$cache_file"
return 0
fi
if [ -z "$_CURRENT_DOCKER_COMPOSE" ]; then
err "${FUNCNAME[0]} is meant to be called after"\
"\$_CURRENT_DOCKER_COMPOSE has been calculated."
echo " Called by:" >&2
printf " - %s\n" "${FUNCNAME[@]:1}" >&2
return 1
fi
master_service="$(get_top_master_service_for_service "$service")" || {
err "Could not compute master service for service $DARKYELLOW$service$NORMAL."
return 1
}
service_def="$(get_service_def "$master_service")" || {
err "Could not get docker-compose service definition for $DARKYELLOW$master_service$NORMAL."
if [ "$master_service" != "$service" ]; then
image=$(service_ensure_image_ready "$master_service") || return 1
printf "%s" "$image" | tee "$cache_file"
return $?
fi
## check if \$_CURRENT_DOCKER_COMPOSE's service def is already correctly setup
local charm="$(get_service_charm "$service")" || return 1
local charm_image_name="cache/charm/$charm" || return 1
local service_def="$(get_service_def "$service")" || {
err "Could not get docker-compose service definition for $DARKYELLOW$service$NORMAL."
return 1
}
service_image=$(echo "$service_def" | shyaml get-value image 2>/dev/null)
if [ "$?" != 0 ]; then
## According to https://stackoverflow.com/questions/32230577 , if there's a build,
## then the builded image will get name ${project}_${service}
project=$(get_default_project_name) || return 1
image_name="${project}_${service}"
if ! docker_has_image "$image_name"; then
service_build=$(echo "$service_def" | shyaml get-value build 2>/dev/null)
if [ "$?" != 0 ]; then
err "Service $DARKYELLOW$service$NORMAL has no ${WHITE}image${NORMAL} nor ${WHITE}build${NORMAL} parameter."
echo "$service_def" >&2
return 1
fi
local service_quoted=${service//./\\.}
docker build "$service_build" -t "${project}_${service}" >&2 || {
err "Failed to build image for ${DARKYELLOW}$service${NORMAL}."
return 1
}
if specified_image=$(echo "$service_def" | shyaml get-value image 2>/dev/null); then
if [ "$specified_image" == "$charm_image_name"* ]; then
## Assume we already did the change
printf "%s" "$specified_image" | tee "$cache_file"
return 0
fi
printf "%s" "${project}_${service}"
if [[ "$specified_image" == "${COMPOSE_DOCKER_REGISTRY}/"* ]]; then
if [ -n "$DEBUG" ]; then
Elt "using ${DARKPINK}$charm${NORMAL}'s specified image from $COMPOSE_DOCKER_REGISTRY" >&2
print_status noop >&2
Feed >&2
fi
## Already on the cache server
printf "%s" "$specified_image" | tee "$cache_file"
return 0
fi
src="$specified_image"
hash=$(echo "$specified_image" | md5sum | cut -f 1 -d " ") || return 1
type=fetch
## replace image by charm image
yq -i ".services.[\"${service_quoted}\"].image = \"${charm_image_name}:${hash}\"" \
"$_CURRENT_DOCKER_COMPOSE" || return 1
else
printf "%s" "${service_image}"
fi | tee "$cache_file"
if [ "${PIPESTATUS[0]}" != 0 ]; then
rm "$cache_file"
return 1
if ! src=$(echo "$service_def" | shyaml get-value build 2>/dev/null); then
err "Service $DARKYELLOW$service$NORMAL has no ${WHITE}image${NORMAL} nor ${WHITE}build${NORMAL} parameter."
echo "$service_def" >&2
return 1
fi
## According to https://stackoverflow.com/questions/32230577 , if there's a build,
## then the built image will get name ${project}_${service}
hash=$(get_build_hash "$src") || return 1
type=build
## delete build key from service_def and add image to charm_image_name
yq -i "del(.services.[\"${service_quoted}\"].build) |
.services.[\"${service_quoted}\"].image = \"${charm_image_name}:${hash}\"" \
"$_CURRENT_DOCKER_COMPOSE" || return 1
fi
if [ "$COMPOSE_ACTION" != "build" ] && docker_has_image "${charm_image_name}:${hash}"; then
if [ -n "$DEBUG" ]; then
Elt "using ${DARKPINK}$charm${NORMAL}'s image from local cache" >&2
print_status noop >&2
Feed >&2
fi
cache:image:registry:put "$charm" "$hash" "$service" || return 1
printf "%s" "${charm_image_name}:${hash}" | tee "$cache_file"
return $?
fi
## Can we pull it ? Let's check on $COMPOSE_DOCKER_REGISTRY
if [ "$COMPOSE_ACTION" != "build" ] && [ -n "$COMPOSE_DOCKER_REGISTRY" ]; then
img=$(cache:image:registry:get "$charm" "$hash" "$service") || {
err "Failed to get image '$charm_image_name:$hash' from registry for ${DARKYELLOW}$service${NORMAL}."
return 1
}
[ -n "$img" ] && {
printf "%s" "$img" | tee "$cache_file"
return $?
}
fi
cache:image:produce "$type" "$src" "$charm" "$hash" "$service" || return 1
cache:image:registry:put "$charm" "$hash" "$service" || return 1
printf "%s" "${charm_image_name}:$hash" | tee "$cache_file"
return $?
}
export -f service_base_docker_image
export -f service_ensure_image_ready
get_charm_relation_def () {
@ -1609,6 +1842,31 @@ get_ordered_service_dependencies() {
export -f get_ordered_service_dependencies
run_service_acquire_images () {
local service subservice subservices loaded
declare -A loaded
for service in "$@"; do
subservices=$(get_ordered_service_dependencies "$service") || return 1
for subservice in $subservices; do
if [ "${loaded[$subservice]}" ]; then
## Prevent double inclusion of same service if this
## service is deps of two or more of your
## requirements.
continue
fi
type=$(get_service_type "$subservice") || return 1
MASTER_BASE_SERVICE_NAME=$(get_top_master_service_for_service "$subservice") || return 1
if [ "$type" != "stub" ]; then
DOCKER_BASE_IMAGE=$(service_ensure_image_ready "$MASTER_BASE_SERVICE_NAME") || return 1
fi
loaded[$subservice]=1
done
done
return 0
}
run_service_hook () {
local action="$1" service subservice subservices loaded
shift
@ -1631,7 +1889,7 @@ run_service_hook () {
MASTER_BASE_SERVICE_NAME=$(get_top_master_service_for_service "$subservice") || return 1
MASTER_BASE_CHARM_NAME=$(get_service_charm "$MASTER_BASE_SERVICE_NAME") || return 1
if [ "$type" != "stub" ]; then
DOCKER_BASE_IMAGE=$(service_base_docker_image "$MASTER_BASE_SERVICE_NAME") || return 1
DOCKER_BASE_IMAGE=$(service_ensure_image_ready "$MASTER_BASE_SERVICE_NAME") || return 1
fi
Wrap "${wrap_opts[@]}" -d "running $YELLOW$action$NORMAL hook of $DARKYELLOW$subservice$NORMAL in charm $DARKPINK$charm$NORMAL" <<EOF || return 1
@ -1764,7 +2022,7 @@ cfg-get-value () {
err "The key $WHITE$key$NORMAL was not found in relation's data."
return 1
fi
echo "$out" | yaml_get_interpret
printf "%s\n" "$out" | yaml_get_interpret
}
export -f cfg-get-value
@ -1807,8 +2065,7 @@ export -f expand_vars
yaml_get_interpret() {
local content tag
content=$(cat -)
tag=$(echo "$content" | shyaml -y get-value) || return 1
tag="${tag%% *}"
tag=$(echo "$content" | shyaml get-type) || return 1
content=$(echo "$content" | shyaml get-value) || return 1
if ! [ "${tag:0:1}" == "!" ]; then
echo "$content" || return 1
@ -1987,7 +2244,7 @@ _run_service_relation () {
RELATION_CONFIG="$relation_dir/config_provider"
type=$(get_service_type "$target_service") || return 1
if [ "$type" != "stub" ]; then
DOCKER_BASE_IMAGE=$(service_base_docker_image "$target_service") || return 1
DOCKER_BASE_IMAGE=$(service_ensure_image_ready "$target_service") || return 1
fi
export DOCKER_BASE_IMAGE RELATION_CONFIG RELATION_DATA
{
@ -2020,7 +2277,7 @@ _run_service_relation () {
"for $DARKYELLOW$service$NORMAL (charm $DARKPINK$charm$NORMAL)"
RELATION_CONFIG="$relation_dir/config_providee"
RELATION_DATA="$(cat "$RELATION_DATA_FILE")"
DOCKER_BASE_IMAGE=$(service_base_docker_image "$service") || return 1
DOCKER_BASE_IMAGE=$(service_ensure_image_ready "$service") || return 1
export DOCKER_BASE_IMAGE RELATION_CONFIG RELATION_DATA
{
(
@ -2210,6 +2467,37 @@ get_service_relation() {
export -f get_service_relation
## From a service and a relation, get all relations targeting given
## service with given relation.
##
## Returns a NUL separated list of couple of:
## (base_service, relation_config)
##
get_service_incoming_relations() {
local service="$1" relation="$2" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$@" "$ALL_RELATIONS")" \
s rn ts rc td
if [ -e "$cache_file" ]; then
#debug "$FUNCNAME: SESSION cache hit $1"
cat "$cache_file"
return 0
fi
if [ -z "$ALL_RELATIONS" ]; then
err "Can't access global \$ALL_RELATIONS"
return 1
fi
while read-0 s rn ts rc _td; do
[[ "$ts" == "$service" ]] || continue
[[ "$rn" == "$relation" ]] || continue
relation_data_file=$(get_relation_data_file "$s" "$ts" "$rn" "$rc") || return 1
printf "%s\0" "$s" "$(cat "$relation_data_file")"
done < <(cat "$ALL_RELATIONS") > "$cache_file"
cat "$cache_file"
}
export -f get_service_incoming_relations
export TRAVERSE_SEPARATOR=:
## Traverse on first service satisfying relation
service:traverse() {
@ -2403,7 +2691,7 @@ _get_metadata_provides() {
return 0
fi
provides=$(printf "%s" "$metadata" | shyaml get-value -y -q provides "")
provides=$(printf "%s" "$metadata" | shyaml -q get-value -y provides "")
[ "$provides" -a "$provides" != "''" ] || { touch "$cache_file"; return 0; }
_get_provides_provides "$provides" | tee "$cache_file"
@ -2577,7 +2865,7 @@ get_all_relations () {
all_services=("$@")
while [ "${#all_services[@]}" != 0 ]; do
array_pop all_services service
while read-0 relation_name ts relation_config tech_dep; do
while read-0-err E relation_name ts relation_config tech_dep; do
[ "${without_relations[$service:$relation_name]}" ] && {
debug "Ignoring compose $DARKYELLOW$service$NORMAL --$DARKBLUE$relation_name$NORMAL--> ${DARKYELLOW}$ts$NORMAL"
continue
@ -2590,7 +2878,11 @@ get_all_relations () {
array_read-0 services_uses < <(_get_services_uses "$ts")
all_services+=("$ts")
services[$ts]=1
done < <(get_compose_relations "$service")
done < <(p-err get_compose_relations "$service")
if [ "$E" != 0 ]; then
err "Failed to get relations for $DARKYELLOW$service$NORMAL."
return 1
fi
done > "${cache_file}.wip"
while true; do
@ -2648,7 +2940,7 @@ get_all_relations () {
_out_new_relation_from_defs "$service" "$relation_name" "$ts" \
"${providers_def[0]}" "$relation_def" \
>> "${cache_file}.wip"
>> "${cache_file}.wip" || return 1
## Adding service
[ "${services[$ts]}" ] && continue
@ -2656,16 +2948,14 @@ get_all_relations () {
services[$ts]=1
changed=1
continue
elif [ "${#providers[@]}" -gt 1 ]; then
fi
if [ "${#providers[@]}" -gt 1 ]; then
msg=""
warn "No auto-pairing ${DARKYELLOW}$service${NORMAL}" \
"--${DARKBLUE}$relation_name${NORMAL}--> ($DARKYELLOW""${providers[@]}""$NORMAL)"\
"(> 1 provider)."
continue
else
if [ "$auto" == "summon" ]; then
summon+=("$service" "$relation_name" "$relation_def")
fi
elif [ "$auto" == "summon" ]; then ## no provider
summon+=("$service" "$relation_name" "$relation_def")
fi
;;
null|disable|disabled)
@ -2739,7 +3029,7 @@ get_all_relations () {
_out_new_relation_from_defs "$service" "$relation_name" "$ts" \
"${providers_def[0]}" "$relation_def" \
>> "${cache_file}.wip"
>> "${cache_file}.wip" || return 1
## Adding service
[ "${services[$ts]}" ] && continue
@ -2916,7 +3206,7 @@ run_service_relations () {
RELATION_TARGET_COMPOSE_DEF=$(get_compose_service_def "$target_service") || return 1
export RELATION_TARGET_COMPOSE_DEF MASTER_TARGET_{CHARM,SERVICE}_NAME
Wrap "${wrap_opts[@]}" -d "Building $DARKYELLOW$subservice$NORMAL --$DARKBLUE$relation_name$NORMAL--> $DARKYELLOW$target_service$NORMAL" <<EOF || return 1
Wrap "${wrap_opts[@]}" -d "building $DARKYELLOW$subservice$NORMAL --$DARKBLUE$relation_name$NORMAL--> $DARKYELLOW$target_service$NORMAL" <<EOF || return 1
_run_service_relation "$relation_name" "$subservice" "$target_service" "\$relation_config"
EOF
done < <(get_service_relations "$subservice") || return 1
@ -2950,7 +3240,7 @@ _run_service_action_direct() {
export ACTION_NAME=$action
export ACTION_SCRIPT_PATH="$action_script_path"
export CONTAINER_NAME=$(get_top_master_service_for_service "$service")
export DOCKER_BASE_IMAGE=$(service_base_docker_image "$CONTAINER_NAME")
export DOCKER_BASE_IMAGE=$(service_ensure_image_ready "$CONTAINER_NAME")
export SERVICE_DATASTORE="$DATASTORE/$service"
export SERVICE_CONFIGSTORE="$CONFIGSTORE/$service"
exname="$exname $ACTION_NAME $SERVICE_NAME" \
@ -2984,7 +3274,7 @@ _run_service_action_relation() {
export ACTION_NAME=$action
export ACTION_SCRIPT_PATH="$action_script_path"
export CONTAINER_NAME=$(get_top_master_service_for_service "$service")
export DOCKER_BASE_IMAGE=$(service_base_docker_image "$CONTAINER_NAME")
export DOCKER_BASE_IMAGE=$(service_ensure_image_ready "$CONTAINER_NAME")
export SERVICE_DATASTORE="$DATASTORE/$service"
export SERVICE_CONFIGSTORE="$CONFIGSTORE/$service"
exname="$exname $ACTION_NAME $SERVICE_NAME" \
@ -3018,7 +3308,8 @@ export -f get_relation_data_dir
get_relation_data_file() {
local service="$1" target_service="$2" relation_name="$3" relation_config="$4"
local service="$1" target_service="$2" relation_name="$3" relation_config="$4" \
new new_md5 relation_dir relation_data_file
relation_dir=$(get_relation_data_dir "$service" "$target_service" "$relation_name") || return 1
relation_data_file="$relation_dir/data"
@ -3034,9 +3325,11 @@ get_relation_data_file() {
new=true
fi
if [ "$new" ]; then
if [ -n "$new" ]; then
OLDUMASK=$(umask)
umask 0077
e "$relation_config" > "$relation_data_file"
chmod go-rwx "$relation_data_file" ## protecting this file
umask "$OLDUMASK"
e "$relation_config" | md5_compat > "$relation_data_file.md5_ref"
fi
echo "$relation_data_file"
@ -3171,7 +3464,7 @@ _get_master_service_for_service_cached () {
return 0
fi
if [ "$(echo "$metadata" | shyaml get-value "subordinate" 2>/dev/null)" != "True" ]; then
if ! [[ "$(echo "$metadata" | shyaml get-value "subordinate" 2>/dev/null)" =~ ^True|true$ ]]; then
## just return service name
echo "$service" | tee "$cache_file"
return 0
@ -3295,7 +3588,7 @@ _get_docker_compose_mixin_from_metadata_cached() {
mixins+=("$docker_compose")
fi
if [ "$(echo "$metadata" | shyaml get-value "subordinate" 2>/dev/null)" == "True" ]; then
if [[ "$(echo "$metadata" | shyaml get-value "subordinate" 2>/dev/null)" =~ ^True|true$ ]]; then
subordinate=true
fi
fi
@ -3473,7 +3766,7 @@ switch_to_relation_service() {
export SERVICE_NAME="$ts"
export SERVICE_DATASTORE="$DATASTORE/$SERVICE_NAME"
DOCKER_BASE_IMAGE=$(service_base_docker_image "$SERVICE_NAME")
DOCKER_BASE_IMAGE=$(service_ensure_image_ready "$SERVICE_NAME")
export DOCKER_BASE_IMAGE
target_charm=$(get_service_charm "$ts") || return 1
@ -3597,7 +3890,14 @@ launch_docker_compose() {
mkdir -p "$docker_compose_tmpdir/$project"
docker_compose_dir="$docker_compose_tmpdir/$project"
get_docker_compose $SERVICE_PACK > "$docker_compose_dir/docker-compose.yml" || return 1
if [ -z "$_CURRENT_DOCKER_COMPOSE" ]; then
err "${FUNCNAME[0]} is meant to be called after"\
"\$_CURRENT_DOCKER_COMPOSE has been calculated."
echo " Called by:" >&2
printf " - %s\n" "${FUNCNAME[@]:1}" >&2
return 1
fi
cat "$_CURRENT_DOCKER_COMPOSE" > "$docker_compose_dir/docker-compose.yml" || return 1
if [ -e "$state_tmpdir/to-merge-in-docker-compose.yml" ]; then
# debug "Merging some config data in docker-compose.yml:"
# debug "$(cat $state_tmpdir/to-merge-in-docker-compose.yml)"
@ -4025,6 +4325,7 @@ display_help() {
print_help
echo "${WHITE}Usage${NORMAL}:"
echo " $usage"
echo " $usage cache {clean|clear}"
echo "${WHITE}Options${NORMAL}:"
echo " -h, --help Print this message and quit"
echo " (ignoring any other options)"
@ -4053,6 +4354,7 @@ display_help() {
echo " --add-compose-content, -Y YAML"
echo " Will merge some direct YAML with the current compose"
echo " -c, --color Force color mode (default is to detect if in tty mode)"
echo " --push-builds Will push cached docker images to docker cache registry"
get_docker_compose_opts_help | remove_options_in_option_help_msg --version --help --verbose |
filter_docker_compose_help_message
@ -4067,7 +4369,7 @@ _graph_service() {
metadata=$(charm.metadata "$charm") || return 1
subordinate=$(echo "$metadata" | shyaml get-value "subordinate" 2>/dev/null)
if [ "$subordinate" == "True" ]; then
if [[ "$subordinate" =~ ^True|true$ ]]; then
requires="$(echo "$metadata" | shyaml get-value "requires" 2>/dev/null)"
master_charm=
while read-0 relation_name relation; do
@ -4109,8 +4411,8 @@ _graph_node_service() {
cat <<EOF
"$(_graph_node_service_label ${service})" [
style = "filled, $([ "$subordinate" == "True" ] && echo "dashed" || echo "bold")"
penwidth = $([ "$subordinate" == "True" ] && echo "3" || echo "5")
style = "filled, $([[ "$subordinate" =~ ^True|true$ ]] && echo "dashed" || echo "bold")"
penwidth = $([[ "$subordinate" =~ ^True|true$ ]] && echo "3" || echo "5")
color = $([ "$base" ] && echo "blue" || echo "black")
fillcolor = "white"
fontname = "Courier New"
@ -4245,11 +4547,11 @@ export -f cached_wget
trap_add "EXIT" clean_cache
export COMPOSE_DOCKER_REGISTRY="${COMPOSE_DOCKER_REGISTRY:-docker.0k.io}"
if [ -z "$DISABLE_SYSTEM_CONFIG_FILE" ]; then
if [ -r /etc/default/charm ]; then
. /etc/default/charm
. "/etc/default/charm"
fi
if [ -r "/etc/default/$exname" ]; then
@ -4373,6 +4675,9 @@ while read-0 arg; do
rebuild_relations_to_service+=("$value")
shift
;;
--push-builds)
export COMPOSE_PUSH_TO_REGISTRY=1
;;
--debug|-d)
export DEBUG=true
export VERBOSE=true
@ -4483,6 +4788,38 @@ while read-0 arg; do
shift
done < <(cla.normalize "$@")
[ -n "$CACHEDIR" ] || die "No cache directory defined."
[ -d "$CACHEDIR" ] || die "Cache directory '$CACHEDIR' doesn't exists."
case "$action" in
cache)
case "${remainder_args[0]}" in
clean)
clean_cache
exit 0
;;
clear)
Wrap "${wrap_opts[@]}" -v -d "clear cache directory" -- rm -rf "$CACHEDIR/"*
## clear all docker caches
## image name are like '[$COMPOSE_DOCKER_REGISTRY]cache/charm/CHARM_NAME:HASH'
Wrap "${wrap_opts[@]}" -v -d "clear docker cache" <<EOF
docker images --format "{{.Repository}}:{{.Tag}}" |
egrep "^($COMPOSE_DOCKER_REGISTRY/)?cache/charm/[a-zA-Z0-9._-]+:[0-9a-f]{32,32}$" |
while read -r image; do
docker rmi "\$image" || true
done
EOF
exit 0
;;
*)
err "Unknown cache command: ${DARKCYAN}${remainder_args[0]}${NORMAL}"
exit 1
;;
esac
;;
esac
export compose_contents
[ "${services_args[*]}" ] && debug " ${DARKWHITE}Services:$NORMAL ${DARKYELLOW}${services_args[*]}$NORMAL"
@ -4612,6 +4949,9 @@ get_docker_compose "${services_args[@]}" >/dev/null || { ## precalculate variab
full_init=
case "$action" in
build)
full_init=true ## will actually stop after build
;;
up|run)
full_init=true
post_hook=true
@ -4637,16 +4977,21 @@ case "$action" in
;;
esac
if [ -n "$full_init" ]; then
## init in order
if [[ -z "$no_init" && -z "$no_hooks" ]]; then
if [[ "$action" == "build" ]] || [[ -z "$no_init" && -z "$no_hooks" ]]; then
[[ "$action" == "build" ]] || Section "acquire charm's images"
run_service_acquire_images "${services_args[@]}" || exit 1
Feed
[ "$action" == "build" ] && {
exit 0
}
Section setup host resources
setup_host_resources "${services_args[@]}" || exit 1
## init in order
Section initialisation
run_service_hook init "${services_args[@]}" || exit 1
fi
## Get relations
if [[ -z "$no_relations" && -z "$no_hooks" ]]; then
if [ "${#rebuild_relations_to_service[@]}" != 0 ]; then
@ -4673,6 +5018,8 @@ fi | log
if [ "${PIPESTATUS[0]}" != 0 ]; then
exit 1
fi
[ "$action" == "build" ] && exit 0
if [ "$action" == "run" ] && [ "${#services_args}" != 0 ]; then
charm=$(get_service_charm "${services_args[0]}") || exit 1

Loading…
Cancel
Save