diff options
Diffstat (limited to 'images')
18 files changed, 1081 insertions, 0 deletions
diff --git a/images/installer/Dockerfile b/images/installer/Dockerfile new file mode 100644 index 000000000..0d977d48f --- /dev/null +++ b/images/installer/Dockerfile @@ -0,0 +1,46 @@ +FROM centos:7 + +MAINTAINER OpenShift Team <dev@lists.openshift.redhat.com> + +USER root + +# Add origin repo for including the oc client +COPY images/installer/origin-extra-root / + +# install ansible and deps +RUN INSTALL_PKGS="python-lxml pyOpenSSL python2-cryptography openssl java-1.8.0-openjdk-headless python2-passlib httpd-tools openssh-clients origin-clients" \ + && yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS \ + && EPEL_PKGS="ansible python2-boto" \ + && yum install -y epel-release \ + && yum install -y --setopt=tsflags=nodocs $EPEL_PKGS \ + && rpm -V $INSTALL_PKGS $EPEL_PKGS \ + && yum clean all + +LABEL name="openshift/origin-ansible" \ + summary="OpenShift's installation and configuration tool" \ + description="A containerized openshift-ansible image to let you run playbooks to install, upgrade, maintain and check an OpenShift cluster" \ + url="https://github.com/openshift/openshift-ansible" \ + io.k8s.display-name="openshift-ansible" \ + io.k8s.description="A containerized openshift-ansible image to let you run playbooks to install, upgrade, maintain and check an OpenShift cluster" \ + io.openshift.expose-services="" \ + io.openshift.tags="openshift,install,upgrade,ansible" \ + atomic.run="once" + +ENV USER_UID=1001 \ + HOME=/opt/app-root/src \ + WORK_DIR=/usr/share/ansible/openshift-ansible \ + OPTS="-v" + +# Add image scripts and files for running as a system container +COPY images/installer/root / +# Include playbooks, roles, plugins, etc. from this repo +COPY . ${WORK_DIR} + +RUN /usr/local/bin/user_setup \ + && rm /usr/local/bin/usage.ocp + +USER ${USER_UID} + +WORKDIR ${WORK_DIR} +ENTRYPOINT [ "/usr/local/bin/entrypoint" ] +CMD [ "/usr/local/bin/run" ] diff --git a/images/installer/Dockerfile.rhel7 b/images/installer/Dockerfile.rhel7 new file mode 100644 index 000000000..5245771d0 --- /dev/null +++ b/images/installer/Dockerfile.rhel7 @@ -0,0 +1,46 @@ +FROM rhel7.3:7.3-released + +MAINTAINER OpenShift Team <dev@lists.openshift.redhat.com> + +USER root + +# Playbooks, roles, and their dependencies are installed from packages. +RUN INSTALL_PKGS="atomic-openshift-utils atomic-openshift-clients python-boto openssl java-1.8.0-openjdk-headless httpd-tools" \ + && yum repolist > /dev/null \ + && yum-config-manager --enable rhel-7-server-ose-3.7-rpms \ + && yum-config-manager --enable rhel-7-server-rh-common-rpms \ + && yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS \ + && rpm -q $INSTALL_PKGS \ + && yum clean all + +LABEL name="openshift3/ose-ansible" \ + summary="OpenShift's installation and configuration tool" \ + description="A containerized openshift-ansible image to let you run playbooks to install, upgrade, maintain and check an OpenShift cluster" \ + url="https://github.com/openshift/openshift-ansible" \ + io.k8s.display-name="openshift-ansible" \ + io.k8s.description="A containerized openshift-ansible image to let you run playbooks to install, upgrade, maintain and check an OpenShift cluster" \ + io.openshift.expose-services="" \ + io.openshift.tags="openshift,install,upgrade,ansible" \ + com.redhat.component="aos3-installation-docker" \ + version="v3.6.0" \ + release="1" \ + architecture="x86_64" \ + atomic.run="once" + +ENV USER_UID=1001 \ + HOME=/opt/app-root/src \ + WORK_DIR=/usr/share/ansible/openshift-ansible \ + ANSIBLE_CONFIG=/usr/share/atomic-openshift-utils/ansible.cfg \ + OPTS="-v" + +# Add image scripts and files for running as a system container +COPY root / + +RUN /usr/local/bin/user_setup \ + && mv /usr/local/bin/usage{.ocp,} + +USER ${USER_UID} + +WORKDIR ${WORK_DIR} +ENTRYPOINT [ "/usr/local/bin/entrypoint" ] +CMD [ "/usr/local/bin/run" ] diff --git a/images/installer/README_CONTAINER_IMAGE.md b/images/installer/README_CONTAINER_IMAGE.md new file mode 100644 index 000000000..bfe3661c0 --- /dev/null +++ b/images/installer/README_CONTAINER_IMAGE.md @@ -0,0 +1,50 @@ +ORIGIN-ANSIBLE IMAGE INSTALLER +=============================== + +Contains Dockerfile information for building an openshift/origin-ansible image +based on `centos:7` or `rhel7.3:7.3-released`. + +Read additional setup information for this image at: https://hub.docker.com/r/openshift/origin-ansible/ + +Read additional information about the `openshift/origin-ansible` at: https://github.com/openshift/openshift-ansible/blob/master/README_CONTAINER_IMAGE.md + +Also contains necessary components for running the installer using an Atomic System Container. + + +System container installer +========================== + +These files are needed to run the installer using an [Atomic System container](http://www.projectatomic.io/blog/2016/09/intro-to-system-containers/). +These files can be found under `root/exports`: + +* config.json.template - Template of the configuration file used for running containers. + +* manifest.json - Used to define various settings for the system container, such as the default values to use for the installation. + +* service.template - Template file for the systemd service. + +* tmpfiles.template - Template file for systemd-tmpfiles. + +These files can be found under `root/usr/local/bin`: + +* run-system-container.sh - Entrypoint to the container. + +## Options + +These options may be set via the ``atomic`` ``--set`` flag. For defaults see ``root/exports/manifest.json`` + +* OPTS - Additional options to pass to ansible when running the installer + +* VAR_LIB_OPENSHIFT_INSTALLER - Full path of the installer code to mount into the container + +* VAR_LOG_OPENSHIFT_LOG - Full path of the log file to mount into the container + +* PLAYBOOK_FILE - Full path of the playbook inside the container + +* HOME_ROOT - Full path on host to mount as the root home directory inside the container (for .ssh/, etc..) + +* ANSIBLE_CONFIG - Full path for the ansible configuration file to use inside the container + +* INVENTORY_FILE - Full path for the inventory to use from the host + +* INVENTORY_DIR - Full path for the inventory directory to use (e.g. for use with a hybrid dynamic/static inventory) diff --git a/images/installer/README_INVENTORY_GENERATOR.md b/images/installer/README_INVENTORY_GENERATOR.md new file mode 100644 index 000000000..9c10e4b71 --- /dev/null +++ b/images/installer/README_INVENTORY_GENERATOR.md @@ -0,0 +1,85 @@ +Dynamic Inventory Generation +============================ + +Script within the openshift-ansible image that can dynamically +generate an Ansible inventory file from an existing cluster. + +## Configure + +User configuration helps to provide additional details when creating an inventory file. +The default location of this file is in `/etc/inventory-generator-config.yaml`. The +following configuration values are either expected or default to the given values when omitted: + +- `master_config_path`: + - specifies where to look for the bind-mounted `master-config.yaml` file in the container + - if omitted or a `null` value is given, its value is defaulted to `/opt/app-root/src/master-config.yaml` + +- `admin_kubeconfig_path`: + - specifies where to look for the bind-mounted `admin.kubeconfig` file in the container + - if omitted or a `null` value is given, its value is defaulted to `/opt/app-root/src/.kube/config` + +- `ansible_ssh_user`: + - specifies the ssh user to be used by Ansible when running the specified `PLAYBOOK_FILE` (see `README_CONTAINER_IMAGE.md` for additional information on this environment variable). + - if omitted, its value is defaulted to `root` + +- `ansible_become_user`: + - specifies a user to "become" on the remote host. Used for privilege escalation. + - If a non-null value is specified, `ansible_become` is implicitly set to `yes` in the resulting inventory file. + +See the supplied sample user configuration file in [`root/etc/inventory-generator-config.yaml`](./root/etc/inventory-generator-config.yaml) for additional optional inventory variables that may be specified. + +## Build + +See `README_CONTAINER_IMAGE.md` for information on building this image. + +## Run + +Given a master node's `master-config.yaml` file, a user configuration file (see "Configure" section), and an `admin.kubeconfig` file, the command below will: + +1. Use `oc` to query the host about additional node information (using the supplied `kubeconfig` file) +2. Generate an inventory file based on information retrieved from `oc get nodes` and the given `master-config.yaml` file. +3. run the specified [openshift-ansible](https://github.com/openshift/openshift-ansible) `health.yml` playbook using the generated inventory file from the previous step + +``` +docker run -u `id -u` \ + -v $HOME/.ssh/id_rsa:/opt/app-root/src/.ssh/id_rsa:Z,ro \ + -v /tmp/origin/master/admin.kubeconfig:/opt/app-root/src/.kube/config:Z \ + -v /tmp/origin/master/master-config.yaml:/opt/app-root/src/master-config.yaml:Z \ + -e OPTS="-v --become-user root" \ + -e PLAYBOOK_FILE=playbooks/byo/openshift-checks/health.yml \ + -e GENERATE_INVENTORY=true \ + -e USER=`whoami` \ + openshift/origin-ansible + +``` + +**Note** In the command above, specifying the `GENERATE_INVENTORY` environment variable will automatically generate the inventory file in an expected location. +An `INVENTORY_FILE` variable (or any other inventory location) does not need to be supplied when generating an inventory. + +## Debug + +To debug the `generate` script, run the above script interactively +and manually execute `/usr/local/bin/generate`: + +``` +... +docker run -u `id -u` \ + -v ... + ... + -it openshift/origin-ansible /bin/bash + +--- + +bash-4.2$ cd $HOME +bash-4.2$ ls +master-config.yaml +bash-4.2$ /usr/local/bin/generate $HOME/generated_hosts +bash-4.2$ ls +generated_hosts master-config.yaml +bash-4.2$ less generated_hosts +... +``` + +## Notes + +See `README_CONTAINER_IMAGE.md` for additional information about this image. diff --git a/images/installer/origin-extra-root/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-PaaS b/images/installer/origin-extra-root/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-PaaS new file mode 100644 index 000000000..fcbaaca0e --- /dev/null +++ b/images/installer/origin-extra-root/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-PaaS @@ -0,0 +1,20 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2.0.22 (GNU/Linux) + +mQENBFc8iwUBCADadBGYmA2nFvq79/5uxUQOiPqC/QflWcPX1B6SQKniUhyqaSes +gNMJsPppKRV4NZKITcL8lZ90+Gds0fmL3b5xz1r5Rfm3ilSItEqeGlLIJZBvANyx +rAT3q8EgkkVRyhZPseUMZj04O8OKnt1jrHakVkOp0lJClqhZ+bs/7yLRmaLXTcum ++ouqUKzQoAEDnqe9nJmmJhC6n2vg7o0PCo/9qOf/scQbv4FNoJfmkcVLRmwmqzgh +bGj6QaOgij3sl94pZ3HFop4f+eU0kNbyt9J18fKI8X0DdHkDW8kO1UwwHT2ibJ1t +mBaUsE1zZ0DvfyFad1xXAgm+SIlJgdpPvPNLABEBAAG0WUNlbnRPUyBQYWFTIFNJ +RyAoaHR0cHM6Ly93aWtpLmNlbnRvcy5vcmcvU3BlY2lhbEludGVyZXN0R3JvdXAv +UGFhUykgPHNlY3VyaXR5QGNlbnRvcy5vcmc+iQE5BBMBAgAjBQJXPIsFAhsDBwsJ +CAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQw0xb1C8pfsyT2gf9FqJoc8oZ+T5A +8cZslMyCWziPi0o7kd/Rw91T7dkV+VIC+sFlVga7fkPEAiD8U7JFE+a1IlcjfGuY +my4S6UH8K5zL36CRg2MF112HE5TWoBxF3KZg9nOJQ2NLapJowaP8uITYG4vlgV3g +GJD2OC191tjcqmelFnhAN0EBdxrRrBJ7tr3OCtL6bJ6NPQ0bXPI2Fjbm7SbxTfpE +ggEU8R7WZQApYgl8zRfyS12SfpFV8ZU+lIBmJaU1qaY4/BmNgG6e7clmq8xVZQLg +ZH9qi9+HPh+80+8/WhJUddlVXc2g6c4VjnnFpZfsrMdTAFuEsrjkyaxqeBjXCgbb +pzGjTg0LXg== +=CVSF +-----END PGP PUBLIC KEY BLOCK----- diff --git a/images/installer/origin-extra-root/etc/yum.repos.d/centos-openshift-origin.repo b/images/installer/origin-extra-root/etc/yum.repos.d/centos-openshift-origin.repo new file mode 100644 index 000000000..d18994fdd --- /dev/null +++ b/images/installer/origin-extra-root/etc/yum.repos.d/centos-openshift-origin.repo @@ -0,0 +1,7 @@ + +[centos-openshift-origin] +name=CentOS OpenShift Origin +baseurl=http://mirror.centos.org/centos/7/paas/x86_64/openshift-origin/ +enabled=1 +gpgcheck=1 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-PaaS diff --git a/images/installer/root/etc/inventory-generator-config.yaml b/images/installer/root/etc/inventory-generator-config.yaml new file mode 100644 index 000000000..d56e3f4d2 --- /dev/null +++ b/images/installer/root/etc/inventory-generator-config.yaml @@ -0,0 +1,20 @@ +--- +# meta config +master_config_path: "/opt/app-root/src/master-config.yaml" +admin_kubeconfig_path: "/opt/app-root/src/.kube/config" + +# default user configuration +ansible_ssh_user: ec2-user +ansible_become: "yes" +ansible_become_user: "root" + +# openshift-ansible inventory vars +openshift_uninstall_images: false +openshift_install_examples: true +openshift_deployment_type: origin + +openshift_release: 3.6 +openshift_image_tag: v3.6.0 +openshift_hosted_logging_deploy: null # defaults to "true" if loggingPublicURL is set in master-config.yaml +openshift_logging_image_version: v3.6.0 +openshift_disable_check: "" diff --git a/images/installer/root/exports/config.json.template b/images/installer/root/exports/config.json.template new file mode 100644 index 000000000..1a009fa7b --- /dev/null +++ b/images/installer/root/exports/config.json.template @@ -0,0 +1,234 @@ +{ + "ociVersion": "1.0.0", + "platform": { + "os": "linux", + "arch": "amd64" + }, + "process": { + "terminal": false, + "consoleSize": { + "height": 0, + "width": 0 + }, + "user": { + "uid": 0, + "gid": 0 + }, + "args": [ + "/usr/local/bin/run-system-container.sh" + ], + "env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + "OPTS=$OPTS", + "PLAYBOOK_FILE=$PLAYBOOK_FILE", + "ANSIBLE_CONFIG=$ANSIBLE_CONFIG" + ], + "cwd": "/usr/share/ansible/openshift-ansible", + "rlimits": [ + { + "type": "RLIMIT_NOFILE", + "hard": 1024, + "soft": 1024 + } + ], + "noNewPrivileges": true + }, + "root": { + "path": "rootfs", + "readonly": true + }, + "mounts": [ + { + "destination": "/proc", + "type": "proc", + "source": "proc" + }, + { + "destination": "/dev", + "type": "tmpfs", + "source": "tmpfs", + "options": [ + "nosuid", + "strictatime", + "mode=755", + "size=65536k" + ] + }, + { + "destination": "/dev/pts", + "type": "devpts", + "source": "devpts", + "options": [ + "nosuid", + "noexec", + "newinstance", + "ptmxmode=0666", + "mode=0620", + "gid=5" + ] + }, + { + "destination": "/dev/shm", + "type": "tmpfs", + "source": "shm", + "options": [ + "nosuid", + "noexec", + "nodev", + "mode=1777", + "size=65536k" + ] + }, + { + "destination": "/dev/mqueue", + "type": "mqueue", + "source": "mqueue", + "options": [ + "nosuid", + "noexec", + "nodev" + ] + }, + { + "destination": "/sys", + "type": "sysfs", + "source": "sysfs", + "options": [ + "nosuid", + "noexec", + "nodev", + "ro" + ] + }, + { + "type": "bind", + "source": "$HOME_ROOT/.ssh", + "destination": "/opt/app-root/src/.ssh", + "options": [ + "bind", + "rw", + "mode=755" + ] + }, + { + "type": "bind", + "source": "$HOME_ROOT", + "destination": "/root", + "options": [ + "bind", + "rw", + "mode=755" + ] + }, + { + "type": "bind", + "source": "$VAR_LIB_OPENSHIFT_INSTALLER", + "destination": "/var/lib/openshift-installer", + "options": [ + "bind", + "rw", + "mode=755" + ] + }, + { + "type": "bind", + "source": "$VAR_LOG_OPENSHIFT_LOG", + "destination": "/var/log/ansible.log", + "options": [ + "bind", + "rw", + "mode=755" + ] + }, + { + "destination": "/root/.ansible", + "type": "tmpfs", + "source": "tmpfs", + "options": [ + "nosuid", + "strictatime", + "mode=755" + ] + }, + { + "destination": "/tmp", + "type": "tmpfs", + "source": "tmpfs", + "options": [ + "nosuid", + "strictatime", + "mode=755" + ] + }, + { + "type": "bind", + "source": "$INVENTORY_FILE", + "destination": "/etc/ansible/hosts", + "options": [ + "bind", + "rw", + "mode=755" + ] + }, + { + "destination": "/etc/resolv.conf", + "type": "bind", + "source": "/etc/resolv.conf", + "options": [ + "ro", + "rbind", + "rprivate" + ] + }, + { + "destination": "/sys/fs/cgroup", + "type": "cgroup", + "source": "cgroup", + "options": [ + "nosuid", + "noexec", + "nodev", + "relatime", + "ro" + ] + } + ], + "hooks": { + + }, + "linux": { + "resources": { + "devices": [ + { + "allow": false, + "access": "rwm" + } + ] + }, + "namespaces": [ + { + "type": "pid" + }, + { + "type": "mount" + } + ], + "maskedPaths": [ + "/proc/kcore", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware" + ], + "readonlyPaths": [ + "/proc/asound", + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + } +} diff --git a/images/installer/root/exports/manifest.json b/images/installer/root/exports/manifest.json new file mode 100644 index 000000000..8b984d7a3 --- /dev/null +++ b/images/installer/root/exports/manifest.json @@ -0,0 +1,12 @@ +{ + "version": "1.0", + "defaultValues": { + "OPTS": "", + "VAR_LIB_OPENSHIFT_INSTALLER" : "/var/lib/openshift-installer", + "VAR_LOG_OPENSHIFT_LOG": "/var/log/ansible.log", + "PLAYBOOK_FILE": "/usr/share/ansible/openshift-ansible/playbooks/byo/config.yml", + "HOME_ROOT": "/root", + "ANSIBLE_CONFIG": "/usr/share/atomic-openshift-utils/ansible.cfg", + "INVENTORY_FILE": "/dev/null" + } +} diff --git a/images/installer/root/exports/service.template b/images/installer/root/exports/service.template new file mode 100644 index 000000000..bf5316af6 --- /dev/null +++ b/images/installer/root/exports/service.template @@ -0,0 +1,6 @@ +[Service] +ExecStart=$EXEC_START +ExecStop=-$EXEC_STOP +Restart=no +WorkingDirectory=$DESTDIR +Type=oneshot diff --git a/images/installer/root/exports/tmpfiles.template b/images/installer/root/exports/tmpfiles.template new file mode 100644 index 000000000..b1f6caf47 --- /dev/null +++ b/images/installer/root/exports/tmpfiles.template @@ -0,0 +1,2 @@ +d $VAR_LIB_OPENSHIFT_INSTALLER - - - - - +f $VAR_LOG_OPENSHIFT_LOG - - - - - diff --git a/images/installer/root/usr/local/bin/entrypoint b/images/installer/root/usr/local/bin/entrypoint new file mode 100755 index 000000000..777bf3f11 --- /dev/null +++ b/images/installer/root/usr/local/bin/entrypoint @@ -0,0 +1,17 @@ +#!/bin/bash -e +# +# This file serves as the main entrypoint to the openshift-ansible image. +# +# For more information see the documentation: +# https://github.com/openshift/openshift-ansible/blob/master/README_CONTAINER_IMAGE.md + + +# Patch /etc/passwd file with the current user info. +# The current user's entry must be correctly defined in this file in order for +# the `ssh` command to work within the created container. + +if ! whoami &>/dev/null; then + echo "${USER:-default}:x:$(id -u):$(id -g):Default User:$HOME:/sbin/nologin" >> /etc/passwd +fi + +exec "$@" diff --git a/images/installer/root/usr/local/bin/generate b/images/installer/root/usr/local/bin/generate new file mode 100755 index 000000000..3db7a3ee8 --- /dev/null +++ b/images/installer/root/usr/local/bin/generate @@ -0,0 +1,397 @@ +#!/bin/env python + +""" +Attempts to read 'master-config.yaml' and extract remote +host information to dynamically create an inventory file +in order to run Ansible playbooks against that host. +""" + +import os +import re +import shlex +import shutil +import subprocess +import sys +import yaml + +try: + HOME = os.environ['HOME'] +except KeyError: + print 'A required environment variable "$HOME" has not been set' + exit(1) + +DEFAULT_USER_CONFIG_PATH = '/etc/inventory-generator-config.yaml' +DEFAULT_MASTER_CONFIG_PATH = HOME + '/master-config.yaml' +DEFAULT_ADMIN_KUBECONFIG_PATH = HOME + '/.kube/config' + +INVENTORY_FULL_PATH = HOME + '/generated_hosts' +USE_STDOUT = True + +if len(sys.argv) > 1: + INVENTORY_FULL_PATH = sys.argv[1] + USE_STDOUT = False + + +class OpenShiftClientError(Exception): + """Base exception class for OpenShift CLI wrapper""" + pass + + +class InvalidHost(Exception): + """Base exception class for host creation problems.""" + pass + + +class InvalidHostGroup(Exception): + """Base exception class for host-group creation problems.""" + pass + + +class OpenShiftClient: + oc = None + kubeconfig = None + + def __init__(self, kubeconfig=DEFAULT_ADMIN_KUBECONFIG_PATH): + """Find and store path to oc binary""" + # https://github.com/openshift/openshift-ansible/issues/3410 + # oc can be in /usr/local/bin in some cases, but that may not + # be in $PATH due to ansible/sudo + paths = os.environ.get("PATH", os.defpath).split(os.pathsep) + ['/usr/local/bin', os.path.expanduser('~/bin')] + + oc_binary_name = 'oc' + oc_binary = None + + # Use shutil.which if it is available, otherwise fallback to a naive path search + try: + which_result = shutil.which(oc_binary_name, path=os.pathsep.join(paths)) + if which_result is not None: + oc_binary = which_result + except AttributeError: + for path in paths: + if os.path.exists(os.path.join(path, oc_binary_name)): + oc_binary = os.path.join(path, oc_binary_name) + break + + if oc_binary is None: + raise OpenShiftClientError('Unable to locate `oc` binary. Not present in PATH.') + + self.oc = oc_binary + self.kubeconfig = kubeconfig + + def call(self, cmd_str): + """Execute a remote call using `oc`""" + cmd = [ + self.oc, + '--config', + self.kubeconfig + ] + shlex.split(cmd_str) + try: + out = subprocess.check_output(list(cmd), stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as err: + raise OpenShiftClientError('[rc {}] {}\n{}'.format(err.returncode, ' '.join(err.cmd), err.output)) + return out + + def whoami(self): + """Retrieve information about the current user in the given kubeconfig""" + return self.call('whoami') + + def get_nodes(self): + """Retrieve remote node information as a yaml object""" + return self.call('get nodes -o yaml') + + +class HostGroup: + groupname = "" + hosts = list() + + def __init__(self, hosts): + if not hosts: + return + first = hosts[0].get_group_name() + for h in hosts: + if h.get_group_name() != first: + raise InvalidHostGroup("Attempt to create HostGroup with hosts of varying groups.") + + self.hosts = hosts + self.groupname = first + + def add_host(self, host): + """Add a new host to this group.""" + self.hosts.append(host) + + def get_group_name(self): + """Return the groupname associated with each aggregated host.""" + return self.groupname + + def get_hosts(self): + """Return aggregated hosts""" + return self.hosts + + def string(self): + """Call the print method for each aggregated host; separated by newlines.""" + infos = "" + for host in self.hosts: + infos += host.string() + "\n" + return infos + + +class Host: + group = "masters" + alias = "" + hostname = "" + public_hostname = "" + ip_addr = "" + public_ip_addr = "" + + def __init__(self, groupname): + if not groupname: + raise InvalidHost("Attempt to create Host with no group name provided.") + self.group = groupname + + def get_group_name(self): + return self.group + + def get_openshift_hostname(self): + return self.hostname + + def host_alias(self, hostalias): + """Set an alias for this host.""" + self.alias = hostalias + + def address(self, ip): + """Set the ip address for this host.""" + self.ip_addr = ip + + def public_address(self, ip): + """Set the external ip address for this host.""" + self.public_ip_addr = ip + + def host_name(self, hname): + self.hostname = parse_hostname(hname) + + def public_host_name(self, phname): + self.public_hostname = parse_hostname(phname) + + def string(self): + """Print an inventory-file compatible string with host information""" + info = "" + if self.alias: + info += self.alias + " " + elif self.hostname: + info += self.hostname + " " + elif self.ip_addr: + info += self.ip_addr + " " + if self.ip_addr: + info += "openshift_ip=" + self.ip_addr + " " + if self.public_ip_addr: + info += "openshift_public_ip=" + self.public_ip_addr + " " + if self.hostname: + info += "openshift_hostname=" + self.hostname + " " + if self.public_hostname: + info += "openshift_public_hostname=" + self.public_hostname + + return info + + +def parse_hostname(host): + """Remove protocol and port from given hostname. + Return parsed string""" + no_proto = re.split('^http(s)?\:\/\/', host) + if no_proto: + host = no_proto[-1] + + no_port = re.split('\:[0-9]+(/)?$', host) + if no_port: + host = no_port[0] + + return host + + +def main(): + """Parse master-config file and populate inventory file.""" + # set default values + USER_CONFIG = os.environ.get('CONFIG') + if not USER_CONFIG: + USER_CONFIG = DEFAULT_USER_CONFIG_PATH + + # read user configuration + try: + config_file_obj = open(USER_CONFIG, 'r') + raw_config_file = config_file_obj.read() + user_config = yaml.load(raw_config_file) + if not user_config: + user_config = dict() + except IOError as err: + print "Unable to find or read user configuration file '{}': {}".format(USER_CONFIG, err) + exit(1) + + master_config_path = user_config.get('master_config_path', DEFAULT_MASTER_CONFIG_PATH) + if not master_config_path: + master_config_path = DEFAULT_MASTER_CONFIG_PATH + + admin_kubeconfig_path = user_config.get('admin_kubeconfig_path', DEFAULT_ADMIN_KUBECONFIG_PATH) + if not admin_kubeconfig_path: + admin_kubeconfig_path = DEFAULT_ADMIN_KUBECONFIG_PATH + + try: + file_obj = open(master_config_path, 'r') + except IOError as err: + print "Unable to find or read host master configuration file '{}': {}".format(master_config_path, err) + exit(1) + + raw_text = file_obj.read() + + y = yaml.load(raw_text) + if y.get("kind", "") != "MasterConfig": + print "Bind-mounted host master configuration file is not of 'kind' MasterConfig. Aborting..." + exit(1) + + # finish reading config file and begin gathering + # cluster information for inventory file + file_obj.close() + + # set inventory values based on user configuration + ansible_ssh_user = user_config.get('ansible_ssh_user', 'root') + ansible_become_user = user_config.get('ansible_become_user') + + openshift_uninstall_images = user_config.get('openshift_uninstall_images', False) + openshift_install_examples = user_config.get('openshift_install_examples', True) + openshift_deployment_type = user_config.get('openshift_deployment_type', 'origin') + + openshift_release = user_config.get('openshift_release') + openshift_image_tag = user_config.get('openshift_image_tag') + openshift_logging_image_version = user_config.get('openshift_logging_image_version') + openshift_disable_check = user_config.get('openshift_disable_check') + + # extract host config info from parsed yaml file + asset_config = y.get("assetConfig") + master_config = y.get("kubernetesMasterConfig") + etcd_config = y.get("etcdClientInfo") + + # if master_config is missing, error out; we expect to be running on a master to be able to + # gather enough information to generate the rest of the inventory file. + if not master_config: + msg = "'kubernetesMasterConfig' missing from '{}'; unable to gather all necessary host information..." + print msg.format(master_config_path) + exit(1) + + master_public_url = y.get("masterPublicURL") + if not master_public_url: + msg = "'kubernetesMasterConfig.masterPublicURL' missing from '{}'; Unable to connect to master host..." + print msg.format(master_config_path) + exit(1) + + oc = OpenShiftClient(admin_kubeconfig_path) + + # ensure kubeconfig is logged in with provided user, or fail with a friendly message otherwise + try: + oc.whoami() + except OpenShiftClientError as err: + msg = ("Unable to obtain user information using the provided kubeconfig file. " + "Current context does not appear to be able to authenticate to the server. " + "Error returned from server:\n\n{}") + print msg.format(str(err)) + exit(1) + + # connect to remote host using the provided config and extract all possible node information + nodes_config = yaml.load(oc.get_nodes()) + + # contains host types (e.g. masters, nodes, etcd) + host_groups = dict() + openshift_hosted_logging_deploy = False + is_etcd_deployed = master_config.get("storage-backend", "") in ["etcd3", "etcd2", "etcd"] + + if asset_config and asset_config.get('loggingPublicURL'): + openshift_hosted_logging_deploy = True + + openshift_hosted_logging_deploy = user_config.get("openshift_hosted_logging_deploy", openshift_hosted_logging_deploy) + + m = Host("masters") + m.address(master_config["masterIP"]) + m.public_host_name(master_public_url) + host_groups["masters"] = HostGroup([m]) + + if nodes_config: + node_hosts = list() + for node in nodes_config.get("items", []): + if node["kind"] != "Node": + continue + + n = Host("nodes") + + address = "" + internal_hostname = "" + for item in node["status"].get("addresses", []): + if not address and item['type'] in ['InternalIP', 'LegacyHostIP']: + address = item['address'] + + if item['type'] == 'Hostname': + internal_hostname = item['address'] + + n.address(address) + n.host_name(internal_hostname) + node_hosts.append(n) + + host_groups["nodes"] = HostGroup(node_hosts) + + if etcd_config: + etcd_hosts = list() + for url in etcd_config.get("urls", []): + e = Host("etcd") + e.host_name(url) + etcd_hosts.append(e) + + host_groups["etcd"] = HostGroup(etcd_hosts) + + # open new inventory file for writing + if USE_STDOUT: + inv_file_obj = sys.stdout + else: + try: + inv_file_obj = open(INVENTORY_FULL_PATH, 'w+') + except IOError as err: + print "Unable to create or open generated inventory file: {}".format(err) + exit(1) + + inv_file_obj.write("[OSEv3:children]\n") + for group in host_groups: + inv_file_obj.write("{}\n".format(group)) + inv_file_obj.write("\n") + + inv_file_obj.write("[OSEv3:vars]\n") + if ansible_ssh_user: + inv_file_obj.write("ansible_ssh_user={}\n".format(ansible_ssh_user)) + if ansible_become_user: + inv_file_obj.write("ansible_become_user={}\n".format(ansible_become_user)) + inv_file_obj.write("ansible_become=yes\n") + + if openshift_uninstall_images: + inv_file_obj.write("openshift_uninstall_images={}\n".format(str(openshift_uninstall_images))) + if openshift_deployment_type: + inv_file_obj.write("openshift_deployment_type={}\n".format(openshift_deployment_type)) + if openshift_install_examples: + inv_file_obj.write("openshift_install_examples={}\n".format(str(openshift_install_examples))) + + if openshift_release: + inv_file_obj.write("openshift_release={}\n".format(str(openshift_release))) + if openshift_image_tag: + inv_file_obj.write("openshift_image_tag={}\n".format(str(openshift_image_tag))) + if openshift_logging_image_version: + inv_file_obj.write("openshift_logging_image_version={}\n".format(str(openshift_logging_image_version))) + if openshift_disable_check: + inv_file_obj.write("openshift_disable_check={}\n".format(str(openshift_disable_check))) + inv_file_obj.write("\n") + + inv_file_obj.write("openshift_hosted_logging_deploy={}\n".format(str(openshift_hosted_logging_deploy))) + inv_file_obj.write("\n") + + for group in host_groups: + inv_file_obj.write("[{}]\n".format(host_groups[group].get_group_name())) + inv_file_obj.write(host_groups[group].string()) + inv_file_obj.write("\n") + + inv_file_obj.close() + + +if __name__ == '__main__': + main() diff --git a/images/installer/root/usr/local/bin/run b/images/installer/root/usr/local/bin/run new file mode 100755 index 000000000..cd38a6ff0 --- /dev/null +++ b/images/installer/root/usr/local/bin/run @@ -0,0 +1,52 @@ +#!/bin/bash -e +# +# This file serves as the default command to the openshift-ansible image. +# Runs a playbook with inventory as specified by environment variables. +# +# For more information see the documentation: +# https://github.com/openshift/openshift-ansible/blob/master/README_CONTAINER_IMAGE.md + +# SOURCE and HOME DIRECTORY: /opt/app-root/src + +if [[ -z "${PLAYBOOK_FILE}" ]]; then + echo + echo "PLAYBOOK_FILE must be provided." + exec /usr/local/bin/usage +fi + +INVENTORY="$(mktemp)" +if [[ -v INVENTORY_FILE ]]; then + # Make a copy so that ALLOW_ANSIBLE_CONNECTION_LOCAL below + # does not attempt to modify the original + cp -a ${INVENTORY_FILE} ${INVENTORY} +elif [[ -v INVENTORY_DIR ]]; then + INVENTORY="$(mktemp -d)" + cp -R ${INVENTORY_DIR}/* ${INVENTORY} +elif [[ -v INVENTORY_URL ]]; then + curl -o ${INVENTORY} ${INVENTORY_URL} +elif [[ -v DYNAMIC_SCRIPT_URL ]]; then + curl -o ${INVENTORY} ${DYNAMIC_SCRIPT_URL} + chmod 755 ${INVENTORY} +elif [[ -v GENERATE_INVENTORY ]]; then + # dynamically generate inventory file using bind-mounted info + /usr/local/bin/generate ${INVENTORY} +else + echo + echo "One of INVENTORY_FILE, INVENTORY_DIR, INVENTORY_URL, GENERATE_INVENTORY, or DYNAMIC_SCRIPT_URL must be provided." + exec /usr/local/bin/usage +fi +INVENTORY_ARG="-i ${INVENTORY}" + +if [[ "$ALLOW_ANSIBLE_CONNECTION_LOCAL" = false ]]; then + sed -i s/ansible_connection=local// ${INVENTORY} +fi + +if [[ -v VAULT_PASS ]]; then + VAULT_PASS_FILE="$(mktemp)" + echo ${VAULT_PASS} > ${VAULT_PASS_FILE} + VAULT_PASS_ARG="--vault-password-file ${VAULT_PASS_FILE}" +fi + +cd ${WORK_DIR} + +exec ansible-playbook ${INVENTORY_ARG} ${VAULT_PASS_ARG} ${OPTS} ${PLAYBOOK_FILE} diff --git a/images/installer/root/usr/local/bin/run-system-container.sh b/images/installer/root/usr/local/bin/run-system-container.sh new file mode 100755 index 000000000..9ce7c7328 --- /dev/null +++ b/images/installer/root/usr/local/bin/run-system-container.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +export ANSIBLE_LOG_PATH=/var/log/ansible.log +exec ansible-playbook -i /etc/ansible/hosts ${OPTS} ${PLAYBOOK_FILE} diff --git a/images/installer/root/usr/local/bin/usage b/images/installer/root/usr/local/bin/usage new file mode 100755 index 000000000..3518d7f19 --- /dev/null +++ b/images/installer/root/usr/local/bin/usage @@ -0,0 +1,33 @@ +#!/bin/bash -e +cat <<"EOF" + +The origin-ansible image provides several options to control the behaviour of the containers. +For more details on these options see the documentation: + + https://github.com/openshift/openshift-ansible/blob/master/README_CONTAINER_IMAGE.md + +At a minimum, when running a container using this image you must provide: + +* ssh keys so that Ansible can reach your hosts. These should be mounted as a volume under + /opt/app-root/src/.ssh +* An inventory file. This can be mounted inside the container as a volume and specified with the + INVENTORY_FILE environment variable. Alternatively you can serve the inventory file from a web + server and use the INVENTORY_URL environment variable to fetch it. +* The playbook to run. This is set using the PLAYBOOK_FILE environment variable. + +Here is an example of how to run a containerized origin-ansible with +the openshift_facts playbook, which collects and displays facts about your +OpenShift environment. The inventory and ssh keys are mounted as volumes +(the latter requires setting the uid in the container and SELinux label +in the key file via :Z so they can be accessed) and the PLAYBOOK_FILE +environment variable is set to point to the playbook within the image: + +docker run -tu `id -u` \ + -v $HOME/.ssh/id_rsa:/opt/app-root/src/.ssh/id_rsa:Z,ro \ + -v /etc/ansible/hosts:/tmp/inventory:Z,ro \ + -e INVENTORY_FILE=/tmp/inventory \ + -e OPTS="-v" \ + -e PLAYBOOK_FILE=playbooks/byo/openshift_facts.yml \ + openshift/origin-ansible + +EOF diff --git a/images/installer/root/usr/local/bin/usage.ocp b/images/installer/root/usr/local/bin/usage.ocp new file mode 100755 index 000000000..50593af6e --- /dev/null +++ b/images/installer/root/usr/local/bin/usage.ocp @@ -0,0 +1,33 @@ +#!/bin/bash -e +cat <<"EOF" + +The ose-ansible image provides several options to control the behaviour of the containers. +For more details on these options see the documentation: + + https://github.com/openshift/openshift-ansible/blob/master/README_CONTAINER_IMAGE.md + +At a minimum, when running a container using this image you must provide: + +* ssh keys so that Ansible can reach your hosts. These should be mounted as a volume under + /opt/app-root/src/.ssh +* An inventory file. This can be mounted inside the container as a volume and specified with the + INVENTORY_FILE environment variable. Alternatively you can serve the inventory file from a web + server and use the INVENTORY_URL environment variable to fetch it. +* The playbook to run. This is set using the PLAYBOOK_FILE environment variable. + +Here is an example of how to run a containerized ose-ansible with +the openshift_facts playbook, which collects and displays facts about your +OpenShift environment. The inventory and ssh keys are mounted as volumes +(the latter requires setting the uid in the container and SELinux label +in the key file via :Z so they can be accessed) and the PLAYBOOK_FILE +environment variable is set to point to the playbook within the image: + +docker run -tu `id -u` \ + -v $HOME/.ssh/id_rsa:/opt/app-root/src/.ssh/id_rsa:Z,ro \ + -v /etc/ansible/hosts:/tmp/inventory:Z,ro \ + -e INVENTORY_FILE=/tmp/inventory \ + -e OPTS="-v" \ + -e PLAYBOOK_FILE=playbooks/byo/openshift_facts.yml \ + openshift3/ose-ansible + +EOF diff --git a/images/installer/root/usr/local/bin/user_setup b/images/installer/root/usr/local/bin/user_setup new file mode 100755 index 000000000..b76e60a4d --- /dev/null +++ b/images/installer/root/usr/local/bin/user_setup @@ -0,0 +1,17 @@ +#!/bin/sh +set -x + +# ensure $HOME exists and is accessible by group 0 (we don't know what the runtime UID will be) +mkdir -p ${HOME} +chown ${USER_UID}:0 ${HOME} +chmod ug+rwx ${HOME} + +# runtime user will need to be able to self-insert in /etc/passwd +chmod g+rw /etc/passwd + +# ensure that the ansible content is accessible +chmod -R g+r ${WORK_DIR} +find ${WORK_DIR} -type d -exec chmod g+x {} + + +# no need for this script to remain in the image after running +rm $0 |