Prechádzať zdrojové kódy

Merge pull request #9827 from mgugino-upstream-stage/refactor-registry-image-checks

Refactor image health checks
OpenShift Merge Robot 6 rokov pred
rodič
commit
dc4bf75fce

+ 5 - 3
playbooks/openshift-hosted/private/cockpit-ui.yml

@@ -1,9 +1,11 @@
 ---
 - name: Create Hosted Resources - cockpit-ui
   hosts: oo_first_master
-  roles:
-  - role: cockpit-ui
+  tasks:
+  - import_role:
+      name: cockpit-ui
+      tasks_from: install.yml
     when:
     - openshift_hosted_manage_registry | default(true) | bool
-    - openshift_hosted_manage_registry_console | default(true) | bool
+    - openshift_hosted_manage_registry_console | bool
     - not (openshift_docker_hosted_registry_insecure | default(false)) | bool

+ 1 - 0
roles/cockpit-ui/defaults/main.yml

@@ -1,4 +1,5 @@
 ---
+openshift_hosted_manage_registry_console: True
 l_os_cockpit_image_version_dict:
   origin: 'latest'
   openshift-enterprise: "{{ openshift_image_tag }}"

+ 69 - 0
roles/cockpit-ui/tasks/install.yml

@@ -0,0 +1,69 @@
+---
+- name: Create local temp dir for registry-console template
+  command: mktemp -d /tmp/openshift-ansible-XXXXXXX
+  register: mktemp
+  # AUDIT:changed_when: not set here because this task actually
+  # creates something
+
+- name: Copy the admin client config(s)
+  command: >
+    cp {{ openshift.common.config_base }}/master/admin.kubeconfig {{ mktemp.stdout }}/admin.kubeconfig
+  changed_when: False
+
+- name: Copy registry-console template to tmp dir
+  copy:
+    src: "registry-console.yaml"
+    dest: "{{ mktemp.stdout }}/registry-console.yaml"
+
+- name: Create registry-console template
+  command: >
+    {{ openshift_client_binary }} create
+    -f {{ mktemp.stdout }}/registry-console.yaml
+    --config={{ mktemp.stdout }}/admin.kubeconfig
+    -n openshift
+  register: oht_import_templates
+  failed_when: "'already exists' not in oht_import_templates.stderr and oht_import_templates.rc != 0"
+  changed_when: "'created' in oht_import_templates.stdout"
+
+# When openshift_hosted_manage_registry=true the openshift_hosted
+# role will create the appropriate route for the docker-registry.
+# When openshift_hosted_manage_registry=false then this code will
+# not be run.
+- name: fetch the docker-registry route
+  oc_route:
+    kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig"
+    name: docker-registry
+    namespace: default
+    state: list
+  register: docker_registry_route
+
+- name: Create passthrough route for registry-console
+  oc_route:
+    kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig"
+    name: registry-console
+    namespace: default
+    service_name: registry-console
+    state: present
+    tls_termination: passthrough
+  register: registry_console_cockpit_kube
+
+- name: Deploy registry-console
+  command: >
+    {{ openshift_client_binary }} new-app --template=registry-console
+    -p IMAGE_NAME="{{ openshift_cockpit_deployer_image }}"
+    -p OPENSHIFT_OAUTH_PROVIDER_URL="{{ openshift.master.public_api_url }}"
+    -p REGISTRY_HOST="{{ docker_registry_route.results[0].spec.host }}"
+    -p COCKPIT_KUBE_URL="https://{{ registry_console_cockpit_kube.results.results[0].spec.host }}"
+    --config={{ mktemp.stdout }}/admin.kubeconfig
+    -n default
+  register: deploy_registry_console
+  changed_when: "'already exists' not in deploy_registry_console.stderr"
+  failed_when:
+  - "'already exists' not in deploy_registry_console.stderr"
+  - "deploy_registry_console.rc != 0"
+
+- name: Delete temp directory
+  file:
+    name: "{{ mktemp.stdout }}"
+    state: absent
+  changed_when: False

+ 1 - 68
roles/cockpit-ui/tasks/main.yml

@@ -1,69 +1,2 @@
 ---
