示例#1
0
class DockerClient(object):
    def __init__(self, base_url):
        self.client = APIClient(base_url=base_url, version='auto', timeout=30)
        self.auth_config = {
            'username': app.config['DOCKER_REGISTRY_AUTH'].get('username'),
            'password': app.config['DOCKER_REGISTRY_AUTH'].get('password')
        }

    def __repr__(self):
        return '<DockerClient %r>' % self.client.base_url

    def __del__(self):
        self.client.close()

    @property
    def api_version(self):
        return self.client.api_version

    def docker_info(self):
        return self.client.info()

    def pull_image(self, image, tag, stream=False):
        if stream:
            return self.client.pull(image, tag, auth_config=self.auth_config, stream=True)
        rst = self.client.pull(image, tag, auth_config=self.auth_config)
        last_message = json.loads(rst.split('\r\n')[-2])
        if last_message.get('error'):
            raise APIError(last_message['error'])

    def prune_images(self, filters=None):
        return self.client.prune_images(filters=filters)
示例#2
0
class TestSystem(unittest.TestCase):
    podman = None  # initialized podman configuration for tests
    service = None  # podman service instance
    topContainerId = ""

    def setUp(self):
        super().setUp()
        self.client = APIClient(base_url="tcp://127.0.0.1:8080", timeout=15)

        TestSystem.podman.restore_image_from_cache(self.client)
        TestSystem.topContainerId = common.run_top_container(self.client)

    def tearDown(self):
        common.remove_all_containers(self.client)
        common.remove_all_images(self.client)
        self.client.close()
        return super().tearDown()

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        TestSystem.podman = Podman()
        TestSystem.service = TestSystem.podman.open(
            "system", "service", "tcp:127.0.0.1:8080", "--time=0"
        )
        # give the service some time to be ready...
        time.sleep(2)

        returncode = TestSystem.service.poll()
        if returncode is not None:
            raise subprocess.CalledProcessError(returncode, "podman system service")

    @classmethod
    def tearDownClass(cls):
        TestSystem.service.terminate()
        stdout, stderr = TestSystem.service.communicate(timeout=0.5)
        if stdout:
            sys.stdout.write("\nImages Service Stdout:\n" + stdout.decode("utf-8"))
        if stderr:
            sys.stderr.write("\nImAges Service Stderr:\n" + stderr.decode("utf-8"))

        TestSystem.podman.tear_down()
        return super().tearDownClass()

    def test_Info(self):
        self.assertIsNotNone(self.client.info())

    def test_info_container_details(self):
        info = self.client.info()
        self.assertEqual(info["Containers"], 1)
        self.client.create_container(image=constant.ALPINE)
        info = self.client.info()
        self.assertEqual(info["Containers"], 2)

    def test_version(self):
        self.assertIsNotNone(self.client.version())
示例#3
0
class DockerBuilder(Builder[DockerBuildConfiguration, str,
                            DockerChecksumCalculator]):
    """
    Builder of Docker images.
    """
    def __init__(
        self,
        managed_build_configurations: Iterable[BuildConfigurationType] = None,
        checksum_retriever: ChecksumRetriever = None,
        checksum_calculator_factory: Callable[
            [], DockerChecksumCalculator] = DockerChecksumCalculator):
        super().__init__(managed_build_configurations, checksum_retriever,
                         checksum_calculator_factory)
        self.checksum_calculator.managed_build_configurations = self.managed_build_configurations
        self._docker_client = APIClient()

    def __del__(self):
        self._docker_client.close()

    def _build(self, build_configuration: DockerBuildConfiguration) -> str:
        logger.info(f"Building Docker image: {build_configuration.identifier}")
        logger.debug(
            f"{build_configuration.identifier} to be built using dockerfile "
            f"\"{build_configuration.dockerfile_location}\" in context \"{build_configuration.context}\""
        )
        log_generator = self._docker_client.build(
            path=build_configuration.context,
            tag=build_configuration.identifier,
            dockerfile=build_configuration.dockerfile_location,
            decode=True)

        log = {}
        try:
            for log in log_generator:
                details = log.get("stream", "").strip()
                if len(details) > 0:
                    logger.debug(details)
        except APIError as e:
            if e.status_code == 400 and "parse error" in e.explanation:
                dockerfile_location = build_configuration.dockerfile_location
                with open(dockerfile_location, "r") as file:
                    raise InvalidDockerfileBuildError(build_configuration.name,
                                                      dockerfile_location,
                                                      file.read())
            raise e

        if "error" in log:
            error_details = log["errorDetail"]
            raise BuildStepError(build_configuration.name,
                                 error_details["message"],
                                 error_details.get("code"))

        return build_configuration.identifier
