diff options
46 files changed, 881 insertions, 292 deletions
diff --git a/playbooks/adhoc/uninstall.yml b/playbooks/adhoc/uninstall.yml index 6119990fe..1c8257162 100644 --- a/playbooks/adhoc/uninstall.yml +++ b/playbooks/adhoc/uninstall.yml @@ -313,7 +313,7 @@ - name: restart docker service: name=docker state=restarted ignore_errors: true - when: "container_engine.state != 'started'" + when: not (container_engine | changed) - name: restart NetworkManager service: name=NetworkManager state=restarted diff --git a/playbooks/common/openshift-cluster/config.yml b/playbooks/common/openshift-cluster/config.yml index c320b80ed..1482b3a3f 100644 --- a/playbooks/common/openshift-cluster/config.yml +++ b/playbooks/common/openshift-cluster/config.yml @@ -8,10 +8,10 @@ tags: - always gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: disable + roles: + - role: openshift_excluder + r_openshift_excluder_action: disable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" - include: ../openshift-etcd/config.yml tags: @@ -50,7 +50,7 @@ tags: - always gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: enable + roles: + - role: openshift_excluder + r_openshift_excluder_action: enable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" diff --git a/playbooks/common/openshift-cluster/upgrades/disable_master_excluders.yml b/playbooks/common/openshift-cluster/upgrades/disable_master_excluders.yml index 35da3b6c3..800621857 100644 --- a/playbooks/common/openshift-cluster/upgrades/disable_master_excluders.yml +++ b/playbooks/common/openshift-cluster/upgrades/disable_master_excluders.yml @@ -2,13 +2,11 @@ - name: Disable excluders hosts: oo_masters_to_config gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: verify_upgrade - - include_role: - name: openshift_excluder - tasks_from: disable - vars: - openshift_excluder_package_state: latest - docker_excluder_package_state: latest + roles: + - role: openshift_excluder + r_openshift_excluder_action: disable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" + r_openshift_excluder_verify_upgrade: true + r_openshift_excluder_upgrade_target: "{{ openshift_upgrade_target }}" + r_openshift_excluder_package_state: latest + r_openshift_excluder_docker_package_state: latest diff --git a/playbooks/common/openshift-cluster/upgrades/disable_node_excluders.yml b/playbooks/common/openshift-cluster/upgrades/disable_node_excluders.yml index 847c22085..7988e97ab 100644 --- a/playbooks/common/openshift-cluster/upgrades/disable_node_excluders.yml +++ b/playbooks/common/openshift-cluster/upgrades/disable_node_excluders.yml @@ -2,13 +2,11 @@ - name: Disable excluders hosts: oo_nodes_to_config gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: verify_upgrade - - include_role: - name: openshift_excluder - tasks_from: disable - vars: - openshift_excluder_package_state: latest - docker_excluder_package_state: latest + roles: + - role: openshift_excluder + r_openshift_excluder_action: disable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" + r_openshift_excluder_verify_upgrade: true + r_openshift_excluder_upgrade_target: "{{ openshift_upgrade_target }}" + r_openshift_excluder_package_state: latest + r_openshift_excluder_docker_package_state: latest diff --git a/playbooks/common/openshift-cluster/upgrades/post_control_plane.yml b/playbooks/common/openshift-cluster/upgrades/post_control_plane.yml index 8a60ef236..9b76f1dd0 100644 --- a/playbooks/common/openshift-cluster/upgrades/post_control_plane.yml +++ b/playbooks/common/openshift-cluster/upgrades/post_control_plane.yml @@ -124,7 +124,7 @@ tags: - always gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: enable + roles: + - role: openshift_excluder + r_openshift_excluder_action: enable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" diff --git a/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml b/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml index 2a5ac0aef..4d455fe0a 100644 --- a/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml +++ b/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml @@ -34,6 +34,9 @@ - openshift_facts - docker - openshift_node_upgrade + - role: openshift_excluder + r_openshift_excluder_action: enable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" post_tasks: - name: Set node schedulability @@ -46,13 +49,3 @@ register: node_schedulable until: node_schedulable|succeeded when: node_unschedulable|changed - -- name: Re-enable excluder if it was previously enabled - hosts: oo_nodes_to_config - tags: - - always - gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: enable diff --git a/playbooks/common/openshift-cluster/upgrades/v3_6/validator.yml b/playbooks/common/openshift-cluster/upgrades/v3_6/validator.yml index ac5704f69..78c1767b8 100644 --- a/playbooks/common/openshift-cluster/upgrades/v3_6/validator.yml +++ b/playbooks/common/openshift-cluster/upgrades/v3_6/validator.yml @@ -7,4 +7,6 @@ hosts: oo_first_master roles: - { role: lib_openshift } - tasks: [] + tasks: + - name: Check for invalid namespaces and SDN errors + oc_objectvalidator: diff --git a/playbooks/common/openshift-master/scaleup.yml b/playbooks/common/openshift-master/scaleup.yml index 49594d294..bc61ee9bb 100644 --- a/playbooks/common/openshift-master/scaleup.yml +++ b/playbooks/common/openshift-master/scaleup.yml @@ -65,10 +65,10 @@ tags: - always gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: disable + roles: + - role: openshift_excluder + r_openshift_excluder_action: disable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" - include: ../openshift-master/config.yml @@ -81,7 +81,7 @@ tags: - always gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: enable + roles: + - role: openshift_excluder + r_openshift_excluder_action: enable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" diff --git a/playbooks/common/openshift-node/scaleup.yml b/playbooks/common/openshift-node/scaleup.yml index d94df553c..40da8990d 100644 --- a/playbooks/common/openshift-node/scaleup.yml +++ b/playbooks/common/openshift-node/scaleup.yml @@ -32,10 +32,10 @@ tags: - always gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: disable + roles: + - role: openshift_excluder + r_openshift_excluder_action: disable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" - include: ../openshift-node/config.yml @@ -44,7 +44,7 @@ tags: - always gather_facts: no - tasks: - - include_role: - name: openshift_excluder - tasks_from: enable + roles: + - role: openshift_excluder + r_openshift_excluder_action: enable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" diff --git a/roles/calico/README.md b/roles/calico/README.md index 99e870521..9b9458bfa 100644 --- a/roles/calico/README.md +++ b/roles/calico/README.md @@ -20,6 +20,15 @@ To install, set the following inventory configuration parameters: * `openshift_use_openshift_sdn=False` * `os_sdn_network_plugin_name='cni'` +## Additional Calico/Node and Felix Configuration Options + +Additional parameters that can be defined in the inventory are: + +| Environment | Description | Schema | Default | +|---------|----------------------|---------|---------| +|CALICO_IPV4POOL_CIDR| The IPv4 Pool to create if none exists at start up. It is invalid to define this variable and NO_DEFAULT_POOLS. |IPv4 CIDR | 192.168.0.0/16 | +| CALICO_IPV4POOL_IPIP | IPIP Mode to use for the IPv4 POOL created at start up. | off, always, cross-subnet | always | +| CALICO_LOG_DIR | Directory on the host machine where Calico Logs are written.| String | /var/log/calico | ### Contact Information diff --git a/roles/calico/defaults/main.yaml b/roles/calico/defaults/main.yaml index a16a7da71..03c612982 100644 --- a/roles/calico/defaults/main.yaml +++ b/roles/calico/defaults/main.yaml @@ -12,3 +12,9 @@ calico_etcd_key_file: "/etc/origin/calico/calico.etcd-client.key" calico_url_cni: "https://github.com/projectcalico/cni-plugin/releases/download/v1.5.5/calico" calico_url_ipam: "https://github.com/projectcalico/cni-plugin/releases/download/v1.5.5/calico-ipam" + +calico_ipv4pool_ipip: "always" +calico_ipv4pool_cidr: "192.168.0.0/16" + +calico_log_dir: "/var/log/calico" +calico_node_image: "calico/node:v1.1.0" diff --git a/roles/calico/tasks/main.yml b/roles/calico/tasks/main.yml index abdbcf8d6..fa5e338b3 100644 --- a/roles/calico/tasks/main.yml +++ b/roles/calico/tasks/main.yml @@ -7,7 +7,7 @@ etcd_ca_host: "{{ groups.oo_etcd_to_config.0 }}" etcd_cert_subdir: "openshift-calico-{{ openshift.common.hostname }}" -- name: Assure the calico certs have been generated +- name: Calico Node | Assure the calico certs have been generated stat: path: "{{ item }}" with_items: @@ -15,12 +15,12 @@ - "{{ calico_etcd_cert_file}}" - "{{ calico_etcd_key_file }}" -- name: Configure Calico service unit file +- name: Calico Node | Configure Calico service unit file template: dest: "/lib/systemd/system/calico.service" src: calico.service.j2 -- name: Enable calico +- name: Calico Node | Enable calico become: yes systemd: name: calico @@ -29,46 +29,46 @@ enabled: yes register: start_result -- name: Assure CNI conf dir exists +- name: Calico Node | Assure CNI conf dir exists become: yes file: path="{{ cni_conf_dir }}" state=directory -- name: Generate Calico CNI config +- name: Calico Node | Generate Calico CNI config become: yes template: - src: "calico.conf.j2" + src: "10-calico.conf.j2" dest: "{{ cni_conf_dir }}/10-calico.conf" -- name: Assures Kuberentes CNI bin dir exists +- name: Calico Node | Assures Kuberentes CNI bin dir exists become: yes file: path="{{ cni_bin_dir }}" state=directory -- name: Download Calico CNI Plugin +- name: Calico Node | Download Calico CNI Plugin become: yes get_url: url: "{{ calico_url_cni }}" dest: "{{ cni_bin_dir }}" mode: a+x -- name: Download Calico IPAM Plugin +- name: Calico Node | Download Calico IPAM Plugin become: yes get_url: url: "{{ calico_url_ipam }}" dest: "{{ cni_bin_dir }}" mode: a+x -- name: Download and unzip standard CNI plugins +- name: Calico Node | Download and extract standard CNI plugins become: yes unarchive: remote_src: True src: "{{ cni_url }}" dest: "{{ cni_bin_dir }}" -- name: Assure Calico conf dir exists +- name: Calico Node | Assure Calico conf dir exists become: yes file: path=/etc/calico/ state=directory -- name: Set calicoctl.cfg +- name: Calico Node | Set calicoctl.cfg template: - src: calico.cfg.j2 + src: calicoctl.cfg.j2 dest: "/etc/calico/calicoctl.cfg" diff --git a/roles/calico/templates/calico.cfg.j2 b/roles/calico/templates/10-calico.cfg.j2 index 722385ed8..722385ed8 100644 --- a/roles/calico/templates/calico.cfg.j2 +++ b/roles/calico/templates/10-calico.cfg.j2 diff --git a/roles/calico/templates/calico.service.j2 b/roles/calico/templates/calico.service.j2 index 7a1236392..719d7ba0d 100644 --- a/roles/calico/templates/calico.service.j2 +++ b/roles/calico/templates/calico.service.j2 @@ -10,7 +10,8 @@ ExecStart=/usr/bin/docker run --net=host --privileged \ --name=calico-node \ -e WAIT_FOR_DATASTORE=true \ -e FELIX_DEFAULTENDPOINTTOHOSTACTION=ACCEPT \ - -e CALICO_IPV4POOL_IPIP=always \ + -e CALICO_IPV4POOL_IPIP={{ calico_ipv4pool_ipip }} \ + -e CALICO_IPV4POOL_CIDR={{ calico_ipv4pool_cidr }} \ -e FELIX_IPV6SUPPORT=false \ -e ETCD_ENDPOINTS={{ etcd_endpoints }} \ -v /etc/origin/calico:/etc/origin/calico \ @@ -18,10 +19,11 @@ ExecStart=/usr/bin/docker run --net=host --privileged \ -e ETCD_CERT_FILE={{ calico_etcd_cert_file }} \ -e ETCD_KEY_FILE={{ calico_etcd_key_file }} \ -e NODENAME={{ openshift.common.hostname }} \ - -v /var/log/calico:/var/log/calico \ + -v {{ calico_log_dir }}:/var/log/calico\ -v /lib/modules:/lib/modules \ -v /var/run/calico:/var/run/calico \ - calico/node:v1.1.0 + {{ calico_node_image }} + ExecStop=-/usr/bin/docker stop calico-node diff --git a/roles/calico/templates/calico.conf.j2 b/roles/calico/templates/calicoctl.conf.j2 index 3c8c6b046..3c8c6b046 100644 --- a/roles/calico/templates/calico.conf.j2 +++ b/roles/calico/templates/calicoctl.conf.j2 diff --git a/roles/calico_master/README.md b/roles/calico_master/README.md index 2d34a967c..6f5ed0664 100644 --- a/roles/calico_master/README.md +++ b/roles/calico_master/README.md @@ -21,6 +21,18 @@ To install, set the following inventory configuration parameters: * `os_sdn_network_plugin_name='cni'` + +## Additional Calico/Node and Felix Configuration Options + +Additional parameters that can be defined in the inventory are: + + +| Environment | Description | Schema | Default | +|---------|----------------------|---------|---------| +|CALICO_IPV4POOL_CIDR| The IPv4 Pool to create if none exists at start up. It is invalid to define this variable and NO_DEFAULT_POOLS. |IPv4 CIDR | 192.168.0.0/16 | +| CALICO_IPV4POOL_IPIP | IPIP Mode to use for the IPv4 POOL created at start up. | off, always, cross-subnet | always | +| CALICO_LOG_DIR | Directory on the host machine where Calico Logs are written.| String | /var/log/calico | + ### Contact Information Author: Dan Osborne <dan@projectcalico.org> diff --git a/roles/calico_master/defaults/main.yaml b/roles/calico_master/defaults/main.yaml index db0d17884..5b324bce5 100644 --- a/roles/calico_master/defaults/main.yaml +++ b/roles/calico_master/defaults/main.yaml @@ -1,2 +1,6 @@ --- kubeconfig: "{{ openshift.common.config_base }}/master/openshift-master.kubeconfig" + +calicoctl_bin_dir: "/usr/local/bin/" + +calico_url_calicoctl: "https://github.com/projectcalico/calicoctl/releases/download/v1.1.3/calicoctl" diff --git a/roles/calico_master/tasks/main.yml b/roles/calico_master/tasks/main.yml index 3358abe23..8ddca26d6 100644 --- a/roles/calico_master/tasks/main.yml +++ b/roles/calico_master/tasks/main.yml @@ -1,5 +1,5 @@ --- -- name: Assure the calico certs have been generated +- name: Calico Master | Assure the calico certs have been generated stat: path: "{{ item }}" with_items: @@ -7,17 +7,17 @@ - "{{ calico_etcd_cert_file}}" - "{{ calico_etcd_key_file }}" -- name: Create temp directory for policy controller definition +- name: Calico Master | Create temp directory for policy controller definition command: mktemp -d /tmp/openshift-ansible-XXXXXXX register: mktemp changed_when: False -- name: Write Calico Policy Controller definition +- name: Calico Master | Write Calico Policy Controller definition template: dest: "{{ mktemp.stdout }}/calico-policy-controller.yml" src: calico-policy-controller.yml.j2 -- name: Launch Calico Policy Controller +- name: Calico Master | Launch Calico Policy Controller command: > {{ openshift.common.client_binary }} create -f {{ mktemp.stdout }}/calico-policy-controller.yml @@ -26,16 +26,23 @@ failed_when: ('already exists' not in calico_create_output.stderr) and ('created' not in calico_create_output.stdout) changed_when: ('created' in calico_create_output.stdout) -- name: Delete temp directory +- name: Calico Master | Delete temp directory file: name: "{{ mktemp.stdout }}" state: absent changed_when: False -- name: oc adm policy add-scc-to-user privileged system:serviceaccount:kube-system:calico +- name: Calico Master | oc adm policy add-scc-to-user privileged system:serviceaccount:kube-system:calico oc_adm_policy_user: user: system:serviceaccount:kube-system:calico resource_kind: scc resource_name: privileged state: present + +- name: Download Calicoctl + become: yes + get_url: + url: "{{ calico_url_calicoctl }}" + dest: "{{ calicoctl_bin_dir }}" + mode: a+x diff --git a/roles/etcd_client_certificates/tasks/main.yml b/roles/etcd_client_certificates/tasks/main.yml index 450b65209..bbd29ece1 100644 --- a/roles/etcd_client_certificates/tasks/main.yml +++ b/roles/etcd_client_certificates/tasks/main.yml @@ -84,7 +84,6 @@ register: g_etcd_client_mktemp changed_when: False when: etcd_client_certs_missing | bool - delegate_to: localhost become: no - name: Create a tarball of the etcd certs @@ -133,8 +132,7 @@ when: etcd_client_certs_missing | bool - name: Delete temporary directory - file: name={{ g_etcd_client_mktemp.stdout }} state=absent + local_action: file path="{{ g_etcd_client_mktemp.stdout }}" state=absent changed_when: False when: etcd_client_certs_missing | bool - delegate_to: localhost become: no diff --git a/roles/etcd_server_certificates/tasks/main.yml b/roles/etcd_server_certificates/tasks/main.yml index 956f5cc55..3ac7f3401 100644 --- a/roles/etcd_server_certificates/tasks/main.yml +++ b/roles/etcd_server_certificates/tasks/main.yml @@ -107,7 +107,6 @@ register: g_etcd_server_mktemp changed_when: False when: etcd_server_certs_missing | bool - delegate_to: localhost - name: Create a tarball of the etcd certs command: > @@ -176,11 +175,10 @@ when: etcd_server_certs_missing | bool - name: Delete temporary directory - file: name={{ g_etcd_server_mktemp.stdout }} state=absent + local_action: file path="{{ g_etcd_server_mktemp.stdout }}" state=absent become: no changed_when: False when: etcd_server_certs_missing | bool - delegate_to: localhost - name: Validate permissions on certificate files file: diff --git a/roles/lib_utils/library/repoquery.py b/roles/lib_utils/library/repoquery.py index cf33e48d5..95a305b58 100644 --- a/roles/lib_utils/library/repoquery.py +++ b/roles/lib_utils/library/repoquery.py @@ -465,7 +465,7 @@ class Repoquery(RepoqueryCLI): version_dict = defaultdict(dict) - for version in query_output.split('\n'): + for version in query_output.decode().split('\n'): pkg_info = version.split("|") pkg_version = {} diff --git a/roles/lib_utils/src/class/repoquery.py b/roles/lib_utils/src/class/repoquery.py index 28e3a3e89..e997780ad 100644 --- a/roles/lib_utils/src/class/repoquery.py +++ b/roles/lib_utils/src/class/repoquery.py @@ -48,7 +48,7 @@ class Repoquery(RepoqueryCLI): version_dict = defaultdict(dict) - for version in query_output.split('\n'): + for version in query_output.decode().split('\n'): pkg_info = version.split("|") pkg_version = {} diff --git a/roles/lib_utils/src/test/unit/test_repoquery.py b/roles/lib_utils/src/test/unit/test_repoquery.py index 9991ecd14..325f41dab 100755 --- a/roles/lib_utils/src/test/unit/test_repoquery.py +++ b/roles/lib_utils/src/test/unit/test_repoquery.py @@ -45,7 +45,7 @@ class RepoQueryTest(unittest.TestCase): # Return values of our mocked function call. These get returned once per call. mock_cmd.side_effect = [ - (0, '4.2.46|21.el7_3|x86_64|rhel-7-server-rpms|4.2.46-21.el7_3', valid_stderr), # first call to the mock + (0, b'4.2.46|21.el7_3|x86_64|rhel-7-server-rpms|4.2.46-21.el7_3', valid_stderr), # first call to the mock ] # Act diff --git a/roles/openshift_excluder/README.md b/roles/openshift_excluder/README.md index df45c28bf..80cb88d45 100644 --- a/roles/openshift_excluder/README.md +++ b/roles/openshift_excluder/README.md @@ -1,50 +1,69 @@ OpenShift Excluder -================ +================== Manages the excluder packages which add yum and dnf exclusions ensuring that -the packages we care about are not inadvertantly updated. See +the packages we care about are not inadvertently updated. See https://github.com/openshift/origin/tree/master/contrib/excluder Requirements ------------ -openshift_facts +None -Facts ------ +Inventory Variables +------------------- -| Name | Default Value | Description | ------------------------------|---------------|----------------------------------------| -| enable_docker_excluder | enable_excluders | Enable docker excluder. If not set, the docker excluder is ignored. | -| enable_openshift_excluder | enable_excluders | Enable openshift excluder. If not set, the openshift excluder is ignored. | -| enable_excluders | None | Enable all excluders +| Name | Default Value | Description | +---------------------------------------|----------------------------|----------------------------------------| +| openshift_enable_excluders | True | Enable all excluders | +| openshift_enable_docker_excluder | openshift_enable_excluders | Enable docker excluder. If not set, the docker excluder is ignored. | +| openshift_enable_openshift_excluder | openshift_enable_excluders | Enable openshift excluder. If not set, the openshift excluder is ignored. | Role Variables -------------- -None + +| Name | Default | Choices | Description | +|-------------------------------------------|---------|-----------------|---------------------------------------------------------------------------| +| r_openshift_excluder_action | enable | enable, disable | Action to perform when calling this role | +| r_openshift_excluder_verify_upgrade | false | true, false | When upgrading, this variable should be set to true when calling the role | +| r_openshift_excluder_package_state | present | present, latest | Use 'latest' to upgrade openshift_excluder package | +| r_openshift_excluder_docker_package_state | present | present, latest | Use 'latest' to upgrade docker_excluder package | +| r_openshift_excluder_service_type | None | | (Required) Defined as openshift.common.service_type e.g. atomic-openshift | +| r_openshift_excluder_upgrade_target | None | | Required when r_openshift_excluder_verify_upgrade is true, defined as openshift_upgrade_target by Upgrade playbooks e.g. '3.6'| Dependencies ------------ -- openshift_facts -- openshift_repos -- lib_utils - -Tasks to include ----------------- - -- exclude: enable excluders -- unexclude: disable excluders -- install: install excluders (installation is followed by excluder enabling) -- enable: enable excluders (install excluder(s) if not installed) -- disabled: disable excluders (install excluder(s) if not installed) +- lib_utils Example Playbook ---------------- +```yaml +- name: Demonstrate OpenShift Excluder usage + hosts: oo_masters_to_config:oo_nodes_to_config + roles: + # Disable all excluders + - role: openshift_excluder + r_openshift_excluder_action: disable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" + # Enable all excluders + - role: openshift_excluder + r_openshift_excluder_action: enable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" + # Disable all excluders and verify appropriate excluder packages are available for upgrade + - role: openshift_excluder + r_openshift_excluder_action: disable + r_openshift_excluder_service_type: "{{ openshift.common.service_type }}" + r_openshift_excluder_verify_upgrade: true + r_openshift_excluder_upgrade_target: "{{ openshift_upgrade_target }}" + r_openshift_excluder_package_state: latest + r_openshift_excluder_docker_package_state: latest +``` TODO ---- + It should be possible to manage the two excluders independently though that's not a hard requirement. However it should be done to manage docker on RHEL Containerized hosts. License diff --git a/roles/openshift_excluder/defaults/main.yml b/roles/openshift_excluder/defaults/main.yml index 7c3ae2a86..d4f151142 100644 --- a/roles/openshift_excluder/defaults/main.yml +++ b/roles/openshift_excluder/defaults/main.yml @@ -1,6 +1,19 @@ --- # keep the 'current' package or update to 'latest' if available? -openshift_excluder_package_state: present -docker_excluder_package_state: present +r_openshift_excluder_package_state: present +r_openshift_excluder_docker_package_state: present -enable_excluders: true +# Legacy variables are included for backwards compatibility with v3.5 +# Inventory variables Legacy +# openshift_enable_excluders enable_excluders +# openshift_enable_openshift_excluder enable_openshift_excluder +# openshift_enable_docker_excluder enable_docker_excluder +r_openshift_excluder_enable_excluders: "{{ openshift_enable_excluders | default(enable_excluders) | default(true) }}" +r_openshift_excluder_enable_openshift_excluder: "{{ openshift_enable_openshift_excluder | default(enable_openshift_excluder) | default(r_openshift_excluder_enable_excluders) }}" +r_openshift_excluder_enable_docker_excluder: "{{ openshift_enable_docker_excluder | default(enable_docker_excluder) | default(r_openshift_excluder_enable_excluders) }}" + +# Default action when calling this role +r_openshift_excluder_action: enable + +# When upgrading, this variable should be set to true when calling the role +r_openshift_excluder_verify_upgrade: false diff --git a/roles/openshift_excluder/meta/main.yml b/roles/openshift_excluder/meta/main.yml index c6081cdb2..871081c19 100644 --- a/roles/openshift_excluder/meta/main.yml +++ b/roles/openshift_excluder/meta/main.yml @@ -1,7 +1,7 @@ --- galaxy_info: author: Scott Dodson - description: OpenShift Examples + description: OpenShift Excluder company: Red Hat, Inc. license: Apache License, Version 2.0 min_ansible_version: 2.2 @@ -12,6 +12,4 @@ galaxy_info: categories: - cloud dependencies: -- { role: openshift_facts } -- { role: openshift_repos } -- { role: lib_utils } +- role: lib_utils diff --git a/roles/openshift_excluder/tasks/disable.yml b/roles/openshift_excluder/tasks/disable.yml index 97044fff6..8d5a08874 100644 --- a/roles/openshift_excluder/tasks/disable.yml +++ b/roles/openshift_excluder/tasks/disable.yml @@ -1,47 +1,38 @@ --- -# input variables -# - excluder_package_state -# - docker_excluder_package_state -- include: init.yml +- when: r_openshift_excluder_verify_upgrade + block: + - name: Include verify_upgrade.yml when upgrading + include: verify_upgrade.yml # unexclude the current openshift/origin-excluder if it is installed so it can be updated -- include: unexclude.yml +- name: Disable OpenShift excluder so it can be updated + include: unexclude.yml vars: unexclude_docker_excluder: false - unexclude_openshift_excluder: "{{ openshift_excluder_on | bool }}" - when: - - not openshift.common.is_atomic | bool + unexclude_openshift_excluder: "{{ r_openshift_excluder_enable_openshift_excluder }}" # Install any excluder that is enabled -- include: install.yml - vars: - # Both docker_excluder_on and openshift_excluder_on are set in openshift_excluder->init task - install_docker_excluder: "{{ docker_excluder_on | bool }}" - install_openshift_excluder: "{{ openshift_excluder_on | bool }}" - when: docker_excluder_on or openshift_excluder_on - - # if the docker excluder is not enabled, we don't care about its status - # it the docker excluder is enabled, we install it and in case its status is non-zero - # it is enabled no matter what +- name: Include install.yml + include: install.yml # And finally adjust an excluder in order to update host components correctly. First # exclude then unexclude -- block: - - include: exclude.yml - vars: - # Enable the docker excluder only if it is overrided - # BZ #1430612: docker excluders should be enabled even during installation and upgrade - exclude_docker_excluder: "{{ docker_excluder_on | bool }}" - # excluder is to be disabled by default - exclude_openshift_excluder: false - # All excluders that are to be disabled are disabled - - include: unexclude.yml - vars: - # If the docker override is not set, default to the generic behaviour - # BZ #1430612: docker excluders should be enabled even during installation and upgrade - unexclude_docker_excluder: false - # disable openshift excluder is never overrided to be enabled - # disable it if the docker excluder is enabled - unexclude_openshift_excluder: "{{ openshift_excluder_on | bool }}" - when: - - not openshift.common.is_atomic | bool +- name: Include exclude.yml + include: exclude.yml + vars: + # Enable the docker excluder only if it is overridden + # BZ #1430612: docker excluders should be enabled even during installation and upgrade + exclude_docker_excluder: "{{ r_openshift_excluder_enable_docker_excluder }}" + # excluder is to be disabled by default + exclude_openshift_excluder: false + +# All excluders that are to be disabled are disabled +- name: Include unexclude.yml + include: unexclude.yml + vars: + # If the docker override is not set, default to the generic behaviour + # BZ #1430612: docker excluders should be enabled even during installation and upgrade + unexclude_docker_excluder: false + # disable openshift excluder is never overridden to be enabled + # disable it if the docker excluder is enabled + unexclude_openshift_excluder: "{{ r_openshift_excluder_enable_openshift_excluder }}" diff --git a/roles/openshift_excluder/tasks/enable.yml b/roles/openshift_excluder/tasks/enable.yml index e719325bc..fce44cfb5 100644 --- a/roles/openshift_excluder/tasks/enable.yml +++ b/roles/openshift_excluder/tasks/enable.yml @@ -1,18 +1,6 @@ --- -# input variables: -- block: - - include: init.yml +- name: Install excluders + include: install.yml - - include: install.yml - vars: - install_docker_excluder: "{{ docker_excluder_on | bool }}" - install_openshift_excluder: "{{ openshift_excluder_on | bool }}" - when: docker_excluder_on or openshift_excluder_on | bool - - - include: exclude.yml - vars: - exclude_docker_excluder: "{{ docker_excluder_on | bool }}" - exclude_openshift_excluder: "{{ openshift_excluder_on | bool }}" - - when: - - not openshift.common.is_atomic | bool +- name: Enable excluders + include: exclude.yml diff --git a/roles/openshift_excluder/tasks/exclude.yml b/roles/openshift_excluder/tasks/exclude.yml index ca18d343f..934f1b2d2 100644 --- a/roles/openshift_excluder/tasks/exclude.yml +++ b/roles/openshift_excluder/tasks/exclude.yml @@ -1,30 +1,22 @@ --- -# input variables: -# - exclude_docker_excluder -# - exclude_openshift_excluder -- block: +- name: Check for docker-excluder + stat: + path: /sbin/{{ r_openshift_excluder_service_type }}-docker-excluder + register: docker_excluder_stat - - name: Check for docker-excluder - stat: - path: /sbin/{{ openshift.common.service_type }}-docker-excluder - register: docker_excluder_stat - - name: Enable docker excluder - command: "{{ openshift.common.service_type }}-docker-excluder exclude" - when: - - exclude_docker_excluder | default(false) | bool - - docker_excluder_stat.stat.exists +- name: Enable docker excluder + command: "{{ r_openshift_excluder_service_type }}-docker-excluder exclude" + when: + - r_openshift_excluder_enable_docker_excluder | bool + - docker_excluder_stat.stat.exists - - name: Check for openshift excluder - stat: - path: /sbin/{{ openshift.common.service_type }}-excluder - register: openshift_excluder_stat - - name: Enable openshift excluder - command: "{{ openshift.common.service_type }}-excluder exclude" - # if the openshift override is set, it means the openshift excluder is disabled no matter what - # if the openshift override is not set, the excluder is set based on enable_openshift_excluder - when: - - exclude_openshift_excluder | default(false) | bool - - openshift_excluder_stat.stat.exists +- name: Check for openshift excluder + stat: + path: /sbin/{{ r_openshift_excluder_service_type }}-excluder + register: openshift_excluder_stat +- name: Enable openshift excluder + command: "{{ r_openshift_excluder_service_type }}-excluder exclude" when: - - not openshift.common.is_atomic | bool + - r_openshift_excluder_enable_openshift_excluder | bool + - openshift_excluder_stat.stat.exists diff --git a/roles/openshift_excluder/tasks/init.yml b/roles/openshift_excluder/tasks/init.yml deleted file mode 100644 index 1ea18f363..000000000 --- a/roles/openshift_excluder/tasks/init.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -- name: Evalute if docker excluder is to be enabled - set_fact: - docker_excluder_on: "{{ enable_docker_excluder | default(enable_excluders) | bool }}" - -- debug: var=docker_excluder_on - -- name: Evalute if openshift excluder is to be enabled - set_fact: - openshift_excluder_on: "{{ enable_openshift_excluder | default(enable_excluders) | bool }}" - -- debug: var=openshift_excluder_on diff --git a/roles/openshift_excluder/tasks/install.yml b/roles/openshift_excluder/tasks/install.yml index 3490a613e..d09358bee 100644 --- a/roles/openshift_excluder/tasks/install.yml +++ b/roles/openshift_excluder/tasks/install.yml @@ -1,21 +1,14 @@ --- -# input Variables -# - install_docker_excluder -# - install_openshift_excluder -- block: - - - name: Install docker excluder - package: - name: "{{ openshift.common.service_type }}-docker-excluder{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) + '*' }}" - state: "{{ docker_excluder_package_state }}" - when: - - install_docker_excluder | default(true) | bool +- name: Install docker excluder + package: + name: "{{ r_openshift_excluder_service_type }}-docker-excluder{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) + '*' }}" + state: "{{ r_openshift_excluder_docker_package_state }}" + when: + - r_openshift_excluder_enable_docker_excluder | bool - - name: Install openshift excluder - package: - name: "{{ openshift.common.service_type }}-excluder{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) + '*' }}" - state: "{{ openshift_excluder_package_state }}" - when: - - install_openshift_excluder | default(true) | bool +- name: Install openshift excluder + package: + name: "{{ r_openshift_excluder_service_type }}-excluder{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) + '*' }}" + state: "{{ r_openshift_excluder_package_state }}" when: - - not openshift.common.is_atomic | bool + - r_openshift_excluder_enable_openshift_excluder | bool diff --git a/roles/openshift_excluder/tasks/main.yml b/roles/openshift_excluder/tasks/main.yml new file mode 100644 index 000000000..db20b4012 --- /dev/null +++ b/roles/openshift_excluder/tasks/main.yml @@ -0,0 +1,38 @@ +--- +- name: Detecting Atomic Host Operating System + stat: + path: /run/ostree-booted + register: ostree_booted + +- block: + + - name: Debug r_openshift_excluder_enable_docker_excluder + debug: + var: r_openshift_excluder_enable_docker_excluder + + - name: Debug r_openshift_excluder_enable_openshift_excluder + debug: + var: r_openshift_excluder_enable_openshift_excluder + + - name: Fail if invalid openshift_excluder_action provided + fail: + msg: "openshift_excluder role can only be called with 'enable' or 'disable'" + when: r_openshift_excluder_action not in ['enable', 'disable'] + + - name: Fail if r_openshift_excluder_service_type is not defined + fail: + msg: "r_openshift_excluder_service_type must be specified for this role" + when: r_openshift_excluder_service_type is not defined + + - name: Fail if r_openshift_excluder_upgrade_target is not defined + fail: + msg: "r_openshift_excluder_upgrade_target must be provided when using this role for upgrades" + when: + - r_openshift_excluder_verify_upgrade | bool + - r_openshift_excluder_upgrade_target is not defined + + - name: Include main action task file + include: "{{ r_openshift_excluder_action }}.yml" + + when: + - not ostree_booted.stat.exists | bool diff --git a/roles/openshift_excluder/tasks/unexclude.yml b/roles/openshift_excluder/tasks/unexclude.yml index 4df7f14b4..a5ce8d5c7 100644 --- a/roles/openshift_excluder/tasks/unexclude.yml +++ b/roles/openshift_excluder/tasks/unexclude.yml @@ -2,27 +2,25 @@ # input variables: # - unexclude_docker_excluder # - unexclude_openshift_excluder -- block: - - name: Check for docker-excluder - stat: - path: /sbin/{{ openshift.common.service_type }}-docker-excluder - register: docker_excluder_stat - - name: disable docker excluder - command: "{{ openshift.common.service_type }}-docker-excluder unexclude" - when: - - unexclude_docker_excluder | default(false) | bool - - docker_excluder_stat.stat.exists +- name: Check for docker-excluder + stat: + path: /sbin/{{ r_openshift_excluder_service_type }}-docker-excluder + register: docker_excluder_stat - - name: Check for openshift excluder - stat: - path: /sbin/{{ openshift.common.service_type }}-excluder - register: openshift_excluder_stat - - name: disable openshift excluder - command: "{{ openshift.common.service_type }}-excluder unexclude" - when: - - unexclude_openshift_excluder | default(false) | bool - - openshift_excluder_stat.stat.exists +- name: disable docker excluder + command: "{{ r_openshift_excluder_service_type }}-docker-excluder unexclude" + when: + - unexclude_docker_excluder | default(false) | bool + - docker_excluder_stat.stat.exists + +- name: Check for openshift excluder + stat: + path: /sbin/{{ r_openshift_excluder_service_type }}-excluder + register: openshift_excluder_stat +- name: disable openshift excluder + command: "{{ r_openshift_excluder_service_type }}-excluder unexclude" when: - - not openshift.common.is_atomic | bool + - unexclude_openshift_excluder | default(false) | bool + - openshift_excluder_stat.stat.exists diff --git a/roles/openshift_excluder/tasks/verify_excluder.yml b/roles/openshift_excluder/tasks/verify_excluder.yml index aebdb8c58..c35639c1b 100644 --- a/roles/openshift_excluder/tasks/verify_excluder.yml +++ b/roles/openshift_excluder/tasks/verify_excluder.yml @@ -1,35 +1,32 @@ --- # input variables: -# - repoquery_cmd # - excluder -# - openshift_upgrade_target -- block: - - name: Get available excluder version - repoquery: - name: "{{ excluder }}" - ignore_excluders: true - register: excluder_out +- name: Get available excluder version + repoquery: + name: "{{ excluder }}" + ignore_excluders: true + register: repoquery_out - - fail: - msg: "Package {{ excluder }} not found" - when: not excluder_out.results.package_found +- name: Fail when excluder package is not found + fail: + msg: "Package {{ excluder }} not found" + when: not repoquery_out.results.package_found - - set_fact: - excluder_version: "{{ excluder_out.results.versions.available_versions.0 }}" +- name: Set fact excluder_version + set_fact: + excluder_version: "{{ repoquery_out.results.versions.available_versions.0 }}" - - name: "{{ excluder }} version detected" - debug: - msg: "{{ excluder }}: {{ excluder_version }}" +- name: "{{ excluder }} version detected" + debug: + msg: "{{ excluder }}: {{ excluder_version }}" - - name: Printing upgrade target version - debug: - msg: "{{ openshift_upgrade_target }}" +- name: Printing upgrade target version + debug: + msg: "{{ r_openshift_excluder_upgrade_target }}" - - name: Check the available {{ excluder }} version is at most of the upgrade target version - fail: - msg: "Available {{ excluder }} version {{ excluder_version }} is higher than the upgrade target version" - when: - - "{{ excluder_version != '' }}" - - "{{ excluder_version.split('.')[0:2] | join('.') | version_compare(openshift_upgrade_target.split('.')[0:2] | join('.'), '>', strict=True) }}" +- name: Check the available {{ excluder }} version is at most of the upgrade target version + fail: + msg: "Available {{ excluder }} version {{ excluder_version }} is higher than the upgrade target version" when: - - not openshift.common.is_atomic | bool + - excluder_version != '' + - excluder_version.split('.')[0:2] | join('.') | version_compare(r_openshift_excluder_upgrade_target.split('.')[0:2] | join('.'), '>', strict=True) diff --git a/roles/openshift_excluder/tasks/verify_upgrade.yml b/roles/openshift_excluder/tasks/verify_upgrade.yml index 6ea2130ac..42026664a 100644 --- a/roles/openshift_excluder/tasks/verify_upgrade.yml +++ b/roles/openshift_excluder/tasks/verify_upgrade.yml @@ -1,15 +1,12 @@ --- -# input variables -# - repoquery_cmd -# - openshift_upgrade_target -- include: init.yml - -- include: verify_excluder.yml +- name: Verify Docker Excluder version + include: verify_excluder.yml vars: - excluder: "{{ openshift.common.service_type }}-docker-excluder" - when: docker_excluder_on + excluder: "{{ r_openshift_excluder_service_type }}-docker-excluder" + when: r_openshift_excluder_enable_docker_excluder | bool -- include: verify_excluder.yml +- name: Verify OpenShift Excluder version + include: verify_excluder.yml vars: - excluder: "{{ openshift.common.service_type }}-excluder" - when: openshift_excluder_on + excluder: "{{ r_openshift_excluder_service_type }}-excluder" + when: r_openshift_excluder_enable_openshift_excluder | bool diff --git a/roles/openshift_health_checker/library/etcdkeysize.py b/roles/openshift_health_checker/library/etcdkeysize.py new file mode 100644 index 000000000..620e82d87 --- /dev/null +++ b/roles/openshift_health_checker/library/etcdkeysize.py @@ -0,0 +1,122 @@ +#!/usr/bin/python +"""Ansible module that recursively determines if the size of a key in an etcd cluster exceeds a given limit.""" + +from ansible.module_utils.basic import AnsibleModule + + +try: + import etcd + + IMPORT_EXCEPTION_MSG = None +except ImportError as err: + IMPORT_EXCEPTION_MSG = str(err) + + from collections import namedtuple + EtcdMock = namedtuple("etcd", ["EtcdKeyNotFound"]) + etcd = EtcdMock(KeyError) + + +# pylint: disable=too-many-arguments +def check_etcd_key_size(client, key, size_limit, total_size=0, depth=0, depth_limit=1000, visited=None): + """Check size of an etcd path starting at given key. Returns tuple (string, bool)""" + if visited is None: + visited = set() + + if key in visited: + return 0, False + + visited.add(key) + + try: + result = client.read(key, recursive=False) + except etcd.EtcdKeyNotFound: + return 0, False + + size = 0 + limit_exceeded = False + + for node in result.leaves: + if depth >= depth_limit: + raise Exception("Maximum recursive stack depth ({}) exceeded.".format(depth_limit)) + + if size_limit and total_size + size > size_limit: + return size, True + + if not node.dir: + size += len(node.value) + continue + + key_size, limit_exceeded = check_etcd_key_size(client, node.key, + size_limit, + total_size + size, + depth + 1, + depth_limit, visited) + size += key_size + + max_limit_exceeded = limit_exceeded or (total_size + size > size_limit) + return size, max_limit_exceeded + + +def main(): # pylint: disable=missing-docstring,too-many-branches + module = AnsibleModule( + argument_spec=dict( + size_limit_bytes=dict(type="int", default=0), + paths=dict(type="list", default=["/openshift.io/images"]), + host=dict(type="str", default="127.0.0.1"), + port=dict(type="int", default=4001), + protocol=dict(type="str", default="http"), + version_prefix=dict(type="str", default=""), + allow_redirect=dict(type="bool", default=False), + cert=dict(type="dict", default=""), + ca_cert=dict(type="str", default=None), + ), + supports_check_mode=True + ) + + module.params["cert"] = ( + module.params["cert"]["cert"], + module.params["cert"]["key"], + ) + + size_limit = module.params.pop("size_limit_bytes") + paths = module.params.pop("paths") + + limit_exceeded = False + + try: + # pylint: disable=no-member + client = etcd.Client(**module.params) + except AttributeError as attrerr: + msg = str(attrerr) + if IMPORT_EXCEPTION_MSG: + msg = IMPORT_EXCEPTION_MSG + if "No module named etcd" in IMPORT_EXCEPTION_MSG: + # pylint: disable=redefined-variable-type + msg = ('Unable to import the python "etcd" dependency. ' + 'Make sure python-etcd is installed on the host.') + + module.exit_json( + failed=True, + changed=False, + size_limit_exceeded=limit_exceeded, + msg=msg, + ) + + return + + size = 0 + for path in paths: + path_size, limit_exceeded = check_etcd_key_size(client, path, size_limit - size) + size += path_size + + if limit_exceeded: + break + + module.exit_json( + changed=False, + size_limit_exceeded=limit_exceeded, + ) + + +if __name__ == '__main__': + main() diff --git a/roles/openshift_health_checker/openshift_checks/etcd_imagedata_size.py b/roles/openshift_health_checker/openshift_checks/etcd_imagedata_size.py new file mode 100644 index 000000000..c04a69765 --- /dev/null +++ b/roles/openshift_health_checker/openshift_checks/etcd_imagedata_size.py @@ -0,0 +1,84 @@ +""" +Ansible module for determining if the size of OpenShift image data exceeds a specified limit in an etcd cluster. +""" + +from openshift_checks import OpenShiftCheck, OpenShiftCheckException, get_var + + +class EtcdImageDataSize(OpenShiftCheck): + """Check that total size of OpenShift image data does not exceed the recommended limit in an etcd cluster""" + + name = "etcd_imagedata_size" + tags = ["etcd"] + + def run(self, tmp, task_vars): + etcd_mountpath = self._get_etcd_mountpath(get_var(task_vars, "ansible_mounts")) + etcd_avail_diskspace = etcd_mountpath["size_available"] + etcd_total_diskspace = etcd_mountpath["size_total"] + + etcd_imagedata_size_limit = get_var(task_vars, + "etcd_max_image_data_size_bytes", + default=int(0.5 * float(etcd_total_diskspace - etcd_avail_diskspace))) + + etcd_is_ssl = get_var(task_vars, "openshift", "master", "etcd_use_ssl", default=False) + etcd_port = get_var(task_vars, "openshift", "master", "etcd_port", default=2379) + etcd_hosts = get_var(task_vars, "openshift", "master", "etcd_hosts") + + config_base = get_var(task_vars, "openshift", "common", "config_base") + + cert = task_vars.get("etcd_client_cert", config_base + "/master/master.etcd-client.crt") + key = task_vars.get("etcd_client_key", config_base + "/master/master.etcd-client.key") + ca_cert = task_vars.get("etcd_client_ca_cert", config_base + "/master/master.etcd-ca.crt") + + for etcd_host in list(etcd_hosts): + args = { + "size_limit_bytes": etcd_imagedata_size_limit, + "paths": ["/openshift.io/images", "/openshift.io/imagestreams"], + "host": etcd_host, + "port": etcd_port, + "protocol": "https" if etcd_is_ssl else "http", + "version_prefix": "/v2", + "allow_redirect": True, + "ca_cert": ca_cert, + "cert": { + "cert": cert, + "key": key, + }, + } + + etcdkeysize = self.module_executor("etcdkeysize", args, task_vars) + + if etcdkeysize.get("rc", 0) != 0 or etcdkeysize.get("failed"): + msg = 'Failed to retrieve stats for etcd host "{host}": {reason}' + reason = etcdkeysize.get("msg") + if etcdkeysize.get("module_stderr"): + reason = etcdkeysize["module_stderr"] + + msg = msg.format(host=etcd_host, reason=reason) + return {"failed": True, "changed": False, "msg": msg} + + if etcdkeysize["size_limit_exceeded"]: + limit = self._to_gigabytes(etcd_imagedata_size_limit) + msg = ("The size of OpenShift image data stored in etcd host " + "\"{host}\" exceeds the maximum recommended limit of {limit:.2f} GB. " + "Use the `oadm prune images` command to cleanup unused Docker images.") + return {"failed": True, "msg": msg.format(host=etcd_host, limit=limit)} + + return {"changed": False} + + @staticmethod + def _get_etcd_mountpath(ansible_mounts): + valid_etcd_mount_paths = ["/var/lib/etcd", "/var/lib", "/var", "/"] + + mount_for_path = {mnt.get("mount"): mnt for mnt in ansible_mounts} + for path in valid_etcd_mount_paths: + if path in mount_for_path: + return mount_for_path[path] + + paths = ', '.join(sorted(mount_for_path)) or 'none' + msg = "Unable to determine a valid etcd mountpath. Paths mounted: {}.".format(paths) + raise OpenShiftCheckException(msg) + + @staticmethod + def _to_gigabytes(byte_size): + return float(byte_size) / 10.0**9 diff --git a/roles/openshift_health_checker/test/etcd_imagedata_size_test.py b/roles/openshift_health_checker/test/etcd_imagedata_size_test.py new file mode 100644 index 000000000..df9d52d41 --- /dev/null +++ b/roles/openshift_health_checker/test/etcd_imagedata_size_test.py @@ -0,0 +1,328 @@ +import pytest + +from collections import namedtuple +from openshift_checks.etcd_imagedata_size import EtcdImageDataSize, OpenShiftCheckException +from etcdkeysize import check_etcd_key_size + + +def fake_etcd_client(root): + fake_nodes = dict() + fake_etcd_node(root, fake_nodes) + + clientclass = namedtuple("client", ["read"]) + return clientclass(lambda key, recursive: fake_etcd_result(fake_nodes[key])) + + +def fake_etcd_result(fake_node): + resultclass = namedtuple("result", ["leaves"]) + if not fake_node.dir: + return resultclass([fake_node]) + + return resultclass(fake_node.leaves) + + +def fake_etcd_node(node, visited): + min_req_fields = ["dir", "key"] + fields = list(node) + leaves = [] + + if node["dir"] and node.get("leaves"): + for leaf in node["leaves"]: + leaves.append(fake_etcd_node(leaf, visited)) + + if len(set(min_req_fields) - set(fields)) > 0: + raise ValueError("fake etcd nodes require at least {} fields.".format(min_req_fields)) + + if node.get("leaves"): + node["leaves"] = leaves + + nodeclass = namedtuple("node", fields) + nodeinst = nodeclass(**node) + visited[nodeinst.key] = nodeinst + + return nodeinst + + +@pytest.mark.parametrize('ansible_mounts,extra_words', [ + ([], ['none']), # empty ansible_mounts + ([{'mount': '/mnt'}], ['/mnt']), # missing relevant mount paths +]) +def test_cannot_determine_available_mountpath(ansible_mounts, extra_words): + task_vars = dict( + ansible_mounts=ansible_mounts, + ) + check = EtcdImageDataSize(execute_module=fake_execute_module) + + with pytest.raises(OpenShiftCheckException) as excinfo: + check.run(tmp=None, task_vars=task_vars) + + for word in 'determine valid etcd mountpath'.split() + extra_words: + assert word in str(excinfo.value) + + +@pytest.mark.parametrize('ansible_mounts,tree,size_limit,should_fail,extra_words', [ + ( + # test that default image size limit evals to 1/2 * (total size in use) + [{ + 'mount': '/', + 'size_available': 40 * 10**9, + 'size_total': 80 * 10**9, + }], + {"dir": False, "key": "/", "value": "1234"}, + None, + False, + [], + ), + ( + [{ + 'mount': '/', + 'size_available': 40 * 10**9, + 'size_total': 48 * 10**9, + }], + {"dir": False, "key": "/", "value": "1234"}, + None, + False, + [], + ), + ( + # set max size limit for image data to be below total node value + # total node value is defined as the sum of the value field + # from every node + [{ + 'mount': '/', + 'size_available': 40 * 10**9, + 'size_total': 48 * 10**9, + }], + {"dir": False, "key": "/", "value": "12345678"}, + 7, + True, + ["exceeds the maximum recommended limit", "0.00 GB"], + ), + ( + [{ + 'mount': '/', + 'size_available': 48 * 10**9 - 1, + 'size_total': 48 * 10**9, + }], + {"dir": False, "key": "/", "value": "1234"}, + None, + True, + ["exceeds the maximum recommended limit", "0.00 GB"], + ) +]) +def test_check_etcd_key_size_calculates_correct_limit(ansible_mounts, tree, size_limit, should_fail, extra_words): + def execute_module(module_name, args, tmp=None, task_vars=None): + if module_name != "etcdkeysize": + return { + "changed": False, + } + + client = fake_etcd_client(tree) + s, limit_exceeded = check_etcd_key_size(client, tree["key"], args["size_limit_bytes"]) + + return {"size_limit_exceeded": limit_exceeded} + + task_vars = dict( + etcd_max_image_data_size_bytes=size_limit, + ansible_mounts=ansible_mounts, + openshift=dict( + master=dict(etcd_hosts=["localhost"]), + common=dict(config_base="/var/lib/origin") + ) + ) + if size_limit is None: + task_vars.pop("etcd_max_image_data_size_bytes") + + check = EtcdImageDataSize(execute_module=execute_module).run(tmp=None, task_vars=task_vars) + + if should_fail: + assert check["failed"] + + for word in extra_words: + assert word in check["msg"] + else: + assert not check.get("failed", False) + + +@pytest.mark.parametrize('ansible_mounts,tree,root_path,expected_size,extra_words', [ + ( + [{ + 'mount': '/', + 'size_available': 40 * 10**9, + 'size_total': 80 * 10**9, + }], + # test recursive size check on tree with height > 1 + { + "dir": True, + "key": "/", + "leaves": [ + {"dir": False, "key": "/foo1", "value": "1234"}, + {"dir": False, "key": "/foo2", "value": "1234"}, + {"dir": False, "key": "/foo3", "value": "1234"}, + {"dir": False, "key": "/foo4", "value": "1234"}, + { + "dir": True, + "key": "/foo5", + "leaves": [ + {"dir": False, "key": "/foo/bar1", "value": "56789"}, + {"dir": False, "key": "/foo/bar2", "value": "56789"}, + {"dir": False, "key": "/foo/bar3", "value": "56789"}, + { + "dir": True, + "key": "/foo/bar4", + "leaves": [ + {"dir": False, "key": "/foo/bar/baz1", "value": "123"}, + {"dir": False, "key": "/foo/bar/baz2", "value": "123"}, + ] + }, + ] + }, + ] + }, + "/", + 37, + [], + ), + ( + [{ + 'mount': '/', + 'size_available': 40 * 10**9, + 'size_total': 80 * 10**9, + }], + # test correct sub-tree size calculation + { + "dir": True, + "key": "/", + "leaves": [ + {"dir": False, "key": "/foo1", "value": "1234"}, + {"dir": False, "key": "/foo2", "value": "1234"}, + {"dir": False, "key": "/foo3", "value": "1234"}, + {"dir": False, "key": "/foo4", "value": "1234"}, + { + "dir": True, + "key": "/foo5", + "leaves": [ + {"dir": False, "key": "/foo/bar1", "value": "56789"}, + {"dir": False, "key": "/foo/bar2", "value": "56789"}, + {"dir": False, "key": "/foo/bar3", "value": "56789"}, + { + "dir": True, + "key": "/foo/bar4", + "leaves": [ + {"dir": False, "key": "/foo/bar/baz1", "value": "123"}, + {"dir": False, "key": "/foo/bar/baz2", "value": "123"}, + ] + }, + ] + }, + ] + }, + "/foo5", + 21, + [], + ), + ( + [{ + 'mount': '/', + 'size_available': 40 * 10**9, + 'size_total': 80 * 10**9, + }], + # test that a non-existing key is handled correctly + { + "dir": False, + "key": "/", + "value": "1234", + }, + "/missing", + 0, + [], + ), + ( + [{ + 'mount': '/', + 'size_available': 40 * 10**9, + 'size_total': 80 * 10**9, + }], + # test etcd cycle handling + { + "dir": True, + "key": "/", + "leaves": [ + {"dir": False, "key": "/foo1", "value": "1234"}, + {"dir": False, "key": "/foo2", "value": "1234"}, + {"dir": False, "key": "/foo3", "value": "1234"}, + {"dir": False, "key": "/foo4", "value": "1234"}, + { + "dir": True, + "key": "/", + "leaves": [ + {"dir": False, "key": "/foo1", "value": "1"}, + ], + }, + ] + }, + "/", + 16, + [], + ), +]) +def test_etcd_key_size_check_calculates_correct_size(ansible_mounts, tree, root_path, expected_size, extra_words): + def execute_module(module_name, args, tmp=None, task_vars=None): + if module_name != "etcdkeysize": + return { + "changed": False, + } + + client = fake_etcd_client(tree) + size, limit_exceeded = check_etcd_key_size(client, root_path, args["size_limit_bytes"]) + + assert size == expected_size + return { + "size_limit_exceeded": limit_exceeded, + } + + task_vars = dict( + ansible_mounts=ansible_mounts, + openshift=dict( + master=dict(etcd_hosts=["localhost"]), + common=dict(config_base="/var/lib/origin") + ) + ) + + check = EtcdImageDataSize(execute_module=execute_module).run(tmp=None, task_vars=task_vars) + assert not check.get("failed", False) + + +def test_etcdkeysize_module_failure(): + def execute_module(module_name, tmp=None, task_vars=None): + if module_name != "etcdkeysize": + return { + "changed": False, + } + + return { + "rc": 1, + "module_stderr": "failure", + } + + task_vars = dict( + ansible_mounts=[{ + 'mount': '/', + 'size_available': 40 * 10**9, + 'size_total': 80 * 10**9, + }], + openshift=dict( + master=dict(etcd_hosts=["localhost"]), + common=dict(config_base="/var/lib/origin") + ) + ) + + check = EtcdImageDataSize(execute_module=execute_module).run(tmp=None, task_vars=task_vars) + + assert check["failed"] + for word in "Failed to retrieve stats": + assert word in check["msg"] + + +def fake_execute_module(*args): + raise AssertionError('this function should not be called') diff --git a/roles/openshift_logging/tasks/main.yaml b/roles/openshift_logging/tasks/main.yaml index 387da618d..3d8cd3410 100644 --- a/roles/openshift_logging/tasks/main.yaml +++ b/roles/openshift_logging/tasks/main.yaml @@ -28,6 +28,7 @@ register: local_tmp changed_when: False check_mode: no + become: no - debug: msg="Created local temp dir {{local_tmp.stdout}}" diff --git a/roles/openshift_master_certificates/tasks/main.yml b/roles/openshift_master_certificates/tasks/main.yml index 2617efaf1..9706da24b 100644 --- a/roles/openshift_master_certificates/tasks/main.yml +++ b/roles/openshift_master_certificates/tasks/main.yml @@ -124,7 +124,6 @@ register: g_master_certs_mktemp changed_when: False when: master_certs_missing | bool - delegate_to: localhost become: no - name: Create a tarball of the master certs @@ -158,10 +157,10 @@ dest: "{{ openshift_master_config_dir }}" when: master_certs_missing | bool and inventory_hostname != openshift_ca_host -- file: name={{ g_master_certs_mktemp.stdout }} state=absent +- name: Delete local temp directory + local_action: file path="{{ g_master_certs_mktemp.stdout }}" state=absent changed_when: False when: master_certs_missing | bool - delegate_to: localhost become: no - name: Lookup default group for ansible_ssh_user diff --git a/roles/openshift_master_facts/filter_plugins/openshift_master.py b/roles/openshift_master_facts/filter_plugins/openshift_master.py index 65f85066e..b5be193d2 100644 --- a/roles/openshift_master_facts/filter_plugins/openshift_master.py +++ b/roles/openshift_master_facts/filter_plugins/openshift_master.py @@ -495,6 +495,7 @@ class FilterModule(object): return u(yaml.dump([idp.to_dict() for idp in idp_list], allow_unicode=True, default_flow_style=False, + width=float("inf"), Dumper=AnsibleDumper)) @staticmethod diff --git a/roles/openshift_metrics/tasks/main.yaml b/roles/openshift_metrics/tasks/main.yaml index e8b7bea5c..4ca5e6138 100644 --- a/roles/openshift_metrics/tasks/main.yaml +++ b/roles/openshift_metrics/tasks/main.yaml @@ -25,6 +25,7 @@ local_action: command mktemp -d register: local_tmp changed_when: False + become: false - name: Copy the admin client config(s) command: > diff --git a/roles/openshift_node/defaults/main.yml b/roles/openshift_node/defaults/main.yml index bf66ef1d6..5904ca9bc 100644 --- a/roles/openshift_node/defaults/main.yml +++ b/roles/openshift_node/defaults/main.yml @@ -9,3 +9,6 @@ os_firewall_allow: - service: OpenShift OVS sdn port: 4789/udp when: openshift.common.use_openshift_sdn | bool +- service: Calico BGP Port + port: 179/tcp + when: openshift.common.use_calico | bool diff --git a/roles/openshift_node/meta/main.yml b/roles/openshift_node/meta/main.yml index 0da41d0c1..3b7e8126a 100644 --- a/roles/openshift_node/meta/main.yml +++ b/roles/openshift_node/meta/main.yml @@ -33,6 +33,12 @@ dependencies: when: openshift.common.use_openshift_sdn | bool - role: os_firewall os_firewall_allow: + - service: Calico BGP Port + port: 179/tcp + when: openshift.common.use_calico | bool + +- role: os_firewall + os_firewall_allow: - service: Kubernetes service NodePort TCP port: "{{ openshift_node_port_range | default('') }}/tcp" - service: Kubernetes service NodePort UDP diff --git a/roles/openshift_node_certificates/tasks/main.yml b/roles/openshift_node_certificates/tasks/main.yml index 9120915b2..1a775178d 100644 --- a/roles/openshift_node_certificates/tasks/main.yml +++ b/roles/openshift_node_certificates/tasks/main.yml @@ -103,7 +103,6 @@ register: node_cert_mktemp changed_when: False when: node_certs_missing | bool - delegate_to: localhost become: no - name: Create a tarball of the node config directories @@ -141,10 +140,10 @@ dest: "{{ openshift_node_cert_dir }}" when: node_certs_missing | bool -- file: name={{ node_cert_mktemp.stdout }} state=absent +- name: Delete local temp directory + local_action: file path="{{ node_cert_mktemp.stdout }}" state=absent changed_when: False when: node_certs_missing | bool - delegate_to: localhost become: no - name: Copy OpenShift CA to system CA trust diff --git a/roles/openshift_node_upgrade/tasks/main.yml b/roles/openshift_node_upgrade/tasks/main.yml index 94c97d0a5..a41a97e01 100644 --- a/roles/openshift_node_upgrade/tasks/main.yml +++ b/roles/openshift_node_upgrade/tasks/main.yml @@ -127,6 +127,12 @@ - openshift_disable_swap | default(true) | bool # End Disable Swap Block +- name: Reset selinux context + command: restorecon -RF {{ openshift.common.data_dir }}/openshift.local.volumes + when: + - ansible_selinux is defined + - ansible_selinux.status == 'enabled' + # Restart all services - include: restart.yml |