From bb3ae02178689193305f753647ee1e2de952cfdf Mon Sep 17 00:00:00 2001 From: William Temple Date: Mon, 20 Jul 2015 13:49:28 -0400 Subject: [PATCH] Integrate remote inspection with Atomic info. Signed-off-by: William Temple --- Atomic/atomic.py | 49 +++++++++++++++++++++++++-------- Atomic/util.py | 4 +-- atomic | 3 ++ docs/atomic-info.1.md | 7 ++++- test.sh | 27 +++++++++++------- tests/integration/test_info.sh | 38 +++++++++++++++++++++++++ tests/integration/test_mount.sh | 12 ++++---- tests/integration_template.sh | 4 +-- 8 files changed, 111 insertions(+), 33 deletions(-) create mode 100755 tests/integration/test_info.sh diff --git a/Atomic/atomic.py b/Atomic/atomic.py index d6538fd..a6263bd 100644 --- a/Atomic/atomic.py +++ b/Atomic/atomic.py @@ -482,20 +482,45 @@ removes all containers based on an image. return " ".join(args) def info(self): - self.inspect = self._inspect_image() - if not self.inspect: - self.update() - self.inspect = self._inspect_image() + """ + Retrieve and print all LABEL information for a given image. + """ + def _no_such_image(): + raise ValueError('Could not find any image matching "{}".' + ''.format(self.args.image)) - labels = self._get_labels() - buf = "" + inspection = None + if not self.args.force_remote_info: + try: + inspection = self.d.inspect_image(self.args.image) + except docker.errors.APIError: + # No such image locally, but fall back to remote + pass + if inspection is None: + try: + inspection = self.d.inspect_image(self.args.image, remote=True) + except docker.errors.APIError: + # image does not exist on any configured registry + _no_such_image() + except TypeError: # pragma: no cover + # If a user doesn't have remote-inspection, setting remote=True + # above will raise TypeError. + # TODO: remove if remote inspection is accepted into docker + # But we should error if the user specifically requested remote + if self.args.force_remote_info: + raise ValueError('Your docker daemon does not support ' + 'remote inspection.') + else: + _no_such_image() + # By this point, inspection cannot be "None" + try: + labels = inspection['Config']['Labels'] + except TypeError: # pragma: no cover + # Some images may not have a 'Labels' key. + raise ValueError('{} has no label information.' + ''.format(self.args.image)) for label in labels: - buf +=("%-13s: %s\n" % (label, labels[label])) - if buf: - self.writeOut(buf.strip()) - else: - raise ValueError("No label information for that " - "image.") + self.writeOut('{0}: {1}'.format(label, labels[label])) def dangling(self, image): if image == "": diff --git a/Atomic/util.py b/Atomic/util.py index cae8101..c972d8d 100644 --- a/Atomic/util.py +++ b/Atomic/util.py @@ -52,7 +52,7 @@ def image_by_name(img_name): return valid_images -def subp(cmd): # pragma: no cover +def subp(cmd): """ Run a command as a subprocess. Return a triple of return code, standard out, standard err. @@ -63,7 +63,7 @@ def subp(cmd): # pragma: no cover return ReturnTuple(proc.returncode, stdout=out, stderr=err) -def default_container_context(): # pragma: no cover +def default_container_context(): if selinux.is_selinux_enabled() != 0: fd = open(selinux.selinux_lxc_contexts_path()) for i in fd.readlines(): diff --git a/atomic b/atomic index 2228450..96fd8b7 100755 --- a/atomic +++ b/atomic @@ -81,6 +81,9 @@ if __name__ == '__main__': help=_("display label information about an image"), epilog="atomic info attempts to read and display the LABEL information about an image") infop.set_defaults(func=atomic.info) + infop.add_argument("--remote", dest="force_remote_info", + action='store_true', default=False, + help=_('ignore local images and only scan registries')) infop.add_argument("image", help=_("container image")) installp = subparser.add_parser("install", diff --git a/docs/atomic-info.1.md b/docs/atomic-info.1.md index c88db13..c8d58ea 100644 --- a/docs/atomic-info.1.md +++ b/docs/atomic-info.1.md @@ -7,14 +7,19 @@ atomic-info - Display LABEL information about an image # SYNOPSIS **atomic info** [**-h**] +[**--remote**] IMAGE # DESCRIPTION -**atomic info** displays the LABEL fields within an image +**atomic info** displays the LABEL fields within an image. By default, it +will check first for a local image and then all configured registries. # OPTIONS: **--help** Print usage statement +**--remote** + Ignore all local images, only search configured registries. + # HISTORY January 2015, Originally compiled by Daniel Walsh (dwalsh at redhat dot com) diff --git a/test.sh b/test.sh index 4d29163..080598a 100755 --- a/test.sh +++ b/test.sh @@ -68,29 +68,32 @@ make_docker_images # Python unit tests. echo "UNIT TESTS:" -COVERAGE="/usr/bin/coverage" -if [[ ! -x "${COVERAGE}" ]]; then +COVERAGE_BIN="/usr/bin/coverage" +if [[ ! -x "${COVERAGE_BIN}" ]]; then # The executable is "coverage2" on systems with default python3 and no # python3 install. - COVERAGE="/usr/bin/coverage2" + COVERAGE_BIN="/usr/bin/coverage2" fi set +e +COVERAGE="${COVERAGE_BIN} +run +--source=./Atomic/ +--branch" -${COVERAGE} run --source=./Atomic/ --branch -m unittest discover \ - ./tests/unit | tee -a ${LOG} +${COVERAGE} -m unittest discover ./tests/unit/ | tee -a ${LOG} _UNIT_FAIL="$?" + set -e -echo "Coverage report:" | tee -a ${LOG} - -${COVERAGE} report | tee -a ${LOG} - # CLI integration tests. -# TODO: I would like to be able to include CLI tests in the coverage report. let failures=0 || true printf "\nINTEGRATION TESTS:\n" | tee -a ${LOG} +export ATOMIC="${COVERAGE} +--append +atomic" + for tf in `find ./tests/integration/ -name test_*.sh`; do printf "Running test $(basename ${tf})...\t\t" printf "\n==== ${tf} ====\n" >> ${LOG} @@ -102,6 +105,10 @@ for tf in `find ./tests/integration/ -name test_*.sh`; do fi done +echo "Coverage report:" | tee -a ${LOG} + +${COVERAGE_BIN} report | tee -a ${LOG} + if [[ "${failures}" -eq "0" ]]; then if [[ $_UNIT_FAIL -eq 0 ]]; then echo "ALL TESTS PASSED" diff --git a/tests/integration/test_info.sh b/tests/integration/test_info.sh new file mode 100755 index 0000000..436cf58 --- /dev/null +++ b/tests/integration/test_info.sh @@ -0,0 +1,38 @@ +#!/bin/bash -x +set -euo pipefail +IFS=$'\n\t' + +EXPECTED_T1="Checksum: $(sha256sum ./tests/test-images/Dockerfile.1)" + +validTest1 () { + for e in ${TEST_1}; do + [ "$e" = "${EXPECTED_T1}" ] && return 0; + done + return 1 +} + +TEST_1=`${ATOMIC} info atomic-test-1` +TEST_RHEL_REMOTE=`${ATOMIC} info --remote rhel7:7.1-9` +TEST_RHEL=`${ATOMIC} info rhel7:7.1-9` + +set +e + +TEST_DOES_NOT_EXIST=`${ATOMIC} info this-is-not-a-real-image` + +set -e + +echo $TEST_1 + +if [[ "${TEST_RHEL_REMOTE}" != "${TEST_RHEL}" ]]; then + exit 1 +fi + +if [[ "${TEST_DOES_NOT_EXIST}" != "" ]]; then + exit 1 +fi + +validTest1 + +if [[ $? -ne 0 ]]; then + exit 1 +fi diff --git a/tests/integration/test_mount.sh b/tests/integration/test_mount.sh index c8931c8..6924695 100755 --- a/tests/integration/test_mount.sh +++ b/tests/integration/test_mount.sh @@ -36,24 +36,24 @@ cleanup_container () { } trap cleanup_container EXIT -./atomic mount ${id} ${MNT_WORK}/container -./atomic mount ${INAME} ${MNT_WORK}/image +${ATOMIC} mount ${id} ${MNT_WORK}/container +${ATOMIC} mount ${INAME} ${MNT_WORK}/image # Expect failure set +e -./atomic mount ${id} --live ${MNT_WORK}/container +${ATOMIC} mount ${id} --live ${MNT_WORK}/container if [ "$?" -eq "0" ]; then exit 1 fi -./atomic mount ${INAME} --live ${MNT_WORK}/image +${ATOMIC} mount ${INAME} --live ${MNT_WORK}/image if [ "$?" -eq "0" ]; then exit 1 fi set -e cleanup_mount () { - ./atomic unmount ${MNT_WORK}/container - ./atomic unmount ${MNT_WORK}/image + ${ATOMIC} unmount ${MNT_WORK}/container + ${ATOMIC} unmount ${MNT_WORK}/image cleanup_container } trap cleanup_mount EXIT diff --git a/tests/integration_template.sh b/tests/integration_template.sh index 96f42c0..9386888 100644 --- a/tests/integration_template.sh +++ b/tests/integration_template.sh @@ -8,8 +8,8 @@ IFS = $'\n\t' # testing: PYTHONPATH (python module import path # WORK_DIR (a directory that is safe to modify) # DOCKER (the docker executable location) -# SECRET (a generated sha256 hash inserted into test -# containers) +# ATOMIC (an invocation of 'atomic' which measures code coverage) +# SECRET (a generated sha256 hash inserted into test containers) # In addition, the test harness creates some images for use in testing. # See tests/test-images/