示例#4
0
class TestContainers(unittest.TestCase):
    podman = None  # initialized podman configuration for tests
    service = None  # podman service instance
    topContainerId = ""

    def setUp(self):
        super().setUp()
        self.client = APIClient(base_url="tcp://127.0.0.1:8080", timeout=15)
        TestContainers.podman.restore_image_from_cache(self.client)
        TestContainers.topContainerId = common.run_top_container(self.client)
        self.assertIsNotNone(TestContainers.topContainerId)

    def tearDown(self):
        common.remove_all_containers(self.client)
        common.remove_all_images(self.client)
        self.client.close()
        return super().tearDown()

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        TestContainers.podman = Podman()
        TestContainers.service = TestContainers.podman.open(
            "system", "service", "tcp:127.0.0.1:8080", "--time=0"
        )
        # give the service some time to be ready...
        time.sleep(2)

        rc = TestContainers.service.poll()
        if rc is not None:
            raise subprocess.CalledProcessError(rc, "podman system service")

    @classmethod
    def tearDownClass(cls):
        TestContainers.service.terminate()
        stdout, stderr = TestContainers.service.communicate(timeout=0.5)
        if stdout:
            sys.stdout.write("\nContainers Service Stdout:\n" + stdout.decode("utf-8"))
        if stderr:
            sys.stderr.write("\nContainers Service Stderr:\n" + stderr.decode("utf-8"))

        TestContainers.podman.tear_down()
        return super().tearDownClass()

    def test_inspect_container(self):
        # Inspect bogus container
        with self.assertRaises(errors.NotFound) as error:
            self.client.inspect_container("dummy")
        self.assertEqual(error.exception.response.status_code, 404)

        # Inspect valid container by Id
        container = self.client.inspect_container(TestContainers.topContainerId)
        self.assertIn("top", container["Name"])

        # Inspect valid container by name
        container = self.client.inspect_container("top")
        self.assertIn(TestContainers.topContainerId, container["Id"])

    def test_create_container(self):
        # Run a container with detach mode
        container = self.client.create_container(image="alpine", detach=True)
        self.assertEqual(len(container), 2)

    def test_start_container(self):
        # Start bogus container
        with self.assertRaises(errors.NotFound) as error:
            self.client.start("dummy")
        self.assertEqual(error.exception.response.status_code, 404)

        # Podman docs says it should give a 304 but returns with no response
        # # Start a already started container should return 304
        # response = self.client.start(container=TestContainers.topContainerId)
        # self.assertEqual(error.exception.response.status_code, 304)

        # Create a new container and validate the count
        self.client.create_container(image=constant.ALPINE, name="container2")
        containers = self.client.containers(quiet=True, all=True)
        self.assertEqual(len(containers), 2)

    def test_stop_container(self):
        # Stop bogus container
        with self.assertRaises(errors.NotFound) as error:
            self.client.stop("dummy")
        self.assertEqual(error.exception.response.status_code, 404)

        # Validate the container state
        container = self.client.inspect_container("top")
        self.assertEqual(container["State"]["Status"], "running")

        # Stop a running container and validate the state
        self.client.stop(TestContainers.topContainerId)
        container = self.client.inspect_container("top")
        self.assertIn(
            container["State"]["Status"],
            "stopped exited",
        )

    def test_restart_container(self):
        # Restart bogus container
        with self.assertRaises(errors.NotFound) as error:
            self.client.restart("dummy")
        self.assertEqual(error.exception.response.status_code, 404)

        # Validate the container state
        self.client.stop(TestContainers.topContainerId)
        container = self.client.inspect_container("top")
        self.assertEqual(container["State"]["Status"], "stopped")

        # restart a running container and validate the state
        self.client.restart(TestContainers.topContainerId)
        container = self.client.inspect_container("top")
        self.assertEqual(container["State"]["Status"], "running")

    def test_remove_container(self):
        # Remove bogus container
        with self.assertRaises(errors.NotFound) as error:
            self.client.remove_container("dummy")
        self.assertEqual(error.exception.response.status_code, 404)

        # Remove container by ID with force
        self.client.remove_container(TestContainers.topContainerId, force=True)
        containers = self.client.containers()
        self.assertEqual(len(containers), 0)

    def test_remove_container_without_force(self):
        # Validate current container count
        containers = self.client.containers()
        self.assertTrue(len(containers), 1)

        # Remove running container should throw error
        with self.assertRaises(errors.APIError) as error:
            self.client.remove_container(TestContainers.topContainerId)
        self.assertEqual(error.exception.response.status_code, 500)

        # Remove container by ID with force
        self.client.stop(TestContainers.topContainerId)
        self.client.remove_container(TestContainers.topContainerId)
        containers = self.client.containers()
        self.assertEqual(len(containers), 0)

    def test_pause_container(self):
        # Pause bogus container
        with self.assertRaises(errors.NotFound) as error:
            self.client.pause("dummy")
        self.assertEqual(error.exception.response.status_code, 404)

        # Validate the container state
        container = self.client.inspect_container("top")
        self.assertEqual(container["State"]["Status"], "running")

        # Pause a running container and validate the state
        self.client.pause(container["Id"])
        container = self.client.inspect_container("top")
        self.assertEqual(container["State"]["Status"], "paused")

    def test_pause_stopped_container(self):
        # Stop the container
        self.client.stop(TestContainers.topContainerId)

        # Pause exited container should trow error
        with self.assertRaises(errors.APIError) as error:
            self.client.pause(TestContainers.topContainerId)
        self.assertEqual(error.exception.response.status_code, 500)

    def test_unpause_container(self):
        # Unpause bogus container
        with self.assertRaises(errors.NotFound) as error:
            self.client.unpause("dummy")
        self.assertEqual(error.exception.response.status_code, 404)

        # Validate the container state
        self.client.pause(TestContainers.topContainerId)
        container = self.client.inspect_container("top")
        self.assertEqual(container["State"]["Status"], "paused")

        # Pause a running container and validate the state
        self.client.unpause(TestContainers.topContainerId)
        container = self.client.inspect_container("top")
        self.assertEqual(container["State"]["Status"], "running")

    def test_list_container(self):
        # Add container and validate the count
        self.client.create_container(image="alpine", detach=True)
        containers = self.client.containers(all=True)
        self.assertEqual(len(containers), 2)

    def test_filters(self):
        self.skipTest("TODO Endpoint does not yet support filters")

        # List container with filter by id
        filters = {"id": TestContainers.topContainerId}
        ctnrs = self.client.containers(all=True, filters=filters)
        self.assertEqual(len(ctnrs), 1)

        # List container with filter by name
        filters = {"name": "top"}
        ctnrs = self.client.containers(all=True, filters=filters)
        self.assertEqual(len(ctnrs), 1)

    def test_rename_container(self):
        # rename bogus container
        with self.assertRaises(errors.APIError) as error:
            self.client.rename(container="dummy", name="newname")
        self.assertEqual(error.exception.response.status_code, 404)
