diff options
167 files changed, 4820 insertions, 1415 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dafa73bad..502ef6aa5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -152,3 +152,17 @@ are run on a different Jenkins host that is not publicly accessible, however the test results are posted to S3 buckets when complete. The test output of each job is also posted to the Pull Request as comments. + +--- + +## Appendix + +### Finding unused Python code + +If you are contributing with Python code, you can use the tool +[`vulture`](https://pypi.python.org/pypi/vulture) to verify that you are not +introducing unused code by accident. + +This tool is not used in an automated form in CI nor otherwise because it may +produce both false positives and false negatives. Still, it can be helpful to +detect dead code that escapes our eyes. diff --git a/filter_plugins/oo_filters.py b/filter_plugins/oo_filters.py index c9390efe6..a833a5d93 100644 --- a/filter_plugins/oo_filters.py +++ b/filter_plugins/oo_filters.py @@ -242,6 +242,28 @@ def oo_combine_dict(data, in_joiner='=', out_joiner=' '): return out_joiner.join([in_joiner.join([k, str(v)]) for k, v in data.items()]) +def oo_dict_to_list_of_dict(data, key_title='key', value_title='value'): + """Take a dict and arrange them as a list of dicts + + Input data: + {'region': 'infra', 'test_k': 'test_v'} + + Return data: + [{'key': 'region', 'value': 'infra'}, {'key': 'test_k', 'value': 'test_v'}] + + Written for use of the oc_label module + """ + if not isinstance(data, dict): + # pylint: disable=line-too-long + raise errors.AnsibleFilterError("|failed expects first param is a dict. Got %s. Type: %s" % (str(data), str(type(data)))) + + rval = [] + for label in data.items(): + rval.append({key_title: label[0], value_title: label[1]}) + + return rval + + def oo_ami_selector(data, image_name): """ This takes a list of amis and an image name and attempts to return the latest ami. @@ -951,6 +973,7 @@ class FilterModule(object): "oo_ec2_volume_definition": oo_ec2_volume_definition, "oo_combine_key_value": oo_combine_key_value, "oo_combine_dict": oo_combine_dict, + "oo_dict_to_list_of_dict": oo_dict_to_list_of_dict, "oo_split": oo_split, "oo_filter_list": oo_filter_list, "oo_parse_heat_stack_outputs": oo_parse_heat_stack_outputs, diff --git a/openshift-ansible.spec b/openshift-ansible.spec index 0ec12daa0..c03d4eb8f 100644 --- a/openshift-ansible.spec +++ b/openshift-ansible.spec @@ -25,6 +25,7 @@ Requires: openshift-ansible-docs = %{version}-%{release} Requires: java-1.8.0-openjdk-headless Requires: httpd-tools Requires: python-ruamel-yaml +Requires: libselinux-python %description Openshift and Atomic Enterprise Ansible @@ -72,6 +73,10 @@ find -L %{buildroot}%{_datadir}/ansible/%{name}/playbooks -name filter_plugins - # openshift-ansible-roles install cp -rp roles %{buildroot}%{_datadir}/ansible/%{name}/ +# openshift_master_facts symlinks filter_plugins/oo_filters.py from ansible_plugins/filter_plugins +pushd %{buildroot}%{_datadir}/ansible/%{name}/roles/openshift_master_facts/filter_plugins +ln -sf ../../../../../ansible_plugins/filter_plugins/oo_filters.py oo_filters.py +popd # openshift-ansible-filter-plugins install cp -rp filter_plugins %{buildroot}%{_datadir}/ansible_plugins/ diff --git a/playbooks/adhoc/s3_registry/s3_registry.j2 b/playbooks/adhoc/s3_registry/s3_registry.j2 deleted file mode 100644 index 10454ad11..000000000 --- a/playbooks/adhoc/s3_registry/s3_registry.j2 +++ /dev/null @@ -1,23 +0,0 @@ -version: 0.1 -log: - level: debug -http: - addr: :5000 -storage: - cache: - layerinfo: inmemory - s3: - accesskey: {{ aws_access_key }} - secretkey: {{ aws_secret_key }} - region: {{ aws_bucket_region }} - bucket: {{ aws_bucket_name }} - encrypt: true - secure: true - v4auth: true - rootdirectory: /registry -auth: - openshift: - realm: openshift -middleware: - repository: - - name: openshift diff --git a/playbooks/adhoc/s3_registry/s3_registry.yml b/playbooks/adhoc/s3_registry/s3_registry.yml deleted file mode 100644 index 2c79a1b4d..000000000 --- a/playbooks/adhoc/s3_registry/s3_registry.yml +++ /dev/null @@ -1,78 +0,0 @@ ---- -# This playbook creates an S3 bucket named after your cluster and configures the docker-registry service to use the bucket as its backend storage. -# Usage: -# ansible-playbook s3_registry.yml -e clusterid="mycluster" -e aws_bucket="clusterid-docker" -e aws_region="us-east-1" -# -# The AWS access/secret keys should be the keys of a separate user (not your main user), containing only the necessary S3 access role. -# The 'clusterid' is the short name of your cluster. - -- hosts: tag_clusterid_{{ clusterid }}:&tag_host-type_openshift-master - remote_user: root - gather_facts: False - - vars: - aws_access_key: "{{ lookup('env', 'S3_ACCESS_KEY_ID') }}" - aws_secret_key: "{{ lookup('env', 'S3_SECRET_ACCESS_KEY') }}" - aws_bucket_name: "{{ aws_bucket | default(clusterid ~ '-docker') }}" - aws_bucket_region: "{{ aws_region | default(lookup('env', 'S3_REGION') | default('us-east-1', true)) }}" - aws_create_bucket: "{{ aws_create | default(True) }}" - aws_tmp_path: "{{ aws_tmp_pathfile | default('/root/config.yml')}}" - aws_delete_tmp_file: "{{ aws_delete_tmp | default(True) }}" - - tasks: - - - name: Check for AWS creds - fail: - msg: "Couldn't find {{ item }} creds in ENV" - when: "{{ item }} == ''" - with_items: - - aws_access_key - - aws_secret_key - - - name: Scale down registry - command: oc scale --replicas=0 dc/docker-registry - - - name: Create S3 bucket - when: aws_create_bucket | bool - local_action: - module: s3 bucket="{{ aws_bucket_name }}" mode=create - - - name: Set up registry environment variable - command: oc env dc/docker-registry REGISTRY_CONFIGURATION_PATH=/etc/registryconfig/config.yml - - - name: Generate docker registry config - template: src="s3_registry.j2" dest="/root/config.yml" owner=root mode=0600 - - - name: Determine if new secrets are needed - command: oc get secrets - register: secrets - - - name: Create registry secrets - command: oc secrets new dockerregistry /root/config.yml - when: "'dockerregistry' not in secrets.stdout" - - - name: Determine if service account contains secrets - command: oc describe serviceaccount/registry - register: serviceaccount - - - name: Add secrets to registry service account - command: oc secrets add serviceaccount/registry secrets/dockerregistry - when: "'dockerregistry' not in serviceaccount.stdout" - - - name: Determine if deployment config contains secrets - command: oc volume dc/docker-registry --list - register: dc - - - name: Add secrets to registry deployment config - command: oc volume dc/docker-registry --add --name=dockersecrets -m /etc/registryconfig --type=secret --secret-name=dockerregistry - when: "'dockersecrets' not in dc.stdout" - - - name: Wait for deployment config to take effect before scaling up - pause: seconds=30 - - - name: Scale up registry - command: oc scale --replicas=1 dc/docker-registry - - - name: Delete temporary config file - file: path={{ aws_tmp_path }} state=absent - when: aws_delete_tmp_file | bool diff --git a/playbooks/adhoc/uninstall.yml b/playbooks/adhoc/uninstall.yml index 147e84131..ffdcd0ce1 100644 --- a/playbooks/adhoc/uninstall.yml +++ b/playbooks/adhoc/uninstall.yml @@ -148,6 +148,22 @@ - vovsbr when: "{{ openshift_remove_all | default(true) | bool }}" + - shell: atomic uninstall "{{ item }}"-master-api + changed_when: False + failed_when: False + with_items: + - openshift-enterprise + - atomic-enterprise + - origin + + - shell: atomic uninstall "{{ item }}"-master-controllers + changed_when: False + failed_when: False + with_items: + - openshift-enterprise + - atomic-enterprise + - origin + - shell: atomic uninstall "{{ item }}"-master changed_when: False failed_when: False diff --git a/playbooks/byo/openshift-cluster/upgrades/docker/docker_upgrade.yml b/playbooks/byo/openshift-cluster/upgrades/docker/docker_upgrade.yml index 13e1da961..4ee6afe2a 100644 --- a/playbooks/byo/openshift-cluster/upgrades/docker/docker_upgrade.yml +++ b/playbooks/byo/openshift-cluster/upgrades/docker/docker_upgrade.yml @@ -22,16 +22,28 @@ hosts: oo_masters_to_config:oo_nodes_to_upgrade:oo_etcd_to_config serial: 1 any_errors_fatal: true + + roles: + - lib_openshift + tasks: - - name: Prepare for Node draining - command: > - {{ openshift.common.client_binary }} adm manage-node {{ openshift.node.nodename }} --schedulable=false + - name: Mark node unschedulable + oadm_manage_node: + node: "{{ openshift.node.nodename | lower }}" + schedulable: False delegate_to: "{{ groups.oo_first_master.0 }}" - when: l_docker_upgrade is defined and l_docker_upgrade | bool and inventory_hostname in groups.oo_nodes_to_upgrade + retries: 10 + delay: 5 + register: node_unschedulable + until: node_unschedulable|succeeded + when: + - l_docker_upgrade is defined + - l_docker_upgrade | bool + - inventory_hostname in groups.oo_nodes_to_upgrade - name: Drain Node for Kubelet upgrade command: > - {{ openshift.common.admin_binary }} drain {{ openshift.node.nodename }} --force --delete-local-data + {{ openshift.common.admin_binary }} drain {{ openshift.node.nodename }} --force --delete-local-data --ignore-daemonsets delegate_to: "{{ groups.oo_first_master.0 }}" when: l_docker_upgrade is defined and l_docker_upgrade | bool and inventory_hostname in groups.oo_nodes_to_upgrade @@ -39,7 +51,12 @@ when: l_docker_upgrade is defined and l_docker_upgrade | bool - name: Set node schedulability - command: > - {{ openshift.common.client_binary }} adm manage-node {{ openshift.node.nodename }} --schedulable=true + oadm_manage_node: + node: "{{ openshift.node.nodename | lower }}" + schedulable: True delegate_to: "{{ groups.oo_first_master.0 }}" - when: l_docker_upgrade is defined and l_docker_upgrade | bool and inventory_hostname in groups.oo_nodes_to_upgrade and openshift.node.schedulable | bool + retries: 10 + delay: 5 + register: node_schedulable + until: node_schedulable|succeeded + when: node_unschedulable|changed diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_3/upgrade_control_plane.yml b/playbooks/byo/openshift-cluster/upgrades/v3_3/upgrade_control_plane.yml index d791e89f6..b61d9e58a 100644 --- a/playbooks/byo/openshift-cluster/upgrades/v3_3/upgrade_control_plane.yml +++ b/playbooks/byo/openshift-cluster/upgrades/v3_3/upgrade_control_plane.yml @@ -25,6 +25,9 @@ openshift_upgrade_min: "{{ '1.2' if deployment_type == 'origin' else '3.2' }}" # Pre-upgrade +- include: ../../../../common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml + tags: + - pre_upgrade - name: Update repos on control plane hosts hosts: oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade_control_plane.yml b/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade_control_plane.yml index d1c2bd17a..7ae1b3e6e 100644 --- a/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade_control_plane.yml +++ b/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade_control_plane.yml @@ -25,6 +25,9 @@ openshift_upgrade_min: "{{ '1.3' if deployment_type == 'origin' else '3.3' }}" # Pre-upgrade +- include: ../../../../common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml + tags: + - pre_upgrade - name: Update repos on control plane hosts hosts: oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade_nodes.yml b/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade_nodes.yml index f6e66c477..ec63ea60e 100644 --- a/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade_nodes.yml +++ b/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade_nodes.yml @@ -47,7 +47,7 @@ tags: - pre_upgrade -- include: ../disable_excluder.yml +- include: ../../../../common/openshift-cluster/disable_excluder.yml tags: - pre_upgrade diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml index e55ab1b16..69cabcd33 100644 --- a/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml +++ b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml @@ -101,3 +101,5 @@ - include: ../../../../common/openshift-cluster/upgrades/upgrade_nodes.yml - include: ../../../../common/openshift-cluster/upgrades/post_control_plane.yml + +- include: ../../../../common/openshift-cluster/upgrades/v3_5/storage_upgrade.yml diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_control_plane.yml b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_control_plane.yml index e18b4280c..719057d2b 100644 --- a/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_control_plane.yml +++ b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_control_plane.yml @@ -25,6 +25,9 @@ openshift_upgrade_min: "{{ '1.4' if deployment_type == 'origin' else '3.4' }}" # Pre-upgrade +- include: ../../../../common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml + tags: + - pre_upgrade - name: Update repos on control plane hosts hosts: oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config @@ -104,3 +107,5 @@ - include: ../../../../common/openshift-cluster/upgrades/upgrade_control_plane.yml - include: ../../../../common/openshift-cluster/upgrades/post_control_plane.yml + +- include: ../../../../common/openshift-cluster/upgrades/v3_5/storage_upgrade.yml diff --git a/playbooks/byo/openshift-preflight/check.yml b/playbooks/byo/openshift-preflight/check.yml index 32673d01d..c5f05d0f0 100644 --- a/playbooks/byo/openshift-preflight/check.yml +++ b/playbooks/byo/openshift-preflight/check.yml @@ -1,31 +1,12 @@ --- - hosts: OSEv3 - roles: - - openshift_preflight/init - -- hosts: OSEv3 - name: checks that apply to all hosts - gather_facts: no - ignore_errors: yes - roles: - - openshift_preflight/common - -- hosts: masters - name: checks that apply to masters - gather_facts: no - ignore_errors: yes - roles: - - openshift_preflight/masters - -- hosts: nodes - name: checks that apply to nodes - gather_facts: no - ignore_errors: yes - roles: - - openshift_preflight/nodes - -- hosts: OSEv3 - name: verify check results - gather_facts: no - roles: - - openshift_preflight/verify_status + name: run OpenShift health checks + roles: + - openshift_health_checker + post_tasks: + # NOTE: we need to use the old "action: name" syntax until + # https://github.com/ansible/ansible/issues/20513 is fixed. + - action: openshift_health_check + args: + checks: + - '@preflight' diff --git a/roles/openshift_certificate_expiry/examples/playbooks/default.yaml b/playbooks/certificate_expiry/default.yaml index 630135cae..630135cae 100644 --- a/roles/openshift_certificate_expiry/examples/playbooks/default.yaml +++ b/playbooks/certificate_expiry/default.yaml diff --git a/roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml b/playbooks/certificate_expiry/easy-mode.yaml index d0209426f..ae41c7c14 100644 --- a/roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml +++ b/playbooks/certificate_expiry/easy-mode.yaml @@ -4,8 +4,6 @@ # # This example enables HTML and JSON reports # -# The warning window is set very large so you will almost always get results back -# # All certificates (healthy or not) are included in the results - name: Check cert expirys @@ -13,7 +11,6 @@ become: yes gather_facts: no vars: - openshift_certificate_expiry_warning_days: 1500 openshift_certificate_expiry_save_json_results: yes openshift_certificate_expiry_generate_html_report: yes openshift_certificate_expiry_show_all: yes diff --git a/roles/openshift_certificate_expiry/examples/playbooks/html_and_json_default_paths.yaml b/playbooks/certificate_expiry/html_and_json_default_paths.yaml index d80cb6ff4..d80cb6ff4 100644 --- a/roles/openshift_certificate_expiry/examples/playbooks/html_and_json_default_paths.yaml +++ b/playbooks/certificate_expiry/html_and_json_default_paths.yaml diff --git a/roles/openshift_certificate_expiry/examples/playbooks/longer-warning-period-json-results.yaml b/playbooks/certificate_expiry/longer-warning-period-json-results.yaml index 87a0f3be4..87a0f3be4 100644 --- a/roles/openshift_certificate_expiry/examples/playbooks/longer-warning-period-json-results.yaml +++ b/playbooks/certificate_expiry/longer-warning-period-json-results.yaml diff --git a/roles/openshift_certificate_expiry/examples/playbooks/longer_warning_period.yaml b/playbooks/certificate_expiry/longer_warning_period.yaml index 960457c4b..960457c4b 100644 --- a/roles/openshift_certificate_expiry/examples/playbooks/longer_warning_period.yaml +++ b/playbooks/certificate_expiry/longer_warning_period.yaml diff --git a/playbooks/certificate_expiry/roles b/playbooks/certificate_expiry/roles new file mode 120000 index 000000000..b741aa3db --- /dev/null +++ b/playbooks/certificate_expiry/roles @@ -0,0 +1 @@ +../../roles
\ No newline at end of file diff --git a/playbooks/common/openshift-cluster/redeploy-certificates/registry.yml b/playbooks/common/openshift-cluster/redeploy-certificates/registry.yml index 18b93e1d6..6771cc98d 100644 --- a/playbooks/common/openshift-cluster/redeploy-certificates/registry.yml +++ b/playbooks/common/openshift-cluster/redeploy-certificates/registry.yml @@ -2,6 +2,8 @@ - name: Update registry certificates hosts: oo_first_master vars: + roles: + - lib_openshift tasks: - name: Create temp directory for kubeconfig command: mktemp -d /tmp/openshift-ansible-XXXXXX @@ -46,12 +48,15 @@ # Replace dc/docker-registry certificate secret contents if set. - block: + - name: Load lib_openshift modules + include_role: + name: lib_openshift + - name: Retrieve registry service IP - command: > - {{ openshift.common.client_binary }} get service docker-registry - -o jsonpath='{.spec.clusterIP}' - --config={{ mktemp.stdout }}/admin.kubeconfig - -n default + oc_service: + namespace: default + name: docker-registry + state: list register: docker_registry_service_ip changed_when: false @@ -65,18 +70,22 @@ --signer-cert={{ openshift.common.config_base }}/master/ca.crt --signer-key={{ openshift.common.config_base }}/master/ca.key --signer-serial={{ openshift.common.config_base }}/master/ca.serial.txt - --hostnames="{{ docker_registry_service_ip.stdout }},docker-registry.default.svc.cluster.local,{{ docker_registry_route_hostname }}" + --hostnames="{{ docker_registry_service_ip.results.clusterip }},docker-registry.default.svc.cluster.local,{{ docker_registry_route_hostname }}" --cert={{ openshift.common.config_base }}/master/registry.crt --key={{ openshift.common.config_base }}/master/registry.key - name: Update registry certificates secret - shell: > - {{ openshift.common.client_binary }} secret new registry-certificates - {{ openshift.common.config_base }}/master/registry.crt - {{ openshift.common.config_base }}/master/registry.key - --config={{ mktemp.stdout }}/admin.kubeconfig - -n default - -o json | oc replace -f - + oc_secret: + kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig" + name: registry-certificates + namespace: default + state: present + files: + - name: registry.crt + path: "{{ openshift.common.config_base }}/master/registry.crt" + - name: registry.key + path: "{{ openshift.common.config_base }}/master/registry.key" + run_once: true when: l_docker_registry_dc.rc == 0 and 'registry-certificates' in docker_registry_secrets and 'REGISTRY_HTTP_TLS_CERTIFICATE' in docker_registry_env_vars and 'REGISTRY_HTTP_TLS_KEY' in docker_registry_env_vars - name: Redeploy docker registry diff --git a/playbooks/common/openshift-cluster/redeploy-certificates/router.yml b/playbooks/common/openshift-cluster/redeploy-certificates/router.yml index a9e9f0915..35eedd5ee 100644 --- a/playbooks/common/openshift-cluster/redeploy-certificates/router.yml +++ b/playbooks/common/openshift-cluster/redeploy-certificates/router.yml @@ -2,12 +2,13 @@ - name: Update router certificates hosts: oo_first_master vars: + roles: + - lib_openshift tasks: - name: Create temp directory for kubeconfig command: mktemp -d /tmp/openshift-ansible-XXXXXX register: mktemp changed_when: false - - name: Copy admin client config(s) command: > cp {{ openshift.common.config_base }}/master//admin.kubeconfig {{ mktemp.stdout }}/admin.kubeconfig @@ -45,10 +46,12 @@ - block: - name: Delete existing router certificate secret - command: > - {{ openshift.common.client_binary }} delete secret/router-certs - --config={{ mktemp.stdout }}/admin.kubeconfig - -n default + oc_secret: + kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig" + name: router-certs + namespace: default + state: absent + run_once: true - name: Remove router service annotations command: > diff --git a/playbooks/common/openshift-cluster/upgrades/etcd/backup.yml b/playbooks/common/openshift-cluster/upgrades/etcd/backup.yml index 45aabf3e4..7ef79afa9 100644 --- a/playbooks/common/openshift-cluster/upgrades/etcd/backup.yml +++ b/playbooks/common/openshift-cluster/upgrades/etcd/backup.yml @@ -29,12 +29,18 @@ - name: Check available disk space for etcd backup shell: df --output=avail -k {{ openshift.common.data_dir }} | tail -n 1 register: avail_disk + # AUDIT:changed_when: `false` because we are only inspecting + # state, not manipulating anything + changed_when: false # TODO: replace shell module with command and update later checks - name: Check current embedded etcd disk usage shell: du -k {{ openshift.etcd.etcd_data_dir }} | tail -n 1 | cut -f1 register: etcd_disk_usage when: embedded_etcd | bool + # AUDIT:changed_when: `false` because we are only inspecting + # state, not manipulating anything + changed_when: false - name: Abort if insufficient disk space for etcd backup fail: diff --git a/playbooks/common/openshift-cluster/upgrades/etcd/upgrade.yml b/playbooks/common/openshift-cluster/upgrades/etcd/upgrade.yml index 690858c53..a9b5b94e6 100644 --- a/playbooks/common/openshift-cluster/upgrades/etcd/upgrade.yml +++ b/playbooks/common/openshift-cluster/upgrades/etcd/upgrade.yml @@ -9,21 +9,36 @@ register: etcd_rpm_version failed_when: false when: not openshift.common.is_containerized | bool + # AUDIT:changed_when: `false` because we are only inspecting + # state, not manipulating anything + changed_when: false + - name: Record containerized etcd version command: docker exec etcd_container rpm -qa --qf '%{version}' etcd\* register: etcd_container_version failed_when: false when: openshift.common.is_containerized | bool + # AUDIT:changed_when: `false` because we are only inspecting + # state, not manipulating anything + changed_when: false + - name: Record containerized etcd version command: docker exec etcd_container rpm -qa --qf '%{version}' etcd\* register: etcd_container_version failed_when: false when: openshift.common.is_containerized | bool and not openshift.common.is_etcd_system_container | bool + # AUDIT:changed_when: `false` because we are only inspecting + # state, not manipulating anything + changed_when: false + - name: Record containerized etcd version command: runc exec etcd_container rpm -qa --qf '%{version}' etcd\* register: etcd_container_version failed_when: false when: openshift.common.is_containerized | bool and openshift.common.is_etcd_system_container | bool + # AUDIT:changed_when: `false` because we are only inspecting + # state, not manipulating anything + changed_when: false # I really dislike this copy/pasta but I wasn't able to find a way to get it to loop # through hosts, then loop through tasks only when appropriate diff --git a/playbooks/common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml b/playbooks/common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml index 37c89374c..046535680 100644 --- a/playbooks/common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml +++ b/playbooks/common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml @@ -1,20 +1,17 @@ --- - name: Filter list of nodes to be upgraded if necessary hosts: oo_first_master + + roles: + - lib_openshift + tasks: - name: Retrieve list of openshift nodes matching upgrade label - command: > - {{ openshift.common.client_binary }} - get nodes - --config={{ openshift.common.config_base }}/master/admin.kubeconfig - --selector={{ openshift_upgrade_nodes_label }} - -o jsonpath='{.items[*].metadata.name}' - register: matching_nodes - changed_when: false - when: openshift_upgrade_nodes_label is defined - - - set_fact: - nodes_to_upgrade: "{{ matching_nodes.stdout.split(' ') }}" + oc_obj: + state: list + kind: node + selector: "{{ openshift_upgrade_nodes_label }}" + register: nodes_to_upgrade when: openshift_upgrade_nodes_label is defined # We got a list of nodes with the label, now we need to match these with inventory hosts @@ -26,7 +23,9 @@ ansible_ssh_user: "{{ g_ssh_user | default(omit) }}" ansible_become: "{{ g_sudo | default(omit) }}" with_items: " {{ groups['oo_nodes_to_config'] }}" - when: openshift_upgrade_nodes_label is defined and hostvars[item].openshift.common.hostname in nodes_to_upgrade + when: + - openshift_upgrade_nodes_label is defined + - hostvars[item].openshift.common.hostname in nodes_to_upgrade.results.results[0]['items'] | map(attribute='metadata.name') | list changed_when: false # Build up the oo_nodes_to_upgrade group, use the list filtered by label if diff --git a/playbooks/common/openshift-cluster/upgrades/post_control_plane.yml b/playbooks/common/openshift-cluster/upgrades/post_control_plane.yml index 4135f7e94..01c1e0c15 100644 --- a/playbooks/common/openshift-cluster/upgrades/post_control_plane.yml +++ b/playbooks/common/openshift-cluster/upgrades/post_control_plane.yml @@ -9,77 +9,92 @@ registry_image: "{{ openshift.master.registry_url | replace( '${component}', 'docker-registry' ) | replace ( '${version}', openshift_image_tag ) }}" router_image: "{{ openshift.master.registry_url | replace( '${component}', 'haproxy-router' ) | replace ( '${version}', openshift_image_tag ) }}" oc_cmd: "{{ openshift.common.client_binary }} --config={{ openshift.common.config_base }}/master/admin.kubeconfig" - roles: - - openshift_manageiq - # Create the new templates shipped in 3.2, existing templates are left - # unmodified. This prevents the subsequent role definition for - # openshift_examples from failing when trying to replace templates that do - # not already exist. We could have potentially done a replace --force to - # create and update in one step. - - openshift_examples - - openshift_hosted_templates - # Update the existing templates - - role: openshift_examples - registry_url: "{{ openshift.master.registry_url }}" - openshift_examples_import_command: replace - - role: openshift_hosted_templates - registry_url: "{{ openshift.master.registry_url }}" - openshift_hosted_templates_import_command: replace - pre_tasks: - # TODO: remove temp_skip_router_registry_upgrade variable. This is a short term hack - # to allow ops to use this control plane upgrade, without triggering router/registry - # upgrade which has not yet been synced with their process. + pre_tasks: + - name: Load lib_openshift modules + include_role: + name: lib_openshift - name: Collect all routers - command: > - {{ oc_cmd }} get pods --all-namespaces -l 'router' -o json + oc_obj: + state: list + kind: pods + all_namespaces: True + selector: 'router' register: all_routers - failed_when: false - changed_when: false - when: temp_skip_router_registry_upgrade is not defined - - set_fact: haproxy_routers="{{ (all_routers.stdout | from_json)['items'] | oo_pods_match_component(openshift_deployment_type, 'haproxy-router') | oo_select_keys_from_list(['metadata']) }}" - when: all_routers.rc == 0 and temp_skip_router_registry_upgrade is not defined + - set_fact: haproxy_routers="{{ (all_routers.reults.results[0]['items'] | oo_pods_match_component(openshift_deployment_type, 'haproxy-router') | oo_select_keys_from_list(['metadata']) }}" + when: + - all_routers.results.returncode == 0 - set_fact: haproxy_routers=[] - when: all_routers.rc != 0 and temp_skip_router_registry_upgrade is not defined + when: + - all_routers.results.returncode != 0 - name: Update router image to current version - when: all_routers.rc == 0 and temp_skip_router_registry_upgrade is not defined + when: + - all_routers.results.returncode == 0 command: > {{ oc_cmd }} patch dc/{{ item['labels']['deploymentconfig'] }} -n {{ item['namespace'] }} -p '{"spec":{"template":{"spec":{"containers":[{"name":"router","image":"{{ router_image }}","livenessProbe":{"tcpSocket":null,"httpGet":{"path": "/healthz", "port": 1936, "host": "localhost", "scheme": "HTTP"},"initialDelaySeconds":10,"timeoutSeconds":1}}]}}}}' --api-version=v1 with_items: "{{ haproxy_routers }}" + # AUDIT:changed_when_note: `false` not being set here. What we + # need to do is check the current router image version and see if + # this task needs to be ran. - name: Check for default registry - command: > - {{ oc_cmd }} get -n default dc/docker-registry + oc_obj: + state: list + kind: dc + name: docker-registry register: _default_registry - failed_when: false - changed_when: false - when: temp_skip_router_registry_upgrade is not defined - name: Update registry image to current version - when: _default_registry.rc == 0 and temp_skip_router_registry_upgrade is not defined + when: + - _default_registry.results.results[0] != {} command: > {{ oc_cmd }} patch dc/docker-registry -n default -p '{"spec":{"template":{"spec":{"containers":[{"name":"registry","image":"{{ registry_image }}"}]}}}}' --api-version=v1 + # AUDIT:changed_when_note: `false` not being set here. What we + # need to do is check the current registry image version and see + # if this task needs to be ran. + + roles: + - openshift_manageiq + # Create the new templates shipped in 3.2, existing templates are left + # unmodified. This prevents the subsequent role definition for + # openshift_examples from failing when trying to replace templates that do + # not already exist. We could have potentially done a replace --force to + # create and update in one step. + - openshift_examples + - openshift_hosted_templates + # Update the existing templates + - role: openshift_examples + registry_url: "{{ openshift.master.registry_url }}" + openshift_examples_import_command: replace + - role: openshift_hosted_templates + registry_url: "{{ openshift.master.registry_url }}" + openshift_hosted_templates_import_command: replace # Check for warnings to be printed at the end of the upgrade: - name: Check for warnings hosts: oo_masters_to_config tasks: # Check if any masters are using pluginOrderOverride and warn if so, only for 1.3/3.3 and beyond: - - command: > - grep pluginOrderOverride {{ openshift.common.config_base }}/master/master-config.yaml + - name: grep pluginOrderOverride + command: grep pluginOrderOverride {{ openshift.common.config_base }}/master/master-config.yaml register: grep_plugin_order_override when: openshift.common.version_gte_3_3_or_1_3 | bool + changed_when: false failed_when: false + - name: Warn if pluginOrderOverride is in use in master-config.yaml - debug: msg="WARNING pluginOrderOverride is being deprecated in master-config.yaml, please see https://docs.openshift.com/enterprise/latest/architecture/additional_concepts/admission_controllers.html for more information." - when: not grep_plugin_order_override | skipped and grep_plugin_order_override.rc == 0 + debug: + msg: "WARNING pluginOrderOverride is being deprecated in master-config.yaml, please see https://docs.openshift.com/enterprise/latest/architecture/additional_concepts/admission_controllers.html for more information." + when: + - not grep_plugin_order_override | skipped + - grep_plugin_order_override.rc == 0 - include: ../reset_excluder.yml tags: diff --git a/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml b/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml index db2c27919..fd01a6625 100644 --- a/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml +++ b/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml @@ -238,47 +238,42 @@ any_errors_fatal: true pre_tasks: + - name: Load lib_openshift modules + include_role: + name: lib_openshift + # TODO: To better handle re-trying failed upgrades, it would be nice to check if the node # or docker actually needs an upgrade before proceeding. Perhaps best to save this until # we merge upgrade functionality into the base roles and a normal config.yml playbook run. - - name: Determine if node is currently scheduleable - command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.client_binary }} get node {{ openshift.node.nodename | lower }} -o json - register: node_output - delegate_to: "{{ groups.oo_first_master.0 }}" - changed_when: false - - - set_fact: - was_schedulable: "{{ 'unschedulable' not in (node_output.stdout | from_json).spec }}" - - name: Mark node unschedulable - command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.client_binary }} adm manage-node {{ openshift.node.nodename | lower }} --schedulable=false + oadm_manage_node: + node: "{{ openshift.node.nodename | lower }}" + schedulable: False delegate_to: "{{ groups.oo_first_master.0 }}" - # NOTE: There is a transient "object has been modified" error here, allow a couple - # retries for a more reliable upgrade. - register: node_unsched - until: node_unsched.rc == 0 - retries: 3 - delay: 1 + retries: 10 + delay: 5 + register: node_unschedulable + until: node_unschedulable|succeeded - name: Drain Node for Kubelet upgrade command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.admin_binary }} drain {{ openshift.node.nodename | lower }} --force --delete-local-data + {{ hostvars[groups.oo_first_master.0].openshift.common.admin_binary }} drain {{ openshift.node.nodename | lower }} --force --delete-local-data --ignore-daemonsets delegate_to: "{{ groups.oo_first_master.0 }}" roles: + - lib_openshift - openshift_facts - docker - openshift_node_upgrade post_tasks: - name: Set node schedulability - command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.client_binary }} adm manage-node {{ openshift.node.nodename | lower }} --schedulable=true + oadm_manage_node: + node: "{{ openshift.node.nodename | lower }}" + schedulable: True delegate_to: "{{ groups.oo_first_master.0 }}" - when: was_schedulable | bool - register: node_sched - until: node_sched.rc == 0 - retries: 3 - delay: 1 + retries: 10 + delay: 5 + register: node_schedulable + until: node_schedulable|succeeded + when: node_unschedulable|changed diff --git a/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml b/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml index e45b635f7..4e1838c71 100644 --- a/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml +++ b/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml @@ -7,50 +7,45 @@ any_errors_fatal: true pre_tasks: + - name: Load lib_openshift modules + include_role: + name: lib_openshift + # TODO: To better handle re-trying failed upgrades, it would be nice to check if the node # or docker actually needs an upgrade before proceeding. Perhaps best to save this until # we merge upgrade functionality into the base roles and a normal config.yml playbook run. - - name: Determine if node is currently scheduleable - command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.client_binary }} get node {{ openshift.node.nodename | lower }} -o json - register: node_output - delegate_to: "{{ groups.oo_first_master.0 }}" - changed_when: false - - - set_fact: - was_schedulable: "{{ 'unschedulable' not in (node_output.stdout | from_json).spec }}" - - name: Mark node unschedulable - command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.client_binary }} adm manage-node {{ openshift.node.nodename | lower }} --schedulable=false + oadm_manage_node: + node: "{{ openshift.node.nodename | lower }}" + schedulable: False delegate_to: "{{ groups.oo_first_master.0 }}" - # NOTE: There is a transient "object has been modified" error here, allow a couple - # retries for a more reliable upgrade. - register: node_unsched - until: node_unsched.rc == 0 - retries: 3 - delay: 1 + retries: 10 + delay: 5 + register: node_unschedulable + until: node_unschedulable|succeeded - name: Drain Node for Kubelet upgrade command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.admin_binary }} drain {{ openshift.node.nodename | lower }} --force --delete-local-data + {{ hostvars[groups.oo_first_master.0].openshift.common.admin_binary }} drain {{ openshift.node.nodename | lower }} --force --delete-local-data --ignore-daemonsets delegate_to: "{{ groups.oo_first_master.0 }}" roles: + - lib_openshift - openshift_facts - docker - openshift_node_upgrade post_tasks: - name: Set node schedulability - command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.client_binary }} adm manage-node {{ openshift.node.nodename | lower }} --schedulable=true + oadm_manage_node: + node: "{{ openshift.node.nodename | lower }}" + schedulable: True delegate_to: "{{ groups.oo_first_master.0 }}" - when: was_schedulable | bool - register: node_sched - until: node_sched.rc == 0 - retries: 3 - delay: 1 + retries: 10 + delay: 5 + register: node_schedulable + until: node_schedulable|succeeded + when: node_unschedulable|changed - include: ../reset_excluder.yml tags: diff --git a/playbooks/common/openshift-cluster/upgrades/v3_5/filter_plugins b/playbooks/common/openshift-cluster/upgrades/v3_5/filter_plugins new file mode 120000 index 000000000..7de3c1dd7 --- /dev/null +++ b/playbooks/common/openshift-cluster/upgrades/v3_5/filter_plugins @@ -0,0 +1 @@ +../../../../../filter_plugins/
\ No newline at end of file diff --git a/playbooks/common/openshift-cluster/upgrades/v3_5/roles b/playbooks/common/openshift-cluster/upgrades/v3_5/roles new file mode 120000 index 000000000..415645be6 --- /dev/null +++ b/playbooks/common/openshift-cluster/upgrades/v3_5/roles @@ -0,0 +1 @@ +../../../../../roles/
\ No newline at end of file diff --git a/playbooks/common/openshift-cluster/upgrades/v3_5/storage_upgrade.yml b/playbooks/common/openshift-cluster/upgrades/v3_5/storage_upgrade.yml new file mode 100644 index 000000000..48c69eccd --- /dev/null +++ b/playbooks/common/openshift-cluster/upgrades/v3_5/storage_upgrade.yml @@ -0,0 +1,18 @@ +--- +############################################################################### +# Post upgrade - Upgrade job storage +############################################################################### +- name: Upgrade job storage + hosts: oo_first_master + roles: + - { role: openshift_cli } + vars: + # Another spot where we assume docker is running and do not want to accidentally trigger an unsafe + # restart. + skip_docker_role: True + tasks: + - name: Upgrade job storage + command: > + {{ openshift.common.client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig + migrate storage --include=jobs --confirm + run_once: true diff --git a/playbooks/common/openshift-master/restart_hosts.yml b/playbooks/common/openshift-master/restart_hosts.yml index a9750e40f..67ba0aa2e 100644 --- a/playbooks/common/openshift-master/restart_hosts.yml +++ b/playbooks/common/openshift-master/restart_hosts.yml @@ -7,14 +7,26 @@ ignore_errors: true become: yes +# WARNING: This process is riddled with weird behavior. + +# Workaround for https://github.com/ansible/ansible/issues/21269 +- set_fact: + wait_for_host: "{{ ansible_host }}" + +# Ansible's blog documents this *without* the port, which appears to now +# just wait until the timeout value and then proceed without checking anything. +# port is now required. +# +# However neither ansible_ssh_port or ansible_port are reliably defined, likely +# only if overridden. Assume a default of 22. - name: Wait for master to restart local_action: module: wait_for - host="{{ ansible_host }}" + host="{{ wait_for_host }}" state=started delay=10 timeout=600 - port="{{ ansible_ssh_port }}" + port="{{ ansible_port | default(ansible_ssh_port | default(22,boolean=True),boolean=True) }}" become: no # Now that ssh is back up we can wait for API on the remote system, diff --git a/requirements.txt b/requirements.txt index 8f47033f8..5a6a161cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ ansible>=2.2 six pyOpenSSL PyYAML +ruamel.yaml diff --git a/roles/cockpit-ui/meta/main.yml b/roles/cockpit-ui/meta/main.yml index 6ad2e324a..4d619fff6 100644 --- a/roles/cockpit-ui/meta/main.yml +++ b/roles/cockpit-ui/meta/main.yml @@ -11,3 +11,5 @@ galaxy_info: - 7 categories: - cloud +dependencies: +- role: lib_openshift diff --git a/roles/cockpit-ui/tasks/main.yml b/roles/cockpit-ui/tasks/main.yml index f2ef4f161..8bd68787a 100644 --- a/roles/cockpit-ui/tasks/main.yml +++ b/roles/cockpit-ui/tasks/main.yml @@ -1,86 +1,58 @@ --- -- name: Create temp directory for kubeconfig - command: mktemp -d /tmp/openshift-ansible-XXXXXX - register: mktemp - changed_when: False - -- set_fact: - openshift_hosted_kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig" - -- name: Copy the admin client config(s) - command: > - cp {{ openshift_master_config_dir }}/admin.kubeconfig {{ openshift_hosted_kubeconfig }} - changed_when: False - -- name: Determine if docker-registry service exists - command: > - {{ openshift.common.client_binary }} get svc/docker-registry - --config={{ openshift_hosted_kubeconfig }} - -n default - register: check_docker_registry_exists - failed_when: false - changed_when: false - -- name: Create passthrough route for docker-registry - command: > - {{ openshift.common.client_binary }} create route passthrough - --service docker-registry - --config={{ openshift_hosted_kubeconfig }} - -n default - register: create_docker_registry_route - changed_when: "'already exists' not in create_docker_registry_route.stderr" - failed_when: "'already exists' not in create_docker_registry_route.stderr and create_docker_registry_route.rc != 0" - when: check_docker_registry_exists.rc == 0 - -- name: Create passthrough route for registry-console - command: > - {{ openshift.common.client_binary }} create route passthrough - --service registry-console - --port registry-console - --config={{ openshift_hosted_kubeconfig }} - -n default - register: create_registry_console_route - changed_when: "'already exists' not in create_registry_console_route.stderr" - failed_when: "'already exists' not in create_registry_console_route.stderr and create_registry_console_route.rc != 0" - when: check_docker_registry_exists.rc == 0 - -- name: Retrieve docker-registry route - command: > - {{ openshift.common.client_binary }} get route docker-registry - -o jsonpath='{.spec.host}' - --config={{ openshift_hosted_kubeconfig }} - -n default - register: docker_registry_route - changed_when: false - when: check_docker_registry_exists.rc == 0 - -- name: Retrieve cockpit kube url - command: > - {{ openshift.common.client_binary }} get route registry-console - -o jsonpath='https://{.spec.host}' - -n default - register: registry_console_cockpit_kube_url - changed_when: false - when: check_docker_registry_exists.rc == 0 - -# TODO: Need to fix the origin and enterprise templates so that they both respect IMAGE_PREFIX -- name: Deploy registry-console - command: > - {{ openshift.common.client_binary }} new-app --template=registry-console - {% if openshift_cockpit_deployer_prefix is defined %}-p IMAGE_PREFIX="{{ openshift_cockpit_deployer_prefix }}"{% endif %} - {% if openshift_cockpit_deployer_version is defined %}-p IMAGE_VERSION="{{ openshift_cockpit_deployer_version }}"{% endif %} - -p OPENSHIFT_OAUTH_PROVIDER_URL="{{ openshift.master.public_api_url }}" - -p REGISTRY_HOST="{{ docker_registry_route.stdout }}" - -p COCKPIT_KUBE_URL="{{ registry_console_cockpit_kube_url.stdout }}" - --config={{ openshift_hosted_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 and deploy_registry_console.rc != 0" - when: check_docker_registry_exists.rc == 0 - -- name: Delete temp directory - file: - name: "{{ mktemp.stdout }}" - state: absent - changed_when: False +- block: + - name: Create passthrough route for docker-registry + oc_route: + kubeconfig: "{{ openshift_master_config_dir }}/admin.kubeconfig" + name: docker-registry + namespace: default + service_name: docker-registry + state: present + tls_termination: passthrough + register: docker_registry_route + + - name: Create passthrough route for registry-console + oc_route: + kubeconfig: "{{ openshift_master_config_dir }}/admin.kubeconfig" + name: registry-console + namespace: default + service_name: registry-console + state: present + tls_termination: passthrough + register: registry_console_cockpit_kube + + # XXX: Required for items still using command + - name: Create temp directory for kubeconfig + command: mktemp -d /tmp/openshift-ansible-XXXXXX + register: mktemp + changed_when: False + + - set_fact: + openshift_hosted_kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig" + + - name: Copy the admin client config(s) + command: > + cp {{ openshift_master_config_dir }}/admin.kubeconfig {{ openshift_hosted_kubeconfig }} + changed_when: False + + # TODO: Need to fix the origin and enterprise templates so that they both respect IMAGE_PREFIX + - name: Deploy registry-console + command: > + {{ openshift.common.client_binary }} new-app --template=registry-console + {% if openshift_cockpit_deployer_prefix is defined %}-p IMAGE_PREFIX="{{ openshift_cockpit_deployer_prefix }}"{% endif %} + {% if openshift_cockpit_deployer_version is defined %}-p IMAGE_VERSION="{{ openshift_cockpit_deployer_version }}"{% endif %} + -p OPENSHIFT_OAUTH_PROVIDER_URL="{{ openshift.master.public_api_url }}" + -p REGISTRY_HOST="{{ docker_registry_route.results.results[0].spec.host }}" + -p COCKPIT_KUBE_URL="https://{{ registry_console_cockpit_kube.results.results[0].spec.host }}" + --config={{ openshift_hosted_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 and deploy_registry_console.rc != 0" + + - name: Delete temp directory + file: + name: "{{ mktemp.stdout }}" + state: absent + changed_when: False + # XXX: End required for items still using command + run_once: true diff --git a/roles/lib_openshift/library/oadm_manage_node.py b/roles/lib_openshift/library/oadm_manage_node.py index 8e217ac28..0ac233c72 100644 --- a/roles/lib_openshift/library/oadm_manage_node.py +++ b/roles/lib_openshift/library/oadm_manage_node.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -330,11 +335,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -374,10 +383,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -542,12 +557,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -560,11 +582,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -929,7 +957,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1006,7 +1034,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1088,7 +1121,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1296,7 +1333,7 @@ class ManageNode(OpenShiftCLI): config, verbose=False): ''' Constructor for ManageNode ''' - super(ManageNode, self).__init__(None, config.kubeconfig) + super(ManageNode, self).__init__(None, kubeconfig=config.kubeconfig, verbose=verbose) self.config = config def evacuate(self): diff --git a/roles/lib_openshift/library/oc_edit.py b/roles/lib_openshift/library/oc_edit.py index 11b87a015..1b1faf496 100644 --- a/roles/lib_openshift/library/oc_edit.py +++ b/roles/lib_openshift/library/oc_edit.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -358,11 +363,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -402,10 +411,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -570,12 +585,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -588,11 +610,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -957,7 +985,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1034,7 +1062,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1116,7 +1149,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1314,13 +1351,10 @@ class Edit(OpenShiftCLI): separator='.', verbose=False): ''' Constructor for OpenshiftOC ''' - super(Edit, self).__init__(namespace, kubeconfig) - self.namespace = namespace + super(Edit, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.kind = kind self.name = resource_name - self.kubeconfig = kubeconfig self.separator = separator - self.verbose = verbose def get(self): '''return a secret by name ''' diff --git a/roles/lib_openshift/library/oc_env.py b/roles/lib_openshift/library/oc_env.py new file mode 100644 index 000000000..7c2ccb98f --- /dev/null +++ b/roles/lib_openshift/library/oc_env.py @@ -0,0 +1,1825 @@ +#!/usr/bin/env python +# pylint: disable=missing-docstring +# flake8: noqa: T001 +# ___ ___ _ _ ___ ___ _ _____ ___ ___ +# / __| __| \| | __| _ \ /_\_ _| __| \ +# | (_ | _|| .` | _|| / / _ \| | | _|| |) | +# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____ +# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _| +# | |) | (_) | | .` | (_) || | | _|| |) | | | | +# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_| +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# -*- -*- -*- Begin included fragment: lib/import.py -*- -*- -*- +''' + OpenShiftCLI class that wraps the oc commands in a subprocess +''' +# pylint: disable=too-many-lines + +from __future__ import print_function +import atexit +import copy +import json +import os +import re +import shutil +import subprocess +import tempfile +# pylint: disable=import-error +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + +from ansible.module_utils.basic import AnsibleModule + +# -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: doc/env -*- -*- -*- + +DOCUMENTATION = ''' +--- +module: oc_env +short_description: Modify, and idempotently manage openshift environment variables on pods, deploymentconfigs, and replication controllers. +description: + - Modify openshift environment variables programmatically. +options: + state: + description: + - Supported states, present, absent, list + - present - will ensure object is created or updated to the value specified + - list - will return a list of environment variables + - absent - will remove the environment varibale from the object + required: False + default: present + choices: ["present", 'absent', 'list'] + aliases: [] + kubeconfig: + description: + - The path for the kubeconfig file to use for authentication + required: false + default: /etc/origin/master/admin.kubeconfig + aliases: [] + debug: + description: + - Turn on debug output. + required: false + default: False + aliases: [] + name: + description: + - Name of the object that is being queried. + required: false + default: None + aliases: [] + namespace: + description: + - The namespace where the object lives. + required: false + default: str + aliases: [] + kind: + description: + - The kind attribute of the object. + required: False + default: dc + choices: + - rc + - dc + - pods + aliases: [] + env_vars: + description: + - The environment variables to insert. The format is a dict of value pairs. + - e.g. {key1: value1, key2: value2}) + required: False + default: None + aliases: [] +author: +- "Kenny Woodson <kwoodson@redhat.com>" +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +- name: query a list of env vars on dc + oc_env: + kind: dc + name: myawesomedc + namespace: phpapp + +- name: Set the following variables. + oc_env: + kind: dc + name: myawesomedc + namespace: phpapp + env_vars: + SUPER_TURBO_MODE: 'true' + ENABLE_PORTS: 'false' + SERVICE_PORT: 9999 +''' + +# -*- -*- -*- End included fragment: doc/env -*- -*- -*- + +# -*- -*- -*- Begin included fragment: ../../lib_utils/src/class/yedit.py -*- -*- -*- +# noqa: E301,E302 + + +class YeditException(Exception): + ''' Exception class for Yedit ''' + pass + + +# pylint: disable=too-many-public-methods +class Yedit(object): + ''' Class to modify yaml files ''' + re_valid_key = r"(((\[-?\d+\])|([0-9a-zA-Z%s/_-]+)).?)+$" + re_key = r"(?:\[(-?\d+)\])|([0-9a-zA-Z%s/_-]+)" + com_sep = set(['.', '#', '|', ':']) + + # pylint: disable=too-many-arguments + def __init__(self, + filename=None, + content=None, + content_type='yaml', + separator='.', + backup=False): + self.content = content + self._separator = separator + self.filename = filename + self.__yaml_dict = content + self.content_type = content_type + self.backup = backup + self.load(content_type=self.content_type) + if self.__yaml_dict is None: + self.__yaml_dict = {} + + @property + def separator(self): + ''' getter method for yaml_dict ''' + return self._separator + + @separator.setter + def separator(self): + ''' getter method for yaml_dict ''' + return self._separator + + @property + def yaml_dict(self): + ''' getter method for yaml_dict ''' + return self.__yaml_dict + + @yaml_dict.setter + def yaml_dict(self, value): + ''' setter method for yaml_dict ''' + self.__yaml_dict = value + + @staticmethod + def parse_key(key, sep='.'): + '''parse the key allowing the appropriate separator''' + common_separators = list(Yedit.com_sep - set([sep])) + return re.findall(Yedit.re_key % ''.join(common_separators), key) + + @staticmethod + def valid_key(key, sep='.'): + '''validate the incoming key''' + common_separators = list(Yedit.com_sep - set([sep])) + if not re.match(Yedit.re_valid_key % ''.join(common_separators), key): + return False + + return True + + @staticmethod + def remove_entry(data, key, sep='.'): + ''' remove data at location key ''' + if key == '' and isinstance(data, dict): + data.clear() + return True + elif key == '' and isinstance(data, list): + del data[:] + return True + + if not (key and Yedit.valid_key(key, sep)) and \ + isinstance(data, (list, dict)): + return None + + key_indexes = Yedit.parse_key(key, sep) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif (arr_ind and isinstance(data, list) and + int(arr_ind) <= len(data) - 1): + data = data[int(arr_ind)] + else: + return None + + # process last index for remove + # expected list entry + if key_indexes[-1][0]: + if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: # noqa: E501 + del data[int(key_indexes[-1][0])] + return True + + # expected dict entry + elif key_indexes[-1][1]: + if isinstance(data, dict): + del data[key_indexes[-1][1]] + return True + + @staticmethod + def add_entry(data, key, item=None, sep='.'): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a#b + return c + ''' + if key == '': + pass + elif (not (key and Yedit.valid_key(key, sep)) and + isinstance(data, (list, dict))): + return None + + key_indexes = Yedit.parse_key(key, sep) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key: + if isinstance(data, dict) and dict_key in data and data[dict_key]: # noqa: E501 + data = data[dict_key] + continue + + elif data and not isinstance(data, dict): + return None + + data[dict_key] = {} + data = data[dict_key] + + elif (arr_ind and isinstance(data, list) and + int(arr_ind) <= len(data) - 1): + data = data[int(arr_ind)] + else: + return None + + if key == '': + data = item + + # process last index for add + # expected list entry + elif key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: # noqa: E501 + data[int(key_indexes[-1][0])] = item + + # expected dict entry + elif key_indexes[-1][1] and isinstance(data, dict): + data[key_indexes[-1][1]] = item + + return data + + @staticmethod + def get_entry(data, key, sep='.'): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if key == '': + pass + elif (not (key and Yedit.valid_key(key, sep)) and + isinstance(data, (list, dict))): + return None + + key_indexes = Yedit.parse_key(key, sep) + for arr_ind, dict_key in key_indexes: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif (arr_ind and isinstance(data, list) and + int(arr_ind) <= len(data) - 1): + data = data[int(arr_ind)] + else: + return None + + return data + + @staticmethod + def _write(filename, contents): + ''' Actually write the file contents to disk. This helps with mocking. ''' + + tmp_filename = filename + '.yedit' + + with open(tmp_filename, 'w') as yfd: + yfd.write(contents) + + os.rename(tmp_filename, filename) + + def write(self): + ''' write to file ''' + if not self.filename: + raise YeditException('Please specify a filename.') + + if self.backup and self.file_exists(): + shutil.copy(self.filename, self.filename + '.orig') + + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() + + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) + + return (True, self.yaml_dict) + + def read(self): + ''' read from file ''' + # check if it exists + if self.filename is None or not self.file_exists(): + return None + + contents = None + with open(self.filename) as yfd: + contents = yfd.read() + + return contents + + def file_exists(self): + ''' return whether file exists ''' + if os.path.exists(self.filename): + return True + + return False + + def load(self, content_type='yaml'): + ''' return yaml file ''' + contents = self.read() + + if not contents and not self.content: + return None + + if self.content: + if isinstance(self.content, dict): + self.yaml_dict = self.content + return self.yaml_dict + elif isinstance(self.content, str): + contents = self.content + + # check if it is yaml + try: + if content_type == 'yaml' and contents: + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() + + elif content_type == 'json' and contents: + self.yaml_dict = json.loads(contents) + except yaml.YAMLError as err: + # Error loading yaml or json + raise YeditException('Problem with loading yaml file. %s' % err) + + return self.yaml_dict + + def get(self, key): + ''' get a specified key''' + try: + entry = Yedit.get_entry(self.yaml_dict, key, self.separator) + except KeyError: + entry = None + + return entry + + def pop(self, path, key_or_item): + ''' remove a key, value pair from a dict or an item for a list''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if entry is None: + return (False, self.yaml_dict) + + if isinstance(entry, dict): + # pylint: disable=no-member,maybe-no-member + if key_or_item in entry: + entry.pop(key_or_item) + return (True, self.yaml_dict) + return (False, self.yaml_dict) + + elif isinstance(entry, list): + # pylint: disable=no-member,maybe-no-member + ind = None + try: + ind = entry.index(key_or_item) + except ValueError: + return (False, self.yaml_dict) + + entry.pop(ind) + return (True, self.yaml_dict) + + return (False, self.yaml_dict) + + def delete(self, path): + ''' remove path from a dict''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if entry is None: + return (False, self.yaml_dict) + + result = Yedit.remove_entry(self.yaml_dict, path, self.separator) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def exists(self, path, value): + ''' check if value exists at path''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if isinstance(entry, list): + if value in entry: + return True + return False + + elif isinstance(entry, dict): + if isinstance(value, dict): + rval = False + for key, val in value.items(): + if entry[key] != val: + rval = False + break + else: + rval = True + return rval + + return value in entry + + return entry == value + + def append(self, path, value): + '''append value to a list''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if entry is None: + self.put(path, []) + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + if not isinstance(entry, list): + return (False, self.yaml_dict) + + # pylint: disable=no-member,maybe-no-member + entry.append(value) + return (True, self.yaml_dict) + + # pylint: disable=too-many-arguments + def update(self, path, value, index=None, curr_value=None): + ''' put path, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if isinstance(entry, dict): + # pylint: disable=no-member,maybe-no-member + if not isinstance(value, dict): + raise YeditException('Cannot replace key, value entry in ' + + 'dict with non-dict type. value=[%s] [%s]' % (value, type(value))) # noqa: E501 + + entry.update(value) + return (True, self.yaml_dict) + + elif isinstance(entry, list): + # pylint: disable=no-member,maybe-no-member + ind = None + if curr_value: + try: + ind = entry.index(curr_value) + except ValueError: + return (False, self.yaml_dict) + + elif index is not None: + ind = index + + if ind is not None and entry[ind] != value: + entry[ind] = value + return (True, self.yaml_dict) + + # see if it exists in the list + try: + ind = entry.index(value) + except ValueError: + # doesn't exist, append it + entry.append(value) + return (True, self.yaml_dict) + + # already exists, return + if ind is not None: + return (False, self.yaml_dict) + return (False, self.yaml_dict) + + def put(self, path, value): + ''' put path, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if entry == value: + return (False, self.yaml_dict) + + # deepcopy didn't work + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + + result = Yedit.add_entry(tmp_copy, path, value, self.separator) + if not result: + return (False, self.yaml_dict) + + self.yaml_dict = tmp_copy + + return (True, self.yaml_dict) + + def create(self, path, value): + ''' create a yaml file ''' + if not self.file_exists(): + # deepcopy didn't work + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + + result = Yedit.add_entry(tmp_copy, path, value, self.separator) + if result: + self.yaml_dict = tmp_copy + return (True, self.yaml_dict) + + return (False, self.yaml_dict) + + @staticmethod + def get_curr_value(invalue, val_type): + '''return the current value''' + if invalue is None: + return None + + curr_value = invalue + if val_type == 'yaml': + curr_value = yaml.load(invalue) + elif val_type == 'json': + curr_value = json.loads(invalue) + + return curr_value + + @staticmethod + def parse_value(inc_value, vtype=''): + '''determine value type passed''' + true_bools = ['y', 'Y', 'yes', 'Yes', 'YES', 'true', 'True', 'TRUE', + 'on', 'On', 'ON', ] + false_bools = ['n', 'N', 'no', 'No', 'NO', 'false', 'False', 'FALSE', + 'off', 'Off', 'OFF'] + + # It came in as a string but you didn't specify value_type as string + # we will convert to bool if it matches any of the above cases + if isinstance(inc_value, str) and 'bool' in vtype: + if inc_value not in true_bools and inc_value not in false_bools: + raise YeditException('Not a boolean type. str=[%s] vtype=[%s]' + % (inc_value, vtype)) + elif isinstance(inc_value, bool) and 'str' in vtype: + inc_value = str(inc_value) + + # If vtype is not str then go ahead and attempt to yaml load it. + if isinstance(inc_value, str) and 'str' not in vtype: + try: + inc_value = yaml.load(inc_value) + except Exception: + raise YeditException('Could not determine type of incoming ' + + 'value. value=[%s] vtype=[%s]' + % (type(inc_value), vtype)) + + return inc_value + + # pylint: disable=too-many-return-statements,too-many-branches + @staticmethod + def run_ansible(module): + '''perform the idempotent crud operations''' + yamlfile = Yedit(filename=module.params['src'], + backup=module.params['backup'], + separator=module.params['separator']) + + if module.params['src']: + rval = yamlfile.load() + + if yamlfile.yaml_dict is None and \ + module.params['state'] != 'present': + return {'failed': True, + 'msg': 'Error opening file [%s]. Verify that the ' + + 'file exists, that it is has correct' + + ' permissions, and is valid yaml.'} + + if module.params['state'] == 'list': + if module.params['content']: + content = Yedit.parse_value(module.params['content'], + module.params['content_type']) + yamlfile.yaml_dict = content + + if module.params['key']: + rval = yamlfile.get(module.params['key']) or {} + + return {'changed': False, 'result': rval, 'state': "list"} + + elif module.params['state'] == 'absent': + if module.params['content']: + content = Yedit.parse_value(module.params['content'], + module.params['content_type']) + yamlfile.yaml_dict = content + + if module.params['update']: + rval = yamlfile.pop(module.params['key'], + module.params['value']) + else: + rval = yamlfile.delete(module.params['key']) + + if rval[0] and module.params['src']: + yamlfile.write() + + return {'changed': rval[0], 'result': rval[1], 'state': "absent"} + + elif module.params['state'] == 'present': + # check if content is different than what is in the file + if module.params['content']: + content = Yedit.parse_value(module.params['content'], + module.params['content_type']) + + # We had no edits to make and the contents are the same + if yamlfile.yaml_dict == content and \ + module.params['value'] is None: + return {'changed': False, + 'result': yamlfile.yaml_dict, + 'state': "present"} + + yamlfile.yaml_dict = content + + # we were passed a value; parse it + if module.params['value']: + value = Yedit.parse_value(module.params['value'], + module.params['value_type']) + key = module.params['key'] + if module.params['update']: + # pylint: disable=line-too-long + curr_value = Yedit.get_curr_value(Yedit.parse_value(module.params['curr_value']), # noqa: E501 + module.params['curr_value_format']) # noqa: E501 + + rval = yamlfile.update(key, value, module.params['index'], curr_value) # noqa: E501 + + elif module.params['append']: + rval = yamlfile.append(key, value) + else: + rval = yamlfile.put(key, value) + + if rval[0] and module.params['src']: + yamlfile.write() + + return {'changed': rval[0], + 'result': rval[1], 'state': "present"} + + # no edits to make + if module.params['src']: + # pylint: disable=redefined-variable-type + rval = yamlfile.write() + return {'changed': rval[0], + 'result': rval[1], + 'state': "present"} + + return {'failed': True, 'msg': 'Unkown state passed'} + +# -*- -*- -*- End included fragment: ../../lib_utils/src/class/yedit.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: lib/base.py -*- -*- -*- +# pylint: disable=too-many-lines +# noqa: E301,E302,E303,T001 + + +class OpenShiftCLIError(Exception): + '''Exception class for openshiftcli''' + pass + + +# pylint: disable=too-few-public-methods +class OpenShiftCLI(object): + ''' Class to wrap the command line tools ''' + def __init__(self, + namespace, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False, + all_namespaces=False): + ''' Constructor for OpenshiftCLI ''' + self.namespace = namespace + self.verbose = verbose + self.kubeconfig = Utils.create_tmpfile_copy(kubeconfig) + self.all_namespaces = all_namespaces + + # Pylint allows only 5 arguments to be passed. + # pylint: disable=too-many-arguments + def _replace_content(self, resource, rname, content, force=False, sep='.'): + ''' replace the current object with the content ''' + res = self._get(resource, rname) + if not res['results']: + return res + + fname = Utils.create_tmpfile(rname + '-') + + yed = Yedit(fname, res['results'][0], separator=sep) + changes = [] + for key, value in content.items(): + changes.append(yed.put(key, value)) + + if any([change[0] for change in changes]): + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self._replace(fname, force) + + return {'returncode': 0, 'updated': False} + + def _replace(self, fname, force=False): + '''replace the current object with oc replace''' + cmd = ['replace', '-f', fname] + if force: + cmd.append('--force') + return self.openshift_cmd(cmd) + + def _create_from_content(self, rname, content): + '''create a temporary file and then call oc create on it''' + fname = Utils.create_tmpfile(rname + '-') + yed = Yedit(fname, content=content) + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self._create(fname) + + def _create(self, fname): + '''call oc create on a filename''' + return self.openshift_cmd(['create', '-f', fname]) + + def _delete(self, resource, rname, selector=None): + '''call oc delete on a resource''' + cmd = ['delete', resource, rname] + if selector: + cmd.append('--selector=%s' % selector) + + return self.openshift_cmd(cmd) + + def _process(self, template_name, create=False, params=None, template_data=None): # noqa: E501 + '''process a template + + template_name: the name of the template to process + create: whether to send to oc create after processing + params: the parameters for the template + template_data: the incoming template's data; instead of a file + ''' + cmd = ['process'] + if template_data: + cmd.extend(['-f', '-']) + else: + cmd.append(template_name) + if params: + param_str = ["%s=%s" % (key, value) for key, value in params.items()] + cmd.append('-v') + cmd.extend(param_str) + + results = self.openshift_cmd(cmd, output=True, input_data=template_data) + + if results['returncode'] != 0 or not create: + return results + + fname = Utils.create_tmpfile(template_name + '-') + yed = Yedit(fname, results['results']) + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self.openshift_cmd(['create', '-f', fname]) + + def _get(self, resource, rname=None, selector=None): + '''return a resource by name ''' + cmd = ['get', resource] + if selector: + cmd.append('--selector=%s' % selector) + elif rname: + cmd.append(rname) + + cmd.extend(['-o', 'json']) + + rval = self.openshift_cmd(cmd, output=True) + + # Ensure results are retuned in an array + if 'items' in rval: + rval['results'] = rval['items'] + elif not isinstance(rval['results'], list): + rval['results'] = [rval['results']] + + return rval + + def _schedulable(self, node=None, selector=None, schedulable=True): + ''' perform oadm manage-node scheduable ''' + cmd = ['manage-node'] + if node: + cmd.extend(node) + else: + cmd.append('--selector=%s' % selector) + + cmd.append('--schedulable=%s' % schedulable) + + return self.openshift_cmd(cmd, oadm=True, output=True, output_type='raw') # noqa: E501 + + def _list_pods(self, node=None, selector=None, pod_selector=None): + ''' perform oadm list pods + + node: the node in which to list pods + selector: the label selector filter if provided + pod_selector: the pod selector filter if provided + ''' + cmd = ['manage-node'] + if node: + cmd.extend(node) + else: + cmd.append('--selector=%s' % selector) + + if pod_selector: + cmd.append('--pod-selector=%s' % pod_selector) + + cmd.extend(['--list-pods', '-o', 'json']) + + return self.openshift_cmd(cmd, oadm=True, output=True, output_type='raw') + + # pylint: disable=too-many-arguments + def _evacuate(self, node=None, selector=None, pod_selector=None, dry_run=False, grace_period=None, force=False): + ''' perform oadm manage-node evacuate ''' + cmd = ['manage-node'] + if node: + cmd.extend(node) + else: + cmd.append('--selector=%s' % selector) + + if dry_run: + cmd.append('--dry-run') + + if pod_selector: + cmd.append('--pod-selector=%s' % pod_selector) + + if grace_period: + cmd.append('--grace-period=%s' % int(grace_period)) + + if force: + cmd.append('--force') + + cmd.append('--evacuate') + + return self.openshift_cmd(cmd, oadm=True, output=True, output_type='raw') + + def _version(self): + ''' return the openshift version''' + return self.openshift_cmd(['version'], output=True, output_type='raw') + + def _import_image(self, url=None, name=None, tag=None): + ''' perform image import ''' + cmd = ['import-image'] + + image = '{0}'.format(name) + if tag: + image += ':{0}'.format(tag) + + cmd.append(image) + + if url: + cmd.append('--from={0}/{1}'.format(url, image)) + + cmd.append('-n{0}'.format(self.namespace)) + + cmd.append('--confirm') + return self.openshift_cmd(cmd) + + def _run(self, cmds, input_data): + ''' Actually executes the command. This makes mocking easier. ''' + curr_env = os.environ.copy() + curr_env.update({'KUBECONFIG': self.kubeconfig}) + proc = subprocess.Popen(cmds, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=curr_env) + + stdout, stderr = proc.communicate(input_data) + + return proc.returncode, stdout.decode(), stderr.decode() + + # pylint: disable=too-many-arguments,too-many-branches + def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): + '''Base command for oc ''' + cmds = [] + if oadm: + cmds = ['oadm'] + else: + cmds = ['oc'] + + if self.all_namespaces: + cmds.extend(['--all-namespaces']) + elif self.namespace is not None and self.namespace.lower() not in ['none', 'emtpy']: # E501 + cmds.extend(['-n', self.namespace]) + + cmds.extend(cmd) + + rval = {} + results = '' + err = None + + if self.verbose: + print(' '.join(cmds)) + + returncode, stdout, stderr = self._run(cmds, input_data) + + rval = {"returncode": returncode, + "results": results, + "cmd": ' '.join(cmds)} + + if returncode == 0: + if output: + if output_type == 'json': + try: + rval['results'] = json.loads(stdout) + except ValueError as err: + if "No JSON object could be decoded" in err.args: + err = err.args + elif output_type == 'raw': + rval['results'] = stdout + + if self.verbose: + print("STDOUT: {0}".format(stdout)) + print("STDERR: {0}".format(stderr)) + + if err: + rval.update({"err": err, + "stderr": stderr, + "stdout": stdout, + "cmd": cmds}) + + else: + rval.update({"stderr": stderr, + "stdout": stdout, + "results": {}}) + + return rval + + +class Utils(object): + ''' utilities for openshiftcli modules ''' + + @staticmethod + def _write(filename, contents): + ''' Actually write the file contents to disk. This helps with mocking. ''' + + with open(filename, 'w') as sfd: + sfd.write(contents) + + @staticmethod + def create_tmp_file_from_contents(rname, data, ftype='yaml'): + ''' create a file in tmp with name and contents''' + + tmp = Utils.create_tmpfile(prefix=rname) + + if ftype == 'yaml': + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + + elif ftype == 'json': + Utils._write(tmp, json.dumps(data)) + else: + Utils._write(tmp, data) + + # Register cleanup when module is done + atexit.register(Utils.cleanup, [tmp]) + return tmp + + @staticmethod + def create_tmpfile_copy(inc_file): + '''create a temporary copy of a file''' + tmpfile = Utils.create_tmpfile('lib_openshift-') + Utils._write(tmpfile, open(inc_file).read()) + + # Cleanup the tmpfile + atexit.register(Utils.cleanup, [tmpfile]) + + return tmpfile + + @staticmethod + def create_tmpfile(prefix='tmp'): + ''' Generates and returns a temporary file name ''' + + with tempfile.NamedTemporaryFile(prefix=prefix, delete=False) as tmp: + return tmp.name + + @staticmethod + def create_tmp_files_from_contents(content, content_type=None): + '''Turn an array of dict: filename, content into a files array''' + if not isinstance(content, list): + content = [content] + files = [] + for item in content: + path = Utils.create_tmp_file_from_contents(item['path'] + '-', + item['data'], + ftype=content_type) + files.append({'name': os.path.basename(item['path']), + 'path': path}) + return files + + @staticmethod + def cleanup(files): + '''Clean up on exit ''' + for sfile in files: + if os.path.exists(sfile): + if os.path.isdir(sfile): + shutil.rmtree(sfile) + elif os.path.isfile(sfile): + os.remove(sfile) + + @staticmethod + def exists(results, _name): + ''' Check to see if the results include the name ''' + if not results: + return False + + if Utils.find_result(results, _name): + return True + + return False + + @staticmethod + def find_result(results, _name): + ''' Find the specified result by name''' + rval = None + for result in results: + if 'metadata' in result and result['metadata']['name'] == _name: + rval = result + break + + return rval + + @staticmethod + def get_resource_file(sfile, sfile_type='yaml'): + ''' return the service file ''' + contents = None + with open(sfile) as sfd: + contents = sfd.read() + + if sfile_type == 'yaml': + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) + elif sfile_type == 'json': + contents = json.loads(contents) + + return contents + + @staticmethod + def filter_versions(stdout): + ''' filter the oc version output ''' + + version_dict = {} + version_search = ['oc', 'openshift', 'kubernetes'] + + for line in stdout.strip().split('\n'): + for term in version_search: + if not line: + continue + if line.startswith(term): + version_dict[term] = line.split()[-1] + + # horrible hack to get openshift version in Openshift 3.2 + # By default "oc version in 3.2 does not return an "openshift" version + if "openshift" not in version_dict: + version_dict["openshift"] = version_dict["oc"] + + return version_dict + + @staticmethod + def add_custom_versions(versions): + ''' create custom versions strings ''' + + versions_dict = {} + + for tech, version in versions.items(): + # clean up "-" from version + if "-" in version: + version = version.split("-")[0] + + if version.startswith('v'): + versions_dict[tech + '_numeric'] = version[1:].split('+')[0] + # "v3.3.0.33" is what we have, we want "3.3" + versions_dict[tech + '_short'] = version[1:4] + + return versions_dict + + @staticmethod + def openshift_installed(): + ''' check if openshift is installed ''' + import yum + + yum_base = yum.YumBase() + if yum_base.rpmdb.searchNevra(name='atomic-openshift'): + return True + + return False + + # Disabling too-many-branches. This is a yaml dictionary comparison function + # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements + @staticmethod + def check_def_equal(user_def, result_def, skip_keys=None, debug=False): + ''' Given a user defined definition, compare it with the results given back by our query. ''' + + # Currently these values are autogenerated and we do not need to check them + skip = ['metadata', 'status'] + if skip_keys: + skip.extend(skip_keys) + + for key, value in result_def.items(): + if key in skip: + continue + + # Both are lists + if isinstance(value, list): + if key not in user_def: + if debug: + print('User data does not have key [%s]' % key) + print('User data: %s' % user_def) + return False + + if not isinstance(user_def[key], list): + if debug: + print('user_def[key] is not a list key=[%s] user_def[key]=%s' % (key, user_def[key])) + return False + + if len(user_def[key]) != len(value): + if debug: + print("List lengths are not equal.") + print("key=[%s]: user_def[%s] != value[%s]" % (key, len(user_def[key]), len(value))) + print("user_def: %s" % user_def[key]) + print("value: %s" % value) + return False + + for values in zip(user_def[key], value): + if isinstance(values[0], dict) and isinstance(values[1], dict): + if debug: + print('sending list - list') + print(type(values[0])) + print(type(values[1])) + result = Utils.check_def_equal(values[0], values[1], skip_keys=skip_keys, debug=debug) + if not result: + print('list compare returned false') + return False + + elif value != user_def[key]: + if debug: + print('value should be identical') + print(value) + print(user_def[key]) + return False + + # recurse on a dictionary + elif isinstance(value, dict): + if key not in user_def: + if debug: + print("user_def does not have key [%s]" % key) + return False + if not isinstance(user_def[key], dict): + if debug: + print("dict returned false: not instance of dict") + return False + + # before passing ensure keys match + api_values = set(value.keys()) - set(skip) + user_values = set(user_def[key].keys()) - set(skip) + if api_values != user_values: + if debug: + print("keys are not equal in dict") + print(api_values) + print(user_values) + return False + + result = Utils.check_def_equal(user_def[key], value, skip_keys=skip_keys, debug=debug) + if not result: + if debug: + print("dict returned false") + print(result) + return False + + # Verify each key, value pair is the same + else: + if key not in user_def or value != user_def[key]: + if debug: + print("value not equal; user_def does not have key") + print(key) + print(value) + if key in user_def: + print(user_def[key]) + return False + + if debug: + print('returning true') + return True + + +class OpenShiftCLIConfig(object): + '''Generic Config''' + def __init__(self, rname, namespace, kubeconfig, options): + self.kubeconfig = kubeconfig + self.name = rname + self.namespace = namespace + self._options = options + + @property + def config_options(self): + ''' return config options ''' + return self._options + + def to_option_list(self): + '''return all options as a string''' + return self.stringify() + + def stringify(self): + ''' return the options hash as cli params in a string ''' + rval = [] + for key, data in self.config_options.items(): + if data['include'] \ + and (data['value'] or isinstance(data['value'], int)): + rval.append('--%s=%s' % (key.replace('_', '-'), data['value'])) + + return rval + + +# -*- -*- -*- End included fragment: lib/base.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: lib/deploymentconfig.py -*- -*- -*- + + +# pylint: disable=too-many-public-methods +class DeploymentConfig(Yedit): + ''' Class to wrap the oc command line tools ''' + default_deployment_config = ''' +apiVersion: v1 +kind: DeploymentConfig +metadata: + name: default_dc + namespace: default +spec: + replicas: 0 + selector: + default_dc: default_dc + strategy: + resources: {} + rollingParams: + intervalSeconds: 1 + maxSurge: 0 + maxUnavailable: 25% + timeoutSeconds: 600 + updatePercent: -25 + updatePeriodSeconds: 1 + type: Rolling + template: + metadata: + spec: + containers: + - env: + - name: default + value: default + image: default + imagePullPolicy: IfNotPresent + name: default_dc + ports: + - containerPort: 8000 + hostPort: 8000 + protocol: TCP + name: default_port + resources: {} + terminationMessagePath: /dev/termination-log + dnsPolicy: ClusterFirst + hostNetwork: true + nodeSelector: + type: compute + restartPolicy: Always + securityContext: {} + serviceAccount: default + serviceAccountName: default + terminationGracePeriodSeconds: 30 + triggers: + - type: ConfigChange +''' + + replicas_path = "spec.replicas" + env_path = "spec.template.spec.containers[0].env" + volumes_path = "spec.template.spec.volumes" + container_path = "spec.template.spec.containers" + volume_mounts_path = "spec.template.spec.containers[0].volumeMounts" + + def __init__(self, content=None): + ''' Constructor for deploymentconfig ''' + if not content: + content = DeploymentConfig.default_deployment_config + + super(DeploymentConfig, self).__init__(content=content) + + # pylint: disable=no-member + def add_env_value(self, key, value): + ''' add key, value pair to env array ''' + rval = False + env = self.get_env_vars() + if env: + env.append({'name': key, 'value': value}) + rval = True + else: + result = self.put(DeploymentConfig.env_path, {'name': key, 'value': value}) + rval = result[0] + + return rval + + def exists_env_value(self, key, value): + ''' return whether a key, value pair exists ''' + results = self.get_env_vars() + if not results: + return False + + for result in results: + if result['name'] == key and result['value'] == value: + return True + + return False + + def exists_env_key(self, key): + ''' return whether a key, value pair exists ''' + results = self.get_env_vars() + if not results: + return False + + for result in results: + if result['name'] == key: + return True + + return False + + def get_env_vars(self): + '''return a environment variables ''' + return self.get(DeploymentConfig.env_path) or [] + + def delete_env_var(self, keys): + '''delete a list of keys ''' + if not isinstance(keys, list): + keys = [keys] + + env_vars_array = self.get_env_vars() + modified = False + idx = None + for key in keys: + for env_idx, env_var in enumerate(env_vars_array): + if env_var['name'] == key: + idx = env_idx + break + + if idx: + modified = True + del env_vars_array[idx] + + if modified: + return True + + return False + + def update_env_var(self, key, value): + '''place an env in the env var list''' + + env_vars_array = self.get_env_vars() + idx = None + for env_idx, env_var in enumerate(env_vars_array): + if env_var['name'] == key: + idx = env_idx + break + + if idx: + env_vars_array[idx]['value'] = value + else: + self.add_env_value(key, value) + + return True + + def exists_volume_mount(self, volume_mount): + ''' return whether a volume mount exists ''' + exist_volume_mounts = self.get_volume_mounts() + + if not exist_volume_mounts: + return False + + volume_mount_found = False + for exist_volume_mount in exist_volume_mounts: + if exist_volume_mount['name'] == volume_mount['name']: + volume_mount_found = True + break + + return volume_mount_found + + def exists_volume(self, volume): + ''' return whether a volume exists ''' + exist_volumes = self.get_volumes() + + volume_found = False + for exist_volume in exist_volumes: + if exist_volume['name'] == volume['name']: + volume_found = True + break + + return volume_found + + def find_volume_by_name(self, volume, mounts=False): + ''' return the index of a volume ''' + volumes = [] + if mounts: + volumes = self.get_volume_mounts() + else: + volumes = self.get_volumes() + for exist_volume in volumes: + if exist_volume['name'] == volume['name']: + return exist_volume + + return None + + def get_replicas(self): + ''' return replicas setting ''' + return self.get(DeploymentConfig.replicas_path) + + def get_volume_mounts(self): + '''return volume mount information ''' + return self.get_volumes(mounts=True) + + def get_volumes(self, mounts=False): + '''return volume mount information ''' + if mounts: + return self.get(DeploymentConfig.volume_mounts_path) or [] + + return self.get(DeploymentConfig.volumes_path) or [] + + def delete_volume_by_name(self, volume): + '''delete a volume ''' + modified = False + exist_volume_mounts = self.get_volume_mounts() + exist_volumes = self.get_volumes() + del_idx = None + for idx, exist_volume in enumerate(exist_volumes): + if 'name' in exist_volume and exist_volume['name'] == volume['name']: + del_idx = idx + break + + if del_idx != None: + del exist_volumes[del_idx] + modified = True + + del_idx = None + for idx, exist_volume_mount in enumerate(exist_volume_mounts): + if 'name' in exist_volume_mount and exist_volume_mount['name'] == volume['name']: + del_idx = idx + break + + if del_idx != None: + del exist_volume_mounts[idx] + modified = True + + return modified + + def add_volume_mount(self, volume_mount): + ''' add a volume or volume mount to the proper location ''' + exist_volume_mounts = self.get_volume_mounts() + + if not exist_volume_mounts and volume_mount: + self.put(DeploymentConfig.volume_mounts_path, [volume_mount]) + else: + exist_volume_mounts.append(volume_mount) + + def add_volume(self, volume): + ''' add a volume or volume mount to the proper location ''' + exist_volumes = self.get_volumes() + if not volume: + return + + if not exist_volumes: + self.put(DeploymentConfig.volumes_path, [volume]) + else: + exist_volumes.append(volume) + + def update_replicas(self, replicas): + ''' update replicas value ''' + self.put(DeploymentConfig.replicas_path, replicas) + + def update_volume(self, volume): + '''place an env in the env var list''' + exist_volumes = self.get_volumes() + + if not volume: + return False + + # update the volume + update_idx = None + for idx, exist_vol in enumerate(exist_volumes): + if exist_vol['name'] == volume['name']: + update_idx = idx + break + + if update_idx != None: + exist_volumes[update_idx] = volume + else: + self.add_volume(volume) + + return True + + def update_volume_mount(self, volume_mount): + '''place an env in the env var list''' + modified = False + + exist_volume_mounts = self.get_volume_mounts() + + if not volume_mount: + return False + + # update the volume mount + for exist_vol_mount in exist_volume_mounts: + if exist_vol_mount['name'] == volume_mount['name']: + if 'mountPath' in exist_vol_mount and \ + str(exist_vol_mount['mountPath']) != str(volume_mount['mountPath']): + exist_vol_mount['mountPath'] = volume_mount['mountPath'] + modified = True + break + + if not modified: + self.add_volume_mount(volume_mount) + modified = True + + return modified + + def needs_update_volume(self, volume, volume_mount): + ''' verify a volume update is needed ''' + exist_volume = self.find_volume_by_name(volume) + exist_volume_mount = self.find_volume_by_name(volume, mounts=True) + results = [] + results.append(exist_volume['name'] == volume['name']) + + if 'secret' in volume: + results.append('secret' in exist_volume) + results.append(exist_volume['secret']['secretName'] == volume['secret']['secretName']) + results.append(exist_volume_mount['name'] == volume_mount['name']) + results.append(exist_volume_mount['mountPath'] == volume_mount['mountPath']) + + elif 'emptyDir' in volume: + results.append(exist_volume_mount['name'] == volume['name']) + results.append(exist_volume_mount['mountPath'] == volume_mount['mountPath']) + + elif 'persistentVolumeClaim' in volume: + pvc = 'persistentVolumeClaim' + results.append(pvc in exist_volume) + if results[-1]: + results.append(exist_volume[pvc]['claimName'] == volume[pvc]['claimName']) + + if 'claimSize' in volume[pvc]: + results.append(exist_volume[pvc]['claimSize'] == volume[pvc]['claimSize']) + + elif 'hostpath' in volume: + results.append('hostPath' in exist_volume) + results.append(exist_volume['hostPath']['path'] == volume_mount['mountPath']) + + return not all(results) + + def needs_update_replicas(self, replicas): + ''' verify whether a replica update is needed ''' + current_reps = self.get(DeploymentConfig.replicas_path) + return not current_reps == replicas + +# -*- -*- -*- End included fragment: lib/deploymentconfig.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: class/oc_env.py -*- -*- -*- + + +# pylint: disable=too-many-instance-attributes +class OCEnv(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + + container_path = {"pod": "spec.containers[0].env", + "dc": "spec.template.spec.containers[0].env", + "rc": "spec.template.spec.containers[0].env", + } + + # pylint allows 5. we need 6 + # pylint: disable=too-many-arguments + def __init__(self, + namespace, + kind, + env_vars, + resource_name=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(OCEnv, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) + self.kind = kind + self.name = resource_name + self.env_vars = env_vars + self._resource = None + + @property + def resource(self): + ''' property function for resource var''' + if not self._resource: + self.get() + return self._resource + + @resource.setter + def resource(self, data): + ''' setter function for resource var''' + self._resource = data + + def key_value_exists(self, key, value): + ''' return whether a key, value pair exists ''' + return self.resource.exists_env_value(key, value) + + def key_exists(self, key): + ''' return whether a key exists ''' + return self.resource.exists_env_key(key) + + def get(self): + '''return environment variables ''' + result = self._get(self.kind, self.name) + if result['returncode'] == 0: + if self.kind == 'dc': + self.resource = DeploymentConfig(content=result['results'][0]) + result['results'] = self.resource.get(OCEnv.container_path[self.kind]) or [] + return result + + def delete(self): + ''' delete environment variables ''' + if self.resource.delete_env_var(self.env_vars.keys()): + return self._replace_content(self.kind, self.name, self.resource.yaml_dict) + + return {'returncode': 0, 'changed': False} + + def put(self): + '''place env vars into dc ''' + for update_key, update_value in self.env_vars.items(): + self.resource.update_env_var(update_key, update_value) + + return self._replace_content(self.kind, self.name, self.resource.yaml_dict) + + # pylint: disable=too-many-return-statements + @staticmethod + def run_ansible(params, check_mode): + '''run the idempotent ansible code''' + + ocenv = OCEnv(params['namespace'], + params['kind'], + params['env_vars'], + resource_name=params['name'], + kubeconfig=params['kubeconfig'], + verbose=params['debug']) + + state = params['state'] + + api_rval = ocenv.get() + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': "list"} + + ######## + # Delete + ######## + if state == 'absent': + for key in params.get('env_vars', {}).keys(): + if ocenv.resource.exists_env_key(key): + + if check_mode: + return {'changed': False, + 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = ocenv.delete() + + return {'changed': True, 'state': 'absent'} + + return {'changed': False, 'state': 'absent'} + + if state == 'present': + ######## + # Create + ######## + for key, value in params.get('env_vars', {}).items(): + if not ocenv.key_value_exists(key, value): + + if check_mode: + return {'changed': False, + 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = ocenv.put() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = ocenv.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval['results'], 'state': 'present'} + + return {'changed': False, 'results': api_rval['results'], 'state': 'present'} + + + return {'failed': True, + 'changed': False, + 'msg': 'Unknown state passed. %s' % state} + +# -*- -*- -*- End included fragment: class/oc_env.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: ansible/oc_env.py -*- -*- -*- + +def main(): + ''' + ansible oc module for environment variables + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + kind=dict(default='rc', choices=['dc', 'rc', 'pods'], type='str'), + namespace=dict(default='default', type='str'), + name=dict(default=None, required=True, type='str'), + env_vars=dict(default=None, type='dict'), + ), + mutually_exclusive=[["content", "files"]], + + supports_check_mode=True, + ) + results = OCEnv.run_ansible(module.params, module.check_mode) + + if 'failed' in results: + module.fail_json(**results) + + module.exit_json(**results) + + +if __name__ == '__main__': + main() + +# -*- -*- -*- End included fragment: ansible/oc_env.py -*- -*- -*- diff --git a/roles/lib_openshift/library/oc_label.py b/roles/lib_openshift/library/oc_label.py index f67eb2552..0db8585a4 100644 --- a/roles/lib_openshift/library/oc_label.py +++ b/roles/lib_openshift/library/oc_label.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -334,11 +339,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -378,10 +387,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -546,12 +561,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -564,11 +586,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -933,7 +961,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1010,7 +1038,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1092,7 +1125,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1294,11 +1331,9 @@ class OCLabel(OpenShiftCLI): selector=None, verbose=False): ''' Constructor for OCLabel ''' - super(OCLabel, self).__init__(namespace, kubeconfig) + super(OCLabel, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.name = name - self.namespace = namespace self.kind = kind - self.kubeconfig = kubeconfig self.labels = labels self._curr_labels = None self.selector = selector diff --git a/roles/lib_openshift/library/oc_obj.py b/roles/lib_openshift/library/oc_obj.py index e4b8ac26c..6d0b391b9 100644 --- a/roles/lib_openshift/library/oc_obj.py +++ b/roles/lib_openshift/library/oc_obj.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -337,11 +342,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -381,10 +390,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -549,12 +564,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -567,11 +589,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -936,7 +964,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1013,7 +1041,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1095,7 +1128,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1296,14 +1333,11 @@ class OCObject(OpenShiftCLI): verbose=False, all_namespaces=False): ''' Constructor for OpenshiftOC ''' - super(OCObject, self).__init__(namespace, kubeconfig, + super(OCObject, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose, all_namespaces=all_namespaces) self.kind = kind - self.namespace = namespace self.name = rname self.selector = selector - self.kubeconfig = kubeconfig - self.verbose = verbose def get(self): '''return a kind by name ''' diff --git a/roles/lib_openshift/library/oc_process.py b/roles/lib_openshift/library/oc_process.py index 702cb02d4..a1ee79f6e 100644 --- a/roles/lib_openshift/library/oc_process.py +++ b/roles/lib_openshift/library/oc_process.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -326,11 +331,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -370,10 +379,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -538,12 +553,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -556,11 +578,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -925,7 +953,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1002,7 +1030,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1084,7 +1117,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1286,14 +1323,11 @@ class OCProcess(OpenShiftCLI): tdata=None, verbose=False): ''' Constructor for OpenshiftOC ''' - super(OCProcess, self).__init__(namespace, kubeconfig) - self.namespace = namespace + super(OCProcess, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.name = tname self.data = tdata self.params = params self.create = create - self.kubeconfig = kubeconfig - self.verbose = verbose self._template = None @property diff --git a/roles/lib_openshift/library/oc_route.py b/roles/lib_openshift/library/oc_route.py index 982a43ba3..4b5c4460c 100644 --- a/roles/lib_openshift/library/oc_route.py +++ b/roles/lib_openshift/library/oc_route.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -368,11 +373,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -412,10 +421,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -580,12 +595,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -598,11 +620,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -967,7 +995,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1044,7 +1072,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1126,7 +1159,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1461,9 +1498,8 @@ class OCRoute(OpenShiftCLI): config, verbose=False): ''' Constructor for OCVolume ''' - super(OCRoute, self).__init__(config.namespace, config.kubeconfig) + super(OCRoute, self).__init__(config.namespace, kubeconfig=config.kubeconfig, verbose=verbose) self.config = config - self.namespace = config.namespace self._route = None @property @@ -1512,7 +1548,7 @@ class OCRoute(OpenShiftCLI): def needs_update(self): ''' verify an update is needed ''' skip = [] - return not Utils.check_def_equal(self.config.data, self.route.yaml_dict, skip_keys=skip, debug=True) + return not Utils.check_def_equal(self.config.data, self.route.yaml_dict, skip_keys=skip, debug=self.verbose) @staticmethod def get_cert_data(path, content): diff --git a/roles/lib_openshift/library/oc_scale.py b/roles/lib_openshift/library/oc_scale.py index 48a629b5e..a37b2aba0 100644 --- a/roles/lib_openshift/library/oc_scale.py +++ b/roles/lib_openshift/library/oc_scale.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -312,11 +317,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -356,10 +365,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -524,12 +539,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -542,11 +564,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -911,7 +939,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -988,7 +1016,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1070,7 +1103,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1629,13 +1666,10 @@ class OCScale(OpenShiftCLI): kubeconfig='/etc/origin/master/admin.kubeconfig', verbose=False): ''' Constructor for OCScale ''' - super(OCScale, self).__init__(namespace, kubeconfig) + super(OCScale, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.kind = kind self.replicas = replicas self.name = resource_name - self.namespace = namespace - self.kubeconfig = kubeconfig - self.verbose = verbose self._resource = None @property diff --git a/roles/lib_openshift/library/oc_secret.py b/roles/lib_openshift/library/oc_secret.py index 526474f17..f3bb3c413 100644 --- a/roles/lib_openshift/library/oc_secret.py +++ b/roles/lib_openshift/library/oc_secret.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -358,11 +363,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -402,10 +411,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -570,12 +585,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -588,11 +610,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -957,7 +985,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1034,7 +1062,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1116,7 +1149,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1418,12 +1455,9 @@ class OCSecret(OpenShiftCLI): kubeconfig='/etc/origin/master/admin.kubeconfig', verbose=False): ''' Constructor for OpenshiftOC ''' - super(OCSecret, self).__init__(namespace, kubeconfig) - self.namespace = namespace + super(OCSecret, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.name = secret_name - self.kubeconfig = kubeconfig self.decode = decode - self.verbose = verbose def get(self): '''return a secret by name ''' diff --git a/roles/lib_openshift/library/oc_service.py b/roles/lib_openshift/library/oc_service.py index a9baef765..319ec4bd7 100644 --- a/roles/lib_openshift/library/oc_service.py +++ b/roles/lib_openshift/library/oc_service.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -364,11 +369,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -408,10 +417,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -576,12 +591,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -594,11 +616,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -963,7 +991,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -1040,7 +1068,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1122,7 +1155,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) diff --git a/roles/lib_openshift/library/oc_serviceaccount.py b/roles/lib_openshift/library/oc_serviceaccount.py index cd0847963..0d1705414 100644 --- a/roles/lib_openshift/library/oc_serviceaccount.py +++ b/roles/lib_openshift/library/oc_serviceaccount.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -310,11 +315,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -354,10 +363,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -522,12 +537,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -540,11 +562,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -909,7 +937,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -986,7 +1014,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1068,7 +1101,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1396,9 +1433,8 @@ class OCServiceAccount(OpenShiftCLI): config, verbose=False): ''' Constructor for OCVolume ''' - super(OCServiceAccount, self).__init__(config.namespace, config.kubeconfig) + super(OCServiceAccount, self).__init__(config.namespace, kubeconfig=config.kubeconfig, verbose=verbose) self.config = config - self.namespace = config.namespace self.service_account = None def exists(self): diff --git a/roles/lib_openshift/library/oc_serviceaccount_secret.py b/roles/lib_openshift/library/oc_serviceaccount_secret.py index e22ccbfc2..5f07528a0 100644 --- a/roles/lib_openshift/library/oc_serviceaccount_secret.py +++ b/roles/lib_openshift/library/oc_serviceaccount_secret.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -310,11 +315,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -354,10 +363,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -522,12 +537,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -540,11 +562,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -909,7 +937,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -986,7 +1014,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1068,7 +1101,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) @@ -1391,7 +1428,7 @@ class OCServiceAccountSecret(OpenShiftCLI): kind = 'sa' def __init__(self, config, verbose=False): ''' Constructor for OpenshiftOC ''' - super(OCServiceAccountSecret, self).__init__(config.namespace, config.kubeconfig) + super(OCServiceAccountSecret, self).__init__(config.namespace, kubeconfig=config.kubeconfig, verbose=verbose) self.config = config self.verbose = verbose self._service_account = None diff --git a/roles/lib_openshift/library/oc_version.py b/roles/lib_openshift/library/oc_version.py index e44375ffa..9b660e1d3 100644 --- a/roles/lib_openshift/library/oc_version.py +++ b/roles/lib_openshift/library/oc_version.py @@ -33,6 +33,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -40,7 +41,11 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -282,11 +287,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -326,10 +335,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -494,12 +509,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -512,11 +534,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy @@ -881,7 +909,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -958,7 +986,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -1040,7 +1073,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) diff --git a/roles/lib_openshift/src/ansible/oc_env.py b/roles/lib_openshift/src/ansible/oc_env.py new file mode 100644 index 000000000..e49295873 --- /dev/null +++ b/roles/lib_openshift/src/ansible/oc_env.py @@ -0,0 +1,33 @@ +# pylint: skip-file +# flake8: noqa + +def main(): + ''' + ansible oc module for environment variables + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + kind=dict(default='rc', choices=['dc', 'rc', 'pods'], type='str'), + namespace=dict(default='default', type='str'), + name=dict(default=None, required=True, type='str'), + env_vars=dict(default=None, type='dict'), + ), + mutually_exclusive=[["content", "files"]], + + supports_check_mode=True, + ) + results = OCEnv.run_ansible(module.params, module.check_mode) + + if 'failed' in results: + module.fail_json(**results) + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/roles/lib_openshift/src/class/oadm_manage_node.py b/roles/lib_openshift/src/class/oadm_manage_node.py index 61b6a5ebe..c07320477 100644 --- a/roles/lib_openshift/src/class/oadm_manage_node.py +++ b/roles/lib_openshift/src/class/oadm_manage_node.py @@ -23,7 +23,7 @@ class ManageNode(OpenShiftCLI): config, verbose=False): ''' Constructor for ManageNode ''' - super(ManageNode, self).__init__(None, config.kubeconfig) + super(ManageNode, self).__init__(None, kubeconfig=config.kubeconfig, verbose=verbose) self.config = config def evacuate(self): diff --git a/roles/lib_openshift/src/class/oc_edit.py b/roles/lib_openshift/src/class/oc_edit.py index 0734e2085..629e5a007 100644 --- a/roles/lib_openshift/src/class/oc_edit.py +++ b/roles/lib_openshift/src/class/oc_edit.py @@ -13,13 +13,10 @@ class Edit(OpenShiftCLI): separator='.', verbose=False): ''' Constructor for OpenshiftOC ''' - super(Edit, self).__init__(namespace, kubeconfig) - self.namespace = namespace + super(Edit, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.kind = kind self.name = resource_name - self.kubeconfig = kubeconfig self.separator = separator - self.verbose = verbose def get(self): '''return a secret by name ''' diff --git a/roles/lib_openshift/src/class/oc_env.py b/roles/lib_openshift/src/class/oc_env.py new file mode 100644 index 000000000..748b46cb5 --- /dev/null +++ b/roles/lib_openshift/src/class/oc_env.py @@ -0,0 +1,142 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-instance-attributes +class OCEnv(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + + container_path = {"pod": "spec.containers[0].env", + "dc": "spec.template.spec.containers[0].env", + "rc": "spec.template.spec.containers[0].env", + } + + # pylint allows 5. we need 6 + # pylint: disable=too-many-arguments + def __init__(self, + namespace, + kind, + env_vars, + resource_name=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(OCEnv, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) + self.kind = kind + self.name = resource_name + self.env_vars = env_vars + self._resource = None + + @property + def resource(self): + ''' property function for resource var''' + if not self._resource: + self.get() + return self._resource + + @resource.setter + def resource(self, data): + ''' setter function for resource var''' + self._resource = data + + def key_value_exists(self, key, value): + ''' return whether a key, value pair exists ''' + return self.resource.exists_env_value(key, value) + + def key_exists(self, key): + ''' return whether a key exists ''' + return self.resource.exists_env_key(key) + + def get(self): + '''return environment variables ''' + result = self._get(self.kind, self.name) + if result['returncode'] == 0: + if self.kind == 'dc': + self.resource = DeploymentConfig(content=result['results'][0]) + result['results'] = self.resource.get(OCEnv.container_path[self.kind]) or [] + return result + + def delete(self): + ''' delete environment variables ''' + if self.resource.delete_env_var(self.env_vars.keys()): + return self._replace_content(self.kind, self.name, self.resource.yaml_dict) + + return {'returncode': 0, 'changed': False} + + def put(self): + '''place env vars into dc ''' + for update_key, update_value in self.env_vars.items(): + self.resource.update_env_var(update_key, update_value) + + return self._replace_content(self.kind, self.name, self.resource.yaml_dict) + + # pylint: disable=too-many-return-statements + @staticmethod + def run_ansible(params, check_mode): + '''run the idempotent ansible code''' + + ocenv = OCEnv(params['namespace'], + params['kind'], + params['env_vars'], + resource_name=params['name'], + kubeconfig=params['kubeconfig'], + verbose=params['debug']) + + state = params['state'] + + api_rval = ocenv.get() + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': "list"} + + ######## + # Delete + ######## + if state == 'absent': + for key in params.get('env_vars', {}).keys(): + if ocenv.resource.exists_env_key(key): + + if check_mode: + return {'changed': False, + 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = ocenv.delete() + + return {'changed': True, 'state': 'absent'} + + return {'changed': False, 'state': 'absent'} + + if state == 'present': + ######## + # Create + ######## + for key, value in params.get('env_vars', {}).items(): + if not ocenv.key_value_exists(key, value): + + if check_mode: + return {'changed': False, + 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = ocenv.put() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = ocenv.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval['results'], 'state': 'present'} + + return {'changed': False, 'results': api_rval['results'], 'state': 'present'} + + + return {'failed': True, + 'changed': False, + 'msg': 'Unknown state passed. %s' % state} diff --git a/roles/lib_openshift/src/class/oc_label.py b/roles/lib_openshift/src/class/oc_label.py index 8e1ba9ceb..bd312c170 100644 --- a/roles/lib_openshift/src/class/oc_label.py +++ b/roles/lib_openshift/src/class/oc_label.py @@ -17,11 +17,9 @@ class OCLabel(OpenShiftCLI): selector=None, verbose=False): ''' Constructor for OCLabel ''' - super(OCLabel, self).__init__(namespace, kubeconfig) + super(OCLabel, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.name = name - self.namespace = namespace self.kind = kind - self.kubeconfig = kubeconfig self.labels = labels self._curr_labels = None self.selector = selector diff --git a/roles/lib_openshift/src/class/oc_obj.py b/roles/lib_openshift/src/class/oc_obj.py index 2ec20e72c..21129a50c 100644 --- a/roles/lib_openshift/src/class/oc_obj.py +++ b/roles/lib_openshift/src/class/oc_obj.py @@ -16,14 +16,11 @@ class OCObject(OpenShiftCLI): verbose=False, all_namespaces=False): ''' Constructor for OpenshiftOC ''' - super(OCObject, self).__init__(namespace, kubeconfig, + super(OCObject, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose, all_namespaces=all_namespaces) self.kind = kind - self.namespace = namespace self.name = rname self.selector = selector - self.kubeconfig = kubeconfig - self.verbose = verbose def get(self): '''return a kind by name ''' diff --git a/roles/lib_openshift/src/class/oc_process.py b/roles/lib_openshift/src/class/oc_process.py index 80d81448d..9d29938aa 100644 --- a/roles/lib_openshift/src/class/oc_process.py +++ b/roles/lib_openshift/src/class/oc_process.py @@ -17,14 +17,11 @@ class OCProcess(OpenShiftCLI): tdata=None, verbose=False): ''' Constructor for OpenshiftOC ''' - super(OCProcess, self).__init__(namespace, kubeconfig) - self.namespace = namespace + super(OCProcess, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.name = tname self.data = tdata self.params = params self.create = create - self.kubeconfig = kubeconfig - self.verbose = verbose self._template = None @property diff --git a/roles/lib_openshift/src/class/oc_route.py b/roles/lib_openshift/src/class/oc_route.py index 42388ad0b..448457292 100644 --- a/roles/lib_openshift/src/class/oc_route.py +++ b/roles/lib_openshift/src/class/oc_route.py @@ -11,9 +11,8 @@ class OCRoute(OpenShiftCLI): config, verbose=False): ''' Constructor for OCVolume ''' - super(OCRoute, self).__init__(config.namespace, config.kubeconfig) + super(OCRoute, self).__init__(config.namespace, kubeconfig=config.kubeconfig, verbose=verbose) self.config = config - self.namespace = config.namespace self._route = None @property @@ -62,7 +61,7 @@ class OCRoute(OpenShiftCLI): def needs_update(self): ''' verify an update is needed ''' skip = [] - return not Utils.check_def_equal(self.config.data, self.route.yaml_dict, skip_keys=skip, debug=True) + return not Utils.check_def_equal(self.config.data, self.route.yaml_dict, skip_keys=skip, debug=self.verbose) @staticmethod def get_cert_data(path, content): diff --git a/roles/lib_openshift/src/class/oc_scale.py b/roles/lib_openshift/src/class/oc_scale.py index 16255688b..6c3ceb8cf 100644 --- a/roles/lib_openshift/src/class/oc_scale.py +++ b/roles/lib_openshift/src/class/oc_scale.py @@ -15,13 +15,10 @@ class OCScale(OpenShiftCLI): kubeconfig='/etc/origin/master/admin.kubeconfig', verbose=False): ''' Constructor for OCScale ''' - super(OCScale, self).__init__(namespace, kubeconfig) + super(OCScale, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.kind = kind self.replicas = replicas self.name = resource_name - self.namespace = namespace - self.kubeconfig = kubeconfig - self.verbose = verbose self._resource = None @property diff --git a/roles/lib_openshift/src/class/oc_secret.py b/roles/lib_openshift/src/class/oc_secret.py index e99999c37..5eac27572 100644 --- a/roles/lib_openshift/src/class/oc_secret.py +++ b/roles/lib_openshift/src/class/oc_secret.py @@ -17,12 +17,9 @@ class OCSecret(OpenShiftCLI): kubeconfig='/etc/origin/master/admin.kubeconfig', verbose=False): ''' Constructor for OpenshiftOC ''' - super(OCSecret, self).__init__(namespace, kubeconfig) - self.namespace = namespace + super(OCSecret, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) self.name = secret_name - self.kubeconfig = kubeconfig self.decode = decode - self.verbose = verbose def get(self): '''return a secret by name ''' diff --git a/roles/lib_openshift/src/class/oc_serviceaccount.py b/roles/lib_openshift/src/class/oc_serviceaccount.py index 47c7b5c94..d6777afc1 100644 --- a/roles/lib_openshift/src/class/oc_serviceaccount.py +++ b/roles/lib_openshift/src/class/oc_serviceaccount.py @@ -12,9 +12,8 @@ class OCServiceAccount(OpenShiftCLI): config, verbose=False): ''' Constructor for OCVolume ''' - super(OCServiceAccount, self).__init__(config.namespace, config.kubeconfig) + super(OCServiceAccount, self).__init__(config.namespace, kubeconfig=config.kubeconfig, verbose=verbose) self.config = config - self.namespace = config.namespace self.service_account = None def exists(self): diff --git a/roles/lib_openshift/src/class/oc_serviceaccount_secret.py b/roles/lib_openshift/src/class/oc_serviceaccount_secret.py index 750a74d33..4f1c8c926 100644 --- a/roles/lib_openshift/src/class/oc_serviceaccount_secret.py +++ b/roles/lib_openshift/src/class/oc_serviceaccount_secret.py @@ -7,7 +7,7 @@ class OCServiceAccountSecret(OpenShiftCLI): kind = 'sa' def __init__(self, config, verbose=False): ''' Constructor for OpenshiftOC ''' - super(OCServiceAccountSecret, self).__init__(config.namespace, config.kubeconfig) + super(OCServiceAccountSecret, self).__init__(config.namespace, kubeconfig=config.kubeconfig, verbose=verbose) self.config = config self.verbose = verbose self._service_account = None diff --git a/roles/lib_openshift/src/doc/env b/roles/lib_openshift/src/doc/env new file mode 100644 index 000000000..36edcd211 --- /dev/null +++ b/roles/lib_openshift/src/doc/env @@ -0,0 +1,83 @@ +# flake8: noqa +# pylint: skip-file + +DOCUMENTATION = ''' +--- +module: oc_env +short_description: Modify, and idempotently manage openshift environment variables on pods, deploymentconfigs, and replication controllers. +description: + - Modify openshift environment variables programmatically. +options: + state: + description: + - Supported states, present, absent, list + - present - will ensure object is created or updated to the value specified + - list - will return a list of environment variables + - absent - will remove the environment varibale from the object + required: False + default: present + choices: ["present", 'absent', 'list'] + aliases: [] + kubeconfig: + description: + - The path for the kubeconfig file to use for authentication + required: false + default: /etc/origin/master/admin.kubeconfig + aliases: [] + debug: + description: + - Turn on debug output. + required: false + default: False + aliases: [] + name: + description: + - Name of the object that is being queried. + required: false + default: None + aliases: [] + namespace: + description: + - The namespace where the object lives. + required: false + default: str + aliases: [] + kind: + description: + - The kind attribute of the object. + required: False + default: dc + choices: + - rc + - dc + - pods + aliases: [] + env_vars: + description: + - The environment variables to insert. The format is a dict of value pairs. + - e.g. {key1: value1, key2: value2}) + required: False + default: None + aliases: [] +author: +- "Kenny Woodson <kwoodson@redhat.com>" +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +- name: query a list of env vars on dc + oc_env: + kind: dc + name: myawesomedc + namespace: phpapp + +- name: Set the following variables. + oc_env: + kind: dc + name: myawesomedc + namespace: phpapp + env_vars: + SUPER_TURBO_MODE: 'true' + ENABLE_PORTS: 'false' + SERVICE_PORT: 9999 +''' diff --git a/roles/lib_openshift/src/lib/base.py b/roles/lib_openshift/src/lib/base.py index a895b40b3..9cad5e667 100644 --- a/roles/lib_openshift/src/lib/base.py +++ b/roles/lib_openshift/src/lib/base.py @@ -218,7 +218,7 @@ class OpenShiftCLI(object): stdout, stderr = proc.communicate(input_data) - return proc.returncode, stdout, stderr + return proc.returncode, stdout.decode(), stderr.decode() # pylint: disable=too-many-arguments,too-many-branches def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): @@ -295,7 +295,12 @@ class Utils(object): tmp = Utils.create_tmpfile(prefix=rname) if ftype == 'yaml': - Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + elif ftype == 'json': Utils._write(tmp, json.dumps(data)) else: @@ -377,7 +382,11 @@ class Utils(object): contents = sfd.read() if sfile_type == 'yaml': - contents = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) elif sfile_type == 'json': contents = json.loads(contents) diff --git a/roles/lib_openshift/src/lib/import.py b/roles/lib_openshift/src/lib/import.py index 6344c1a54..a79297898 100644 --- a/roles/lib_openshift/src/lib/import.py +++ b/roles/lib_openshift/src/lib/import.py @@ -7,6 +7,7 @@ from __future__ import print_function import atexit +import copy import json import os import re @@ -14,5 +15,9 @@ import shutil import subprocess import tempfile # pylint: disable=import-error -import ruamel.yaml as yaml +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + from ansible.module_utils.basic import AnsibleModule diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml index e9056655d..091aaef2e 100644 --- a/roles/lib_openshift/src/sources.yml +++ b/roles/lib_openshift/src/sources.yml @@ -19,6 +19,17 @@ oc_edit.py: - class/oc_edit.py - ansible/oc_edit.py +oc_env.py: +- doc/generated +- doc/license +- lib/import.py +- doc/env +- ../../lib_utils/src/class/yedit.py +- lib/base.py +- lib/deploymentconfig.py +- class/oc_env.py +- ansible/oc_env.py + oc_label.py: - doc/generated - doc/license diff --git a/roles/lib_openshift/src/test/integration/oc_env.yml b/roles/lib_openshift/src/test/integration/oc_env.yml new file mode 100755 index 000000000..cbb97ed46 --- /dev/null +++ b/roles/lib_openshift/src/test/integration/oc_env.yml @@ -0,0 +1,75 @@ +#!/usr/bin/ansible-playbook --module-path=../../../library/ +# ./oc_env.yml -e "cli_master_test=$OPENSHIFT_MASTER +--- +- hosts: "{{ cli_master_test }}" + gather_facts: no + user: root + vars: + my_env_var: + SOMEKEY: SOMEVALUE + + check_env_var: + name: DEFAULT_CERTIFICATE_DIR + value: /etc/pki/tls/private + + tasks: + - name: list environment variables from router dc + oc_env: + state: list + name: router + namespace: default + kind: dc + register: envout + - debug: var=envout + + - assert: + that: + - "'{{ check_env_var.name }}' == '{{ envout.results[0].name }}'" + - "{{ envout.results|length }} > 0" + msg: "Did not find environment variables." + + - name: list environment variables from router dc + oc_env: + state: present + name: router + namespace: default + kind: dc + env_vars: "{{ my_env_var }}" + register: envout + - debug: var=envout + + - assert: + that: + - "'SOMEKEY' == '{{ envout.results[-1].name }}'" + - "'SOMEVALUE' == '{{ envout.results[-1].value }}'" + msg: "Did not find updated environment variables." + + - name: remove environment variables from router dc + oc_env: + state: absent + name: router + namespace: default + kind: dc + env_vars: "{{ my_env_var }}" + register: envout + - debug: var=envout + + - assert: + that: + - envout.changed == True + msg: "state: Absent failed." + + - name: list environment variables from router dc + oc_env: + state: list + name: router + namespace: default + kind: dc + register: envout + - debug: var=envout + + - assert: + that: + - "'SOMEKEY' != '{{ envout.results[-1].name }}'" + - "'SOMEVALUE' != '{{ envout.results[-1].value }}'" + msg: "Did find updated environment variables." diff --git a/roles/lib_openshift/src/test/unit/oadm_manage_node.py b/roles/lib_openshift/src/test/unit/test_oadm_manage_node.py index b0786dfac..b0786dfac 100755..100644 --- a/roles/lib_openshift/src/test/unit/oadm_manage_node.py +++ b/roles/lib_openshift/src/test/unit/test_oadm_manage_node.py diff --git a/roles/lib_openshift/src/test/unit/test_oc_env.py b/roles/lib_openshift/src/test/unit/test_oc_env.py new file mode 100644 index 000000000..15bd7e464 --- /dev/null +++ b/roles/lib_openshift/src/test/unit/test_oc_env.py @@ -0,0 +1,444 @@ +#!/usr/bin/env python2 +''' + Unit tests for oc_env +''' +# To run: +# ./oc_env.py +# +# . +# Ran 1 test in 0.002s +# +# OK + +import os +import sys +import unittest +import mock + +# Removing invalid variable names for tests so that I can +# keep them brief +# pylint: disable=invalid-name,no-name-in-module +# Disable import-error b/c our libraries aren't loaded in jenkins +# pylint: disable=import-error,wrong-import-position +# place class in our python path +module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library') # noqa: E501 +sys.path.insert(0, module_path) +from oc_env import OCEnv # noqa: E402 + + +class OCEnvTest(unittest.TestCase): + ''' + Test class for OCEnv + ''' + + def setUp(self): + ''' setup method will create a file and set to known configuration ''' + pass + + @mock.patch('oc_env.Utils.create_tmpfile_copy') + @mock.patch('oc_env.OCEnv._run') + def test_listing_all_env_vars(self, mock_cmd, mock_tmpfile_copy): + ''' Testing listing all environment variables from a dc''' + + # Arrange + + # run_ansible input parameters + params = { + 'state': 'list', + 'namespace': 'default', + 'name': 'router', + 'kind': 'dc', + 'env_vars': None, + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False, + } + + dc_results = '''{ + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": "2017-02-02T15:58:49Z", + "generation": 8, + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default", + "resourceVersion": "513678" + }, + "spec": { + "replicas": 2, + "selector": { + "router": "router" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "router": "router" + } + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DEFAULT_CERTIFICATE_DIR", + "value": "/etc/pki/tls/private" + }, + { + "name": "DEFAULT_CERTIFICATE_PATH", + "value": "/etc/pki/tls/private/tls.crt" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HOSTNAME" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_INSECURE", + "value": "false" + } + ], + "name": "router" + } + ] + } + }, + "test": false, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + }''' + + # Return values of our mocked function call. These get returned once per call. + mock_cmd.side_effect = [ + (0, dc_results, ''), # First call to the mock + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mock_adminkubeconfig', + ] + + # Act + results = OCEnv.run_ansible(params, False) + + # Assert + self.assertFalse(results['changed']) + for env_var in results['results']: + if env_var == {'name': 'DEFAULT_CERTIFICATE_DIR', 'value': '/etc/pki/tls/private'}: + break + else: + self.fail('Did not find environment variables in results.') + self.assertEqual(results['state'], 'list') + + # Making sure our mocks were called as we expected + mock_cmd.assert_has_calls([ + mock.call(['oc', '-n', 'default', 'get', 'dc', 'router', '-o', 'json'], None), + ]) + + @mock.patch('oc_env.Utils.create_tmpfile_copy') + @mock.patch('oc_env.OCEnv._run') + def test_adding_env_vars(self, mock_cmd, mock_tmpfile_copy): + ''' Test add environment variables to a dc''' + + # Arrange + + # run_ansible input parameters + params = { + 'state': 'present', + 'namespace': 'default', + 'name': 'router', + 'kind': 'dc', + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False, + 'env_vars': {'SOMEKEY': 'SOMEVALUE'}, + } + + dc_results = '''{ + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": "2017-02-02T15:58:49Z", + "generation": 8, + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default", + "resourceVersion": "513678" + }, + "spec": { + "replicas": 2, + "selector": { + "router": "router" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "router": "router" + } + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DEFAULT_CERTIFICATE_DIR", + "value": "/etc/pki/tls/private" + }, + { + "name": "DEFAULT_CERTIFICATE_PATH", + "value": "/etc/pki/tls/private/tls.crt" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HOSTNAME" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_INSECURE", + "value": "false" + } + ], + "name": "router" + } + ] + } + }, + "test": false, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + }''' + + dc_results_after = '''{ + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": "2017-02-02T15:58:49Z", + "generation": 8, + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default", + "resourceVersion": "513678" + }, + "spec": { + "replicas": 2, + "selector": { + "router": "router" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "router": "router" + } + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DEFAULT_CERTIFICATE_DIR", + "value": "/etc/pki/tls/private" + }, + { + "name": "DEFAULT_CERTIFICATE_PATH", + "value": "/etc/pki/tls/private/tls.crt" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HOSTNAME" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_INSECURE", + "value": "false" + }, + { + "name": "SOMEKEY", + "value": "SOMEVALUE" + } + ], + "name": "router" + } + ] + } + }, + "test": false, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + }''' + + # Return values of our mocked function call. These get returned once per call. + mock_cmd.side_effect = [ + (0, dc_results, ''), + (0, dc_results, ''), + (0, dc_results_after, ''), + (0, dc_results_after, ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mock_adminkubeconfig', + ] + + # Act + results = OCEnv.run_ansible(params, False) + + # Assert + self.assertTrue(results['changed']) + for env_var in results['results']: + if env_var == {'name': 'SOMEKEY', 'value': 'SOMEVALUE'}: + break + else: + self.fail('Did not find environment variables in results.') + self.assertEqual(results['state'], 'present') + + # Making sure our mocks were called as we expected + mock_cmd.assert_has_calls([ + mock.call(['oc', '-n', 'default', 'get', 'dc', 'router', '-o', 'json'], None), + ]) + + @mock.patch('oc_env.Utils.create_tmpfile_copy') + @mock.patch('oc_env.OCEnv._run') + def test_removing_env_vars(self, mock_cmd, mock_tmpfile_copy): + ''' Test add environment variables to a dc''' + + # Arrange + + # run_ansible input parameters + params = { + 'state': 'absent', + 'namespace': 'default', + 'name': 'router', + 'kind': 'dc', + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False, + 'env_vars': {'SOMEKEY': 'SOMEVALUE'}, + } + + dc_results_before = '''{ + "apiVersion": "v1", + "kind": "DeploymentConfig", + "metadata": { + "creationTimestamp": "2017-02-02T15:58:49Z", + "generation": 8, + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default", + "resourceVersion": "513678" + }, + "spec": { + "replicas": 2, + "selector": { + "router": "router" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "router": "router" + } + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DEFAULT_CERTIFICATE_DIR", + "value": "/etc/pki/tls/private" + }, + { + "name": "DEFAULT_CERTIFICATE_PATH", + "value": "/etc/pki/tls/private/tls.crt" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HOSTNAME" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_HTTP_VSERVER" + }, + { + "name": "ROUTER_EXTERNAL_HOST_INSECURE", + "value": "false" + }, + { + "name": "SOMEKEY", + "value": "SOMEVALUE" + } + ], + "name": "router" + } + ] + } + }, + "test": false, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + }''' + + # Return values of our mocked function call. These get returned once per call. + mock_cmd.side_effect = [ + (0, dc_results_before, ''), + (0, dc_results_before, ''), + (0, '', ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mock_adminkubeconfig', + ] + + # Act + results = OCEnv.run_ansible(params, False) + + # Assert + self.assertTrue(results['changed']) + self.assertEqual(results['state'], 'absent') + + # Making sure our mocks were called as we expected + mock_cmd.assert_has_calls([ + mock.call(['oc', '-n', 'default', 'get', 'dc', 'router', '-o', 'json'], None), + ]) + + def tearDown(self): + '''TearDown method''' + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/roles/lib_openshift/src/test/unit/oc_label.py b/roles/lib_openshift/src/test/unit/test_oc_label.py index 3176987b0..3176987b0 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_label.py +++ b/roles/lib_openshift/src/test/unit/test_oc_label.py diff --git a/roles/lib_openshift/src/test/unit/oc_process.py b/roles/lib_openshift/src/test/unit/test_oc_process.py index 450ff7071..450ff7071 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_process.py +++ b/roles/lib_openshift/src/test/unit/test_oc_process.py diff --git a/roles/lib_openshift/src/test/unit/oc_route.py b/roles/lib_openshift/src/test/unit/test_oc_route.py index fcfa88cbf..361b61f4b 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_route.py +++ b/roles/lib_openshift/src/test/unit/test_oc_route.py @@ -35,8 +35,9 @@ class OCRouteTest(unittest.TestCase): ''' setup method will create a file and set to known configuration ''' pass + @mock.patch('oc_route.Utils.create_tmpfile_copy') @mock.patch('oc_route.OCRoute._run') - def test_list_route(self, mock_cmd): + def test_list_route(self, mock_cmd, mock_tmpfile_copy): ''' Testing getting a route ''' # Arrange @@ -114,6 +115,10 @@ class OCRouteTest(unittest.TestCase): (0, route_result, ''), ] + mock_tmpfile_copy.side_effect = [ + '/tmp/mock.kubeconfig', + ] + # Act results = OCRoute.run_ansible(params, False) @@ -127,11 +132,11 @@ class OCRouteTest(unittest.TestCase): mock.call(['oc', '-n', 'default', 'get', 'route', 'test', '-o', 'json'], None), ]) + @mock.patch('oc_route.Utils.create_tmpfile_copy') @mock.patch('oc_route.Yedit._write') @mock.patch('oc_route.OCRoute._run') - def test_create_route(self, mock_cmd, mock_write): + def test_create_route(self, mock_cmd, mock_write, mock_tmpfile_copy): ''' Testing getting a route ''' - # Arrange # run_ansible input parameters @@ -230,6 +235,10 @@ metadata: (0, route_result, ''), ] + mock_tmpfile_copy.side_effect = [ + '/tmp/mock.kubeconfig', + ] + mock_write.assert_has_calls = [ # First call to mock mock.call('/tmp/test', test_route) @@ -246,7 +255,8 @@ metadata: # Making sure our mock was called as we expected mock_cmd.assert_has_calls([ mock.call(['oc', '-n', 'default', 'get', 'route', 'test', '-o', 'json'], None), - mock.call(['oc', '-n', 'default', 'create', '-f', '/tmp/test'], None), + mock.call(['oc', '-n', 'default', 'create', '-f', mock.ANY], None), + mock.call(['oc', '-n', 'default', 'get', 'route', 'test', '-o', 'json'], None), ]) def tearDown(self): diff --git a/roles/lib_openshift/src/test/unit/oc_scale.py b/roles/lib_openshift/src/test/unit/test_oc_scale.py index f15eb164d..f15eb164d 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_scale.py +++ b/roles/lib_openshift/src/test/unit/test_oc_scale.py diff --git a/roles/lib_openshift/src/test/unit/oc_secret.py b/roles/lib_openshift/src/test/unit/test_oc_secret.py index 645aac82b..645aac82b 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_secret.py +++ b/roles/lib_openshift/src/test/unit/test_oc_secret.py diff --git a/roles/lib_openshift/src/test/unit/oc_service.py b/roles/lib_openshift/src/test/unit/test_oc_service.py index 4a845e9f3..4a845e9f3 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_service.py +++ b/roles/lib_openshift/src/test/unit/test_oc_service.py diff --git a/roles/lib_openshift/src/test/unit/oc_serviceaccount.py b/roles/lib_openshift/src/test/unit/test_oc_serviceaccount.py index 256b569eb..256b569eb 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_serviceaccount.py +++ b/roles/lib_openshift/src/test/unit/test_oc_serviceaccount.py diff --git a/roles/lib_openshift/src/test/unit/oc_serviceaccount_secret.py b/roles/lib_openshift/src/test/unit/test_oc_serviceaccount_secret.py index 4d555d412..213c581aa 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_serviceaccount_secret.py +++ b/roles/lib_openshift/src/test/unit/test_oc_serviceaccount_secret.py @@ -25,6 +25,12 @@ module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), sys.path.insert(0, module_path) from oc_serviceaccount_secret import OCServiceAccountSecret # noqa: E402 +try: + import ruamel.yaml as yaml # noqa: EF401 + YAML_TYPE = 'ruamel' +except ImportError: + YAML_TYPE = 'pyyaml' + class OCServiceAccountSecretTest(unittest.TestCase): ''' @@ -54,8 +60,13 @@ class OCServiceAccountSecretTest(unittest.TestCase): } oc_get_sa_before = '''{ - "kind": "ServiceAccount", "apiVersion": "v1", + "imagePullSecrets": [ + { + "name": "builder-dockercfg-rsrua" + } + ], + "kind": "ServiceAccount", "metadata": { "name": "builder", "namespace": "default", @@ -72,18 +83,18 @@ class OCServiceAccountSecretTest(unittest.TestCase): "name": "builder-token-akqxi" } - ], - "imagePullSecrets": [ - { - "name": "builder-dockercfg-rsrua" - } ] } ''' oc_get_sa_after = '''{ - "kind": "ServiceAccount", "apiVersion": "v1", + "imagePullSecrets": [ + { + "name": "builder-dockercfg-rsrua" + } + ], + "kind": "ServiceAccount", "metadata": { "name": "builder", "namespace": "default", @@ -103,16 +114,10 @@ class OCServiceAccountSecretTest(unittest.TestCase): "name": "newsecret" } - ], - "imagePullSecrets": [ - { - "name": "builder-dockercfg-rsrua" - } ] } ''' - - builder_yaml_file = '''\ + builder_ryaml_file = '''\ secrets: - name: builder-dockercfg-rsrua - name: builder-token-akqxi @@ -130,6 +135,24 @@ metadata: uid: cf47bca7-ebc4-11e6-b041-0ed9df7abc38 ''' + builder_pyyaml_file = '''\ +apiVersion: v1 +imagePullSecrets: +- name: builder-dockercfg-rsrua +kind: ServiceAccount +metadata: + creationTimestamp: '2017-02-05T17:02:00Z' + name: builder + namespace: default + resourceVersion: '302879' + selfLink: /api/v1/namespaces/default/serviceaccounts/builder + uid: cf47bca7-ebc4-11e6-b041-0ed9df7abc38 +secrets: +- name: builder-dockercfg-rsrua +- name: builder-token-akqxi +- name: newsecret +''' + # Return values of our mocked function call. These get returned once per call. mock_cmd.side_effect = [ (0, oc_get_sa_before, ''), # First call to the mock @@ -158,8 +181,12 @@ metadata: mock.call(['oc', '-n', 'default', 'get', 'sa', 'builder', '-o', 'json'], None) ]) + yaml_file = builder_pyyaml_file + + if YAML_TYPE == 'ruamel': + yaml_file = builder_ryaml_file mock_write.assert_has_calls([ - mock.call(mock.ANY, builder_yaml_file) + mock.call(mock.ANY, yaml_file) ]) @mock.patch('oc_serviceaccount_secret.Utils.create_tmpfile_copy') @@ -181,8 +208,13 @@ metadata: } oc_get_sa_before = '''{ - "kind": "ServiceAccount", "apiVersion": "v1", + "imagePullSecrets": [ + { + "name": "builder-dockercfg-rsrua" + } + ], + "kind": "ServiceAccount", "metadata": { "name": "builder", "namespace": "default", @@ -202,16 +234,11 @@ metadata: "name": "newsecret" } - ], - "imagePullSecrets": [ - { - "name": "builder-dockercfg-rsrua" - } ] } ''' - builder_yaml_file = '''\ + builder_ryaml_file = '''\ secrets: - name: builder-dockercfg-rsrua - name: builder-token-akqxi @@ -228,6 +255,23 @@ metadata: uid: cf47bca7-ebc4-11e6-b041-0ed9df7abc38 ''' + builder_pyyaml_file = '''\ +apiVersion: v1 +imagePullSecrets: +- name: builder-dockercfg-rsrua +kind: ServiceAccount +metadata: + creationTimestamp: '2017-02-05T17:02:00Z' + name: builder + namespace: default + resourceVersion: '302879' + selfLink: /api/v1/namespaces/default/serviceaccounts/builder + uid: cf47bca7-ebc4-11e6-b041-0ed9df7abc38 +secrets: +- name: builder-dockercfg-rsrua +- name: builder-token-akqxi +''' + # Return values of our mocked function call. These get returned once per call. mock_cmd.side_effect = [ (0, oc_get_sa_before, ''), # First call to the mock @@ -254,8 +298,12 @@ metadata: mock.call(['oc', '-n', 'default', 'replace', '-f', mock.ANY], None), ]) + yaml_file = builder_pyyaml_file + + if YAML_TYPE == 'ruamel': + yaml_file = builder_ryaml_file mock_write.assert_has_calls([ - mock.call(mock.ANY, builder_yaml_file) + mock.call(mock.ANY, yaml_file) ]) def tearDown(self): diff --git a/roles/lib_openshift/src/test/unit/oc_version.py b/roles/lib_openshift/src/test/unit/test_oc_version.py index 67dea415b..67dea415b 100755..100644 --- a/roles/lib_openshift/src/test/unit/oc_version.py +++ b/roles/lib_openshift/src/test/unit/test_oc_version.py diff --git a/roles/lib_openshift/tasks/main.yml b/roles/lib_openshift/tasks/main.yml index 2980c8a8d..77366c65e 100644 --- a/roles/lib_openshift/tasks/main.yml +++ b/roles/lib_openshift/tasks/main.yml @@ -1,5 +1,11 @@ --- +- name: lib_openshift detect ostree + stat: + path: /run/ostree-booted + register: ostree_booted + - name: lib_openshift ensure python-ruamel-yaml package is on target package: name: python-ruamel-yaml state: present + when: not ostree_booted.stat.exists diff --git a/roles/lib_utils/library/repoquery.py b/roles/lib_utils/library/repoquery.py index 7f0105290..f31c8911b 100644 --- a/roles/lib_utils/library/repoquery.py +++ b/roles/lib_utils/library/repoquery.py @@ -29,13 +29,18 @@ # pylint: disable=wrong-import-order,wrong-import-position,unused-import from __future__ import print_function # noqa: F401 +import copy # noqa: F401 import json # noqa: F401 import os # noqa: F401 import re # noqa: F401 -# pylint: disable=import-error -import ruamel.yaml as yaml # noqa: F401 import shutil # noqa: F401 +# pylint: disable=import-error +try: + import ruamel.yaml as yaml # noqa: F401 +except ImportError: + import yaml # noqa: F401 + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -472,7 +477,7 @@ class Repoquery(RepoqueryCLI): ''' Gather and present the versions of each package ''' versions_dict = {} - versions_dict['available_versions_full'] = formatted_versions.keys() + versions_dict['available_versions_full'] = list(formatted_versions.keys()) # set the match version, if called if self.match_version: diff --git a/roles/lib_utils/library/yedit.py b/roles/lib_utils/library/yedit.py index 1c74b4d3f..a358e980e 100644 --- a/roles/lib_utils/library/yedit.py +++ b/roles/lib_utils/library/yedit.py @@ -29,13 +29,18 @@ # pylint: disable=wrong-import-order,wrong-import-position,unused-import from __future__ import print_function # noqa: F401 +import copy # noqa: F401 import json # noqa: F401 import os # noqa: F401 import re # noqa: F401 -# pylint: disable=import-error -import ruamel.yaml as yaml # noqa: F401 import shutil # noqa: F401 +# pylint: disable=import-error +try: + import ruamel.yaml as yaml # noqa: F401 +except ImportError: + import yaml # noqa: F401 + from ansible.module_utils.basic import AnsibleModule # -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- @@ -375,11 +380,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -419,10 +428,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -587,12 +602,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -605,11 +627,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy diff --git a/roles/lib_utils/src/class/yedit.py b/roles/lib_utils/src/class/yedit.py index 8542fe5c7..5275e84eb 100644 --- a/roles/lib_utils/src/class/yedit.py +++ b/roles/lib_utils/src/class/yedit.py @@ -194,11 +194,15 @@ class Yedit(object): if self.backup and self.file_exists(): shutil.copy(self.filename, self.filename + '.orig') - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - self.yaml_dict.fa.set_block_style() + if hasattr(yaml, 'RoundTripDumper'): + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + self.yaml_dict.fa.set_block_style() - Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + # pylint: disable=no-member + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + else: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) return (True, self.yaml_dict) @@ -238,10 +242,16 @@ class Yedit(object): # check if it is yaml try: if content_type == 'yaml' and contents: - self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader) + else: + self.yaml_dict = yaml.safe_load(contents) + # pylint: disable=no-member if hasattr(self.yaml_dict, 'fa'): self.yaml_dict.fa.set_block_style() + elif content_type == 'json' and contents: self.yaml_dict = json.loads(contents) except yaml.YAMLError as err: @@ -406,12 +416,19 @@ class Yedit(object): return (False, self.yaml_dict) # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, - default_flow_style=False), - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if not result: return (False, self.yaml_dict) @@ -424,11 +441,17 @@ class Yedit(object): ''' create a yaml file ''' if not self.file_exists(): # deepcopy didn't work - tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 - yaml.RoundTripLoader) - # pylint: disable=no-member - if hasattr(self.yaml_dict, 'fa'): - tmp_copy.fa.set_block_style() + if hasattr(yaml, 'round_trip_dump'): + # pylint: disable=no-member + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501 + yaml.RoundTripLoader) + + # pylint: disable=no-member + if hasattr(self.yaml_dict, 'fa'): + tmp_copy.fa.set_block_style() + else: + tmp_copy = copy.deepcopy(self.yaml_dict) + result = Yedit.add_entry(tmp_copy, path, value, self.separator) if result: self.yaml_dict = tmp_copy diff --git a/roles/lib_utils/src/lib/import.py b/roles/lib_utils/src/lib/import.py index d892353a1..ed57a68c9 100644 --- a/roles/lib_utils/src/lib/import.py +++ b/roles/lib_utils/src/lib/import.py @@ -4,11 +4,16 @@ # pylint: disable=wrong-import-order,wrong-import-position,unused-import from __future__ import print_function # noqa: F401 +import copy # noqa: F401 import json # noqa: F401 import os # noqa: F401 import re # noqa: F401 -# pylint: disable=import-error -import ruamel.yaml as yaml # noqa: F401 import shutil # noqa: F401 +# pylint: disable=import-error +try: + import ruamel.yaml as yaml # noqa: F401 +except ImportError: + import yaml # noqa: F401 + from ansible.module_utils.basic import AnsibleModule diff --git a/roles/lib_utils/src/test/unit/repoquery.py b/roles/lib_utils/src/test/unit/test_repoquery.py index c487ab254..c487ab254 100755..100644 --- a/roles/lib_utils/src/test/unit/repoquery.py +++ b/roles/lib_utils/src/test/unit/test_repoquery.py diff --git a/roles/lib_utils/src/test/unit/yedit_test.py b/roles/lib_utils/src/test/unit/test_yedit.py index 2793c5c1a..ed07ac96e 100755..100644 --- a/roles/lib_utils/src/test/unit/yedit_test.py +++ b/roles/lib_utils/src/test/unit/test_yedit.py @@ -256,7 +256,7 @@ class YeditTest(unittest.TestCase): def test_pop_list_item_2(self): '''test dict value with none value''' - z = range(10) + z = list(range(10)) yed = Yedit(content=z, separator=':') yed.pop('', 5) z.pop(5) diff --git a/roles/lib_utils/tasks/main.yml b/roles/lib_utils/tasks/main.yml index 8a350da88..32ab9e0c6 100644 --- a/roles/lib_utils/tasks/main.yml +++ b/roles/lib_utils/tasks/main.yml @@ -1,5 +1,11 @@ --- +- name: lib_utils detect ostree + stat: + path: /run/ostree-booted + register: ostree_booted + - name: lib_utils ensure python-ruamel-yaml package is on target package: name: python-ruamel-yaml state: present + when: not ostree_booted.stat.exists diff --git a/roles/nuage_master/meta/main.yml b/roles/nuage_master/meta/main.yml index a8a9bd3b4..e3ed9ac71 100644 --- a/roles/nuage_master/meta/main.yml +++ b/roles/nuage_master/meta/main.yml @@ -17,6 +17,7 @@ dependencies: - role: nuage_common - role: openshift_etcd_client_certificates - role: os_firewall +- role: lib_openshift os_firewall_allow: - service: openshift-monitor port: "{{ nuage_mon_rest_server_port }}/tcp" diff --git a/roles/nuage_master/tasks/serviceaccount.yml b/roles/nuage_master/tasks/serviceaccount.yml index 41143772e..16ea08244 100644 --- a/roles/nuage_master/tasks/serviceaccount.yml +++ b/roles/nuage_master/tasks/serviceaccount.yml @@ -13,20 +13,16 @@ changed_when: false - name: Create Admin Service Account - shell: > - echo {{ nuage_service_account_config | to_json | quote }} | - {{ openshift.common.client_binary }} create - -n default - --config={{nuage_tmp_conf}} - -f - - register: osnuage_create_service_account - failed_when: "'already exists' not in osnuage_create_service_account.stderr and osnuage_create_service_account.rc != 0" - changed_when: osnuage_create_service_account.rc == 0 + oc_serviceaccount: + kubeconfig: "{{ openshift_master_config_dir }}/admin.kubeconfig" + name: nuage + namespace: default + state: present - name: Configure role/user permissions command: > {{ openshift.common.client_binary }} adm {{item}} - --config={{nuage_tmp_conf}} + --config={{ nuage_tmp_conf }} with_items: "{{nuage_tasks}}" register: osnuage_perm_task failed_when: "'the object has been modified' not in osnuage_perm_task.stderr and osnuage_perm_task.rc != 0" diff --git a/roles/nuage_master/vars/main.yaml b/roles/nuage_master/vars/main.yaml index dba399a03..651d5775c 100644 --- a/roles/nuage_master/vars/main.yaml +++ b/roles/nuage_master/vars/main.yaml @@ -22,11 +22,5 @@ nuage_mon_rest_server_host: "{{ openshift.master.cluster_hostname | default(open nuage_master_crt_dir: /usr/share/nuage-openshift-monitor nuage_service_account: system:serviceaccount:default:nuage -nuage_service_account_config: - apiVersion: v1 - kind: ServiceAccount - metadata: - name: nuage - nuage_tasks: - policy add-cluster-role-to-user cluster-reader {{ nuage_service_account }} diff --git a/roles/nuage_node/tasks/iptables.yml b/roles/nuage_node/tasks/iptables.yml index 8e2c29620..847c8395d 100644 --- a/roles/nuage_node/tasks/iptables.yml +++ b/roles/nuage_node/tasks/iptables.yml @@ -2,7 +2,7 @@ - name: IPtables | Get iptables rules command: iptables -L --wait register: iptablesrules - always_run: yes + check_mode: no - name: Allow traffic from overlay to underlay command: /sbin/iptables --wait -I FORWARD 1 -s {{ hostvars[groups.oo_first_master.0].openshift.master.sdn_cluster_network_cidr }} -j ACCEPT -m comment --comment "nuage-overlay-underlay" diff --git a/roles/openshift_certificate_expiry/README.md b/roles/openshift_certificate_expiry/README.md index 327cc004b..df43c3770 100644 --- a/roles/openshift_certificate_expiry/README.md +++ b/roles/openshift_certificate_expiry/README.md @@ -51,11 +51,11 @@ How to use the Certificate Expiration Checking Role. Run one of the example playbooks using an inventory file representative of your existing cluster. Some example playbooks are -included in this repo, or you can read on below after this example to +included in this role, or you can read on below after this example to craft you own. ``` -$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml +$ ansible-playbook -v -i HOSTS playbooks/certificate_expiry/easy-mode.yaml ``` Using the `easy-mode.yaml` playbook will produce: @@ -65,16 +65,19 @@ Using the `easy-mode.yaml` playbook will produce: * A stylized HTML report in `/tmp/` +> **Note:** If you are running from an RPM install use +> `/usr/share/ansible/openshift-ansible/playbooks/certificate_expiry/easy-mode.yaml` +> instead + ## More Example Playbooks > **Note:** These Playbooks are available to run directly out of the -> [examples/playbooks/](examples/playbooks/) directory. +> [/playbooks/certificate_expiry/](../../playbooks/certificate_expiry/) directory. This example playbook is great if you're just wanting to **try the -role out**. This playbook enables HTML and JSON reports. The warning -window is set very large so you will almost always get results back. -All certificates (healthy or not) are included in the results: +role out**. This playbook enables HTML and JSON reports. All +certificates (healthy or not) are included in the results: ```yaml --- @@ -83,7 +86,6 @@ All certificates (healthy or not) are included in the results: become: yes gather_facts: no vars: - openshift_certificate_expiry_warning_days: 1500 openshift_certificate_expiry_save_json_results: yes openshift_certificate_expiry_generate_html_report: yes openshift_certificate_expiry_show_all: yes @@ -91,11 +93,16 @@ All certificates (healthy or not) are included in the results: - role: openshift_certificate_expiry ``` +**From git:** +``` +$ ansible-playbook -v -i HOSTS playbooks/certificate_expiry/easy-mode.yaml +``` +**From openshift-ansible-playbooks rpm:** ``` -$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml +$ ansible-playbook -v -i HOSTS /usr/share/ansible/openshift-ansible/playbooks/certificate_expiry/easy-mode.yaml ``` -> [View This Playbook](examples/playbooks/easy-mode.yaml) +> [View This Playbook](../../playbooks/certificate_expiry/easy-mode.yaml) *** @@ -111,12 +118,16 @@ Default behavior: - role: openshift_certificate_expiry ``` +**From git:** ``` -$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/default.yaml +$ ansible-playbook -v -i HOSTS playbooks/certificate_expiry/default.yaml +``` +**From openshift-ansible-playbooks rpm:** +``` +$ ansible-playbook -v -i HOSTS /usr/share/ansible/openshift-ansible/playbooks/certificate_expiry/default.yaml ``` - -> [View This Playbook](examples/playbooks/default.yaml) +> [View This Playbook](../../playbooks/certificate_expiry/default.yaml) *** @@ -136,12 +147,16 @@ Generate HTML and JSON artifacts in their default paths: - role: openshift_certificate_expiry ``` +**From git:** ``` -$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/html_and_json_default_paths.yaml +$ ansible-playbook -v -i HOSTS playbooks/certificate_expiry/html_and_json_default_paths.yaml +``` +**From openshift-ansible-playbooks rpm:** +``` +$ ansible-playbook -v -i HOSTS /usr/share/ansible/openshift-ansible/playbooks/certificate_expiry/html_and_json_default_paths.yaml ``` - -> [View This Playbook](examples/playbooks/html_and_json_default_paths.yaml) +> [View This Playbook](../../playbooks/certificate_expiry/html_and_json_default_paths.yaml) *** @@ -160,12 +175,16 @@ the module out): - role: openshift_certificate_expiry ``` +**From git:** ``` -$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/longer_warning_period.yaml +$ ansible-playbook -v -i HOSTS playbooks/certificate_expiry/longer_warning_period.yaml +``` +**From openshift-ansible-playbooks rpm:** +``` +$ ansible-playbook -v -i HOSTS /usr/share/ansible/openshift-ansible/playbooks/certificate_expiry/longer_warning_period.yaml ``` - -> [View This Playbook](examples/playbooks/longer_warning_period.yaml) +> [View This Playbook](../../playbooks/certificate_expiry/longer_warning_period.yaml) *** @@ -185,12 +204,16 @@ the module out) and save the results as a JSON file: - role: openshift_certificate_expiry ``` +**From git:** ``` -$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/longer-warning-period-json-results.yaml +$ ansible-playbook -v -i HOSTS playbooks/certificate_expiry/longer-warning-period-json-results.yaml +``` +**From openshift-ansible-playbooks rpm:** +``` +$ ansible-playbook -v -i HOSTS /usr/share/ansible/openshift-ansible/playbooks/certificate_expiry/longer-warning-period-json-results.yaml ``` - -> [View This Playbook](examples/playbooks/longer-warning-period-json-results.yaml) +> [View This Playbook](../../playbooks/certificate_expiry/longer-warning-period-json-results.yaml) diff --git a/roles/openshift_certificate_expiry/examples/playbooks b/roles/openshift_certificate_expiry/examples/playbooks new file mode 120000 index 000000000..586afb0d5 --- /dev/null +++ b/roles/openshift_certificate_expiry/examples/playbooks @@ -0,0 +1 @@ +../../../playbooks/certificate_expiry
\ No newline at end of file diff --git a/roles/openshift_examples/files/examples/v1.5/db-templates/mongodb-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.5/db-templates/mongodb-ephemeral-template.json index 8b8fcb58b..c38d2680b 100644 --- a/roles/openshift_examples/files/examples/v1.5/db-templates/mongodb-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.5/db-templates/mongodb-ephemeral-template.json @@ -196,7 +196,8 @@ "name": "MEMORY_LIMIT", "displayName": "Memory Limit", "description": "Maximum amount of memory the container can use.", - "value": "512Mi" + "value": "512Mi", + "required": true }, { "name": "NAMESPACE", diff --git a/roles/openshift_examples/files/examples/v1.5/db-templates/mongodb-persistent-template.json b/roles/openshift_examples/files/examples/v1.5/db-templates/mongodb-persistent-template.json index 72d3a8556..e8853d8ff 100644 --- a/roles/openshift_examples/files/examples/v1.5/db-templates/mongodb-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.5/db-templates/mongodb-persistent-template.json @@ -213,7 +213,8 @@ "name": "MEMORY_LIMIT", "displayName": "Memory Limit", "description": "Maximum amount of memory the container can use.", - "value": "512Mi" + "value": "512Mi", + "required": true }, { "name": "NAMESPACE", diff --git a/roles/openshift_examples/files/examples/v1.5/db-templates/mysql-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.5/db-templates/mysql-ephemeral-template.json index 34dd2ed78..f7bcfe2ed 100644 --- a/roles/openshift_examples/files/examples/v1.5/db-templates/mysql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.5/db-templates/mysql-ephemeral-template.json @@ -196,7 +196,8 @@ "name": "MEMORY_LIMIT", "displayName": "Memory Limit", "description": "Maximum amount of memory the container can use.", - "value": "512Mi" + "value": "512Mi", + "required": true }, { "name": "NAMESPACE", diff --git a/roles/openshift_examples/files/examples/v1.5/db-templates/postgresql-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.5/db-templates/postgresql-ephemeral-template.json index 1025ab056..64d5e2b32 100644 --- a/roles/openshift_examples/files/examples/v1.5/db-templates/postgresql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.5/db-templates/postgresql-ephemeral-template.json @@ -186,7 +186,8 @@ "name": "MEMORY_LIMIT", "displayName": "Memory Limit", "description": "Maximum amount of memory the container can use.", - "value": "512Mi" + "value": "512Mi", + "required": true }, { "name": "NAMESPACE", diff --git a/roles/openshift_examples/files/examples/v1.5/db-templates/postgresql-persistent-template.json b/roles/openshift_examples/files/examples/v1.5/db-templates/postgresql-persistent-template.json index 1968e727a..6c101f9d2 100644 --- a/roles/openshift_examples/files/examples/v1.5/db-templates/postgresql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.5/db-templates/postgresql-persistent-template.json @@ -203,7 +203,8 @@ "name": "MEMORY_LIMIT", "displayName": "Memory Limit", "description": "Maximum amount of memory the container can use.", - "value": "512Mi" + "value": "512Mi", + "required": true }, { "name": "NAMESPACE", diff --git a/roles/openshift_examples/files/examples/v1.5/db-templates/redis-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.5/db-templates/redis-ephemeral-template.json index c9ae8a539..b97e1fd29 100644 --- a/roles/openshift_examples/files/examples/v1.5/db-templates/redis-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.5/db-templates/redis-ephemeral-template.json @@ -157,7 +157,8 @@ "name": "MEMORY_LIMIT", "displayName": "Memory Limit", "description": "Maximum amount of memory the container can use.", - "value": "512Mi" + "value": "512Mi", + "required": true }, { "name": "NAMESPACE", diff --git a/roles/openshift_examples/files/examples/v1.5/db-templates/redis-persistent-template.json b/roles/openshift_examples/files/examples/v1.5/db-templates/redis-persistent-template.json index e9db9ec9d..dc167da41 100644 --- a/roles/openshift_examples/files/examples/v1.5/db-templates/redis-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.5/db-templates/redis-persistent-template.json @@ -174,7 +174,8 @@ "name": "MEMORY_LIMIT", "displayName": "Memory Limit", "description": "Maximum amount of memory the container can use.", - "value": "512Mi" + "value": "512Mi", + "required": true }, { "name": "NAMESPACE", diff --git a/roles/openshift_examples/files/examples/v1.5/image-streams/image-streams-rhel7.json b/roles/openshift_examples/files/examples/v1.5/image-streams/image-streams-rhel7.json index 9b19b8bd0..eb94c3bb4 100644 --- a/roles/openshift_examples/files/examples/v1.5/image-streams/image-streams-rhel7.json +++ b/roles/openshift_examples/files/examples/v1.5/image-streams/image-streams-rhel7.json @@ -241,7 +241,7 @@ }, "from": { "kind": "ImageStreamTag", - "name": "5.6" + "name": "7.0" } }, { diff --git a/roles/openshift_examples/files/examples/v1.5/xpaas-streams/fis-image-streams.json b/roles/openshift_examples/files/examples/v1.5/xpaas-streams/fis-image-streams.json index ed0e94bed..9d99973be 100644 --- a/roles/openshift_examples/files/examples/v1.5/xpaas-streams/fis-image-streams.json +++ b/roles/openshift_examples/files/examples/v1.5/xpaas-streams/fis-image-streams.json @@ -20,12 +20,22 @@ { "name": "1.0", "annotations": { - "description": "JBoss Fuse Integration Services 6.2.1 Java S2I images.", + "description": "JBoss Fuse Integration Services 1.0 Java S2I images.", "iconClass": "icon-jboss", "tags": "builder,jboss-fuse,java,xpaas", "supports":"jboss-fuse:6.2.1,java:8,xpaas:1.2", "version": "1.0" } + }, + { + "name": "2.0", + "annotations": { + "description": "JBoss Fuse Integration Services 2.0 Java S2I images.", + "iconClass": "icon-jboss", + "tags": "builder,jboss-fuse,java,xpaas", + "supports":"jboss-fuse:6.3.0,java:8,xpaas:1.2", + "version": "2.0" + } } ] } @@ -42,12 +52,22 @@ { "name": "1.0", "annotations": { - "description": "JBoss Fuse Integration Services 6.2.1 Karaf S2I images.", + "description": "JBoss Fuse Integration Services 1.0 Karaf S2I images.", "iconClass": "icon-jboss", "tags": "builder,jboss-fuse,java,karaf,xpaas", "supports":"jboss-fuse:6.2.1,java:8,xpaas:1.2", "version": "1.0" } + }, + { + "name": "2.0", + "annotations": { + "description": "JBoss Fuse Integration Services 2.0 Karaf S2I images.", + "iconClass": "icon-jboss", + "tags": "builder,jboss-fuse,java,karaf,xpaas", + "supports":"jboss-fuse:6.3.0,java:8,xpaas:1.2", + "version": "2.0" + } } ] } diff --git a/roles/openshift_facts/library/openshift_facts.py b/roles/openshift_facts/library/openshift_facts.py index 97b3edd6e..ef7f159c5 100755 --- a/roles/openshift_facts/library/openshift_facts.py +++ b/roles/openshift_facts/library/openshift_facts.py @@ -788,8 +788,8 @@ def set_etcd_facts_if_unset(facts): def set_deployment_facts_if_unset(facts): """ Set Facts that vary based on deployment_type. This currently - includes common.service_type, common.config_base, master.registry_url, - node.registry_url, node.storage_plugin_deps + includes common.service_type, master.registry_url, node.registry_url, + node.storage_plugin_deps Args: facts (dict): existing facts @@ -809,22 +809,6 @@ def set_deployment_facts_if_unset(facts): elif deployment_type in ['enterprise']: service_type = 'openshift' facts['common']['service_type'] = service_type - if 'config_base' not in facts['common']: - config_base = '/etc/origin' - if deployment_type in ['enterprise']: - config_base = '/etc/openshift' - # Handle upgrade scenarios when symlinks don't yet exist: - if not os.path.exists(config_base) and os.path.exists('/etc/openshift'): - config_base = '/etc/openshift' - facts['common']['config_base'] = config_base - if 'data_dir' not in facts['common']: - data_dir = '/var/lib/origin' - if deployment_type in ['enterprise']: - data_dir = '/var/lib/openshift' - # Handle upgrade scenarios when symlinks don't yet exist: - if not os.path.exists(data_dir) and os.path.exists('/var/lib/openshift'): - data_dir = '/var/lib/openshift' - facts['common']['data_dir'] = data_dir if 'docker' in facts: deployment_type = facts['common']['deployment_type'] @@ -2001,7 +1985,9 @@ class OpenShiftFacts(object): client_binary='oc', admin_binary='oadm', dns_domain='cluster.local', install_examples=True, - debug_level=2) + debug_level=2, + config_base='/etc/origin', + data_dir='/var/lib/origin') if 'master' in roles: defaults['master'] = dict(api_use_ssl=True, api_port='8443', diff --git a/roles/openshift_facts/tasks/main.yml b/roles/openshift_facts/tasks/main.yml index 9a1982076..0ec294bbc 100644 --- a/roles/openshift_facts/tasks/main.yml +++ b/roles/openshift_facts/tasks/main.yml @@ -14,6 +14,18 @@ l_is_master_system_container: "{{ (use_master_system_container | default(use_system_containers) | bool) }}" l_is_etcd_system_container: "{{ (use_etcd_system_container | default(use_system_containers) | bool) }}" +- name: Validate python version + fail: + msg: | + openshift-ansible requires Python 3 for {{ ansible_distribution }}; + For information on enabling Python 3 with Ansible, see https://docs.ansible.com/ansible/python_3_support.html + when: ansible_distribution == 'Fedora' and ansible_python['version']['major'] != 3 + +- name: Validate python version + fail: + msg: "openshift-ansible requires Python 2 for {{ ansible_distribution }}" + when: ansible_distribution != 'Fedora' and ansible_python['version']['major'] != 2 + - name: Ensure various deps are installed package: name={{ item }} state=present with_items: "{{ required_packages }}" diff --git a/roles/openshift_health_checker/HOWTO_CHECKS.md b/roles/openshift_health_checker/HOWTO_CHECKS.md new file mode 100644 index 000000000..1573c14da --- /dev/null +++ b/roles/openshift_health_checker/HOWTO_CHECKS.md @@ -0,0 +1,34 @@ +# OpenShift health checks + +This Ansible role contains health checks to diagnose problems in OpenShift +environments. + +Checks are typically implemented as two parts: + +1. a Python module in [openshift_checks/](openshift_checks), with a class that + inherits from `OpenShiftCheck`. +2. a custom Ansible module in [library/](library), for cases when the modules + shipped with Ansible do not provide the required functionality. + +The checks are called from an Ansible playbooks via the `openshift_health_check` +action plugin. See +[playbooks/byo/openshift-preflight/check.yml](../../playbooks/byo/openshift-preflight/check.yml) +for an example. + +The action plugin dynamically discovers all checks and executes only those +selected in the play. + +Checks can determine when they are active by implementing the method +`is_active`. Inactive checks are skipped. This is similar to the `when` +instruction in Ansible plays. + +Checks may have tags, which are a way to group related checks together. For +instance, to run all preflight checks, pass in the group `'@preflight'` to +`openshift_health_check`. + +Groups are automatically computed from tags. + +Groups and individual check names can be used together in the argument list to +`openshift_health_check`. + +Look at existing checks for the implementation details. diff --git a/roles/openshift_health_checker/README.md b/roles/openshift_health_checker/README.md new file mode 100644 index 000000000..4ab5f1f7b --- /dev/null +++ b/roles/openshift_health_checker/README.md @@ -0,0 +1,45 @@ +OpenShift Health Checker +======================== + +This role detects common problems with OpenShift installations or with +environments prior to install. + +For more information about creating new checks, see [HOWTO_CHECKS.md](HOWTO_CHECKS.md). + +Requirements +------------ + +* Ansible 2.2+ + +Role Variables +-------------- + +None + +Dependencies +------------ + +- openshift_facts + +Example Playbook +---------------- + +```yaml +--- +- hosts: OSEv3 + name: run OpenShift health checks + roles: + - openshift_health_checker + post_tasks: + - action: openshift_health_check +``` + +License +------- + +Apache License Version 2.0 + +Author Information +------------------ + +Customer Success team (dev@lists.openshift.redhat.com) diff --git a/roles/openshift_health_checker/action_plugins/openshift_health_check.py b/roles/openshift_health_checker/action_plugins/openshift_health_check.py new file mode 100644 index 000000000..0411797b1 --- /dev/null +++ b/roles/openshift_health_checker/action_plugins/openshift_health_check.py @@ -0,0 +1,116 @@ +""" +Ansible action plugin to execute health checks in OpenShift clusters. +""" +# pylint: disable=wrong-import-position,missing-docstring,invalid-name +import sys +import os + +try: + from __main__ import display +except ImportError: + from ansible.utils.display import Display + display = Display() + +from ansible.plugins.action import ActionBase + +# Augment sys.path so that we can import checks from a directory relative to +# this callback plugin. +sys.path.insert(1, os.path.dirname(os.path.dirname(__file__))) + +from openshift_checks import OpenShiftCheck, OpenShiftCheckException # noqa: E402 + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + result = super(ActionModule, self).run(tmp, task_vars) + + if task_vars is None: + task_vars = {} + + if "openshift" not in task_vars: + result["failed"] = True + result["msg"] = "'openshift' is undefined, did 'openshift_facts' run?" + return result + + try: + known_checks = self.load_known_checks() + except OpenShiftCheckException as e: + result["failed"] = True + result["msg"] = str(e) + return result + + args = self._task.args + requested_checks = resolve_checks(args.get("checks", []), known_checks.values()) + + unknown_checks = requested_checks - set(known_checks) + if unknown_checks: + result["failed"] = True + result["msg"] = ( + "One or more checks are unknown: {}. " + "Make sure there is no typo in the playbook and no files are missing." + ).format(", ".join(unknown_checks)) + return result + + result["checks"] = check_results = {} + + for check_name in requested_checks & set(known_checks): + display.banner("CHECK [{} : {}]".format(check_name, task_vars["ansible_host"])) + check = known_checks[check_name] + + if check.is_active(task_vars): + try: + r = check.run(tmp, task_vars) + except OpenShiftCheckException as e: + r = {} + r["failed"] = True + r["msg"] = str(e) + else: + r = {"skipped": True} + + check_results[check_name] = r + + if r.get("failed", False): + result["failed"] = True + result["msg"] = "One or more checks failed" + + return result + + def load_known_checks(self): + known_checks = {} + + known_check_classes = set(cls for cls in OpenShiftCheck.subclasses()) + + for cls in known_check_classes: + check_name = cls.name + if check_name in known_checks: + other_cls = known_checks[check_name].__class__ + raise OpenShiftCheckException( + "non-unique check name '{}' in: '{}.{}' and '{}.{}'".format( + check_name, + cls.__module__, cls.__name__, + other_cls.__module__, other_cls.__name__)) + known_checks[check_name] = cls(module_executor=self._execute_module) + + return known_checks + + +def resolve_checks(names, all_checks): + """Returns a set of resolved check names. + + Resolving a check name involves expanding tag references (e.g., '@tag') with + all the checks that contain the given tag. + + names should be a sequence of strings. + + all_checks should be a sequence of check classes/instances. + """ + resolved = set() + for name in names: + if name.startswith("@"): + for check in all_checks: + if name[1:] in check.tags: + resolved.add(check.name) + else: + resolved.add(name) + return resolved diff --git a/roles/openshift_preflight/verify_status/callback_plugins/zz_failure_summary.py b/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py index 180ed8d8f..8caefab15 100644 --- a/roles/openshift_preflight/verify_status/callback_plugins/zz_failure_summary.py +++ b/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py @@ -3,6 +3,8 @@ Ansible callback plugin. ''' +from pprint import pformat + from ansible.plugins.callback import CallbackBase from ansible import constants as C from ansible.utils.color import stringc @@ -79,6 +81,8 @@ def _format_failure(failure): (u'Task', task), (u'Message', stringc(msg, C.COLOR_ERROR)), ) + if 'checks' in result._result: + rows += ((u'Details', stringc(pformat(result._result['checks']), C.COLOR_ERROR)),) row_format = '{:10}{}' return [row_format.format(header + u':', body) for header, body in rows] diff --git a/roles/openshift_preflight/base/library/aos_version.py b/roles/openshift_health_checker/library/aos_version.py index f7fcb6da5..13b7d310b 100755 --- a/roles/openshift_preflight/base/library/aos_version.py +++ b/roles/openshift_health_checker/library/aos_version.py @@ -1,57 +1,49 @@ #!/usr/bin/python # vim: expandtab:tabstop=4:shiftwidth=4 ''' -An ansible module for determining if more than one minor version -of any atomic-openshift package is available, which would indicate -that multiple repos are enabled for different versions of the same -thing which may cause problems. +Ansible module for determining if multiple versions of an OpenShift package are +available, and if the version requested is available down to the given +precision. -Also, determine if the version requested is available down to the -precision requested. +Multiple versions available suggest that multiple repos are enabled for the +different versions, which may cause installation problems. ''' -# import os -# import sys import yum # pylint: disable=import-error + from ansible.module_utils.basic import AnsibleModule -def main(): # pylint: disable=missing-docstring +def main(): # pylint: disable=missing-docstring,too-many-branches module = AnsibleModule( argument_spec=dict( - version=dict(required=True) + prefix=dict(required=True), # atomic-openshift, origin, ... + version=dict(required=True), ), supports_check_mode=True ) - # NOTE(rhcarvalho): sosiouxme added _unmute, but I couldn't find a case yet - # for when it is actually necessary. Leaving it commented out for now, - # though this comment and the commented out code related to _unmute should - # be deleted later if not proven necessary. - - # sys.stdout = os.devnull # mute yum so it doesn't break our output - # sys.stderr = os.devnull # mute yum so it doesn't break our output - - # def _unmute(): # pylint: disable=missing-docstring - # sys.stdout = sys.__stdout__ - def bail(error): # pylint: disable=missing-docstring - # _unmute() module.fail_json(msg=error) + rpm_prefix = module.params['prefix'] + + if not rpm_prefix: + bail("prefix must not be empty") + yb = yum.YumBase() # pylint: disable=invalid-name # search for package versions available for aos pkgs expected_pkgs = [ - 'atomic-openshift', - 'atomic-openshift-master', - 'atomic-openshift-node', + rpm_prefix, + rpm_prefix + '-master', + rpm_prefix + '-node', ] try: pkgs = yb.pkgSack.returnPackages(patterns=expected_pkgs) except yum.Errors.PackageSackError as e: # pylint: disable=invalid-name # you only hit this if *none* of the packages are available - bail('Unable to find any atomic-openshift packages. \nCheck your subscription and repo settings. \n%s' % e) + bail('Unable to find any OpenShift packages.\nCheck your subscription and repo settings.\n%s' % e) # determine what level of precision we're expecting for the version expected_version = module.params['version'] @@ -92,7 +84,6 @@ def main(): # pylint: disable=missing-docstring msg += ' %s\n' % name bail(msg + "There should only be one OpenShift version's repository enabled at a time.") - # _unmute() module.exit_json(changed=False) diff --git a/roles/openshift_preflight/base/library/check_yum_update.py b/roles/openshift_health_checker/library/check_yum_update.py index 296ebd44f..9bc14fd47 100755 --- a/roles/openshift_preflight/base/library/check_yum_update.py +++ b/roles/openshift_health_checker/library/check_yum_update.py @@ -8,9 +8,10 @@ parameters: If omitted, all installed RPMs are considered for updates. ''' -# import os import sys + import yum # pylint: disable=import-error + from ansible.module_utils.basic import AnsibleModule @@ -22,18 +23,7 @@ def main(): # pylint: disable=missing-docstring,too-many-branches supports_check_mode=True ) - # NOTE(rhcarvalho): sosiouxme added _unmute, but I couldn't find a case yet - # for when it is actually necessary. Leaving it commented out for now, - # though this comment and the commented out code related to _unmute should - # be deleted later if not proven necessary. - - # sys.stdout = os.devnull # mute yum so it doesn't break our output - - # def _unmute(): # pylint: disable=missing-docstring - # sys.stdout = sys.__stdout__ - def bail(error): # pylint: disable=missing-docstring - # _unmute() module.fail_json(msg=error) yb = yum.YumBase() # pylint: disable=invalid-name @@ -108,7 +98,6 @@ def main(): # pylint: disable=missing-docstring,too-many-branches bail('Unknown error(s) from dependency resolution. Exit Code: %d:\n%s' % (txn_result, txn_msgs)) - # _unmute() module.exit_json(changed=False) diff --git a/roles/openshift_preflight/init/meta/main.yml b/roles/openshift_health_checker/meta/main.yml index 0bbeadd34..0bbeadd34 100644 --- a/roles/openshift_preflight/init/meta/main.yml +++ b/roles/openshift_health_checker/meta/main.yml diff --git a/roles/openshift_health_checker/openshift_checks/__init__.py b/roles/openshift_health_checker/openshift_checks/__init__.py new file mode 100644 index 000000000..c31242624 --- /dev/null +++ b/roles/openshift_health_checker/openshift_checks/__init__.py @@ -0,0 +1,84 @@ +""" +Health checks for OpenShift clusters. +""" + +import os +from abc import ABCMeta, abstractmethod, abstractproperty +from importlib import import_module +import operator + +import six +from six.moves import reduce + + +class OpenShiftCheckException(Exception): + """Raised when a check cannot proceed.""" + pass + + +@six.add_metaclass(ABCMeta) +class OpenShiftCheck(object): + """A base class for defining checks for an OpenShift cluster environment.""" + + def __init__(self, module_executor): + self.module_executor = module_executor + + @abstractproperty + def name(self): + """The name of this check, usually derived from the class name.""" + return "openshift_check" + + @property + def tags(self): + """A list of tags that this check satisfy. + + Tags are used to reference multiple checks with a single '@tagname' + special check name. + """ + return [] + + @classmethod + def is_active(cls, task_vars): # pylint: disable=unused-argument + """Returns true if this check applies to the ansible-playbook run.""" + return True + + @abstractmethod + def run(self, tmp, task_vars): + """Executes a check, normally implemented as a module.""" + return {} + + @classmethod + def subclasses(cls): + """Returns a generator of subclasses of this class and its subclasses.""" + for subclass in cls.__subclasses__(): # pylint: disable=no-member + yield subclass + for subclass in subclass.subclasses(): + yield subclass + + +def get_var(task_vars, *keys, **kwargs): + """Helper function to get deeply nested values from task_vars. + + Ansible task_vars structures are Python dicts, often mapping strings to + other dicts. This helper makes it easier to get a nested value, raising + OpenShiftCheckException when a key is not found. + """ + try: + value = reduce(operator.getitem, keys, task_vars) + except (KeyError, TypeError): + if "default" in kwargs: + return kwargs["default"] + raise OpenShiftCheckException("'{}' is undefined".format(".".join(map(str, keys)))) + return value + + +# Dynamically import all submodules for the side effect of loading checks. + +EXCLUDES = ( + "__init__.py", + "mixins.py", +) + +for name in os.listdir(os.path.dirname(__file__)): + if name.endswith(".py") and name not in EXCLUDES: + import_module(__package__ + "." + name[:-3]) diff --git a/roles/openshift_health_checker/openshift_checks/mixins.py b/roles/openshift_health_checker/openshift_checks/mixins.py new file mode 100644 index 000000000..4029fba62 --- /dev/null +++ b/roles/openshift_health_checker/openshift_checks/mixins.py @@ -0,0 +1,21 @@ +# pylint: disable=missing-docstring +from openshift_checks import get_var + + +class NotContainerized(object): + """Mixin for checks that are only active when not in containerized mode.""" + + @classmethod + def is_active(cls, task_vars): + return ( + # This mixin is meant to be used with subclasses of + # OpenShiftCheck. Pylint disables this by default on mixins, + # though it relies on the class name ending in 'mixin'. + # pylint: disable=no-member + super(NotContainerized, cls).is_active(task_vars) and + not cls.is_containerized(task_vars) + ) + + @staticmethod + def is_containerized(task_vars): + return get_var(task_vars, "openshift", "common", "is_containerized") diff --git a/roles/openshift_health_checker/openshift_checks/package_availability.py b/roles/openshift_health_checker/openshift_checks/package_availability.py new file mode 100644 index 000000000..8faeef5ee --- /dev/null +++ b/roles/openshift_health_checker/openshift_checks/package_availability.py @@ -0,0 +1,66 @@ +# pylint: disable=missing-docstring +from openshift_checks import OpenShiftCheck, get_var +from openshift_checks.mixins import NotContainerized + + +class PackageAvailability(NotContainerized, OpenShiftCheck): + """Check that required RPM packages are available.""" + + name = "package_availability" + tags = ["preflight"] + + def run(self, tmp, task_vars): + rpm_prefix = get_var(task_vars, "openshift", "common", "service_type") + group_names = get_var(task_vars, "group_names", default=[]) + + packages = set() + + if "masters" in group_names: + packages.update(self.master_packages(rpm_prefix)) + if "nodes" in group_names: + packages.update(self.node_packages(rpm_prefix)) + + args = {"packages": sorted(set(packages))} + return self.module_executor("check_yum_update", args, tmp, task_vars) + + @staticmethod + def master_packages(rpm_prefix): + return [ + "{rpm_prefix}".format(rpm_prefix=rpm_prefix), + "{rpm_prefix}-clients".format(rpm_prefix=rpm_prefix), + "{rpm_prefix}-master".format(rpm_prefix=rpm_prefix), + "bash-completion", + "cockpit-bridge", + "cockpit-docker", + "cockpit-kubernetes", + "cockpit-shell", + "cockpit-ws", + "etcd", + "httpd-tools", + ] + + @staticmethod + def node_packages(rpm_prefix): + return [ + "{rpm_prefix}".format(rpm_prefix=rpm_prefix), + "{rpm_prefix}-node".format(rpm_prefix=rpm_prefix), + "{rpm_prefix}-sdn-ovs".format(rpm_prefix=rpm_prefix), + "bind", + "ceph-common", + "dnsmasq", + "docker", + "firewalld", + "flannel", + "glusterfs-fuse", + "iptables-services", + "iptables", + "iscsi-initiator-utils", + "libselinux-python", + "nfs-utils", + "ntp", + "openssl", + "pyparted", + "python-httplib2", + "PyYAML", + "yum-utils", + ] diff --git a/roles/openshift_health_checker/openshift_checks/package_update.py b/roles/openshift_health_checker/openshift_checks/package_update.py new file mode 100644 index 000000000..86b7b6245 --- /dev/null +++ b/roles/openshift_health_checker/openshift_checks/package_update.py @@ -0,0 +1,14 @@ +# pylint: disable=missing-docstring +from openshift_checks import OpenShiftCheck +from openshift_checks.mixins import NotContainerized + + +class PackageUpdate(NotContainerized, OpenShiftCheck): + """Check that there are no conflicts in RPM packages.""" + + name = "package_update" + tags = ["preflight"] + + def run(self, tmp, task_vars): + args = {"packages": []} + return self.module_executor("check_yum_update", args, tmp, task_vars) diff --git a/roles/openshift_health_checker/openshift_checks/package_version.py b/roles/openshift_health_checker/openshift_checks/package_version.py new file mode 100644 index 000000000..7fa09cbfd --- /dev/null +++ b/roles/openshift_health_checker/openshift_checks/package_version.py @@ -0,0 +1,20 @@ +# pylint: disable=missing-docstring +from openshift_checks import OpenShiftCheck, get_var +from openshift_checks.mixins import NotContainerized + + +class PackageVersion(NotContainerized, OpenShiftCheck): + """Check that available RPM packages match the required versions.""" + + name = "package_version" + tags = ["preflight"] + + def run(self, tmp, task_vars): + rpm_prefix = get_var(task_vars, "openshift", "common", "service_type") + openshift_release = get_var(task_vars, "openshift_release") + + args = { + "prefix": rpm_prefix, + "version": openshift_release, + } + return self.module_executor("aos_version", args, tmp, task_vars) diff --git a/roles/openshift_hosted/meta/main.yml b/roles/openshift_hosted/meta/main.yml index ca5e88b15..afea0ac59 100644 --- a/roles/openshift_hosted/meta/main.yml +++ b/roles/openshift_hosted/meta/main.yml @@ -4,7 +4,7 @@ galaxy_info: description: OpenShift Embedded Router company: Red Hat, Inc. license: Apache License, Version 2.0 - min_ansible_version: 1.9 + min_ansible_version: 2.1 platforms: - name: EL versions: @@ -14,6 +14,7 @@ galaxy_info: dependencies: - role: openshift_cli - role: openshift_hosted_facts +- role: lib_openshift - role: openshift_projects openshift_projects: "{{ openshift_additional_projects | default({}) | oo_merge_dicts({'default':{'default_node_selector':''},'openshift-infra':{'default_node_selector':''},'logging':{'default_node_selector':''}}) }}" - role: openshift_serviceaccounts diff --git a/roles/openshift_hosted/tasks/registry/secure.yml b/roles/openshift_hosted/tasks/registry/secure.yml index d87a3847c..8b44b94c6 100644 --- a/roles/openshift_hosted/tasks/registry/secure.yml +++ b/roles/openshift_hosted/tasks/registry/secure.yml @@ -1,13 +1,13 @@ --- - name: Create passthrough route for docker-registry - command: > - {{ openshift.common.client_binary }} create route passthrough - --service docker-registry - --config={{ openshift_hosted_kubeconfig }} - -n default - register: create_docker_registry_route - changed_when: "'already exists' not in create_docker_registry_route.stderr" - failed_when: "'already exists' not in create_docker_registry_route.stderr and create_docker_registry_route.rc != 0" + oc_route: + kubeconfig: "{{ openshift_hosted_kubeconfig }}" + name: docker-registry + namespace: default + service_name: docker-registry + state: present + tls_termination: passthrough + run_once: true - name: Determine if registry certificate must be created stat: @@ -20,11 +20,10 @@ failed_when: false - name: Retrieve registry service IP - command: > - {{ openshift.common.client_binary }} get service docker-registry - -o jsonpath='{.spec.clusterIP}' - --config={{ openshift_hosted_kubeconfig }} - -n default + oc_service: + namespace: default + name: docker-registry + state: list register: docker_registry_service_ip changed_when: false @@ -37,27 +36,32 @@ --signer-cert={{ openshift_master_config_dir }}/ca.crt --signer-key={{ openshift_master_config_dir }}/ca.key --signer-serial={{ openshift_master_config_dir }}/ca.serial.txt - --hostnames="{{ docker_registry_service_ip.stdout }},docker-registry.default.svc.cluster.local,{{ docker_registry_route_hostname }}" + --hostnames="{{ docker_registry_service_ip.results.clusterip }},docker-registry.default.svc.cluster.local,{{ docker_registry_route_hostname }}" --cert={{ openshift_master_config_dir }}/registry.crt --key={{ openshift_master_config_dir }}/registry.key when: False in (docker_registry_certificates_stat_result.results | default([]) | oo_collect(attribute='stat.exists') | list) - name: Create the secret for the registry certificates - command: > - {{ openshift.common.client_binary }} secrets new registry-certificates - {{ openshift_master_config_dir }}/registry.crt - {{ openshift_master_config_dir }}/registry.key - --config={{ openshift_hosted_kubeconfig }} - -n default + oc_secret: + kubeconfig: "{{ openshift_hosted_kubeconfig }}" + name: registry-certificates + namespace: default + state: present + files: + - name: registry.crt + path: "{{ openshift_master_config_dir }}/registry.crt" + - name: registry.key + path: "{{ openshift_master_config_dir }}/registry.key" register: create_registry_certificates_secret - changed_when: "'already exists' not in create_registry_certificates_secret.stderr" - failed_when: "'already exists' not in create_registry_certificates_secret.stderr and create_registry_certificates_secret.rc != 0" + run_once: true - name: "Add the secret to the registry's pod service accounts" - command: > - {{ openshift.common.client_binary }} secrets add {{ item }} registry-certificates - --config={{ openshift_hosted_kubeconfig }} - -n default + oc_serviceaccount_secret: + service_account: "{{ item }}" + secret: registry-certificates + namespace: default + kubeconfig: "{{ openshift_hosted_kubeconfig }}" + state: present with_items: - registry - default diff --git a/roles/openshift_hosted/tasks/registry/storage/object_storage.yml b/roles/openshift_hosted/tasks/registry/storage/object_storage.yml index e56a68e27..15128784e 100644 --- a/roles/openshift_hosted/tasks/registry/storage/object_storage.yml +++ b/roles/openshift_hosted/tasks/registry/storage/object_storage.yml @@ -53,23 +53,13 @@ create -f - when: secrets.rc == 1 -- name: Determine if service account contains secrets - command: > - {{ openshift.common.client_binary }} - --config={{ openshift_hosted_kubeconfig }} - --namespace={{ openshift.hosted.registry.namespace | default('default') }} - get serviceaccounts registry - -o jsonpath='{.secrets[?(@.name=="{{ registry_config_secret_name }}")].name}' - register: serviceaccount - changed_when: false - - name: Add secrets to registry service account - command: > - {{ openshift.common.client_binary }} - --config={{ openshift_hosted_kubeconfig }} - --namespace={{ openshift.hosted.registry.namespace | default('default') }} - secrets add serviceaccount/registry secrets/{{ registry_config_secret_name }} - when: serviceaccount.stdout == '' + oc_serviceaccount_secret: + service_account: registry + secret: "{{ registry_config_secret_name }}" + namespace: "{{ openshift.hosted.registry.namespace | default('default') }}" + kubeconfig: "{{ openshift_hosted_kubeconfig }}" + state: present - name: Determine if deployment config contains secrets command: > diff --git a/roles/openshift_hosted/tasks/registry/storage/s3.yml b/roles/openshift_hosted/tasks/registry/storage/s3.yml index f73d9f0ae..7d51594bd 100644 --- a/roles/openshift_hosted/tasks/registry/storage/s3.yml +++ b/roles/openshift_hosted/tasks/registry/storage/s3.yml @@ -21,13 +21,27 @@ openshift_hosted_registry_storage_s3_cloudfront_keypairid and openshift_hosted_registry_storage_s3_cloudfront_privatekeyfile are required -# Copy the cloudfront.pem to the host if the baseurl is given -- name: Copy cloudfront.pem to the registry - copy: - src: "{{ openshift_hosted_registry_storage_s3_cloudfront_privatekeyfile }}" - dest: /etc/s3-cloudfront/cloudfront.pem - backup: true - owner: root - group: root - mode: 0600 + +# Inject the cloudfront private key as a secret when required +- block: + + - name: Create registry secret for cloudfront + oc_secret: + state: present + namespace: "{{ openshift.hosted.registry.namespace | default('default') }}" + name: docker-registry-s3-cloudfront + contents: + path: cloudfront.pem + data: "{{ lookup('file', openshift_hosted_registry_storage_s3_cloudfront_privatekeyfile) }}" + + - name: Add cloudfront secret to the registry deployment config + command: > + oc volume dc/docker-registry --add --name=cloudfront-vol + --namespace="{{ openshift.hosted.registry.namespace | default('default') }}" + -m /etc/origin --type=secret --secret-name=docker-registry-s3-cloudfront + register: cloudfront_vol_attach + failed_when: + - "'already exists' not in cloudfront_vol_attach.stderr" + - "cloudfront_vol_attach.rc != 0" + when: openshift_hosted_registry_storage_s3_cloudfront_baseurl | default(none) is not none diff --git a/roles/openshift_hosted/templates/registry_config.j2 b/roles/openshift_hosted/templates/registry_config.j2 index 557fd03af..f3336334a 100644 --- a/roles/openshift_hosted/templates/registry_config.j2 +++ b/roles/openshift_hosted/templates/registry_config.j2 @@ -78,7 +78,7 @@ middleware: - name: cloudfront options: baseurl: {{ openshift_hosted_registry_storage_s3_cloudfront_baseurl }} - privatekey: {{ openshift_hosted_registry_storage_s3_cloudfront_privatekeyfile }} + privatekey: /etc/origin/cloudfront.pem keypairid: {{ openshift_hosted_registry_storage_s3_cloudfront_keypairid }} {% elif openshift.common.version_gte_3_3_or_1_3 | bool %} storage: diff --git a/roles/openshift_hosted_templates/files/v1.4/enterprise/metrics-deployer.yaml b/roles/openshift_hosted_templates/files/v1.4/enterprise/metrics-deployer.yaml index 66051755c..6ead122c5 100644 --- a/roles/openshift_hosted_templates/files/v1.4/enterprise/metrics-deployer.yaml +++ b/roles/openshift_hosted_templates/files/v1.4/enterprise/metrics-deployer.yaml @@ -159,9 +159,9 @@ parameters: name: HEAPSTER_NODE_ID value: "nodename" - - description: "How often metrics should be gathered. Defaults value of '15s' for 15 seconds" + description: "How often metrics should be gathered. Defaults value of '30s' for 30 seconds" name: METRIC_RESOLUTION - value: "15s" + value: "30s" - description: "How long in seconds we should wait until Hawkular Metrics and Heapster starts up before attempting a restart" name: STARTUP_TIMEOUT diff --git a/roles/openshift_hosted_templates/files/v1.4/origin/metrics-deployer.yaml b/roles/openshift_hosted_templates/files/v1.4/origin/metrics-deployer.yaml index 54691572a..d191c0439 100644 --- a/roles/openshift_hosted_templates/files/v1.4/origin/metrics-deployer.yaml +++ b/roles/openshift_hosted_templates/files/v1.4/origin/metrics-deployer.yaml @@ -159,9 +159,9 @@ parameters: name: HEAPSTER_NODE_ID value: "nodename" - - description: "How often metrics should be gathered. Defaults value of '15s' for 15 seconds" + description: "How often metrics should be gathered. Defaults value of '30s' for 30 seconds" name: METRIC_RESOLUTION - value: "15s" + value: "30s" - description: "How long in seconds we should wait until Hawkular Metrics and Heapster starts up before attempting a restart" name: STARTUP_TIMEOUT diff --git a/roles/openshift_hosted_templates/files/v1.5/enterprise/metrics-deployer.yaml b/roles/openshift_hosted_templates/files/v1.5/enterprise/metrics-deployer.yaml index 66051755c..6ead122c5 100644 --- a/roles/openshift_hosted_templates/files/v1.5/enterprise/metrics-deployer.yaml +++ b/roles/openshift_hosted_templates/files/v1.5/enterprise/metrics-deployer.yaml @@ -159,9 +159,9 @@ parameters: name: HEAPSTER_NODE_ID value: "nodename" - - description: "How often metrics should be gathered. Defaults value of '15s' for 15 seconds" + description: "How often metrics should be gathered. Defaults value of '30s' for 30 seconds" name: METRIC_RESOLUTION - value: "15s" + value: "30s" - description: "How long in seconds we should wait until Hawkular Metrics and Heapster starts up before attempting a restart" name: STARTUP_TIMEOUT diff --git a/roles/openshift_hosted_templates/files/v1.5/origin/metrics-deployer.yaml b/roles/openshift_hosted_templates/files/v1.5/origin/metrics-deployer.yaml index 54691572a..d191c0439 100644 --- a/roles/openshift_hosted_templates/files/v1.5/origin/metrics-deployer.yaml +++ b/roles/openshift_hosted_templates/files/v1.5/origin/metrics-deployer.yaml @@ -159,9 +159,9 @@ parameters: name: HEAPSTER_NODE_ID value: "nodename" - - description: "How often metrics should be gathered. Defaults value of '15s' for 15 seconds" + description: "How often metrics should be gathered. Defaults value of '30s' for 30 seconds" name: METRIC_RESOLUTION - value: "15s" + value: "30s" - description: "How long in seconds we should wait until Hawkular Metrics and Heapster starts up before attempting a restart" name: STARTUP_TIMEOUT diff --git a/roles/openshift_hosted_templates/tasks/main.yml b/roles/openshift_hosted_templates/tasks/main.yml index 7d176bce3..89b92dfcc 100644 --- a/roles/openshift_hosted_templates/tasks/main.yml +++ b/roles/openshift_hosted_templates/tasks/main.yml @@ -4,6 +4,8 @@ become: False register: copy_hosted_templates_mktemp run_once: True + # AUDIT:changed_when: not set here because this task actually + # creates something - name: Create tar of OpenShift examples local_action: command tar -C "{{ role_path }}/files/{{ content_version }}/{{ hosted_deployment_type }}" -cvf "{{ copy_hosted_templates_mktemp.stdout }}/openshift-hosted-templates.tar" . diff --git a/roles/openshift_logging/defaults/main.yml b/roles/openshift_logging/defaults/main.yml index bdb168921..87fc7068f 100644 --- a/roles/openshift_logging/defaults/main.yml +++ b/roles/openshift_logging/defaults/main.yml @@ -3,7 +3,7 @@ openshift_logging_image_prefix: "{{ openshift_hosted_logging_deployer_prefix | d openshift_logging_image_version: "{{ openshift_hosted_logging_deployer_version | default('latest') }}" openshift_logging_use_ops: False openshift_logging_master_url: "https://kubernetes.default.svc.{{ openshift.common.dns_domain }}" -openshift_logging_master_public_url: "{{ openshift_hosted_logging_master_public_url | default('https://{{openshift.common.public_hostname}}:8443') }}" +openshift_logging_master_public_url: "{{ openshift_hosted_logging_master_public_url | default('https://' + openshift.common.public_hostname + ':8443') }}" openshift_logging_namespace: logging openshift_logging_install_logging: True @@ -19,7 +19,7 @@ openshift_logging_curator_memory_limit: null openshift_logging_curator_ops_cpu_limit: 100m openshift_logging_curator_ops_memory_limit: null -openshift_logging_kibana_hostname: "{{ openshift_hosted_logging_hostname | default(kibana.{{openshift.common.dns_domain}}) }}" +openshift_logging_kibana_hostname: "{{ openshift_hosted_logging_hostname | default('kibana.' + openshift.common.dns_domain) }}" openshift_logging_kibana_cpu_limit: null openshift_logging_kibana_memory_limit: null openshift_logging_kibana_proxy_debug: false @@ -39,7 +39,7 @@ openshift_logging_kibana_key: "" #for the public facing kibana certs openshift_logging_kibana_ca: "" -openshift_logging_kibana_ops_hostname: "{{ openshift_hosted_logging_ops_hostname | default('kibana-ops.{{openshift.common.dns_domain}}') }}" +openshift_logging_kibana_ops_hostname: "{{ openshift_hosted_logging_ops_hostname | default('kibana-ops.' + openshift.common.dns_domain) }}" openshift_logging_kibana_ops_cpu_limit: null openshift_logging_kibana_ops_memory_limit: null openshift_logging_kibana_ops_proxy_debug: false @@ -62,7 +62,7 @@ openshift_logging_es_client_cert: /etc/fluent/keys/cert openshift_logging_es_client_key: /etc/fluent/keys/key openshift_logging_es_cluster_size: "{{ openshift_hosted_logging_elasticsearch_cluster_size | default(1) }}" openshift_logging_es_cpu_limit: null -openshift_logging_es_memory_limit: 1024Mi +openshift_logging_es_memory_limit: 8Gi openshift_logging_es_pv_selector: null openshift_logging_es_pvc_dynamic: "{{ openshift_hosted_logging_elasticsearch_pvc_dynamic | default(False) }}" openshift_logging_es_pvc_size: "{{ openshift_hosted_logging_elasticsearch_pvc_size | default('') }}" @@ -80,7 +80,7 @@ openshift_logging_es_ops_client_cert: /etc/fluent/keys/cert openshift_logging_es_ops_client_key: /etc/fluent/keys/key openshift_logging_es_ops_cluster_size: "{{ openshift_hosted_logging_elasticsearch_ops_cluster_size | default(1) }}" openshift_logging_es_ops_cpu_limit: null -openshift_logging_es_ops_memory_limit: 1024Mi +openshift_logging_es_ops_memory_limit: 8Gi openshift_logging_es_ops_pv_selector: None openshift_logging_es_ops_pvc_dynamic: "{{ openshift_hosted_logging_elasticsearch_ops_pvc_dynamic | default(False) }}" openshift_logging_es_ops_pvc_size: "{{ openshift_hosted_logging_elasticsearch_ops_pvc_size | default('') }}" diff --git a/roles/openshift_logging/tasks/delete_logging.yaml b/roles/openshift_logging/tasks/delete_logging.yaml index 908f3ee88..188ea246c 100644 --- a/roles/openshift_logging/tasks/delete_logging.yaml +++ b/roles/openshift_logging/tasks/delete_logging.yaml @@ -80,16 +80,15 @@ # delete our service accounts - name: delete service accounts - command: > - {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig - delete serviceaccount {{ item }} -n {{ openshift_logging_namespace }} --ignore-not-found=true + oc_serviceaccount: + name: "{{ item }}" + namespace: "{{ openshift_logging_namespace }}" + state: absent with_items: - aggregated-logging-elasticsearch - aggregated-logging-kibana - aggregated-logging-curator - aggregated-logging-fluentd - register: delete_result - changed_when: delete_result.stdout.find("deleted") != -1 and delete_result.rc == 0 # delete our roles - name: delete roles diff --git a/roles/openshift_logging/tasks/generate_secrets.yaml b/roles/openshift_logging/tasks/generate_secrets.yaml index 1829acaee..81fac8b5e 100644 --- a/roles/openshift_logging/tasks/generate_secrets.yaml +++ b/roles/openshift_logging/tasks/generate_secrets.yaml @@ -17,7 +17,7 @@ - name: Generating secrets for logging components template: src=secret.j2 dest={{mktemp.stdout}}/templates/{{secret_name}}-secret.yaml vars: - secret_name: logging-{{component}} + secret_name: "logging-{{component}}" secret_key_file: "{{component}}_key" secret_cert_file: "{{component}}_cert" secrets: diff --git a/roles/openshift_logging/tasks/label_node.yaml b/roles/openshift_logging/tasks/label_node.yaml deleted file mode 100644 index ebe8f1ca8..000000000 --- a/roles/openshift_logging/tasks/label_node.yaml +++ /dev/null @@ -1,52 +0,0 @@ ---- -- command: > - {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig get node {{host}} - -o jsonpath='{.metadata.labels}' - register: node_labels - when: not ansible_check_mode - changed_when: no - -- command: > - {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig label node {{host}} {{label}}={{value}} - register: label_result - failed_when: label_result.rc == 1 and 'exists' not in label_result.stderr - when: - - value is defined - - node_labels.stdout is defined - - label not in node_labels.stdout - - unlabel is not defined or not unlabel - - not ansible_check_mode - -- command: > - {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig get node {{host}} - -o jsonpath='{.metadata.labels.{{ label }}}' - register: label_value - ignore_errors: yes - changed_when: no - when: - - value is defined - - node_labels.stdout is defined - - label in node_labels.stdout - - unlabel is not defined or not unlabel - - not ansible_check_mode - -- command: > - {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig label node {{host}} {{label}}={{value}} --overwrite - register: label_result - failed_when: label_result.rc == 1 and 'exists' not in label_result.stderr - when: - - value is defined - - label_value.stdout is defined - - label_value.stdout != value - - unlabel is not defined or not unlabel - - not ansible_check_mode - -- command: > - {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig label node {{host}} {{label}}- - register: label_result - failed_when: label_result.rc == 1 and 'exists' not in label_result.stderr - when: - - unlabel is defined - - unlabel - - not ansible_check_mode - - label in node_labels.stdout diff --git a/roles/openshift_logging/tasks/start_cluster.yaml b/roles/openshift_logging/tasks/start_cluster.yaml index 69d2b2b6b..edbb62c3e 100644 --- a/roles/openshift_logging/tasks/start_cluster.yaml +++ b/roles/openshift_logging/tasks/start_cluster.yaml @@ -1,125 +1,133 @@ --- -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get node -o name - register: fluentd_hosts +- name: Retrieve list of fluentd hosts + oc_obj: + state: list + kind: node when: "'--all' in openshift_logging_fluentd_hosts" - check_mode: no - changed_when: no + register: fluentd_hosts -- set_fact: openshift_logging_fluentd_hosts={{ fluentd_hosts.stdout_lines | regex_replace('node/', '') }} +- name: Set fact openshift_logging_fluentd_hosts + set_fact: + openshift_logging_fluentd_hosts: "{{ fluentd_hosts.results.results[0]['items'] | map(attribute='metadata.name') | list }}" when: "'--all' in openshift_logging_fluentd_hosts" - name: start fluentd - include: label_node.yaml - vars: - host: "{{fluentd_host}}" - label: "{{openshift_logging_fluentd_nodeselector.keys()[0]}}" - value: "{{openshift_logging_fluentd_nodeselector.values()[0]}}" + oc_label: + name: "{{ fluentd_host }}" + kind: node + state: add + labels: "{{ openshift_logging_fluentd_nodeselector | oo_dict_to_list_of_dict }}" with_items: "{{ openshift_logging_fluentd_hosts }}" loop_control: loop_var: fluentd_host -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=es -o name -n {{openshift_logging_namespace}} +- name: Retrieve elasticsearch + oc_obj: + state: list + kind: dc + selector: "component=es" + namespace: "{{openshift_logging_namespace}}" register: es_dc - check_mode: no - changed_when: no - name: start elasticsearch oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 1 - with_items: "{{es_dc.stdout_lines}}" + with_items: "{{ es_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=kibana -o name -n {{openshift_logging_namespace}} +- name: Retrieve kibana + oc_obj: + state: list + kind: dc + selector: "component=kibana" + namespace: "{{openshift_logging_namespace}}" register: kibana_dc - check_mode: no - changed_when: no - name: start kibana oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: "{{ openshift_logging_kibana_replica_count | default (1) }}" - with_items: "{{kibana_dc.stdout_lines}}" + with_items: "{{ kibana_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=curator -o name -n {{openshift_logging_namespace}} +- name: Retrieve curator + oc_obj: + state: list + kind: dc + selector: "component=curator" + namespace: "{{openshift_logging_namespace}}" register: curator_dc - check_mode: no - changed_when: no - name: start curator oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 1 - with_items: "{{curator_dc.stdout_lines}}" + with_items: "{{ curator_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=es-ops -o name -n {{openshift_logging_namespace}} +- name: Retrieve elasticsearch-ops + oc_obj: + state: list + kind: dc + selector: "component=es-ops" + namespace: "{{openshift_logging_namespace}}" register: es_dc - check_mode: no - changed_when: no - name: start elasticsearch-ops oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 1 - with_items: "{{es_dc.stdout_lines}}" + with_items: "{{ es_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object when: openshift_logging_use_ops | bool -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=kibana-ops -o name -n {{openshift_logging_namespace}} +- name: Retrieve kibana-ops + oc_obj: + state: list + kind: dc + selector: "component=kibana-ops" + namespace: "{{openshift_logging_namespace}}" register: kibana_dc - check_mode: no - changed_when: no - name: start kibana-ops oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: "{{ openshift_logging_kibana_ops_replica_count | default (1) }}" - with_items: "{{kibana_dc.stdout_lines}}" + with_items: "{{ kibana_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object when: openshift_logging_use_ops | bool -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=curator-ops -o name -n {{openshift_logging_namespace}} +- name: Retrieve curator + oc_obj: + state: list + kind: dc + selector: "component=curator-ops" + namespace: "{{openshift_logging_namespace}}" register: curator_dc - check_mode: no - changed_when: no - name: start curator-ops oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 1 - with_items: "{{curator_dc.stdout_lines}}" + with_items: "{{ curator_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object when: openshift_logging_use_ops | bool diff --git a/roles/openshift_logging/tasks/stop_cluster.yaml b/roles/openshift_logging/tasks/stop_cluster.yaml index 7826efabe..4b3722e29 100644 --- a/roles/openshift_logging/tasks/stop_cluster.yaml +++ b/roles/openshift_logging/tasks/stop_cluster.yaml @@ -1,118 +1,133 @@ --- -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get node -o name - register: fluentd_hosts +- name: Retrieve list of fluentd hosts + oc_obj: + state: list + kind: node when: "'--all' in openshift_logging_fluentd_hosts" - changed_when: no + register: fluentd_hosts -- set_fact: openshift_logging_fluentd_hosts={{ fluentd_hosts.stdout_lines | regex_replace('node/', '') }} +- name: Set fact openshift_logging_fluentd_hosts + set_fact: + openshift_logging_fluentd_hosts: "{{ fluentd_hosts.results.results[0]['items'] | map(attribute='metadata.name') | list }}" when: "'--all' in openshift_logging_fluentd_hosts" - name: stop fluentd - include: label_node.yaml - vars: - host: "{{fluentd_host}}" - label: "{{openshift_logging_fluentd_nodeselector.keys()[0]}}" - unlabel: True + oc_label: + name: "{{ fluentd_host }}" + kind: node + state: absent + labels: "{{ openshift_logging_fluentd_nodeselector | oo_dict_to_list_of_dict }}" with_items: "{{ openshift_logging_fluentd_hosts }}" loop_control: loop_var: fluentd_host -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=es -o name -n {{openshift_logging_namespace}} +- name: Retrieve elasticsearch + oc_obj: + state: list + kind: dc + selector: "component=es" + namespace: "{{openshift_logging_namespace}}" register: es_dc - changed_when: no - name: stop elasticsearch oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 0 - with_items: "{{es_dc.stdout_lines}}" + with_items: "{{ es_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=kibana -o name -n {{openshift_logging_namespace}} +- name: Retrieve kibana + oc_obj: + state: list + kind: dc + selector: "component=kibana" + namespace: "{{openshift_logging_namespace}}" register: kibana_dc - changed_when: no - name: stop kibana oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 0 - with_items: "{{kibana_dc.stdout_lines}}" + with_items: "{{ kibana_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=curator -o name -n {{openshift_logging_namespace}} +- name: Retrieve curator + oc_obj: + state: list + kind: dc + selector: "component=curator" + namespace: "{{openshift_logging_namespace}}" register: curator_dc - changed_when: no - name: stop curator oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 0 - with_items: "{{curator_dc.stdout_lines}}" + with_items: "{{ curator_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=es-ops -o name -n {{openshift_logging_namespace}} +- name: Retrieve elasticsearch-ops + oc_obj: + state: list + kind: dc + selector: "component=es-ops" + namespace: "{{openshift_logging_namespace}}" register: es_dc - changed_when: no - name: stop elasticsearch-ops oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 0 - with_items: "{{es_dc.stdout_lines}}" + with_items: "{{ es_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object when: openshift_logging_use_ops | bool -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=kibana-ops -o name -n {{openshift_logging_namespace}} +- name: Retrieve kibana-ops + oc_obj: + state: list + kind: dc + selector: "component=kibana-ops" + namespace: "{{openshift_logging_namespace}}" register: kibana_dc - changed_when: no - name: stop kibana-ops oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 0 - with_items: "{{kibana_dc.stdout_lines}}" + with_items: "{{ kibana_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object when: openshift_logging_use_ops | bool -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=curator-ops -o name -n {{openshift_logging_namespace}} +- name: Retrieve curator + oc_obj: + state: list + kind: dc + selector: "component=curator-ops" + namespace: "{{openshift_logging_namespace}}" register: curator_dc - changed_when: no - name: stop curator-ops oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" - kubeconfig: "{{mktemp.stdout}}/admin.kubeconfig" replicas: 0 - with_items: "{{curator_dc.stdout_lines}}" + with_items: "{{ curator_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object when: openshift_logging_use_ops | bool diff --git a/roles/openshift_logging/tasks/upgrade_logging.yaml b/roles/openshift_logging/tasks/upgrade_logging.yaml index 0dc31932c..0421cdf58 100644 --- a/roles/openshift_logging/tasks/upgrade_logging.yaml +++ b/roles/openshift_logging/tasks/upgrade_logging.yaml @@ -8,29 +8,34 @@ start_cluster: False # start ES so that we can run migrate script -- command: > - {{openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get dc -l component=es -o name -n {{openshift_logging_namespace}} +- name: Retrieve elasticsearch + oc_obj: + state: list + kind: dc + selector: "component=es" + namespace: "{{openshift_logging_namespace}}" register: es_dc - check_mode: no - name: start elasticsearch oc_scale: kind: dc - name: "{{object.split('/')[1]}}" + name: "{{ object }}" namespace: "{{openshift_logging_namespace}}" replicas: 1 - with_items: "{{es_dc.stdout_lines}}" + with_items: "{{ es_dc.results.results[0]['items'] | map(attribute='metadata.name') | list }}" loop_control: loop_var: object -- command: > - {{ openshift.common.client_binary}} --config={{mktemp.stdout}}/admin.kubeconfig get pods -n {{openshift_logging_namespace}} -l component=es -o jsonpath='{.items[?(@.status.phase == "Running")].metadata.name}' +- name: Wait for pods to stop + oc_obj: + state: list + kind: dc + selector: "component=es" + namespace: "{{openshift_logging_namespace}}" register: running_pod - until: running_pod.stdout != '' + until: running_pod.results.results.items[?(@.status.phase == "Running")].metadata.name != '' retries: 30 delay: 10 - changed_when: no - check_mode: no - name: Run upgrade script script: es_migration.sh {{openshift.common.config_base}}/logging/ca.crt {{openshift.common.config_base}}/logging/system.admin.key {{openshift.common.config_base}}/logging/system.admin.crt {{openshift_logging_es_host}} {{openshift_logging_es_port}} {{openshift_logging_namespace}} diff --git a/roles/openshift_manage_node/meta/main.yml b/roles/openshift_manage_node/meta/main.yml new file mode 100644 index 000000000..d90cd28cf --- /dev/null +++ b/roles/openshift_manage_node/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: OpenShift Red Hat + description: OpenShift Manage Node + company: Red Hat, Inc. + license: Apache License, Version 2.0 + min_ansible_version: 2.2 + platforms: + - name: EL + versions: + - 7 + categories: + - cloud +dependencies: +- role: lib_openshift diff --git a/roles/openshift_manage_node/tasks/main.yml b/roles/openshift_manage_node/tasks/main.yml index c06758833..9a883feed 100644 --- a/roles/openshift_manage_node/tasks/main.yml +++ b/roles/openshift_manage_node/tasks/main.yml @@ -1,23 +1,4 @@ --- -- name: Create temp directory for kubeconfig - command: mktemp -d /tmp/openshift-ansible-XXXXXX - register: mktemp - changed_when: False - delegate_to: "{{ openshift_master_host }}" - run_once: true - -- set_fact: - openshift_manage_node_kubeconfig: "{{ mktemp.stdout }}/admin.kubeconfig" - delegate_to: "{{ openshift_master_host }}" - run_once: true - -- name: Copy the admin client config(s) - command: > - cp {{ openshift.common.config_base }}/master/admin.kubeconfig {{ openshift_manage_node_kubeconfig }} - changed_when: False - delegate_to: "{{ openshift_master_host }}" - run_once: true - # Necessary because when you're on a node that's also a master the master will be # restarted after the node restarts docker and it will take up to 60 seconds for # systemd to start the master again @@ -46,38 +27,37 @@ run_once: true - name: Wait for Node Registration - command: > - {{ hostvars[openshift_master_host].openshift.common.client_binary }} get node {{ openshift.node.nodename }} - --config={{ openshift_manage_node_kubeconfig }} - -n default - register: omd_get_node - until: omd_get_node.rc == 0 + oc_obj: + name: "{{ openshift.node.nodename }}" + kind: node + state: list + register: get_node + until: "'metadata' in get_node.results.results[0]" retries: 50 delay: 5 - changed_when: false when: "'nodename' in openshift.node" delegate_to: "{{ openshift_master_host }}" - name: Set node schedulability - command: > - {{ hostvars[openshift_master_host].openshift.common.client_binary }} adm manage-node {{ openshift.node.nodename }} --schedulable={{ 'true' if openshift.node.schedulable | bool else 'false' }} - --config={{ openshift_manage_node_kubeconfig }} - -n default + oadm_manage_node: + node: "{{ openshift.node.nodename | lower }}" + schedulable: "{{ 'true' if openshift.node.schedulable | bool else 'false' }}" + retries: 10 + delay: 5 + register: node_schedulable + until: node_schedulable|succeeded when: "'nodename' in openshift.node" delegate_to: "{{ openshift_master_host }}" - name: Label nodes - command: > - {{ hostvars[openshift_master_host].openshift.common.client_binary }} label --overwrite node {{ openshift.node.nodename }} {{ openshift.node.labels | oo_combine_dict }} - --config={{ openshift_manage_node_kubeconfig }} - -n default - when: "'nodename' in openshift.node and 'labels' in openshift.node and openshift.node.labels != {}" - delegate_to: "{{ openshift_master_host }}" - -- name: Delete temp directory - file: - name: "{{ mktemp.stdout }}" - state: absent - changed_when: False + oc_label: + name: "{{ openshift.node.nodename }}" + kind: node + state: add + labels: "{{ openshift.node.labels | oo_dict_to_list_of_dict }}" + namespace: default + when: + - "'nodename' in openshift.node" + - "'labels' in openshift.node" + - openshift.node.labels != {} delegate_to: "{{ openshift_master_host }}" - run_once: true diff --git a/roles/openshift_manageiq/meta/main.yml b/roles/openshift_manageiq/meta/main.yml new file mode 100644 index 000000000..6c96a91bf --- /dev/null +++ b/roles/openshift_manageiq/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: Erez Freiberger + description: ManageIQ + company: Red Hat, Inc. + license: Apache License, Version 2.0 + min_ansible_version: 2.1 + platforms: + - name: EL + versions: + - 7 + categories: + - cloud +dependencies: +- role: lib_openshift diff --git a/roles/openshift_manageiq/tasks/main.yaml b/roles/openshift_manageiq/tasks/main.yaml index a7214482f..f202486a5 100644 --- a/roles/openshift_manageiq/tasks/main.yaml +++ b/roles/openshift_manageiq/tasks/main.yaml @@ -18,27 +18,15 @@ failed_when: "'already exists' not in osmiq_create_mi_project.stderr and osmiq_create_mi_project.rc != 0" changed_when: osmiq_create_mi_project.rc == 0 -- name: Create Admin Service Account - shell: > - echo {{ manageiq_service_account | to_json | quote }} | - {{ openshift.common.client_binary }} create - -n management-infra - --config={{manage_iq_tmp_conf}} - -f - - register: osmiq_create_service_account - failed_when: "'already exists' not in osmiq_create_service_account.stderr and osmiq_create_service_account.rc != 0" - changed_when: osmiq_create_service_account.rc == 0 - -- name: Create Image Inspector Service Account - shell: > - echo {{ manageiq_image_inspector_service_account | to_json | quote }} | - {{ openshift.common.client_binary }} create - -n management-infra - --config={{manage_iq_tmp_conf}} - -f - - register: osmiq_create_service_account - failed_when: "'already exists' not in osmiq_create_service_account.stderr and osmiq_create_service_account.rc != 0" - changed_when: osmiq_create_service_account.rc == 0 +- name: Create Admin and Image Inspector Service Account + oc_serviceaccount: + kubeconfig: "{{ openshift_master_config_dir }}/admin.kubeconfig" + name: "{{ item }}" + namespace: management-infra + state: present + with_items: + - management-admin + - inspector-admin - name: Create Cluster Role shell: > @@ -59,6 +47,9 @@ register: oshawkular_create_cluster_role failed_when: "'already exists' not in oshawkular_create_cluster_role.stderr and oshawkular_create_cluster_role.rc != 0" changed_when: oshawkular_create_cluster_role.rc == 0 + # AUDIT:changed_when_note: Checking the return code is insufficient + # here. We really need to verify the if the role even exists before + # we run this task. - name: Configure role/user permissions command: > @@ -68,6 +59,10 @@ register: osmiq_perm_task failed_when: "'already exists' not in osmiq_perm_task.stderr and osmiq_perm_task.rc != 0" changed_when: osmiq_perm_task.rc == 0 + # AUDIT:changed_when_note: Checking the return code is insufficient + # here. We really need to compare the current role/user permissions + # with their expected state. I think we may have a module for this? + - name: Configure 3_2 role/user permissions command: > diff --git a/roles/openshift_manageiq/vars/main.yml b/roles/openshift_manageiq/vars/main.yml index 3f24fd6be..9936bb126 100644 --- a/roles/openshift_manageiq/vars/main.yml +++ b/roles/openshift_manageiq/vars/main.yml @@ -1,4 +1,5 @@ --- +openshift_master_config_dir: "{{ openshift.common.config_base }}/master" manageiq_cluster_role: apiVersion: v1 kind: ClusterRole @@ -24,18 +25,6 @@ manageiq_metrics_admin_clusterrole: verbs: - '*' -manageiq_service_account: - apiVersion: v1 - kind: ServiceAccount - metadata: - name: management-admin - -manageiq_image_inspector_service_account: - apiVersion: v1 - kind: ServiceAccount - metadata: - name: inspector-admin - manage_iq_tmp_conf: /tmp/manageiq_admin.kubeconfig manage_iq_tasks: diff --git a/roles/openshift_master/tasks/system_container.yml b/roles/openshift_master/tasks/system_container.yml index e3e3d7948..a0e1ac75e 100644 --- a/roles/openshift_master/tasks/system_container.yml +++ b/roles/openshift_master/tasks/system_container.yml @@ -17,7 +17,8 @@ changed_when: "'Extracting' in update_result.stdout" when: - ("master" in result.stdout) - - (openshift.common.version is defined) and (openshift.common.version == openshift_version) | bool + - l_is_same_version + - not l_is_ha - name: Uninstall Master system container package command: > @@ -25,12 +26,52 @@ failed_when: False when: - ("master" in result.stdout) - - (openshift.common.version is not defined) or (openshift.common.version != openshift_version) | bool + - not l_is_same_version + - not l_is_ha - name: Install Master system container package command: > atomic install --system --name={{ openshift.common.service_type }}-master {{ openshift.common.system_images_registry }}/{{ openshift.master.master_system_image }}:{{ openshift_image_tag }} when: - - (openshift.common.version is not defined) or (openshift.common.version != openshift_version) or ("master" not in result.stdout) | bool + - not l_is_same_version or ("master" not in result.stdout) | bool + - not l_is_ha + notify: + - restart master + +# HA +- name: Update Master HA system container package + command: > + atomic containers update {{ openshift.common.service_type }}-master-{{ item }} + register: update_result + changed_when: "'Extracting' in update_result.stdout" + with_items: + - api + - controllers + when: + - ("master" in result.stdout) + - l_is_same_version + - l_is_ha + +- name: Uninstall Master HA system container package + command: > + atomic uninstall {{ openshift.common.service_type }}-master-{{ item }} + failed_when: False + with_items: + - api + - controllers + when: + - ("master" in result.stdout) + - not l_is_same_version + - l_is_ha + +- name: Install Master HA system container package + command: > + atomic install --system --set COMMAND={{ item }} --name={{ openshift.common.service_type }}-master-{{ item }} {{ openshift.common.system_images_registry }}/{{ openshift.master.master_system_image }}:{{ openshift_image_tag }} + with_items: + - api + - controllers + when: + - not l_is_same_version or ("master" not in result.stdout) | bool + - l_is_ha notify: - restart master diff --git a/roles/openshift_master/tasks/systemd_units.yml b/roles/openshift_master/tasks/systemd_units.yml index 4ab98cbbb..506c8b129 100644 --- a/roles/openshift_master/tasks/systemd_units.yml +++ b/roles/openshift_master/tasks/systemd_units.yml @@ -27,7 +27,9 @@ template: src: "master_docker/master.docker.service.j2" dest: "{{ containerized_svc_dir }}/{{ openshift.common.service_type }}-master.service" - when: openshift.common.is_containerized | bool and (openshift.master.ha is not defined or not openshift.master.ha | bool and not openshift.common.is_master_system_container | bool) + when: + - openshift.common.is_containerized | bool and (openshift.master.ha is not defined or not openshift.master.ha) | bool + - not openshift.common.is_master_system_container | bool register: create_master_unit_file - command: systemctl daemon-reload @@ -37,7 +39,9 @@ template: src: "{{ ha_svc_template_path }}/atomic-openshift-master-{{ item }}.service.j2" dest: "{{ containerized_svc_dir }}/{{ openshift.common.service_type }}-master-{{ item }}.service" - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + when: + - openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + - not openshift.common.is_master_system_container | bool with_items: - api - controllers diff --git a/roles/openshift_master/vars/main.yml b/roles/openshift_master/vars/main.yml index 01cd28c66..1e157097d 100644 --- a/roles/openshift_master/vars/main.yml +++ b/roles/openshift_master/vars/main.yml @@ -18,3 +18,6 @@ openshift_master_valid_grant_methods: - auto - prompt - deny + +l_is_ha: "{{ openshift.master.ha is defined and openshift.master.ha | bool }}" +l_is_same_version: "{{ (openshift.common.version is defined) and (openshift.common.version == openshift_version) | bool }}" diff --git a/roles/openshift_master_facts/filter_plugins/oo_filters.py b/roles/openshift_master_facts/filter_plugins/oo_filters.py new file mode 120000 index 000000000..6f9bc47c1 --- /dev/null +++ b/roles/openshift_master_facts/filter_plugins/oo_filters.py @@ -0,0 +1 @@ +../../../filter_plugins/oo_filters.py
\ No newline at end of file diff --git a/filter_plugins/openshift_master.py b/roles/openshift_master_facts/filter_plugins/openshift_master.py index 6d009077a..6d009077a 100644 --- a/filter_plugins/openshift_master.py +++ b/roles/openshift_master_facts/filter_plugins/openshift_master.py diff --git a/roles/openshift_master_facts/lookup_plugins/oo_option.py b/roles/openshift_master_facts/lookup_plugins/oo_option.py new file mode 120000 index 000000000..5ae43f8dd --- /dev/null +++ b/roles/openshift_master_facts/lookup_plugins/oo_option.py @@ -0,0 +1 @@ +../../../lookup_plugins/oo_option.py
\ No newline at end of file diff --git a/roles/openshift_master_facts/tasks/main.yml b/roles/openshift_master_facts/tasks/main.yml index 74885d713..6f8f09b22 100644 --- a/roles/openshift_master_facts/tasks/main.yml +++ b/roles/openshift_master_facts/tasks/main.yml @@ -6,7 +6,8 @@ openshift_master_default_subdomain: "{{ osm_default_subdomain | default(None) }}" when: openshift_master_default_subdomain is not defined -- fail: +- name: Verify required variables are set + fail: msg: openshift_master_default_subdomain must be set to deploy metrics when: openshift_hosted_metrics_deploy | default(false) | bool and openshift_master_default_subdomain | default("") == "" @@ -17,7 +18,8 @@ # path must stay consistent. As such if openshift_hosted_metrics_public_url is set in # inventory, we extract the hostname, and then reset openshift_hosted_metrics_public_url # to the format that we know is valid. (This may change in future) -- set_fact: +- name: Set g_metrics_hostname + set_fact: g_metrics_hostname: "{{ openshift_hosted_metrics_public_url | default('hawkular-metrics.' ~ (openshift_master_default_subdomain)) | oo_hostname_from_url }}" @@ -108,7 +110,8 @@ path: "{{ openshift_master_scheduler_conf }}" register: scheduler_config_stat -- set_fact: +- name: Set Default scheduler predicates and priorities + set_fact: openshift_master_scheduler_default_predicates: "{{ lookup('openshift_master_facts_default_predicates') }}" openshift_master_scheduler_default_priorities: "{{ lookup('openshift_master_facts_default_priorities') }}" @@ -118,14 +121,17 @@ src: "{{ openshift_master_scheduler_conf }}" register: current_scheduler_config - - set_fact: + - name: Set openshift_master_scheduler_current_config + set_fact: openshift_master_scheduler_current_config: "{{ current_scheduler_config.content | b64decode | from_json }}" - - fail: + - name: Test if scheduler config is readable + fail: msg: "Unknown scheduler config apiVersion {{ openshift_master_scheduler_config.apiVersion }}" when: "{{ openshift_master_scheduler_current_config.apiVersion | default(None) != 'v1' }}" - - set_fact: + - name: Set current scheduler predicates and priorities + set_fact: openshift_master_scheduler_current_predicates: "{{ openshift_master_scheduler_current_config.predicates }}" openshift_master_scheduler_current_priorities: "{{ openshift_master_scheduler_current_config.priorities }}" when: "{{ scheduler_config_stat.stat.exists }}" diff --git a/roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py b/roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py index 68b6deb88..bcfaa55c8 100644 --- a/roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py +++ b/roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py @@ -175,23 +175,6 @@ class TestOpenShiftMasterFactsDefaultPredicates(object): else: assert_equal(results, default_predicates) - def check_defaults_override_vars(self, release, deployment_type, - default_predicates, regions_enabled, - extra_facts=None): - facts = copy.deepcopy(self.default_facts) - facts['openshift']['common']['short_version'] = release - facts['openshift']['common']['deployment_type'] = deployment_type - if extra_facts is not None: - for fact in extra_facts: - facts[fact] = extra_facts[fact] - results = self.lookup.run(None, variables=facts, - regions_enabled=regions_enabled, - return_set_vars=False) - if regions_enabled: - assert_equal(results, default_predicates + [REGION_PREDICATE]) - else: - assert_equal(results, default_predicates) - def test_openshift_version(self): for regions_enabled in (True, False): for release, deployment_type, default_predicates in TEST_VARS: diff --git a/roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py b/roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py index 4e44a2b3d..7ddc5dcb2 100644 --- a/roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py +++ b/roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py @@ -160,23 +160,6 @@ class TestOpenShiftMasterFactsDefaultPredicates(object): else: assert_equal(results, default_priorities) - def check_defaults_override_vars(self, release, deployment_type, - default_priorities, zones_enabled, - extra_facts=None): - facts = copy.deepcopy(self.default_facts) - facts['openshift']['common']['short_version'] = release - facts['openshift']['common']['deployment_type'] = deployment_type - if extra_facts is not None: - for fact in extra_facts: - facts[fact] = extra_facts[fact] - results = self.lookup.run(None, variables=facts, - zones_enabled=zones_enabled, - return_set_vars=False) - if zones_enabled: - assert_equal(results, default_priorities + [ZONE_PRIORITY]) - else: - assert_equal(results, default_priorities) - def test_openshift_version(self): for zones_enabled in (True, False): for release, deployment_type, default_priorities in TEST_VARS: diff --git a/roles/openshift_metrics/defaults/main.yaml b/roles/openshift_metrics/defaults/main.yaml index 0cfbac8a9..edaa7d0df 100644 --- a/roles/openshift_metrics/defaults/main.yaml +++ b/roles/openshift_metrics/defaults/main.yaml @@ -32,10 +32,10 @@ openshift_metrics_heapster_requests_memory: 0.9375G openshift_metrics_heapster_requests_cpu: null openshift_metrics_heapster_nodeselector: "" -openshift_metrics_hostname: "hawkular-metrics.{{openshift_master_default_subdomain}}" +openshift_metrics_hawkular_hostname: "hawkular-metrics.{{openshift_master_default_subdomain}}" openshift_metrics_duration: 7 -openshift_metrics_resolution: 15s +openshift_metrics_resolution: 30s ##### # Caution should be taken for the following defaults before diff --git a/roles/openshift_metrics/tasks/generate_certificates.yaml b/roles/openshift_metrics/tasks/generate_certificates.yaml index f7cba0093..7af3f9467 100644 --- a/roles/openshift_metrics/tasks/generate_certificates.yaml +++ b/roles/openshift_metrics/tasks/generate_certificates.yaml @@ -6,6 +6,6 @@ --key='{{ mktemp.stdout }}/ca.key' --cert='{{ mktemp.stdout }}/ca.crt' --serial='{{ mktemp.stdout }}/ca.serial.txt' - --name="metrics-signer@$(date +%s)" + --name="metrics-signer@{{lookup('pipe','date +%s')}}" - include: generate_hawkular_certificates.yaml diff --git a/roles/openshift_metrics/tasks/generate_hawkular_certificates.yaml b/roles/openshift_metrics/tasks/generate_hawkular_certificates.yaml index 854697abb..9e7140bfa 100644 --- a/roles/openshift_metrics/tasks/generate_hawkular_certificates.yaml +++ b/roles/openshift_metrics/tasks/generate_hawkular_certificates.yaml @@ -3,7 +3,7 @@ include: setup_certificate.yaml vars: component: hawkular-metrics - hostnames: "hawkular-metrics,{{ openshift_metrics_hawkular_hostname }}" + hostnames: "hawkular-metrics,hawkular-metrics.{{ openshift_metrics_project }}.svc.cluster.local,{{ openshift_metrics_hawkular_hostname }}" changed_when: no - name: generate hawkular-cassandra certificates diff --git a/roles/openshift_node/tasks/node_system_container.yml b/roles/openshift_node/tasks/node_system_container.yml index 01e2d33c7..3ae5c7600 100644 --- a/roles/openshift_node/tasks/node_system_container.yml +++ b/roles/openshift_node/tasks/node_system_container.yml @@ -16,14 +16,16 @@ register: update_result changed_when: "'Extracting' in update_result.stdout" when: - - (openshift.common.version is defined) and (openshift.common.version == openshift_version) and ("node" in result.stdout) | bool + - l_is_same_version + - ("node" in result.stdout) - name: Uninstall Node system container package command: > atomic uninstall {{ openshift.common.service_type }}-node failed_when: False when: - - (openshift.common.version is not defined) or (openshift.common.version != openshift_version) and ("node" in result.stdout) | bool + - not l_is_same_version + - ("node" in result.stdout) - name: Install Node system container package command: > @@ -31,4 +33,4 @@ register: install_node_result changed_when: "'Extracting' in pull_result.stdout" when: - - (openshift.common.version is not defined) or (openshift.common.version != openshift_version) or ("node" not in result.stdout) | bool + - not l_is_same_version or ("node" not in result.stdout) | bool diff --git a/roles/openshift_node/tasks/openvswitch_system_container.yml b/roles/openshift_node/tasks/openvswitch_system_container.yml index 47fac99eb..6114230d0 100644 --- a/roles/openshift_node/tasks/openvswitch_system_container.yml +++ b/roles/openshift_node/tasks/openvswitch_system_container.yml @@ -9,8 +9,6 @@ command: > atomic containers list --no-trunc -a -f container=openvswitch register: result - when: - - openshift.common.is_openvswitch_system_container | bool - name: Update OpenvSwitch system container package command: > @@ -18,19 +16,21 @@ register: update_result changed_when: "'Extracting' in update_result.stdout" when: - - (openshift.common.version is defined) and (openshift.common.version == openshift_version) and ("openvswitch" in result.stdout) | bool + - l_is_same_version + - ("openvswitch" in result.stdout) | bool - name: Uninstall OpenvSwitch system container package command: > atomic uninstall openvswitch failed_when: False when: - - (openshift.common.version is not defined) or (openshift.common.version != openshift_version) and ("openvswitch" in result.stdout) | bool + - not l_is_same_version + - ("openvswitch" in result.stdout) | bool - name: Install OpenvSwitch system container package command: > atomic install --system --name=openvswitch {{ openshift.common.system_images_registry }}/{{ openshift.node.ovs_system_image }}:{{ openshift_image_tag }} when: - - (openshift.common.version is not defined) or (openshift.common.version != openshift_version) or ("openvswitch" not in result.stdout) | bool + - not l_is_same_version or ("openvswitch" not in result.stdout) | bool notify: - restart docker diff --git a/roles/openshift_node/vars/main.yml b/roles/openshift_node/vars/main.yml new file mode 100644 index 000000000..0c2abf3b9 --- /dev/null +++ b/roles/openshift_node/vars/main.yml @@ -0,0 +1,2 @@ +--- +l_is_same_version: "{{ (openshift.common.version is defined) and (openshift.common.version == openshift_version) | bool }}" diff --git a/roles/openshift_node_upgrade/README.md b/roles/openshift_node_upgrade/README.md index e21bee412..66bce38ec 100644 --- a/roles/openshift_node_upgrade/README.md +++ b/roles/openshift_node_upgrade/README.md @@ -82,7 +82,7 @@ Including an example of how to use your role (for instance, with variables passe - name: Drain Node for Kubelet upgrade command: > - {{ hostvars[groups.oo_first_master.0].openshift.common.admin_binary }} drain {{ openshift.node.nodename | lower }} --force --delete-local-data + {{ hostvars[groups.oo_first_master.0].openshift.common.admin_binary }} drain {{ openshift.node.nodename | lower }} --force --delete-local-data --ignore-daemonsets delegate_to: "{{ groups.oo_first_master.0 }}" roles: diff --git a/roles/openshift_node_upgrade/tasks/main.yml b/roles/openshift_node_upgrade/tasks/main.yml index b1d5f0e0f..609ca2a6e 100644 --- a/roles/openshift_node_upgrade/tasks/main.yml +++ b/roles/openshift_node_upgrade/tasks/main.yml @@ -75,3 +75,9 @@ # so containerized services should restart quickly as well. retries: 24 delay: 5 + # AUDIT:changed_when: `false` because we are only inspecting the + # state of the node, we aren't changing anything (we changed node + # service state in the previous task). You could say we shouldn't + # override this because something will be changing (the state of a + # service), but that should be part of the last task. + changed_when: false diff --git a/roles/openshift_preflight/README.md b/roles/openshift_preflight/README.md deleted file mode 100644 index b6d3542d3..000000000 --- a/roles/openshift_preflight/README.md +++ /dev/null @@ -1,52 +0,0 @@ -OpenShift Preflight Checks -========================== - -This role detects common problems prior to installing OpenShift. - -Requirements ------------- - -* Ansible 2.2+ - -Role Variables --------------- - -None - -Dependencies ------------- - -None - -Example Playbook ----------------- - -```yaml ---- -- hosts: OSEv3 - roles: - - openshift_preflight/init - -- hosts: OSEv3 - name: checks that apply to all hosts - gather_facts: no - ignore_errors: yes - roles: - - openshift_preflight/common - -- hosts: OSEv3 - name: verify check results - gather_facts: no - roles: - - openshift_preflight/verify_status -``` - -License -------- - -Apache License Version 2.0 - -Author Information ------------------- - -Customer Success team (dev@lists.openshift.redhat.com) diff --git a/roles/openshift_preflight/common/meta/main.yml b/roles/openshift_preflight/common/meta/main.yml deleted file mode 100644 index 6f23cbf3b..000000000 --- a/roles/openshift_preflight/common/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -dependencies: - - role: openshift_preflight/base diff --git a/roles/openshift_preflight/common/tasks/main.yml b/roles/openshift_preflight/common/tasks/main.yml deleted file mode 100644 index f1a4a160e..000000000 --- a/roles/openshift_preflight/common/tasks/main.yml +++ /dev/null @@ -1,21 +0,0 @@ ---- -# check content available on all hosts -- when: not openshift.common.is_containerized | bool - block: - - - name: determine if yum update will work - action: check_yum_update - register: r - - - set_fact: - oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'determine if yum update will work'})] }}" - - - name: determine if expected version matches what is available - aos_version: - version: "{{ openshift_release }}" - when: - - deployment_type == "openshift-enterprise" - register: r - - - set_fact: - oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'determine if expected version matches what is available'})] }}" diff --git a/roles/openshift_preflight/init/tasks/main.yml b/roles/openshift_preflight/init/tasks/main.yml deleted file mode 100644 index bf2d82196..000000000 --- a/roles/openshift_preflight/init/tasks/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -- name: set common variables - set_fact: - oo_preflight_check_results: "{{ oo_preflight_check_results | default([]) }}" diff --git a/roles/openshift_preflight/masters/meta/main.yml b/roles/openshift_preflight/masters/meta/main.yml deleted file mode 100644 index 6f23cbf3b..000000000 --- a/roles/openshift_preflight/masters/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -dependencies: - - role: openshift_preflight/base diff --git a/roles/openshift_preflight/masters/tasks/main.yml b/roles/openshift_preflight/masters/tasks/main.yml deleted file mode 100644 index 35fb1e3ca..000000000 --- a/roles/openshift_preflight/masters/tasks/main.yml +++ /dev/null @@ -1,31 +0,0 @@ ---- -# determine if yum install of master pkgs will work -- when: not openshift.common.is_containerized | bool - block: - - - name: main master packages availability - check_yum_update: - packages: - - "{{ openshift.common.service_type }}" - - "{{ openshift.common.service_type }}-clients" - - "{{ openshift.common.service_type }}-master" - register: r - - - set_fact: - oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'main master packages availability'})] }}" - - - name: other master packages availability - check_yum_update: - packages: - - etcd - - bash-completion - - cockpit-bridge - - cockpit-docker - - cockpit-kubernetes - - cockpit-shell - - cockpit-ws - - httpd-tools - register: r - - - set_fact: - oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'other master packages availability'})] }}" diff --git a/roles/openshift_preflight/nodes/meta/main.yml b/roles/openshift_preflight/nodes/meta/main.yml deleted file mode 100644 index 6f23cbf3b..000000000 --- a/roles/openshift_preflight/nodes/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -dependencies: - - role: openshift_preflight/base diff --git a/roles/openshift_preflight/nodes/tasks/main.yml b/roles/openshift_preflight/nodes/tasks/main.yml deleted file mode 100644 index a10e69024..000000000 --- a/roles/openshift_preflight/nodes/tasks/main.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -# determine if yum install of node pkgs will work -- when: not openshift.common.is_containerized | bool - block: - - - name: main node packages availability - check_yum_update: - packages: - - "{{ openshift.common.service_type }}" - - "{{ openshift.common.service_type }}-node" - - "{{ openshift.common.service_type }}-sdn-ovs" - register: r - - - set_fact: - oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'main node packages availability'})] }}" - - - name: other node packages availability - check_yum_update: - packages: - - docker - - PyYAML - - firewalld - - iptables - - iptables-services - - nfs-utils - - ntp - - yum-utils - - dnsmasq - - libselinux-python - - ceph-common - - glusterfs-fuse - - iscsi-initiator-utils - - pyparted - - python-httplib2 - - openssl - - flannel - - bind - register: r - - - set_fact: - oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'other node packages availability'})] }}" diff --git a/roles/openshift_preflight/verify_status/tasks/main.yml b/roles/openshift_preflight/verify_status/tasks/main.yml deleted file mode 100644 index 36ccf648a..000000000 --- a/roles/openshift_preflight/verify_status/tasks/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -- name: find check failures - set_fact: - oo_preflight_check_failures: "{{ oo_preflight_check_results | select('failed', 'equalto', True) | list }}" - -- name: ensure all checks succeed - action: fail - when: oo_preflight_check_failures diff --git a/roles/openshift_serviceaccounts/meta/main.yml b/roles/openshift_serviceaccounts/meta/main.yml index a2c9fee70..7a30c220f 100644 --- a/roles/openshift_serviceaccounts/meta/main.yml +++ b/roles/openshift_serviceaccounts/meta/main.yml @@ -13,3 +13,4 @@ galaxy_info: - cloud dependencies: - { role: openshift_facts } +- { role: lib_openshift } diff --git a/roles/openshift_serviceaccounts/tasks/main.yml b/roles/openshift_serviceaccounts/tasks/main.yml index d83ccf7de..1d570fa5b 100644 --- a/roles/openshift_serviceaccounts/tasks/main.yml +++ b/roles/openshift_serviceaccounts/tasks/main.yml @@ -1,21 +1,11 @@ --- -- name: test if service accounts exists - command: > - {{ openshift.common.client_binary }} get sa {{ item }} -n {{ openshift_serviceaccounts_namespace }} - with_items: "{{ openshift_serviceaccounts_names }}" - failed_when: false - changed_when: false - register: account_test - - name: create the service account - shell: > - echo {{ lookup('template', '../templates/serviceaccount.j2') - | from_yaml | to_json | quote }} | {{ openshift.common.client_binary }} - -n {{ openshift_serviceaccounts_namespace }} create -f - - when: item.1.rc != 0 - with_together: + oc_serviceaccount: + name: "{{ item }}" + namespace: "{{ openshift_serviceaccounts_namespace }}" + state: present + with_items: - "{{ openshift_serviceaccounts_names }}" - - "{{ account_test.results }}" - name: test if scc needs to be updated command: > @@ -5,12 +5,15 @@ universal=1 [nosetests] -tests=roles/openshift_master_facts/test/, test/ +tests=roles/lib_openshift/src/test/unit, + roles/lib_utils/src/test/unit, + roles/openshift_master_facts/test, + test verbosity=2 with-coverage=1 cover-html=1 cover-inclusive=1 -cover-min-percentage=70 +cover-min-percentage=25 cover-erase=1 detailed-errors=1 cover-branches=1 diff --git a/test-requirements.txt b/test-requirements.txt index 2ee1e657d..8bfc1b594 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,3 +9,4 @@ PyYAML yamllint nose coverage +mock diff --git a/utils/src/ooinstall/cli_installer.py b/utils/src/ooinstall/cli_installer.py index b787741d7..a6d784dea 100644 --- a/utils/src/ooinstall/cli_installer.py +++ b/utils/src/ooinstall/cli_installer.py @@ -72,12 +72,6 @@ You might want to override the default subdomain used for exposed routes. If you return click.prompt('New default subdomain (ENTER for none)', default='') -def list_hosts(hosts): - hosts_idx = range(len(hosts)) - for idx in hosts_idx: - click.echo(' {}: {}'.format(idx, hosts[idx])) - - def collect_hosts(oo_cfg, existing_env=False, masters_set=False, print_summary=True): """ Collect host information from user. This will later be filled in using @@ -656,20 +650,6 @@ https://docs.openshift.com/enterprise/latest/admin_guide/install/prerequisites.h return oo_cfg -def get_role_variable(oo_cfg, role_name, variable_name): - try: - target_role = next(role for role in oo_cfg.deployment.roles if role.name is role_name) - target_variable = target_role.variables[variable_name] - return target_variable - except (StopIteration, KeyError): - return None - - -def set_role_variable(oo_cfg, role_name, variable_name, variable_value): - target_role = next(role for role in oo_cfg.deployment.roles if role.name is role_name) - target_role[variable_name] = variable_value - - def collect_new_nodes(oo_cfg): click.clear() click.echo('*** New Node Configuration ***') diff --git a/utils/src/ooinstall/oo_config.py b/utils/src/ooinstall/oo_config.py index cf14105af..9670c03bb 100644 --- a/utils/src/ooinstall/oo_config.py +++ b/utils/src/ooinstall/oo_config.py @@ -126,15 +126,6 @@ class Host(object): """ Does this host have the etcd role """ return 'etcd' in self.roles - def is_etcd_member(self, all_hosts): - """ Will this host be a member of a standalone etcd cluster. """ - if not self.is_master(): - return False - masters = [host for host in all_hosts if host.is_master()] - if len(masters) > 1: - return True - return False - def is_dedicated_node(self): """ Will this host be a dedicated node. (not a master) """ return self.is_node() and not self.is_master() @@ -436,12 +427,6 @@ class OOConfig(object): def __str__(self): return self.yaml() - def get_host(self, name): - for host in self.deployment.hosts: - if host.connect_to == name: - return host - return None - def get_host_roles_set(self): roles_set = set() for host in self.deployment.hosts: diff --git a/utils/src/ooinstall/variants.py b/utils/src/ooinstall/variants.py index a45be98bf..f25266f29 100644 --- a/utils/src/ooinstall/variants.py +++ b/utils/src/ooinstall/variants.py @@ -39,7 +39,7 @@ class Variant(object): # WARNING: Keep the versions ordered, most recent first: OSE = Variant('openshift-enterprise', 'OpenShift Container Platform', [ - Version('3.4', 'openshift-enterprise'), + Version('3.5', 'openshift-enterprise'), ]) REG = Variant('openshift-enterprise', 'Registry', [ @@ -51,6 +51,7 @@ origin = Variant('origin', 'OpenShift Origin', [ ]) LEGACY = Variant('openshift-enterprise', 'OpenShift Container Platform', [ + Version('3.4', 'openshift-enterprise'), Version('3.3', 'openshift-enterprise'), Version('3.2', 'openshift-enterprise'), Version('3.1', 'openshift-enterprise'), |