summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--roles/openshift_health_checker/openshift_checks/docker_image_availability.py148
-rw-r--r--roles/openshift_health_checker/test/docker_image_availability_test.py147
2 files changed, 143 insertions, 152 deletions
diff --git a/roles/openshift_health_checker/openshift_checks/docker_image_availability.py b/roles/openshift_health_checker/openshift_checks/docker_image_availability.py
index 00c670287..4588ed634 100644
--- a/roles/openshift_health_checker/openshift_checks/docker_image_availability.py
+++ b/roles/openshift_health_checker/openshift_checks/docker_image_availability.py
@@ -13,7 +13,7 @@ class DockerImageAvailability(OpenShiftCheck):
name = "docker_image_availability"
tags = ["preflight"]
- skopeo_image = "openshift/openshift-ansible"
+ dependencies = ["skopeo", "python-docker-py"]
deployment_image_info = {
"origin": {
@@ -35,44 +35,32 @@ class DockerImageAvailability(OpenShiftCheck):
return super(DockerImageAvailability, cls).is_active(task_vars) and has_valid_deployment_type
def run(self, tmp, task_vars):
- required_images = self.required_images(task_vars)
- missing_images = set(required_images) - set(self.local_images(required_images, task_vars))
-
- # exit early if all images were found locally
- if not missing_images:
- return {"changed": False}
-
- msg, failed, changed = self.update_skopeo_image(task_vars)
+ msg, failed, changed = self.ensure_dependencies(task_vars)
# exit early if Skopeo update fails
if failed:
- if "Error connecting: Error while fetching server API version" in msg:
- msg = (
- "It appears Docker is not running.\n"
- "Please start Docker on this host before running this check.\n"
- "The full error reported was:\n " + msg
- )
-
- elif "Failed to import docker-py" in msg:
- msg = (
- "The required Python docker-py module is not installed.\n"
- "Suggestion: install the python-docker-py package on this host."
- )
- else:
- msg = "The full message reported by the docker_image module was:\n" + msg
+ if "No package matching" in msg:
+ msg = "Ensure that all required dependencies can be installed via `yum`.\n"
return {
"failed": True,
"changed": changed,
"msg": (
- "Unable to update the {img_name} image on this host;\n"
- "This is required in order to check Docker image availability.\n"
- "{msg}"
- ).format(img_name=self.skopeo_image, msg=msg),
+ "Unable to update or install required dependency packages on this host;\n"
+ "These are required in order to check Docker image availability:"
+ "\n {deps}\n{msg}"
+ ).format(deps=',\n '.join(self.dependencies), msg=msg),
}
+ required_images = self.required_images(task_vars)
+ missing_images = set(required_images) - set(self.local_images(required_images, task_vars))
+
+ # exit early if all images were found locally
+ if not missing_images:
+ return {"changed": changed}
+
registries = self.known_docker_registries(task_vars)
if not registries:
- return {"failed": True, "msg": "Unable to retrieve any docker registries."}
+ return {"failed": True, "msg": "Unable to retrieve any docker registries.", "changed": changed}
available_images = self.available_images(missing_images, registries, task_vars)
unavailable_images = set(missing_images) - set(available_images)
@@ -81,9 +69,9 @@ class DockerImageAvailability(OpenShiftCheck):
return {
"failed": True,
"msg": (
- "One or more required images are not available: {}.\n"
+ "One or more required Docker images are not available:\n {}\n"
"Configured registries: {}"
- ).format(", ".join(sorted(unavailable_images)), ", ".join(registries)),
+ ).format(",\n ".join(sorted(unavailable_images)), ", ".join(registries)),
"changed": changed,
}
@@ -94,24 +82,47 @@ class DockerImageAvailability(OpenShiftCheck):
image_info = self.deployment_image_info[deployment_type]
openshift_release = get_var(task_vars, "openshift_release", default="latest")
- openshift_image_tag = get_var(task_vars, "openshift_image_tag", default=openshift_release)
- if openshift_image_tag and openshift_image_tag[0] != 'v':
- openshift_image_tag = 'v' + openshift_image_tag
-
+ openshift_image_tag = get_var(task_vars, "openshift_image_tag")
is_containerized = get_var(task_vars, "openshift", "common", "is_containerized")
- images = set(self.non_qualified_docker_images(image_info["namespace"], image_info["name"], openshift_release,
- is_containerized))
+
+ images = set(self.required_docker_images(
+ image_info["namespace"],
+ image_info["name"],
+ ["registry-console"] if "enterprise" in deployment_type else [], # include enterprise-only image names
+ openshift_release,
+ is_containerized,
+ ))
# append images with qualified image tags to our list of required images.
# these are images with a (v0.0.0.0) tag, rather than a standard release
# format tag (v0.0). We want to check this set in both containerized and
# non-containerized installations.
images.update(
- self.qualified_docker_images(image_info["namespace"], image_info["name"], openshift_image_tag)
+ self.required_qualified_docker_images(
+ image_info["namespace"],
+ image_info["name"],
+ openshift_image_tag,
+ ),
)
return images
+ @staticmethod
+ def required_docker_images(namespace, name, additional_image_names, version, is_containerized):
+ if is_containerized:
+ return ["{}/{}:{}".format(namespace, name, version)] if name else []
+
+ # include additional non-containerized images specific to the current deployment type
+ return ["{}/{}:{}".format(namespace, img_name, version) for img_name in additional_image_names]
+
+ @staticmethod
+ def required_qualified_docker_images(namespace, name, version):
+ # pylint: disable=invalid-name
+ return [
+ "{}/{}-{}:{}".format(namespace, name, suffix, version)
+ for suffix in ["haproxy-router", "docker-registry", "deployer", "pod"]
+ ]
+
def local_images(self, images, task_vars):
"""Filter a list of images and return those available locally."""
return [
@@ -126,28 +137,26 @@ class DockerImageAvailability(OpenShiftCheck):
return bool(result.get("images", []))
- def known_docker_registries(self, task_vars):
- result = self.module_executor("docker_info", {}, task_vars)
- if result.get("failed", False):
- return False
+ @staticmethod
+ def known_docker_registries(task_vars):
+ docker_facts = get_var(task_vars, "openshift", "docker")
+ regs = set(docker_facts["additional_registries"])
+
+ deployment_type = get_var(task_vars, "openshift_deployment_type")
+ if deployment_type == "origin":
+ regs.update(["docker.io"])
+ elif "enterprise" in deployment_type:
+ regs.update(["registry.access.redhat.com"])
- docker_info = result.get("info", {})
- return [registry.get("Name", "") for registry in docker_info.get("Registries", {})]
+ return list(regs)
def available_images(self, images, registries, task_vars):
"""Inspect existing images using Skopeo and return all images successfully inspected."""
return [
image for image in images
- if self.is_image_available(image, registries, task_vars)
+ if any(self.is_available_skopeo_image(image, registry, task_vars) for registry in registries)
]
- def is_image_available(self, image, registries, task_vars):
- for registry in registries:
- if self.is_available_skopeo_image(image, registry, task_vars):
- return True
-
- return False
-
def is_available_skopeo_image(self, image, registry, task_vars):
"""Uses Skopeo to determine if required image exists in a given registry."""
@@ -156,32 +165,15 @@ class DockerImageAvailability(OpenShiftCheck):
image=image,
)
- args = {
- "name": "skopeo_inspect",
- "image": self.skopeo_image,
- "command": cmd_str,
- "detach": False,
- "cleanup": True,
- }
- result = self.module_executor("docker_container", args, task_vars)
- return not result.get("failed", False)
+ args = {"_raw_params": cmd_str}
+ result = self.module_executor("command", args, task_vars)
+ return not result.get("failed", False) and result.get("rc", 0) == 0
- @staticmethod
- def non_qualified_docker_images(namespace, name, version, is_containerized):
- if is_containerized:
- return ["{}/{}:{}".format(namespace, name, version)] if name else []
-
- return ["{}/{}:{}".format(namespace, name, version)] if name else []
-
- @staticmethod
- def qualified_docker_images(namespace, name, version):
- return [
- "{}/{}-{}:{}".format(namespace, name, suffix, version)
- for suffix in ["haproxy-router", "docker-registry", "deployer", "pod"]
- ]
+ # ensures that the skopeo and python-docker-py packages exist
+ # check is skipped on atomic installations
+ def ensure_dependencies(self, task_vars):
+ if get_var(task_vars, "openshift", "common", "is_atomic"):
+ return "", False, False
- # ensures that the skopeo docker image exists, and updates it
- # with latest if image was already present locally.
- def update_skopeo_image(self, task_vars):
- result = self.module_executor("docker_image", {"name": self.skopeo_image}, task_vars)
- return result.get("msg", ""), result.get("failed", False), result.get("changed", False)
+ result = self.module_executor("yum", {"name": self.dependencies, "state": "latest"}, task_vars)
+ return result.get("msg", ""), result.get("failed", False) or result.get("rc", 0) != 0, result.get("changed")
diff --git a/roles/openshift_health_checker/test/docker_image_availability_test.py b/roles/openshift_health_checker/test/docker_image_availability_test.py
index f054b9ccc..0379cafb5 100644
--- a/roles/openshift_health_checker/test/docker_image_availability_test.py
+++ b/roles/openshift_health_checker/test/docker_image_availability_test.py
@@ -18,12 +18,17 @@ def test_is_active(deployment_type, is_active):
assert DockerImageAvailability.is_active(task_vars=task_vars) == is_active
-@pytest.mark.parametrize("is_containerized", [
- True,
- False,
+@pytest.mark.parametrize("is_containerized,is_atomic", [
+ (True, True),
+ (False, False),
+ (True, False),
+ (False, True),
])
-def test_all_images_available_locally(is_containerized):
+def test_all_images_available_locally(is_containerized, is_atomic):
def execute_module(module_name, args, task_vars):
+ if module_name == "yum":
+ return {"changed": True}
+
assert module_name == "docker_image_facts"
assert 'name' in args
assert args['name']
@@ -32,10 +37,14 @@ def test_all_images_available_locally(is_containerized):
}
result = DockerImageAvailability(execute_module=execute_module).run(tmp=None, task_vars=dict(
- openshift=dict(common=dict(
- service_type='origin',
- is_containerized=is_containerized,
- )),
+ openshift=dict(
+ common=dict(
+ service_type='origin',
+ is_containerized=is_containerized,
+ is_atomic=is_atomic,
+ ),
+ docker=dict(additional_registries=["docker.io"]),
+ ),
openshift_deployment_type='origin',
openshift_release='v3.4',
openshift_image_tag='3.4',
@@ -44,24 +53,28 @@ def test_all_images_available_locally(is_containerized):
assert not result.get('failed', False)
-@pytest.mark.parametrize("module_failure", [
- True,
+@pytest.mark.parametrize("available_locally", [
False,
+ True,
])
-def test_all_images_available_remotely(module_failure):
+def test_all_images_available_remotely(available_locally):
def execute_module(module_name, args, task_vars):
- return {
- 'docker_image_facts': {'images': [], 'failed': module_failure},
- 'docker_info': {'info': {'Registries': [{'Name': 'docker.io'}, {'Name': 'registry.access.redhat.com'}]}},
- }.get(module_name, {'changed': False})
+ if module_name == 'docker_image_facts':
+ return {'images': [], 'failed': available_locally}
+ return {'changed': False}
result = DockerImageAvailability(execute_module=execute_module).run(tmp=None, task_vars=dict(
- openshift=dict(common=dict(
- service_type='origin',
- is_containerized=False,
- )),
+ openshift=dict(
+ common=dict(
+ service_type='origin',
+ is_containerized=False,
+ is_atomic=False,
+ ),
+ docker=dict(additional_registries=["docker.io", "registry.access.redhat.com"]),
+ ),
openshift_deployment_type='origin',
- openshift_release='3.4'
+ openshift_release='3.4',
+ openshift_image_tag='v3.4',
))
assert not result.get('failed', False)
@@ -69,21 +82,7 @@ def test_all_images_available_remotely(module_failure):
def test_all_images_unavailable():
def execute_module(module_name=None, module_args=None, tmp=None, task_vars=None):
- if module_name == "docker_info":
- return {
- 'info': {
- 'Registries': [
- {
- 'Name': 'docker.io'
- },
- {
- 'Name': 'registry.access.redhat.com'
- }
- ]
- }
- }
-
- if module_name == "docker_container":
+ if module_name == "command":
return {
'failed': True,
}
@@ -94,16 +93,21 @@ def test_all_images_unavailable():
check = DockerImageAvailability(execute_module=execute_module)
actual = check.run(tmp=None, task_vars=dict(
- openshift=dict(common=dict(
- service_type='origin',
- is_containerized=False,
- )),
+ openshift=dict(
+ common=dict(
+ service_type='origin',
+ is_containerized=False,
+ is_atomic=False,
+ ),
+ docker=dict(additional_registries=["docker.io"]),
+ ),
openshift_deployment_type="openshift-enterprise",
openshift_release=None,
+ openshift_image_tag='latest'
))
assert actual['failed']
- assert "required images are not available" in actual['msg']
+ assert "required Docker images are not available" in actual['msg']
@pytest.mark.parametrize("message,extra_words", [
@@ -112,34 +116,33 @@ def test_all_images_unavailable():
["docker image update failure"],
),
(
- "Error connecting: Error while fetching server API version",
- ["Docker is not running"]
+ "No package matching 'skopeo' found available, installed or updated",
+ ["dependencies can be installed via `yum`"]
),
- (
- "Failed to import docker-py",
- ["docker-py module is not installed", "install the python docker-py package"]
- )
])
def test_skopeo_update_failure(message, extra_words):
def execute_module(module_name=None, module_args=None, tmp=None, task_vars=None):
- if module_name == "docker_image":
+ if module_name == "yum":
return {
"failed": True,
"msg": message,
"changed": False,
}
- return {
- 'changed': False,
- }
+ return {'changed': False}
actual = DockerImageAvailability(execute_module=execute_module).run(tmp=None, task_vars=dict(
- openshift=dict(common=dict(
- service_type='origin',
- is_containerized=False,
- )),
+ openshift=dict(
+ common=dict(
+ service_type='origin',
+ is_containerized=False,
+ is_atomic=False,
+ ),
+ docker=dict(additional_registries=["unknown.io"]),
+ ),
openshift_deployment_type="openshift-enterprise",
openshift_release='',
+ openshift_image_tag='',
))
assert actual["failed"]
@@ -147,33 +150,29 @@ def test_skopeo_update_failure(message, extra_words):
assert word in actual["msg"]
-@pytest.mark.parametrize("module_failure", [
- True,
- False,
+@pytest.mark.parametrize("deployment_type,registries", [
+ ("origin", ["unknown.io"]),
+ ("openshift-enterprise", ["registry.access.redhat.com"]),
+ ("openshift-enterprise", []),
])
-def test_no_registries_available(module_failure):
+def test_registry_availability(deployment_type, registries):
def execute_module(module_name=None, module_args=None, tmp=None, task_vars=None):
- if module_name == "docker_info":
- return {
- 'changed': False,
- 'failed': module_failure,
- 'info': {
- 'Registries': [],
- }
- }
-
return {
'changed': False,
}
actual = DockerImageAvailability(execute_module=execute_module).run(tmp=None, task_vars=dict(
- openshift=dict(common=dict(
- service_type='origin',
- is_containerized=False,
- )),
- openshift_deployment_type="openshift-enterprise",
+ openshift=dict(
+ common=dict(
+ service_type='origin',
+ is_containerized=False,
+ is_atomic=False,
+ ),
+ docker=dict(additional_registries=registries),
+ ),
+ openshift_deployment_type=deployment_type,
openshift_release='',
+ openshift_image_tag='',
))
- assert actual['failed']
- assert "docker registries" in actual['msg']
+ assert not actual.get("failed", False)