-- name: Create local temp dir for registry-console template
-  command: mktemp -d /tmp/openshift-ansible-XXXXXXX
-  register: mktemp
-  # AUDIT:changed_when: not set here because this task actually
-  # creates something
-
-- name: Copy the admin client config(s)
-  command: >
-    cp {{ openshift.common.config_base }}/master/admin.kubeconfig {{ mktemp.stdout }}/admin.kubeconfig
-  changed_when: False
-
-- name: Copy registry-console template to tmp dir
-  copy:
-    src: "registry-console.yaml"
-    dest: "{{ mktemp.stdout }}/registry-console.yaml"
-
-- name: Create registry-console template
-  command: >
-    {{ openshift_client_binary }} create
-    -f {{ mktemp.stdout }}/registry-console.yaml
-    --config={{ mktemp.stdout }}/admin.kubeconfig
-    -n openshift
-  register: oht_import_templates
-  failed_when: "'already exists' not in oht_import_templates.stderr and oht_import_templates.rc != 0"
-  changed_when: "'created' in oht_import_templates.stdout"
-
-# When openshift_hosted_manage_registry=true the openshift_hosted
-# role will create the appropriate route for the docker-registry.
-# When openshift_hosted_manage_registry=false then this code will
-# not be run.
-- name: fetch the docker-registry route
-  oc_route:
-    kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig"
-    name: docker-registry
-    namespace: default
-    state: list
-  register: docker_registry_route
-
-- name: Create passthrough route for registry-console
-  oc_route:
-    kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig"
-    name: registry-console
-    namespace: default
-    service_name: registry-console
-    state: present
-    tls_termination: passthrough
-  register: registry_console_cockpit_kube
-
-- name: Deploy registry-console
-  command: >
-    {{ openshift_client_binary }} new-app --template=registry-console
-    -p IMAGE_NAME="{{ openshift_cockpit_deployer_image }}"
-    -p OPENSHIFT_OAUTH_PROVIDER_URL="{{ openshift.master.public_api_url }}"
-    -p REGISTRY_HOST="{{ docker_registry_route.results[0].spec.host }}"
-    -p COCKPIT_KUBE_URL="https://{{ registry_console_cockpit_kube.results.results[0].spec.host }}"
-    --config={{ mktemp.stdout }}/admin.kubeconfig
-    -n default
-  register: deploy_registry_console
-  changed_when: "'already exists' not in deploy_registry_console.stderr"
-  failed_when:
-  - "'already exists' not in deploy_registry_console.stderr"
-  - "deploy_registry_console.rc != 0"
-
-- name: Delete temp directory
-  file:
-    name: "{{ mktemp.stdout }}"
-    state: absent
-  changed_when: False
+# This role is meant to be used with import_role and tasks_from.

+ 16 - 0
roles/openshift_health_checker/defaults/main.yml

@@ -1 +1,17 @@
 ---
+l_host_is_master: "{{ inventory_hostname in groups['oo_masters_to_config'] }}"
+l_host_is_etcd: "{{ inventory_hostname in groups['oo_etcd_to_config'] }}"
+l_host_is_node: "{{ inventory_hostname in groups['oo_nodes_to_config'] }}"
+
+l_node_image_list:
+- "{{ l_osm_registry_url | regex_replace('${component}' | regex_escape, 'haproxy-router') }}"
+- "{{ l_osm_registry_url | regex_replace('${component}' | regex_escape, 'docker-registry') }}"
+- "{{ l_osm_registry_url | regex_replace('${component}' | regex_escape, 'deployer') }}"
+- "{{ l_osm_registry_url | regex_replace('${component}' | regex_escape, 'pod') }}"
+
+l_atomic_node_required_images: "{{ (openshift_is_atomic | bool) | ternary([osn_image], []) }}"
+l_etcd_required_images: "{{ (l_host_is_master and l_host_is_etcd) | ternary([etcd_image], []) }}"
+l_master_required_images: "{{ l_host_is_master | ternary([osm_image], []) }}"
+l_cockpit_images: "{{ (openshift_hosted_manage_registry_console | bool) | ternary([openshift_cockpit_deployer_image], []) }}"
+l_required_node_images: "{{ l_host_is_node | ternary(l_node_image_list, [])}}"
+openshift_health_check_required_images: "{{ [] + l_required_node_images + l_cockpit_images + l_master_required_images + l_etcd_required_images + l_atomic_node_required_images }}"

+ 2 - 0
roles/openshift_health_checker/meta/main.yml

@@ -2,3 +2,5 @@
 dependencies:
 - role: openshift_facts
 - role: lib_utils
+- role: cockpit-ui
+- role: etcd

+ 62 - 222
roles/openshift_health_checker/openshift_checks/docker_image_availability.py

@@ -1,31 +1,11 @@
 """Check that required Docker images are available."""
 
-import re
 from pipes import quote
 from ansible.module_utils import six
 from openshift_checks import OpenShiftCheck
 from openshift_checks.mixins import DockerHostMixin
 
 
