#!/usr/bin/env bash-shlib # -*- mode: shell-script -*- include shunit depends sed grep git mkdir readlink export -f matches export grep tmp=/tmp tprog="../bin/compose" tprog=$(readlink -f $tprog) export PATH=".:$PATH" short_tprog=$(basename "$tprog") ## ## Convenience function ## init_test() { test_tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX) cd "$test_tmpdir" export CACHEDIR="$test_tmpdir/.cache" export VARDIR="$test_tmpdir/.var" mkdir -p "$CACHEDIR" } tear_test() { rm -rf "$test_tmpdir" } ## ## Tests ## ## # Checking arguments test_calling_sourcing() { assert_list < $test_tmpdir/testcharm/metadata.yml EOF2 . "$tprog" _setup_state_dir out="\$(get_docker_compose_mixin_from_metadata testcharm)" expected='\ labels: - compose.charm=testcharm' [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- volumes export CHARM_STORE=$test_tmpdir export CONFIGSTORE=/tmp/CONFIG export DATASTORE=/tmp/DATA mkdir -p $test_tmpdir/testcharm cat < $test_tmpdir/testcharm/metadata.yml data-resources: - /a config-resources: - /b host-resources: - /tmp:/tmp EOF2 . "$tprog" _setup_state_dir out=\$(get_docker_compose_mixin_from_metadata testcharm) || exit 1 expected="\ labels: - compose.charm=testcharm volumes: - /tmp/DATA/testcharm/a:/a:rw - /tmp/CONFIG/testcharm/b:/b:rw - /tmp:/tmp:rw" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- docker-compose export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/testcharm cat < $test_tmpdir/testcharm/metadata.yml docker-compose: volumes: - /any:/vol entrypoint: any EOF2 . "$tprog" _setup_state_dir out="\$(get_docker_compose_mixin_from_metadata testcharm)" || exit 1 expected="\ entrypoint: any labels: - compose.charm=testcharm volumes: - /any:/vol" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- image export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/testcharm cat < $test_tmpdir/testcharm/metadata.yml docker-image: toto EOF2 . "$tprog" _setup_state_dir out="\$(get_docker_compose_mixin_from_metadata testcharm)" || exit 1 expected="\ image: toto labels: - compose.charm=testcharm" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- build export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/testcharm/build cat < $test_tmpdir/testcharm/metadata.yml # XXX new content to invalidate cache EOF2 . "$tprog" _setup_state_dir out="\$(get_docker_compose_mixin_from_metadata testcharm)" || exit 1 expected="\ build: $test_tmpdir/testcharm/build labels: - compose.charm=testcharm" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- subordinate with image export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/testcharm cat < $test_tmpdir/testcharm/metadata.yml subordinate: true docker-image: toto EOF2 . "$tprog" _setup_state_dir ! get_docker_compose_mixin_from_metadata testcharm ## -- subordinate with build subdir export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/testcharm/build cat < $test_tmpdir/testcharm/metadata.yml subordinate: true EOF2 . "$tprog" _setup_state_dir ! get_docker_compose_mixin_from_metadata testcharm EOF tear_test } function test_merge_yaml { init_test assert_list < $test_tmpdir/compose.yml toto: charm: www blabla: xxx EOF2 . "$tprog" _setup_state_dir COMPOSE_YML_FILE=$test_tmpdir/compose.yml out="\$(get_compose_service_def toto)" test "\$out" == "charm: www blabla: xxx" || { echo -e "** get_compose_service_def toto:\n\$out" exit 1 } ## -- Addition of default charm export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/www cat < $test_tmpdir/compose.yml www: blabla: xxx EOF2 . "$tprog" _setup_state_dir COMPOSE_YML_FILE=$test_tmpdir/compose.yml test "\$(get_compose_service_def www)" == "blabla: xxx charm: www" EOF } ## ## ## function test_get_master_service_for_service() { init_test assert_list < $test_tmpdir/www/metadata.yml EOF2 . "$tprog" _setup_state_dir test "\$(get_master_service_for_service www)" == "www" ## -- subordinate export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/{www,mysql} touch $test_tmpdir/mysql/metadata.yml cat < $test_tmpdir/www/metadata.yml subordinate: true requires: a-label-for-relation: interface: a-name-relation scope: container EOF2 cat < $test_tmpdir/compose.yml www: charm: www relations: a-name-relation: mysql: label: value EOF2 . "$tprog" _setup_state_dir COMPOSE_YML_FILE=$test_tmpdir/compose.yml test "\$(get_master_service_for_service www)" == "mysql" EOF } ## ## ## function test_get_docker_compose_service_mixin() { init_test assert_list < $test_tmpdir/www/metadata.yml data-resources: - /tmp/a config-resources: - /tmp/b EOF2 . "$tprog" _setup_state_dir out=\$(_get_docker_compose_service_mixin www | shyaml get-value www.volumes) [[ "\$out" == "\ - /www/tmp/a:/tmp/a:rw - /www/tmp/b:/tmp/b:rw" ]] || { echo -e "** _get_docker_compose_service_mixin www:\n\$out"; exit 1 } ## -- Simple (compose, but no subordinate) export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/{www,mysql} cat < $test_tmpdir/www/metadata.yml data-resources: - /tmp/a config-resources: - /tmp/b EOF2 touch $test_tmpdir/mysql/metadata.yml cat < $test_tmpdir/compose.yml www: charm: www relations: a-name-relation: mysql: label: value EOF2 . "$tprog" _setup_state_dir COMPOSE_YML_FILE=$test_tmpdir/compose.yml out="\$(_get_docker_compose_service_mixin www)" || exit 1 [ "\$out" == "www: labels: - compose.service=www - compose.master-service=www - compose.project=\$(basename "$test_tmpdir") - compose.charm=www links: - mysql volumes: - /www/tmp/a:/tmp/a:rw - /www/tmp/b:/tmp/b:rw" ] || { echo -e "OUT:\n\$out" exit 1 } ## -- compose, subordinate export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/{www,mysql} cat < $test_tmpdir/www/metadata.yml subordinate: true data-resources: - /tmp/a config-resources: - /tmp/b requires: a-name-relation: interface: a-name-relation scope: container EOF2 cat < $test_tmpdir/compose.yml www: charm: www relations: a-name-relation: mysql: label: value EOF2 . "$tprog" _setup_state_dir COMPOSE_YML_FILE=$test_tmpdir/compose.yml out="\$(_get_docker_compose_service_mixin www)" || exit 1 expected="mysql: labels: - compose.service=www - compose.master-service=mysql - compose.project=$(basename "$test_tmpdir") - compose.charm=www volumes: - /www/tmp/a:/tmp/a:rw - /www/tmp/b:/tmp/b:rw" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } EOF } function test_get_docker_compose { init_test assert_list < $test_tmpdir/www/metadata.yml data-resources: - /tmp/a config-resources: - /tmp/b EOF2 . "$tprog" _setup_state_dir out=\$(get_docker_compose www) echo "OUT:" echo "\$out" out=\$(echo "\$out" | shyaml get-value services.www.volumes) echo "OUT volumes:" echo "\$out" test "\$out" == "\\ - /www/tmp/a:/tmp/a:rw - /www/tmp/b:/tmp/b:rw" ## -- simple with docker-compose export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/{www,mysql} cat < $test_tmpdir/www/metadata.yml data-resources: - /tmp/a config-resources: - /tmp/b EOF2 cat < $test_tmpdir/mysql/metadata.yml data-resources: - /tmp/c config-resources: - /tmp/d EOF2 cat < $test_tmpdir/compose.yml web_site: charm: www relations: db-connection: mysql: user: toto dbname: tata EOF2 . "$tprog" COMPOSE_YML_FILE=$test_tmpdir/compose.yml _setup_state_dir out=\$(get_docker_compose www | shyaml get-value services.www.volumes) test "\$out" == "\\ - /www/tmp/a:/tmp/a:rw - /www/tmp/b:/tmp/b:rw" || { echo -e "** get_docker_compose www:\n\$out" exit 1 } out=\$(get_docker_compose_links web_site | shyaml get-value web_site.links) test "\$out" == "- mysql" || { echo -e "** get_docker_compose_links web_site:\n\$out" exit 1 } out=\$(get_docker_compose web_site | shyaml get-value services) expected="\ mysql: labels: - compose.service=mysql - compose.master-service=mysql - compose.project=$(basename "$test_tmpdir") - compose.charm=mysql volumes: - /mysql/tmp/c:/tmp/c:rw - /mysql/tmp/d:/tmp/d:rw web_site: labels: - compose.service=web_site - compose.master-service=web_site - compose.project=$(basename "$test_tmpdir") - compose.charm=www links: - mysql volumes: - /web_site/tmp/a:/tmp/a:rw - /web_site/tmp/b:/tmp/b:rw" test "\$out" == "\$expected" || { echo -e "** get_docker_compose web_site:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- subordinate export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/{www,mysql} cat < $test_tmpdir/www/metadata.yml subordinate: true data-resources: - /tmp/a config-resources: - /tmp/b requires: my-db-connection: interface: db-connection scope: container EOF2 cat < $test_tmpdir/mysql/metadata.yml data-resources: - /tmp/c config-resources: - /tmp/d EOF2 cat < $test_tmpdir/compose.yml web_site: charm: www relations: db-connection: mysql: user: toto dbname: tata EOF2 . "$tprog" COMPOSE_YML_FILE=$test_tmpdir/compose.yml _setup_state_dir # should fail because of missing relations ! get_docker_compose www || exit 1 # volumes gets mixed out="\$(get_docker_compose web_site | shyaml get-value services.mysql.volumes)" test "\$out" == "\ - /web_site/tmp/a:/tmp/a:rw - /web_site/tmp/b:/tmp/b:rw - /mysql/tmp/c:/tmp/c:rw - /mysql/tmp/d:/tmp/d:rw" || { echo -e "OUT:\n\$out" exit 1 } ## -- subordinate with complex features export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/{www,mysql} cat < $test_tmpdir/www/metadata.yml subordinate: true data-resources: - /tmp/a config-resources: - /tmp/b requires: my-db-connection: interface: db-connection scope: container docker-compose: volumes: - /special-volume-from-www:/special-volume-from-www EOF2 cat < $test_tmpdir/mysql/metadata.yml data-resources: - /tmp/c config-resources: - /tmp/d docker-compose: entrypoint: custom-entrypoint volumes: - /special-volume-from-mysql:/special-volume-from-mysql EOF2 cat < $test_tmpdir/compose.yml web_site: charm: www relations: db-connection: mysql: user: toto dbname: tata EOF2 . "$tprog" COMPOSE_YML_FILE=$test_tmpdir/compose.yml _setup_state_dir # should fail because of missing relations #! get_docker_compose www || exit 1 # volumes gets mixed out="\$(get_docker_compose web_site | shyaml get-value services.mysql)" expected="\ entrypoint: custom-entrypoint labels: - compose.service=web_site - compose.charm=www - compose.service=mysql - compose.master-service=mysql - compose.project=$(basename "$test_tmpdir") - compose.charm=mysql volumes: - /web_site/tmp/a:/tmp/a:rw - /web_site/tmp/b:/tmp/b:rw - /special-volume-from-www:/special-volume-from-www - /mysql/tmp/c:/tmp/c:rw - /mysql/tmp/d:/tmp/d:rw - /special-volume-from-mysql:/special-volume-from-mysql" test "\$out" == "\$expected" || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } EOF tear_test } ## XXXvlab: only broken due to Wrap being used for relations # function test_run_service_relations { # init_test # assert_list < $test_tmpdir/www/metadata.yml # data-resources: # - /tmp/a # config-resources: # - /tmp/b # EOF2 # . "$tprog" # _setup_state_dir # echo "Docker Compose:" # get_docker_compose www # echo "Run Service relations:" # _run_service_relation() { # echo "\$FUNCNAME: received $*" # } # out=\$(run_service_relations www) # test -z "\$out" # ## -- simple with docker-compose # export CHARM_STORE=$test_tmpdir # mkdir -p $test_tmpdir/{www,mysql} # cat < $test_tmpdir/www/metadata.yml # data-resources: # - /tmp/a # config-resources: # - /tmp/b # EOF2 # cat < $test_tmpdir/mysql/metadata.yml # data-resources: # - /tmp/c # config-resources: # - /tmp/d # EOF2 # cat < $test_tmpdir/compose.yml # web_site: # charm: www # relations: # db-connection: # mysql: # user: toto # dbname: tata # EOF2 # . "$tprog" # COMPOSE_YML_FILE=$test_tmpdir/compose.yml # _setup_state_dir # _setup_state_dir # echo "Docker Compose:" # get_docker_compose web_site # echo "Run Service relations:" # _run_service_relation() { # echo "\$FUNCNAME \$2 <-- \$1 --> \$3" # } # export -f _run_service_relation # out=\$(run_service_relations www) # test -z "\$out" || exit 1 # out=\$(run_service_relations web_site) # echo "OUT: \$out" # test "\$out" == "_run_service_relation web_site <-- db-connection --> mysql" # ## -- subordinate # export CHARM_STORE=$test_tmpdir # mkdir -p $test_tmpdir/{www,mysql} # cat < $test_tmpdir/www/metadata.yml # subordinate: true # data-resources: # - /tmp/a # config-resources: # - /tmp/b # requires: # my-db-connection: # interface: db-connection # scope: container # EOF2 # cat < $test_tmpdir/mysql/metadata.yml # data-resources: # - /tmp/c # config-resources: # - /tmp/d # EOF2 # cat < $test_tmpdir/compose.yml # web_site: # charm: www # relations: # db-connection: # mysql: # user: toto # dbname: tata # EOF2 # . "$tprog" # COMPOSE_YML_FILE=$test_tmpdir/compose.yml # _setup_state_dir # echo "Docker Compose:" # get_docker_compose web_site # echo "Run Service relations:" # _run_service_relation() { # echo "\$FUNCNAME \$2 <-- \$1 --> \$3" # } # export -f _run_service_relation # out=\$(run_service_relations www) # test -z "\$out" || exit 1 # out=\$(run_service_relations web_site) # echo "\$out" # test "\$out" == "_run_service_relation web_site <-- db-connection --> mysql" # EOF # tear_test # } function test_get_docker_compose_2() { init_test assert_list < $test_tmpdir/compose.yml app: charm: app relations: web-proxy: www: user: toto db: mysql EOF2 . "$tprog" COMPOSE_YML_FILE=$test_tmpdir/compose.yml _setup_state_dir out=\$(get_docker_compose_links "app") test "\$out" == "app: links: - www - mysql" || { echo -e "** get_docker_compose_links:\n\$out"; exit 1 } ## -- reverse-tech-dep export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/{www,mysql} cat < $test_tmpdir/www/metadata.yml provides: web-proxy: tech-dep: reversed EOF2 touch $test_tmpdir/mysql/metadata.yml cat < $test_tmpdir/compose.yml web_site: charm: mysql relations: web-proxy: www: user: toto EOF2 . "$tprog" COMPOSE_YML_FILE=$test_tmpdir/compose.yml _setup_state_dir out=\$(get_charm_relation_def "www" "web-proxy") || exit 1 test "\$out" == "tech-dep: reversed" || { echo -e "** get_charm_relation_def:\n\$out"; exit 1 } out=\$(get_charm_tech_dep_orientation_for_relation "www" "web-proxy") test "\$out" == "reversed" || { echo -e "** get_charm_tech_dep_orientation_for_relation:\n\$out"; exit 1 } out=\$(get_docker_compose_links "web_site") expected="www: links: - web_site" test "\$out" == "\$expected" || { echo -e "** get_docker_compose_links:\n\$out\nExpected:\n\$expected"; exit 1 } out=\$(get_docker_compose web_site | shyaml get-value services.www.links) test "\$out" == "- web_site" || { echo -e "** get_docker_compose:\n\$out"; exit 1 } EOF tear_test } function test_compose_config { init_test export CHARM_STORE=$test_tmpdir mkdir -p $test_tmpdir/{www,mysql} cat < $test_tmpdir/www/metadata.yml subordinate: true data-resources: - /tmp/a config-resources: - /tmp/b requires: my-db-connection: interface: db-connection scope: container docker-compose: volumes: - /special-volume-from-www:/special-volume-from-www EOF2 cat < $test_tmpdir/mysql/metadata.yml docker-image: docker.0k.io/mysql data-resources: - /tmp/c config-resources: - /tmp/d docker-compose: entrypoint: custom-entrypoint volumes: - /special-volume-from-mysql:/special-volume-from-mysql EOF2 cat < $test_tmpdir/compose.yml web_site: charm: www relations: db-connection: mysql: user: toto dbname: tata EOF2 assert_list < $test_tmpdir/www/metadata.yml EOF2 cat < $test_tmpdir/mysql/metadata.yml EOF2 cat < $test_tmpdir/compose.yml web_site: charm: www EOF2 export DISABLE_SYSTEM_CONFIG_FILE=true assert_list <&1 >/dev/null ) expected="docker-compose run web_site" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- simple single dash arg cd "$test_tmpdir" out=\$("$tprog" --dry-compose-run run -T web_site 2>&1 >/dev/null ) expected="docker-compose run -T web_site" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- desaggregation of combined single char args cd "$test_tmpdir" out=\$("$tprog" --dry-compose-run logs -ft --tail 20 web_site 2>&1 >/dev/null) expected="docker-compose logs -f -t --tail 20 web_site" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- desaggregation of combined single char option and valued option char cd "$test_tmpdir" out=\$("$tprog" --dry-compose-run run -Tv x:y web_site 2>&1 >/dev/null) expected="docker-compose run -T -v x:y web_site" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- simple unexpected single dash arg cd "$test_tmpdir" out=\$("$tprog" --dry-compose-run run -Z web_site 2>&1) expected_reg="Unknown option '-Z'" [[ "\$out" =~ \$expected_reg ]] || { echo -e "Can't find '\$expected_reg' in out:\n\$out" exit 1 } ## -- simple unexpected single dash arg after expected one cd "$test_tmpdir" out=\$("$tprog" --dry-compose-run run -T -Z web_site 2>&1) expected_reg="Unknown option '-Z'" [[ "\$out" =~ \$expected_reg ]] || { echo -e "Can't find '\$expected_reg' in out:\n\$out" exit 1 } ## -- simple unexpected single dash arg after expected aggregated one cd "$test_tmpdir" out=\$("$tprog" --dry-compose-run run -TZ web_site 2>&1) expected_reg="Unknown option '-Z'" [[ "\$out" =~ \$expected_reg ]] || { echo -e "Can't find '\$expected_reg' in out:\n\$out" exit 1 } ## -- multiple services cd "$test_tmpdir" out=\$("$tprog" --dry-compose-run logs --tail 15 web_site mysql 2>&1 >/dev/null) expected="docker-compose logs --tail 15 web_site mysql" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } ## -- single services cd "$test_tmpdir" out=\$("$tprog" --dry-compose-run run web_site mysql 2>&1 >/dev/null) expected="docker-compose run web_site mysql" [ "\$out" == "\$expected" ] || { echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))" exit 1 } EOF tear_test } function test_filter_opts { src=$(cat <<'EOF' -d, --detach --name NAME --entrypoint CMD -e KEY=VAL -l, --label KEY=VAL -u, --user="" --no-deps --rm -p, --publish=[] --service-ports --use-aliases -v, --volume=[] -T -w, --workdir="" EOF ) export src assert_list <