diff options
-rw-r--r-- | roles/openshift_cli/library/openshift_container_binary_sync.py | 131 | ||||
-rw-r--r-- | roles/openshift_cli/tasks/main.yml | 20 | ||||
-rw-r--r-- | roles/openshift_cli/templates/openshift.j2 | 28 |
3 files changed, 135 insertions, 44 deletions
diff --git a/roles/openshift_cli/library/openshift_container_binary_sync.py b/roles/openshift_cli/library/openshift_container_binary_sync.py new file mode 100644 index 000000000..fd290c6fc --- /dev/null +++ b/roles/openshift_cli/library/openshift_container_binary_sync.py @@ -0,0 +1,131 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# vim: expandtab:tabstop=4:shiftwidth=4 +# pylint: disable=missing-docstring,invalid-name +# + +import random +import tempfile +import shutil +import os.path + +# pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import +from ansible.module_utils.basic import * + + +DOCUMENTATION = ''' +--- +module: openshift_container_binary_sync +short_description: Copies OpenShift binaries out of the given image tag to host system. +''' + + +class BinarySyncError(Exception): + def __init__(self, msg): + super(BinarySyncError, self).__init__(msg) + self.msg = msg + + +# pylint: disable=too-few-public-methods +class BinarySyncer(object): + """ + Syncs the openshift, oc, oadm, and kubectl binaries/symlinks out of + a container onto the host system. + """ + + def __init__(self, module, image, tag): + self.module = module + self.changed = False + self.output = [] + self.bin_dir = '/usr/local/bin' + self.image = image + self.tag = tag + self.temp_dir = None # TBD + + def sync(self): + container_name = "openshift-cli-%s" % random.randint(1, 100000) + rc, stdout, stderr = self.module.run_command(['docker', 'create', '--name', + container_name, '%s:%s' % (self.image, self.tag)]) + if rc: + raise BinarySyncError("Error creating temporary docker container. stdout=%s, stderr=%s" % + (stdout, stderr)) + self.output.append(stdout) + try: + self.temp_dir = tempfile.mkdtemp() + self.output.append("Using temp dir: %s" % self.temp_dir) + + rc, stdout, stderr = self.module.run_command(['docker', 'cp', "%s:/usr/bin/openshift" % container_name, + self.temp_dir]) + if rc: + raise BinarySyncError("Error copying file from docker container: stdout=%s, stderr=%s" % + (stdout, stderr)) + + rc, stdout, stderr = self.module.run_command(['docker', 'cp', "%s:/usr/bin/oc" % container_name, + self.temp_dir]) + if rc: + raise BinarySyncError("Error copying file from docker container: stdout=%s, stderr=%s" % + (stdout, stderr)) + + self._sync_binary('openshift') + + # In older versions, oc was a symlink to openshift: + if os.path.islink(os.path.join(self.temp_dir, 'oc')): + self._sync_symlink('oc', 'openshift') + else: + self._sync_binary('oc') + + # Ensure correct symlinks created: + self._sync_symlink('kubectl', 'openshift') + self._sync_symlink('oadm', 'openshift') + finally: + shutil.rmtree(self.temp_dir) + self.module.run_command(['docker', 'rm', container_name]) + + def _sync_symlink(self, binary_name, link_to): + """ Ensure the given binary name exists and links to the expected binary. """ + link_path = os.path.join(self.bin_dir, binary_name) + link_dest = os.path.join(self.bin_dir, binary_name) + if not os.path.exists(link_path) or \ + not os.path.islink(link_path) or \ + os.path.realpath(link_path) != os.path.realpath(link_dest): + if os.path.exists(link_path): + os.remove(link_path) + os.symlink(link_to, os.path.join(self.bin_dir, binary_name)) + self.output.append("Symlinked %s to %s." % (link_path, link_dest)) + self.changed = True + + def _sync_binary(self, binary_name): + src_path = os.path.join(self.temp_dir, binary_name) + dest_path = os.path.join(self.bin_dir, binary_name) + incoming_checksum = self.module.run_command(['sha256sum', src_path])[1] + if not os.path.exists(dest_path) or self.module.run_command(['sha256sum', dest_path])[1] != incoming_checksum: + shutil.move(src_path, dest_path) + self.output.append("Moved %s to %s." % (src_path, dest_path)) + self.changed = True + + +def main(): + module = AnsibleModule( + argument_spec=dict( + image=dict(required=True), + tag=dict(required=True), + ), + supports_check_mode=True + ) + + image = module.params['image'] + tag = module.params['tag'] + + binary_syncer = BinarySyncer(module, image, tag) + + try: + binary_syncer.sync() + except BinarySyncError as ex: + module.fail_json(msg=ex.msg) + + return module.exit_json(changed=binary_syncer.changed, + output=binary_syncer.output) + + +if __name__ == '__main__': + main() diff --git a/roles/openshift_cli/tasks/main.yml b/roles/openshift_cli/tasks/main.yml index 509b8bc8a..11c73b25c 100644 --- a/roles/openshift_cli/tasks/main.yml +++ b/roles/openshift_cli/tasks/main.yml @@ -10,22 +10,10 @@ changed_when: "'Downloaded newer image' in pull_result.stdout" when: openshift.common.is_containerized | bool -- name: Create /usr/local/bin/openshift cli wrapper - template: - src: openshift.j2 - dest: /usr/local/bin/openshift - mode: 0755 - when: openshift.common.is_containerized | bool - -- name: Create client symlinks - file: - path: "{{ item }}" - state: link - src: /usr/local/bin/openshift - with_items: - - /usr/local/bin/oadm - - /usr/local/bin/oc - - /usr/local/bin/kubectl +- name: Copy client binaries/symlinks out of CLI image for use on the host + openshift_container_binary_sync: + image: "{{ openshift.common.cli_image }}" + tag: "{{ openshift_image_tag }}" when: openshift.common.is_containerized | bool - name: Reload facts to pick up installed OpenShift version diff --git a/roles/openshift_cli/templates/openshift.j2 b/roles/openshift_cli/templates/openshift.j2 deleted file mode 100644 index 7786acead..000000000 --- a/roles/openshift_cli/templates/openshift.j2 +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -if [ ! -d ~/.kube ]; then - mkdir -m 0700 ~/.kube -fi -cmd=`basename $0` -user=`id -u` -group=`id -g` -image_tag="{{ openshift_image_tag }}" - ->&2 echo """ -================================================================================ -ATTENTION: You are running ${cmd} via a wrapper around 'docker run {{ openshift.common.cli_image }}:${image_tag}'. -This wrapper is intended only to be used to bootstrap an environment. Please -install client tools on another host once you have granted cluster-admin -privileges to a user. -{% if openshift.common.deployment_type in ['openshift-enterprise','atomic-enterprise'] %} -See https://docs.openshift.com/enterprise/latest/cli_reference/get_started_cli.html -{% else %} -See https://docs.openshift.org/latest/cli_reference/get_started_cli.html -{% endif %} -================================================================================= -""" - -if [ -n "$image_tag" ]; then - image_tag=":$image_tag" -fi - -docker run -i --privileged --net=host --user=${user}:${group} -v ~/.kube:/root/.kube -v /tmp:/tmp -v {{ openshift.common.config_base}}:{{ openshift.common.config_base }} -e KUBECONFIG=/root/.kube/config --entrypoint ${cmd} --rm {{ openshift.common.cli_image }}${image_tag} "${@}" |