-NODE_IMAGE_SUFFIXES = ["haproxy-router", "docker-registry", "deployer", "pod"]
-DEPLOYMENT_IMAGE_INFO = {
-    "origin": {
-        "namespace": "openshift",
-        "name": "origin",
-        "registry_console_prefix": "docker.io/cockpit/",
-        "registry_console_basename": "kubernetes",
-        "registry_console_default_version": "latest",
-    },
-    "openshift-enterprise": {
-        "namespace": "openshift3",
-        "name": "ose",
-        "registry_console_prefix": "registry.redhat.io/openshift3/",
-        "registry_console_basename": "registry-console",
-        "registry_console_default_version": "${short_version}",
-    },
-}
-
-
 class DockerImageAvailability(DockerHostMixin, OpenShiftCheck):
     """Check that required Docker images are available.
 
@@ -37,40 +17,31 @@ class DockerImageAvailability(DockerHostMixin, OpenShiftCheck):
     name = "docker_image_availability"
     tags = ["preflight"]
     # we use python-docker-py to check local docker for images, and skopeo
-    # to look for images available remotely without waiting to pull them.
-    dependencies = ["python-docker-py", "skopeo"]
+    # to look for images available remotely without actually pulling them.
+
     # command for checking if remote registries have an image, without docker pull
-    skopeo_command = "{proxyvars} timeout 10 skopeo inspect --tls-verify={tls} {creds} docker://{registry}/{image}"
+    skopeo_command = "{proxyvars} timeout 10 skopeo inspect --tls-verify={tls} {creds} docker://{image}"
     skopeo_example_command = "skopeo inspect [--tls-verify=false] [--creds=<user>:<pass>] docker://<registry>/<image>"
 
+    def ensure_list(self, registry_param):
+        """Return the task var as a list."""
+        # https://bugzilla.redhat.com/show_bug.cgi?id=1497274
+        # If the result was a string type, place it into a list. We must do this
+        # as using list() on a string will split the string into its characters.
+        # Otherwise cast to a list as was done previously.
+        registry = self.get_var(registry_param, default=[])
+        if not isinstance(registry, six.string_types):
+            return list(registry)
+        return self.normalize(registry)
+
     def __init__(self, *args, **kwargs):
         super(DockerImageAvailability, self).__init__(*args, **kwargs)
 
-        self.registries = dict(
-            # set of registries that need to be checked insecurely (note: not accounting for CIDR entries)
-            insecure=set(self.ensure_list("openshift_docker_insecure_registries")),
-            # set of registries that should never be queried even if given in the image
-            blocked=set(self.ensure_list("openshift_docker_blocked_registries")),
-        )
-
-        # ordered list of registries (according to inventory vars) that docker will try for unscoped images
-        regs = self.ensure_list("openshift_docker_additional_registries")
-        # currently one of these registries is added whether the user wants it or not.
-        deployment_type = self.get_var("openshift_deployment_type", default="")
-        if deployment_type == "origin" and "docker.io" not in regs:
-            regs.append("docker.io")
-        elif deployment_type == 'openshift-enterprise' and "registry.redhat.io" not in regs:
-            regs.append("registry.redhat.io")
-        self.registries["configured"] = regs
-
-        # for the oreg_url registry there may be credentials specified
-        oreg_url = self.get_var("oreg_url", default="")
-        oreg_url = self.template_var(oreg_url)
-        components = oreg_url.split('/')
-        self.registries["oreg"] = "" if len(components) < 3 else components[0]
+        self.registries_insecure = set(self.ensure_list(
+            "openshift_docker_insecure_registries"))
 
         # Retrieve and template registry credentials, if provided
-        self.skopeo_command_creds = ""
+        self.skopeo_command_creds = None
         oreg_auth_user = self.get_var('oreg_auth_user', default='')
         oreg_auth_password = self.get_var('oreg_auth_password', default='')
         if oreg_auth_user != '' and oreg_auth_password != '':
@@ -78,9 +49,6 @@ class DockerImageAvailability(DockerHostMixin, OpenShiftCheck):
             oreg_auth_password = self.template_var(oreg_auth_password)
             self.skopeo_command_creds = quote("--creds={}:{}".format(oreg_auth_user, oreg_auth_password))
 
-        # record whether we could reach a registry or not (and remember results)
-        self.reachable_registries = {}
-
         # take note of any proxy settings needed
         proxies = []
         for var in ['http_proxy', 'https_proxy', 'no_proxy']:
@@ -90,22 +58,55 @@ class DockerImageAvailability(DockerHostMixin, OpenShiftCheck):
                 proxies.append(var.upper() + "=" + quote(self.template_var(value)))
         self.skopeo_proxy_vars = " ".join(proxies)
 
-    def is_active(self):
-        """Skip hosts with unsupported deployment types."""
-        deployment_type = self.get_var("openshift_deployment_type")
-        has_valid_deployment_type = deployment_type in DEPLOYMENT_IMAGE_INFO
+    def is_image_local(self, image):
+        """Check if image is already in local docker index."""
+        result = self.execute_module("docker_image_facts", {"name": image})
+        return bool(result.get("images")) and not result.get("failed")
+
+    def local_images(self, images):
+        """Filter a list of images and return those available locally."""
+        found_images = []
+        for image in images:
+            if self.is_image_local(image):
+                found_images.append(image)
+        return found_images
+
+    def is_available_skopeo_image(self, image):
+        """Use Skopeo to determine if required image exists"""
+        if six.PY2:
+            image = image.encode('utf8')
+        use_insecure = False
+        for insec_reg in self.registries_insecure:
+            if insec_reg in image:
+                use_insecure = True
+        args = dict(
+            proxyvars=self.skopeo_proxy_vars,
+            tls="false" if use_insecure else "true",
+            creds=self.skopeo_command_creds if self.skopeo_command_creds else "",
+            image=quote(image),
+        )
+
+        result = self.execute_module_with_retries("command", {
+            "_uses_shell": True,
+            "_raw_params": self.skopeo_command.format(**args),
+        })
+        if result.get("rc", 0) == 0 and not result.get("failed"):
+            return True
+
+        return False
 
-        return super(DockerImageAvailability, self).is_active() and has_valid_deployment_type
+    def available_images(self, images):
+        """Search remotely for images. Returns: list of images found."""
+        return [
+            image for image in images
+            if self.is_available_skopeo_image(image)
+        ]
 
     def run(self):
-        msg, failed = self.ensure_dependencies()
-        if failed:
-            return {
-                "failed": True,
-                "msg": "Some dependencies are required in order to check Docker image availability.\n" + msg
-            }
+        '''Run this check'''
+        required_images = self.template_var(
+            self.get_var('openshift_health_check_required_images'))
 
-        required_images = self.required_images()
         missing_images = set(required_images) - set(self.local_images(required_images))
 
         # exit early if all images were found locally
@@ -116,177 +117,16 @@ class DockerImageAvailability(DockerHostMixin, OpenShiftCheck):
         unavailable_images = set(missing_images) - set(available_images)
 
         if unavailable_images:
-            unreachable = [reg for reg, reachable in self.reachable_registries.items() if not reachable]
-            unreachable_msg = u"Failed connecting to: {}\n".format(u", ".join(unreachable))
-            blocked_msg = u"Blocked registries: {}\n".format(u", ".join(self.registries["blocked"]))
             missing = u",\n    ".join(sorted(unavailable_images))
 
             msg = (
                 u"One or more required container images are not available:\n    {missing}\n"
                 "Checked with: {cmd}\n"
-                "Default registries searched: {registries}\n"
-                "{blocked}"
-                "{unreachable}"
             ).format(
                 missing=missing,
                 cmd=self.skopeo_example_command,
-                registries=", ".join(self.registries["configured"]),
-                blocked=blocked_msg if self.registries["blocked"] else "",
-                unreachable=unreachable_msg if unreachable else "",
             )
 
             return dict(failed=True, msg=msg.encode('utf8') if six.PY2 else msg)
 
         return {}
-
-    def required_images(self):
-        """
-        Determine which images we expect to need for this host.
-        Returns: a set of required images like 'docker.io/openshift/origin:v3.6'
-
-        The thorny issue of determining the image names from the variables is under consideration
-        via https://github.com/openshift/openshift-ansible/issues/4415
-
-        For now we operate as follows:
-        * For containerized components (master, node, ...) we look at the deployment
-          type and use docker.io/openshift/origin or
-          registry.redhat.io/openshift3/ose as the base for those component
-          images. The version is openshift_image_tag as determined by the
-          openshift_version role.
-        * For OpenShift-managed infrastructure (router, registry...) we use oreg_url if
-          it is defined; otherwise we again use the base that depends on the deployment type.
-        Registry is not included in constructed images. It may be in oreg_url or etcd image.
-        """
-        required = set()
-        deployment_type = self.get_var("openshift_deployment_type")
-        host_groups = self.get_var("group_names")
-        # containerized etcd may not have openshift_image_tag, see bz 1466622
-        image_tag = self.get_var("openshift_image_tag", default="latest")
-        image_tag = self.template_var(image_tag)
-        image_info = DEPLOYMENT_IMAGE_INFO[deployment_type]
-
-        # template for images that run on top of OpenShift
-        image_url = "{}/{}-{}:{}".format(image_info["namespace"], image_info["name"], "${component}", "${version}")
-        image_url = self.get_var("oreg_url", default="") or image_url
-        image_url = self.template_var(image_url)
-        if 'oo_nodes_to_config' in host_groups:
-            for suffix in NODE_IMAGE_SUFFIXES:
-                required.add(image_url.replace("${component}", suffix).replace("${version}", image_tag))
-            if self.get_var("osm_use_cockpit", default=True, convert=bool):
-                required.add(self._registry_console_image(image_tag, image_info))
-
-        if self.get_var("openshift_is_atomic", convert=bool):
-            if 'oo_nodes_to_config' in host_groups:
-                required.add(self.template_var(self.get_var('osn_image', default='')))
-
-        return required
-
-    def _registry_console_image(self, image_tag, image_info):
-        """Returns image with logic to parallel what happens with the registry-console template."""
-        # The registry-console is for some reason not prefixed with ose- like the other components.
-        # Nor is it versioned the same. Also a completely different name is used for Origin.
-        prefix = self.get_var(
-            "openshift_cockpit_deployer_prefix",
-            default=image_info["registry_console_prefix"],
-        )
-        basename = self.get_var(
-            "openshift_cockpit_deployer_basename",
-            default=image_info["registry_console_basename"],
-        )
-
-        # enterprise template just uses v3.6, v3.7, etc
-        match = re.match(r'v\d+\.\d+', image_tag)
-        short_version = match.group() if match else image_tag
-        version = image_info["registry_console_default_version"].replace("${short_version}", short_version)
-        version = self.get_var("openshift_cockpit_deployer_version", default=version)
-
-        return prefix + basename + ':' + version
-
-    def local_images(self, images):
-        """Filter a list of images and return those available locally."""
-        found_images = []
-        for image in images:
-            # docker could have the image name as-is or prefixed with any registry
-            imglist = [image] + [reg + "/" + image for reg in self.registries["configured"]]
-            if self.is_image_local(imglist):
-                found_images.append(image)
-        return found_images
-
-    def is_image_local(self, image):
-        """Check if image is already in local docker index."""
-        result = self.execute_module("docker_image_facts", {"name": image})
-        return bool(result.get("images")) and not result.get("failed")
-
-    def ensure_list(self, registry_param):
-        """Return the task var as a list."""
-        # https://bugzilla.redhat.com/show_bug.cgi?id=1497274
-        # If the result was a string type, place it into a list. We must do this
-        # as using list() on a string will split the string into its characters.
-        # Otherwise cast to a list as was done previously.
-        registry = self.get_var(registry_param, default=[])
-        if not isinstance(registry, six.string_types):
-            return list(registry)
-        return self.normalize(registry)
-
-    def available_images(self, images):
-        """Search remotely for images. Returns: list of images found."""
-        return [
-            image for image in images
-            if self.is_available_skopeo_image(image)
-        ]
-
-    def is_available_skopeo_image(self, image):
-        """Use Skopeo to determine if required image exists in known registry(s)."""
-        registries = self.registries["configured"]
-        # If image already includes a registry, only use that.
-        # NOTE: This logic would incorrectly identify images that do not use a namespace, e.g.
-        # registry.redhat.io/rhel7 as if the registry were a namespace.
-        # It's not clear that there's any way to distinguish them, but fortunately
-        # the current set of images all look like [registry/]namespace/name[:version].
-        if image.count("/") > 1:
-            registry, image = image.split("/", 1)
-            registries = [registry]
-
-        for registry in registries:
-            if registry in self.registries["blocked"]:
-                continue  # blocked will never be consulted
-            if registry not in self.reachable_registries:
-                self.reachable_registries[registry] = self.connect_to_registry(registry)
-            if not self.reachable_registries[registry]:
-                continue  # do not keep trying unreachable registries
-
-            if six.PY2:
-                registry = registry.encode('utf8')
-                image = image.encode('utf8')
-
-            args = dict(
-                proxyvars=self.skopeo_proxy_vars,
-                tls="false" if registry in self.registries["insecure"] else "true",
-                creds=self.skopeo_command_creds if registry == self.registries["oreg"] else "",
-                registry=quote(registry),
-                image=quote(image),
-            )
-
-            result = self.execute_module_with_retries("command", {
-                "_uses_shell": True,
-                "_raw_params": self.skopeo_command.format(**args),
-            })
-            if result.get("rc", 0) == 0 and not result.get("failed"):
-                return True
-            if result.get("rc") == 124:  # RC 124 == timed out; mark unreachable
-                self.reachable_registries[registry] = False
-
-        return False
-
-    def connect_to_registry(self, registry):
-        """Use ansible wait_for module to test connectivity from host to registry. Returns bool."""
-        if self.skopeo_proxy_vars != "":
-            # assume we can't connect directly; just waive the test
-            return True
-
-        # test a simple TCP connection
-        host, _, port = registry.partition(":")
-        port = port or 443
-        args = dict(host=host, port=port, state="started", timeout=30)
-        result = self.execute_module("wait_for", args)
-        return result.get("rc", 0) == 0 and not result.get("failed")

+ 41 - 292
roles/openshift_health_checker/test/docker_image_availability_test.py

@@ -1,292 +1,41 @@
-import pytest
-
-from openshift_checks.docker_image_availability import DockerImageAvailability, DEPLOYMENT_IMAGE_INFO
-
-
-@pytest.fixture()
-def task_vars():
-    return dict(
-        openshift_is_atomic=False,
-        openshift_service_type='origin',
-        openshift_deployment_type='origin',
-        openshift_image_tag='',
-        group_names=['oo_nodes_to_config', 'oo_masters_to_config'],
-    )
-
-
-@pytest.mark.parametrize('deployment_type, openshift_is_atomic, group_names, expect_active', [
-    ("invalid", True, [], False),
-    ("", True, [], False),
-    ("origin", False, [], False),
-    ("openshift-enterprise", False, [], False),
-    ("origin", False, ["oo_nodes_to_config", "oo_masters_to_config"], True),
-    ("openshift-enterprise", False, ["oo_etcd_to_config"], False),
-    ("origin", True, ["nfs"], False),
-    ("openshift-enterprise", True, ["lb"], False),
-])
-def test_is_active(task_vars, deployment_type, openshift_is_atomic, group_names, expect_active):
-    task_vars['openshift_deployment_type'] = deployment_type
-    task_vars['openshift_is_atomic'] = openshift_is_atomic
-    task_vars['group_names'] = group_names
-    assert DockerImageAvailability(None, task_vars).is_active() == expect_active
-
-
-@pytest.mark.parametrize("openshift_is_atomic", [
-    True,
-    False,
-    True,
-    False,
-])
-def test_all_images_available_locally(task_vars, openshift_is_atomic):
-    def execute_module(module_name, module_args, *_):
-        if module_name == "yum":
-            return {}
-
-        assert module_name == "docker_image_facts"
-        assert 'name' in module_args
-        assert module_args['name']
-        return {
-            'images': [module_args['name']],
-        }
-
-    task_vars['openshift_is_atomic'] = openshift_is_atomic
-    result = DockerImageAvailability(execute_module, task_vars).run()
-
-    assert not result.get('failed', False)
-
-
-@pytest.mark.parametrize("available_locally", [
-    False,
-    True,
-])
-def test_all_images_available_remotely(task_vars, available_locally):
-    def execute_module(module_name, *_):
-        if module_name == 'docker_image_facts':
-            return {'images': [], 'failed': available_locally}
-        return {}
-
-    task_vars['openshift_docker_additional_registries'] = ["docker.io", "registry.redhat.io"]
-    task_vars['openshift_image_tag'] = 'v3.4'
-    check = DockerImageAvailability(execute_module, task_vars)
-    check._module_retry_interval = 0
-    result = check.run()
-
-    assert not result.get('failed', False)
-
-
-def test_all_images_unavailable(task_vars):
-    def execute_module(module_name=None, *args):
-        if module_name == "wait_for":
-            return {}
-        elif module_name == "command":
-            return {'failed': True}
-
-        return {}  # docker_image_facts failure
-
-    task_vars['openshift_docker_additional_registries'] = ["docker.io"]
-    task_vars['openshift_deployment_type'] = "openshift-enterprise"
-    task_vars['openshift_image_tag'] = 'latest'
-    check = DockerImageAvailability(execute_module, task_vars)
-    check._module_retry_interval = 0
-    actual = check.run()
-
-    assert actual['failed']
-    assert "required container images are not available" in actual['msg']
-
-
-@pytest.mark.parametrize("message,extra_words", [
-    (
-        "docker image update failure",
-        ["docker image update failure"],
-    ),
-    (
-        "No package matching 'skopeo' found available, installed or updated",
-        ["dependencies can be installed via `yum`"]
-    ),
-])
-def test_skopeo_update_failure(task_vars, message, extra_words):
-    def execute_module(module_name=None, *_):
-        if module_name == "yum":
-            return {
-                "failed": True,
-                "msg": message,
-            }
-
-        return {}
-
-    task_vars['openshift_docker_additional_registries'] = ["unknown.io"]
-    task_vars['openshift_deployment_type'] = "openshift-enterprise"
-    check = DockerImageAvailability(execute_module, task_vars)
-    check._module_retry_interval = 0
-    actual = check.run()
-
-    assert actual["failed"]
-    for word in extra_words:
-        assert word in actual["msg"]
-
-
-@pytest.mark.parametrize(
-    "image, registries, connection_test_failed, skopeo_failed, "
-    "expect_success, expect_registries_reached", [
-        (
-            "spam/eggs:v1", ["test.reg"],
-            True, True,
-            False,
-            {"test.reg": False, "docker.io": False},
-        ),
-        (
-            "spam/eggs:v1", ["test.reg"],
-            False, True,
-            False,
-            {"test.reg": True, "docker.io": True},
-        ),
-        (
-            "eggs.reg/spam/eggs:v1", ["test.reg"],
-            False, False,
-            True,
-            {"eggs.reg": True},
-        ),
-    ])
-def test_registry_availability(image, registries, connection_test_failed, skopeo_failed,
-                               expect_success, expect_registries_reached):
-    def execute_module(module_name=None, *_):
-        if module_name == "wait_for":
-            return dict(msg="msg", failed=connection_test_failed)
-        elif module_name == "command":
-            return dict(msg="msg", failed=skopeo_failed)
-
-    tv = task_vars()
-    tv.update({"openshift_docker_additional_registries": registries})
-    check = DockerImageAvailability(execute_module, tv)
-    check._module_retry_interval = 0
-
-    available = check.is_available_skopeo_image(image)
-    assert available == expect_success
-    assert expect_registries_reached == check.reachable_registries
-
-
-@pytest.mark.parametrize("deployment_type, openshift_is_atomic, groups, oreg_url, expected", [
-    (  # standard set of stuff required on nodes
-        "origin", False, ['oo_nodes_to_config'], "docker.io/openshift/origin-${component}:${version}",
-        set([
-            'docker.io/openshift/origin-pod:vtest',
-            'docker.io/openshift/origin-deployer:vtest',
-            'docker.io/openshift/origin-docker-registry:vtest',
-            'docker.io/openshift/origin-haproxy-router:vtest',
-            'docker.io/cockpit/kubernetes:latest',  # origin version of registry-console
-        ])
-    ),
-    (  # set a different URL for images
-        "origin", False, ['oo_nodes_to_config'], 'foo.io/openshift/origin-${component}:${version}',
-        set([
-            'foo.io/openshift/origin-pod:vtest',
-            'foo.io/openshift/origin-deployer:vtest',
-            'foo.io/openshift/origin-docker-registry:vtest',
-            'foo.io/openshift/origin-haproxy-router:vtest',
-            'docker.io/cockpit/kubernetes:latest',  # AFAICS this is not built from the URL
-        ])
-    ),
-    (
-        "origin", True, ['oo_nodes_to_config', 'oo_masters_to_config', 'oo_etcd_to_config'], "docker.io/openshift/origin-${component}:${version}",
-        set([
-            # images running on top of openshift
-            'docker.io/openshift/origin-pod:vtest',
-            'docker.io/openshift/origin-deployer:vtest',
-            'docker.io/openshift/origin-docker-registry:vtest',
-            'docker.io/openshift/origin-haproxy-router:vtest',
-            'docker.io/cockpit/kubernetes:latest',
-            # containerized component images
-            'registry.redhat.io/openshift3/ose-node:vtest',
-        ])
-    ),
-    (  # enterprise images
-        "openshift-enterprise", True, ['oo_nodes_to_config'], 'foo.io/openshift3/ose-${component}:f13ac45',
-        set([
-            'foo.io/openshift3/ose-pod:f13ac45',
-            'foo.io/openshift3/ose-deployer:f13ac45',
-            'foo.io/openshift3/ose-docker-registry:f13ac45',
-            'foo.io/openshift3/ose-haproxy-router:f13ac45',
-            # registry-console is not constructed/versioned the same as the others.
-            'registry.redhat.io/openshift3/registry-console:vtest',
-            # containerized images aren't built from oreg_url
-            'registry.redhat.io/openshift3/ose-node:vtest',
-        ])
-    ),
-
-])
-def test_required_images(deployment_type, openshift_is_atomic, groups, oreg_url, expected):
-    task_vars = dict(
-        openshift_is_atomic=openshift_is_atomic,
-        openshift_deployment_type=deployment_type,
-        group_names=groups,
-        oreg_url=oreg_url,
-        openshift_image_tag='vtest',
-        osn_image='registry.redhat.io/openshift3/ose-node:vtest',
-    )
-
-    assert expected == DockerImageAvailability(task_vars=task_vars).required_images()
-
-
-@pytest.mark.parametrize("task_vars, expected", [
-    (
-        dict(
-            openshift_deployment_type="origin",
-            openshift_image_tag="vtest",
-        ),
-        "docker.io/cockpit/kubernetes:latest",
-    ), (
-        dict(
-            openshift_deployment_type="openshift-enterprise",
-            openshift_image_tag="vtest",
-        ),
-        "registry.redhat.io/openshift3/registry-console:vtest",
-    ), (
-        dict(
-            openshift_deployment_type="openshift-enterprise",
-            openshift_image_tag="v3.7.0-alpha.0",
-            openshift_cockpit_deployer_prefix="registry.example.com/spam/",
-        ),
-        "registry.example.com/spam/registry-console:v3.7",
-    ), (
-        dict(
-            openshift_deployment_type="origin",
-            openshift_image_tag="v3.7.0-alpha.0",
-            openshift_cockpit_deployer_prefix="registry.example.com/eggs/",
-            openshift_cockpit_deployer_version="spam",
-        ),
-        "registry.example.com/eggs/kubernetes:spam",
-    ),
-])
-def test_registry_console_image(task_vars, expected):
-    info = DEPLOYMENT_IMAGE_INFO[task_vars["openshift_deployment_type"]]
-    tag = task_vars["openshift_image_tag"]
-    assert expected == DockerImageAvailability(task_vars=task_vars)._registry_console_image(tag, info)
-
-
-@pytest.mark.parametrize("task_vars, expected", [
-    (
-        dict(
-            group_names=['oo_nodes_to_config'],
-            openshift_image_tag="veggs",
-            osn_image="registry.redhat.io/openshift3/ose-node:vtest",
-        ),
-        set([
-            'registry.redhat.io/openshift3/ose-node:vtest', 'docker.io/cockpit/kubernetes:latest',
-            'docker.io/openshift/origin-haproxy-router:veggs', 'docker.io/openshift/origin-deployer:veggs',
-            'docker.io/openshift/origin-docker-registry:veggs', 'docker.io/openshift/origin-pod:veggs',
-        ]),
-    ), (
-        dict(
-            group_names=['oo_masters_to_config'],
-        ),
-        set(),
-    ),
-])
-def test_containerized(task_vars, expected):
-    task_vars.update(dict(
-        openshift_is_atomic=True,
-        oreg_url="docker.io/openshift/origin-${component}:${version}",
-        openshift_deployment_type="origin",
-    ))
-
-    assert expected == DockerImageAvailability(task_vars=task_vars).required_images()
+from openshift_checks.docker_image_availability import DockerImageAvailability
+
+try:
+    # python3, mock is built in.
+    from unittest.mock import patch
+except ImportError:
+    # In python2, mock is installed via pip.
+    from mock import patch
+
+
+def test_is_available_skopeo_image():
+    result = {'rc': 0}
+    # test unauth secure and insecure
+    openshift_docker_insecure_registries = ['insecure.redhat.io']
+    task_vars = {'openshift_docker_insecure_registries': openshift_docker_insecure_registries}
+    dia = DockerImageAvailability(task_vars=task_vars)
+    with patch.object(DockerImageAvailability, 'execute_module_with_retries') as m1:
+        m1.return_value = result
+        assert dia.is_available_skopeo_image('registry.redhat.io/openshift3/ose-pod') is True
+        m1.assert_called_with('command', {'_uses_shell': True, '_raw_params': ' timeout 10 skopeo inspect --tls-verify=true  docker://registry.redhat.io/openshift3/ose-pod'})
+        assert dia.is_available_skopeo_image('insecure.redhat.io/openshift3/ose-pod') is True
+        m1.assert_called_with('command', {'_uses_shell': True, '_raw_params': ' timeout 10 skopeo inspect --tls-verify=false  docker://insecure.redhat.io/openshift3/ose-pod'})
+
+    # test auth
+    task_vars = {'oreg_auth_user': 'test_user', 'oreg_auth_password': 'test_pass'}
+    dia = DockerImageAvailability(task_vars=task_vars)
+    with patch.object(DockerImageAvailability, 'execute_module_with_retries') as m1:
+        m1.return_value = result
+        assert dia.is_available_skopeo_image('registry.redhat.io/openshift3/ose-pod') is True
+        m1.assert_called_with('command', {'_uses_shell': True, '_raw_params': ' timeout 10 skopeo inspect --tls-verify=true --creds=test_user:test_pass docker://registry.redhat.io/openshift3/ose-pod'})
+
+
+def test_available_images():
+    images = ['image1', 'image2']
+    dia = DockerImageAvailability(task_vars={})
+
+    with patch('openshift_checks.docker_image_availability.DockerImageAvailability.is_available_skopeo_image') as call_mock:
+        call_mock.return_value = True
+
+        images_available = dia.available_images(images)
+        assert images_available == images