示例#5
0
class TestImages(unittest.TestCase):
    podman = None  # initialized podman configuration for tests
    service = None  # podman service instance

    def setUp(self):
        super().setUp()
        self.client = APIClient(base_url="tcp://127.0.0.1:8080", timeout=15)

        TestImages.podman.restore_image_from_cache(self.client)

    def tearDown(self):
        common.remove_all_images(self.client)
        self.client.close()
        return super().tearDown()

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        TestImages.podman = Podman()
        TestImages.service = TestImages.podman.open("system", "service",
                                                    "tcp:127.0.0.1:8080",
                                                    "--time=0")
        # give the service some time to be ready...
        time.sleep(2)

        returncode = TestImages.service.poll()
        if returncode is not None:
            raise subprocess.CalledProcessError(returncode,
                                                "podman system service")

    @classmethod
    def tearDownClass(cls):
        TestImages.service.terminate()
        stdout, stderr = TestImages.service.communicate(timeout=0.5)
        if stdout:
            sys.stdout.write("\nImages Service Stdout:\n" +
                             stdout.decode("utf-8"))
        if stderr:
            sys.stderr.write("\nImAges Service Stderr:\n" +
                             stderr.decode("utf-8"))

        TestImages.podman.tear_down()
        return super().tearDownClass()

    def test_inspect_image(self):
        """Inspect Image"""
        # Check for error with wrong image name
        with self.assertRaises(errors.NotFound):
            self.client.inspect_image("dummy")
        alpine_image = self.client.inspect_image(constant.ALPINE)
        self.assertIn(constant.ALPINE, alpine_image["RepoTags"])

    def test_tag_invalid_image(self):
        """Tag Image

        Validates if invalid image name is given a bad response is encountered
        """
        with self.assertRaises(errors.NotFound):
            self.client.tag("dummy", "demo")

    def test_tag_valid_image(self):
        """Validates if the image is tagged successfully"""
        self.client.tag(constant.ALPINE, "demo", constant.ALPINE_SHORTNAME)
        alpine_image = self.client.inspect_image(constant.ALPINE)
        for x in alpine_image["RepoTags"]:
            self.assertIn("alpine", x)

    # @unittest.skip("doesn't work now")
    def test_retag_valid_image(self):
        """Validates if name updates when the image is retagged"""
        self.client.tag(constant.ALPINE_SHORTNAME, "demo", "rename")
        alpine_image = self.client.inspect_image(constant.ALPINE)
        self.assertNotIn("demo:test", alpine_image["RepoTags"])

    def test_list_images(self):
        """List images"""
        all_images = self.client.images()
        self.assertEqual(len(all_images), 1)
        # Add more images
        self.client.pull(constant.BB)
        all_images = self.client.images()
        self.assertEqual(len(all_images), 2)

        # List images with filter
        filters = {"reference": "alpine"}
        all_images = self.client.images(filters=filters)
        self.assertEqual(len(all_images), 1)

    def test_search_image(self):
        """Search for image"""
        response = self.client.search("libpod/alpine")
        for i in response:
            self.assertIn("quay.io/libpod/alpine", i["Name"])

    def test_remove_image(self):
        """Remove image"""
        # Check for error with wrong image name
        with self.assertRaises(errors.NotFound):
            self.client.remove_image("dummy")
        all_images = self.client.images()
        self.assertEqual(len(all_images), 1)

        alpine_image = self.client.inspect_image(constant.ALPINE)
        self.client.remove_image(alpine_image["Id"])
        all_images = self.client.images()
        self.assertEqual(len(all_images), 0)

    def test_image_history(self):
        """Image history"""
        # Check for error with wrong image name
        with self.assertRaises(errors.NotFound):
            self.client.history("dummy")

        # NOTE: history() has incorrect return type hint
        history = self.client.history(constant.ALPINE)
        alpine_image = self.client.inspect_image(constant.ALPINE)
        image_id = (alpine_image["Id"][7:]
                    if alpine_image["Id"].startswith("sha256:") else
                    alpine_image["Id"])

        found = False
        for change in history:
            found |= image_id in change.values()
        self.assertTrue(found, f"image id {image_id} not found in history")

    def test_get_image_exists_not(self):
        """Negative test for get image"""
        with self.assertRaises(errors.NotFound):
            response = self.client.get_image("image_does_not_exists")
            collections.deque(response)

    def test_export_image(self):
        """Export Image"""
        self.client.pull(constant.BB)
        image = self.client.get_image(constant.BB)

        file = os.path.join(TestImages.podman.image_cache, "busybox.tar")
        with open(file, mode="wb") as tarball:
            for frame in image:
                tarball.write(frame)
        sz = os.path.getsize(file)
        self.assertGreater(sz, 0)

    def test_import_image(self):
        """Import|Load Image"""
        all_images = self.client.images()
        self.assertEqual(len(all_images), 1)

        file = os.path.join(TestImages.podman.image_cache,
                            constant.ALPINE_TARBALL)
        self.client.import_image_from_file(filename=file)

        all_images = self.client.images()
        self.assertEqual(len(all_images), 2)