diff options
Diffstat (limited to 'roles')
330 files changed, 8079 insertions, 1913 deletions
diff --git a/roles/ansible_service_broker/vars/openshift-enterprise.yml b/roles/ansible_service_broker/vars/openshift-enterprise.yml index 0b3a2a69d..ce2ae8365 100644 --- a/roles/ansible_service_broker/vars/openshift-enterprise.yml +++ b/roles/ansible_service_broker/vars/openshift-enterprise.yml @@ -1,7 +1,7 @@ --- __ansible_service_broker_image_prefix: registry.access.redhat.com/openshift3/ose- -__ansible_service_broker_image_tag: latest +__ansible_service_broker_image_tag: v3.6 __ansible_service_broker_etcd_image_prefix: rhel7/ __ansible_service_broker_etcd_image_tag: latest diff --git a/roles/calico/README.md b/roles/calico/README.md index 9b9458bfa..10e5e4e0a 100644 --- a/roles/calico/README.md +++ b/roles/calico/README.md @@ -6,12 +6,6 @@ Configure Calico components for the Master host. * Ansible 2.2 -## Warning: This Calico Integration is in Alpha - -Calico shares the etcd instance used by OpenShift, and distributes client etcd certificates to each node. -For this reason, **we do not (yet) recommend running Calico on any production-like -cluster, or using it for any purpose besides early access testing.** - ## Installation To install, set the following inventory configuration parameters: @@ -20,13 +14,24 @@ 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 +For more information, see [Calico's official OpenShift Installation Documentation](https://docs.projectcalico.org/latest/getting-started/openshift/installation#bring-your-own-etcd) + +## Improving security with BYO-etcd + +By default, Calico uses the etcd set up by OpenShift. To accomplish this, it generates and distributes client etcd certificates to each node. +Distributing these certs across the cluster in this way weakens the overall security, +so Calico should not be deployed in production in this mode. + +Instead, Calico can be installed in BYO-etcd mode, where it connects to an externally +set up etcd. For information on deploying Calico in BYO-etcd mode, see +[Calico's official OpenShift Installation Documentation](https://docs.projectcalico.org/latest/getting-started/openshift/installation#bring-your-own-etcd) + +## Calico 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 | diff --git a/roles/calico/defaults/main.yaml b/roles/calico/defaults/main.yaml index 207dee068..be73e8a73 100644 --- a/roles/calico/defaults/main.yaml +++ b/roles/calico/defaults/main.yaml @@ -1,15 +1,14 @@ --- -kubeconfig: "{{openshift.common.config_base}}/node/{{ 'system:node:' + openshift.common.hostname }}.kubeconfig" +kubeconfig: "{{ openshift.common.config_base }}/node/{{ 'system:node:' + openshift.common.hostname }}.kubeconfig" cni_conf_dir: "/etc/cni/net.d/" cni_bin_dir: "/opt/cni/bin/" cni_url: "https://github.com/containernetworking/cni/releases/download/v0.5.2/cni-amd64-v0.5.2.tgz" -calico_url_cni: "https://github.com/projectcalico/cni-plugin/releases/download/v1.8.3/calico" -calico_url_ipam: "https://github.com/projectcalico/cni-plugin/releases/download/v1.8.3/calico-ipam" +calico_url_cni: "https://github.com/projectcalico/cni-plugin/releases/download/v1.10.0/calico" +calico_url_ipam: "https://github.com/projectcalico/cni-plugin/releases/download/v1.10.0/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.2.1" +calico_node_image: "calico/node:v2.5.0" diff --git a/roles/calico/meta/main.yml b/roles/calico/meta/main.yml index 102b82bde..816c81369 100644 --- a/roles/calico/meta/main.yml +++ b/roles/calico/meta/main.yml @@ -14,3 +14,4 @@ galaxy_info: - system dependencies: - role: openshift_facts +- role: openshift_master_facts diff --git a/roles/calico/tasks/main.yml b/roles/calico/tasks/main.yml index e62378532..39f730462 100644 --- a/roles/calico/tasks/main.yml +++ b/roles/calico/tasks/main.yml @@ -38,7 +38,7 @@ path: "{{ item }}" with_items: - "{{ calico_etcd_ca_cert_file }}" - - "{{ calico_etcd_cert_file}}" + - "{{ calico_etcd_cert_file }}" - "{{ calico_etcd_key_file }}" - name: Calico Node | Configure Calico service unit file diff --git a/roles/calico/templates/calico.service.j2 b/roles/calico/templates/calico.service.j2 index 302c5f34e..7653e19b1 100644 --- a/roles/calico/templates/calico.service.j2 +++ b/roles/calico/templates/calico.service.j2 @@ -11,13 +11,14 @@ ExecStart=/usr/bin/docker run --net=host --privileged \ -e WAIT_FOR_DATASTORE=true \ -e FELIX_DEFAULTENDPOINTTOHOSTACTION=ACCEPT \ -e CALICO_IPV4POOL_IPIP={{ calico_ipv4pool_ipip }} \ - -e CALICO_IPV4POOL_CIDR={{ calico_ipv4pool_cidr }} \ + -e CALICO_IPV4POOL_CIDR={{ openshift.master.sdn_cluster_network_cidr }} \ -e FELIX_IPV6SUPPORT=false \ -e ETCD_ENDPOINTS={{ calico_etcd_endpoints }} \ -v {{ calico_etcd_cert_dir }}:{{ calico_etcd_cert_dir }} \ -e ETCD_CA_CERT_FILE={{ calico_etcd_ca_cert_file }} \ -e ETCD_CERT_FILE={{ calico_etcd_cert_file }} \ -e ETCD_KEY_FILE={{ calico_etcd_key_file }} \ + -e CLUSTER_TYPE=origin,bgp \ -e NODENAME={{ openshift.common.hostname }} \ -v {{ calico_log_dir }}:/var/log/calico\ -v /lib/modules:/lib/modules \ diff --git a/roles/calico_master/README.md b/roles/calico_master/README.md index 6f5ed0664..88fd44086 100644 --- a/roles/calico_master/README.md +++ b/roles/calico_master/README.md @@ -29,7 +29,6 @@ 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 | diff --git a/roles/calico_master/defaults/main.yaml b/roles/calico_master/defaults/main.yaml index b2df0105f..01a2b9529 100644 --- a/roles/calico_master/defaults/main.yaml +++ b/roles/calico_master/defaults/main.yaml @@ -3,5 +3,5 @@ kubeconfig: "{{ openshift.common.config_base }}/master/openshift-master.kubeconf calicoctl_bin_dir: "/usr/local/bin/" -calico_url_calicoctl: "https://github.com/projectcalico/calicoctl/releases/download/v1.1.3/calicoctl" -calico_url_policy_controller: "quay.io/calico/kube-policy-controller:v0.5.4" +calico_url_calicoctl: "https://github.com/projectcalico/calicoctl/releases/download/v1.5.0/calicoctl" +calico_url_policy_controller: "quay.io/calico/kube-policy-controller:v0.7.0" diff --git a/roles/cockpit/defaults/main.yml b/roles/cockpit/defaults/main.yml new file mode 100644 index 000000000..15c40e3b5 --- /dev/null +++ b/roles/cockpit/defaults/main.yml @@ -0,0 +1,8 @@ +--- +r_cockpit_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_cockpit_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + +r_cockpit_os_firewall_deny: [] +r_cockpit_os_firewall_allow: +- service: cockpit-ws + port: 9090/tcp diff --git a/roles/cockpit/meta/main.yml b/roles/cockpit/meta/main.yml index 0f507e75e..8c0ed3cb8 100644 --- a/roles/cockpit/meta/main.yml +++ b/roles/cockpit/meta/main.yml @@ -12,7 +12,4 @@ galaxy_info: categories: - cloud dependencies: -- role: os_firewall - os_firewall_allow: - - service: cockpit-ws - port: 9090/tcp +- role: lib_os_firewall diff --git a/roles/cockpit/tasks/firewall.yml b/roles/cockpit/tasks/firewall.yml new file mode 100644 index 000000000..e597ac84d --- /dev/null +++ b/roles/cockpit/tasks/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_cockpit_firewall_enabled | bool and not r_cockpit_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_cockpit_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_cockpit_os_firewall_deny }}" + +- when: r_cockpit_firewall_enabled | bool and r_cockpit_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_cockpit_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_cockpit_os_firewall_deny }}" diff --git a/roles/cockpit/tasks/main.yml b/roles/cockpit/tasks/main.yml index 57f49ea11..066ee3f3b 100644 --- a/roles/cockpit/tasks/main.yml +++ b/roles/cockpit/tasks/main.yml @@ -1,4 +1,8 @@ --- +- name: setup firewall + include: firewall.yml + static: yes + - name: Install cockpit-ws package: name={{ item }} state=present with_items: diff --git a/roles/docker/meta/main.yml b/roles/docker/meta/main.yml index cd4083572..b773a417c 100644 --- a/roles/docker/meta/main.yml +++ b/roles/docker/meta/main.yml @@ -10,5 +10,4 @@ galaxy_info: versions: - 7 dependencies: -- role: os_firewall - role: lib_openshift diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 1f9ac5059..78c6671d8 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -7,8 +7,8 @@ - set_fact: l_use_system_container: "{{ openshift.docker.use_system_container | default(False) }}" - l_use_crio: "{{ openshift.docker.use_crio | default(False) }}" - l_use_crio_only: "{{ openshift.docker.use_crio_only | default(False) }}" + l_use_crio: "{{ openshift_use_crio | default(False) }}" + l_use_crio_only: "{{ openshift_use_crio_only | default(False) }}" - name: Use Package Docker if Requested include: package_docker.yml diff --git a/roles/docker/templates/crio.conf.j2 b/roles/docker/templates/crio.conf.j2 index eae1759ab..5b31932b1 100644 --- a/roles/docker/templates/crio.conf.j2 +++ b/roles/docker/templates/crio.conf.j2 @@ -43,7 +43,7 @@ stream_port = "10010" # This is a mandatory setting as this runtime will be the default one # and will also be used for untrusted container workloads if # runtime_untrusted_workload is not set. -runtime = "/usr/libexec/crio/runc" +runtime = "/usr/bin/runc" # runtime_untrusted_workload is the OCI compatible runtime used for untrusted # container workloads. This is an optional setting, except if diff --git a/roles/etcd/defaults/main.yaml b/roles/etcd/defaults/main.yaml index c0d1d5946..3cc2bbb18 100644 --- a/roles/etcd/defaults/main.yaml +++ b/roles/etcd/defaults/main.yaml @@ -1,4 +1,7 @@ --- +r_etcd_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_etcd_use_firewalld: "{{ os_firewall_use_firewalld | default(Falsel) }}" + etcd_initial_cluster_state: new etcd_initial_cluster_token: etcd-cluster-1 @@ -7,4 +10,13 @@ etcd_listen_peer_urls: "{{ etcd_peer_url_scheme }}://{{ etcd_ip }}:{{ etcd_peer_ etcd_advertise_client_urls: "{{ etcd_url_scheme }}://{{ etcd_ip }}:{{ etcd_client_port }}" etcd_listen_client_urls: "{{ etcd_url_scheme }}://{{ etcd_ip }}:{{ etcd_client_port }}" +etcd_client_port: 2379 +etcd_peer_port: 2380 + etcd_systemd_dir: "/etc/systemd/system/{{ etcd_service }}.service.d" +r_etcd_os_firewall_deny: [] +r_etcd_os_firewall_allow: +- service: etcd + port: "{{etcd_client_port}}/tcp" +- service: etcd peering + port: "{{ etcd_peer_port }}/tcp" diff --git a/roles/etcd/meta/main.yml b/roles/etcd/meta/main.yml index 689c07a84..9a955c822 100644 --- a/roles/etcd/meta/main.yml +++ b/roles/etcd/meta/main.yml @@ -17,11 +17,6 @@ galaxy_info: - system dependencies: - role: lib_openshift -- role: os_firewall - os_firewall_allow: - - service: etcd - port: "{{etcd_client_port}}/tcp" - - service: etcd peering - port: "{{ etcd_peer_port }}/tcp" +- role: lib_os_firewall - role: etcd_server_certificates - role: etcd_common diff --git a/roles/etcd/tasks/firewall.yml b/roles/etcd/tasks/firewall.yml new file mode 100644 index 000000000..4d0f6290a --- /dev/null +++ b/roles/etcd/tasks/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_etcd_firewall_enabled | bool and not r_etcd_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_etcd_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_etcd_os_firewall_deny }}" + +- when: r_etcd_firewall_enabled | bool and r_etcd_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_etcd_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_etcd_os_firewall_deny }}" diff --git a/roles/etcd/tasks/main.yml b/roles/etcd/tasks/main.yml index 8c2f392ee..78e543ef1 100644 --- a/roles/etcd/tasks/main.yml +++ b/roles/etcd/tasks/main.yml @@ -6,6 +6,10 @@ etcd_hostname: "{{ etcd_hostname }}" etcd_ip: "{{ etcd_ip }}" +- name: setup firewall + include: firewall.yml + static: yes + - name: Install etcd package: name=etcd{{ '-' + etcd_version if etcd_version is defined else '' }} state=present when: not etcd_is_containerized | bool diff --git a/roles/etcd_common/defaults/main.yml b/roles/etcd_common/defaults/main.yml index b1bfa4592..89993f7ea 100644 --- a/roles/etcd_common/defaults/main.yml +++ b/roles/etcd_common/defaults/main.yml @@ -63,3 +63,13 @@ etcd_client_port: 2379 etcd_peer_port: 2380 etcd_url_scheme: http etcd_peer_url_scheme: http + +etcd_initial_cluster_state: new +etcd_initial_cluster_token: etcd-cluster-1 + +etcd_initial_advertise_peer_urls: "{{ etcd_peer_url_scheme }}://{{ etcd_ip }}:{{ etcd_peer_port }}" +etcd_listen_peer_urls: "{{ etcd_peer_url_scheme }}://{{ etcd_ip }}:{{ etcd_peer_port }}" +etcd_advertise_client_urls: "{{ etcd_url_scheme }}://{{ etcd_ip }}:{{ etcd_client_port }}" +etcd_listen_client_urls: "{{ etcd_url_scheme }}://{{ etcd_ip }}:{{ etcd_client_port }}" + +etcd_systemd_dir: "/etc/systemd/system/{{ etcd_service }}.service.d" diff --git a/roles/etcd_common/meta/main.yml b/roles/etcd_common/meta/main.yml index fb9280c9e..dfb1c7a2c 100644 --- a/roles/etcd_common/meta/main.yml +++ b/roles/etcd_common/meta/main.yml @@ -12,5 +12,4 @@ galaxy_info: categories: - cloud - system -dependencies: -- { role: openshift_repos } +dependencies: [] diff --git a/roles/etcd_common/tasks/main.yml b/roles/etcd_common/tasks/main.yml index 6ed87e6c7..f5bcd03ee 100644 --- a/roles/etcd_common/tasks/main.yml +++ b/roles/etcd_common/tasks/main.yml @@ -6,4 +6,4 @@ - name: Include main action task file include: "{{ r_etcd_common_action }}.yml" - when: r_etcd_common_action != "noop" + when: '"noop" not in r_etcd_common_action' diff --git a/roles/etcd_migrate/tasks/add_ttls.yml b/roles/etcd_migrate/tasks/add_ttls.yml new file mode 100644 index 000000000..c10465af9 --- /dev/null +++ b/roles/etcd_migrate/tasks/add_ttls.yml @@ -0,0 +1,33 @@ +--- +# To be executed on first master +- slurp: + src: "{{ openshift.common.config_base }}/master/master-config.yaml" + register: g_master_config_output + +- set_fact: + accessTokenMaxAgeSeconds: "{{ (g_master_config_output.content|b64decode|from_yaml).oauthConfig.tokenConfig.accessTokenMaxAgeSeconds | default(86400) }}" + authroizeTokenMaxAgeSeconds: "{{ (g_master_config_output.content|b64decode|from_yaml).oauthConfig.tokenConfig.authroizeTokenMaxAgeSeconds | default(500) }}" + controllerLeaseTTL: "{{ (g_master_config_output.content|b64decode|from_yaml).controllerLeaseTTL | default(30) }}" +- name: Re-introduce leases (as a replacement for key TTLs) + command: > + oadm migrate etcd-ttl \ + --cert {{ r_etcd_common_master_peer_cert_file }} \ + --key {{ r_etcd_common_master_peer_key_file }} \ + --cacert {{ r_etcd_common_master_peer_ca_file }} \ + --etcd-address 'https://{{ etcd_peer }}:{{ etcd_client_port }}' \ + --ttl-keys-prefix {{ item.keys }} \ + --lease-duration {{ item.ttl }} + environment: + ETCDCTL_API: 3 + PATH: "/usr/local/bin:/var/usrlocal/bin:{{ ansible_env.PATH }}" + with_items: + - keys: "/kubernetes.io/events" + ttl: "1h" + - keys: "/kubernetes.io/masterleases" + ttl: "10s" + - keys: "/openshift.io/oauth/accesstokens" + ttl: "{{ accessTokenMaxAgeSeconds }}s" + - keys: "/openshift.io/oauth/authorizetokens" + ttl: "{{ authroizeTokenMaxAgeSeconds }}s" + - keys: "/openshift.io/leases/controllers" + ttl: "{{ controllerLeaseTTL }}s" diff --git a/roles/etcd_migrate/tasks/check.yml b/roles/etcd_migrate/tasks/check.yml index b66696b55..0804d9e1c 100644 --- a/roles/etcd_migrate/tasks/check.yml +++ b/roles/etcd_migrate/tasks/check.yml @@ -1,7 +1,4 @@ --- -- fail: - msg: "Currently etcd v3 migration is unsupported while we test it more thoroughly" - when: not openshift_enable_unsupported_configurations | default(false) | bool # Check the cluster is healthy - include: check_cluster_health.yml diff --git a/roles/etcd_migrate/tasks/clean_data.yml b/roles/etcd_migrate/tasks/clean_data.yml new file mode 100644 index 000000000..95a0e7c0a --- /dev/null +++ b/roles/etcd_migrate/tasks/clean_data.yml @@ -0,0 +1,5 @@ +--- +- name: Remove member data + file: + path: /var/lib/etcd/member + state: absent diff --git a/roles/etcd_migrate/tasks/main.yml b/roles/etcd_migrate/tasks/main.yml index 409b0b613..e82f6a6b4 100644 --- a/roles/etcd_migrate/tasks/main.yml +++ b/roles/etcd_migrate/tasks/main.yml @@ -1,8 +1,8 @@ --- - name: Fail if invalid r_etcd_migrate_action provided fail: - msg: "etcd_migrate role can only be called with 'check' or 'migrate' or 'configure'" - when: r_etcd_migrate_action not in ['check', 'migrate', 'configure'] + msg: "etcd_migrate role can only be called with 'check', 'migrate', 'configure', 'add_ttls', or 'clean_data'" + when: r_etcd_migrate_action not in ['check', 'migrate', 'configure', 'add_ttls', 'clean_data'] - name: Include main action task file include: "{{ r_etcd_migrate_action }}.yml" diff --git a/roles/etcd_migrate/tasks/migrate.yml b/roles/etcd_migrate/tasks/migrate.yml index b2cf6d20a..54a9c74ff 100644 --- a/roles/etcd_migrate/tasks/migrate.yml +++ b/roles/etcd_migrate/tasks/migrate.yml @@ -3,62 +3,54 @@ - set_fact: l_etcd_service: "{{ 'etcd_container' if openshift.common.is_containerized else 'etcd' }}" -- name: Disable etcd members - service: - name: "{{ l_etcd_service }}" - state: stopped - -# Should we skip all TTL keys? https://bugzilla.redhat.com/show_bug.cgi?id=1389773 - name: Migrate etcd data command: > etcdctl migrate --data-dir={{ etcd_data_dir }} environment: ETCDCTL_API: 3 register: l_etcdctl_migrate - # TODO(jchaloup): If any of the members fails, we need to restore all members to v2 from the pre-migrate backup - name: Check the etcd v2 data are correctly migrated fail: msg: "Failed to migrate a member" when: "'finished transforming keys' not in l_etcdctl_migrate.stdout and 'no v2 keys to migrate' not in l_etcdctl_migrate.stdout" - - name: Migration message debug: msg: "Etcd migration finished with: {{ l_etcdctl_migrate.stdout }}" - -- name: Enable etcd member - service: +- name: Set ETCD_FORCE_NEW_CLUSTER=true on first etcd host + lineinfile: + line: "ETCD_FORCE_NEW_CLUSTER=true" + dest: /etc/etcd/etcd.conf + backup: true +- name: Start etcd + systemd: name: "{{ l_etcd_service }}" state: started - -- name: Wait for cluster to become healthy after migration +- name: Wait for cluster to become healthy after bringing up first member command: > etcdctl --cert-file {{ etcd_peer_cert_file }} --key-file {{ etcd_peer_key_file }} --ca-file {{ etcd_peer_ca_file }} --endpoint https://{{ etcd_peer }}:{{ etcd_client_port }} cluster-health register: l_etcd_migrate_health until: l_etcd_migrate_health.rc == 0 retries: 3 delay: 30 - run_once: true +- name: Unset ETCD_FORCE_NEW_CLUSTER=true on first etcd host + lineinfile: + line: "ETCD_FORCE_NEW_CLUSTER=true" + dest: /etc/etcd/etcd.conf + state: absent + backup: true +- name: Restart first etcd host + systemd: + name: "{{ l_etcd_service }}" + state: restarted -# NOTE: /usr/local/bin may be removed from the PATH by ansible hence why -# it's added to the environment in this task. -- name: Re-introduce leases (as a replacement for key TTLs) +- name: Wait for cluster to become healthy after bringing up first member command: > - oadm migrate etcd-ttl \ - --cert {{ r_etcd_common_master_peer_cert_file }} \ - --key {{ r_etcd_common_master_peer_key_file }} \ - --cacert {{ r_etcd_common_master_peer_ca_file }} \ - --etcd-address 'https://{{ etcd_peer }}:{{ etcd_client_port }}' \ - --ttl-keys-prefix {{ item }} \ - --lease-duration 1h - environment: - ETCDCTL_API: 3 - PATH: "/usr/local/bin:/var/usrlocal/bin:{{ ansible_env.PATH }}" - with_items: - - "/kubernetes.io/events" - - "/kubernetes.io/masterleases" - delegate_to: "{{ groups.oo_first_master[0] }}" - run_once: true + etcdctl --cert-file {{ etcd_peer_cert_file }} --key-file {{ etcd_peer_key_file }} --ca-file {{ etcd_peer_ca_file }} --endpoint https://{{ etcd_peer }}:{{ etcd_client_port }} cluster-health + register: l_etcd_migrate_health + until: l_etcd_migrate_health.rc == 0 + retries: 3 + delay: 30 - set_fact: r_etcd_migrate_success: true diff --git a/roles/lib_openshift/library/oc_adm_ca_server_cert.py b/roles/lib_openshift/library/oc_adm_ca_server_cert.py index 1b73bfd0e..45d7444a4 100644 --- a/roles/lib_openshift/library/oc_adm_ca_server_cert.py +++ b/roles/lib_openshift/library/oc_adm_ca_server_cert.py @@ -1288,13 +1288,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_adm_csr.py b/roles/lib_openshift/library/oc_adm_csr.py new file mode 100644 index 000000000..231857cca --- /dev/null +++ b/roles/lib_openshift/library/oc_adm_csr.py @@ -0,0 +1,1649 @@ +#!/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/csr -*- -*- -*- + +DOCUMENTATION = ''' +--- +module: oc_adm_csr +short_description: Module to approve or deny openshift certificate signing requests +description: + - Wrapper around the openshift `oc adm certificate approve|deny <csr>` command. +options: + state: + description: + - approve|deny|list Approve, deny, and list are the only supported states for certificates + required: false + default: present + choices: + - present + 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: [] + nodes: + description: + - A list of the names of the nodes in which to accept the certificates + required: false + default: None + aliases: [] + timeout: + description: + - This flag allows for a timeout value when approving nodes. + required: false + default: 30 + aliases: [] + timeout: + description: + - This flag allows for a timeout value when doing node approvals. + - A zero value for the timeout will block until the nodes have been accepted + required: false + default: 30 + aliases: [] + approve_all: + description: + - This flag allows for the module to approve all CSRs that are found. + - This facilitates testing. + required: false + default: False + aliases: [] + service_account: + description: + - This parameter tells the approval process which service account is being used for the requests + required: false + default: node-bootstrapper + aliases: [] +author: +- "Kenny Woodson <kwoodson@redhat.com>" +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +- name: Approve certificates for node xyz + oc_adm_scr: + nodes: + - xyz + timeout: 300 + +- name: Approve certificates for node xyz + oc_adm_scr: + nodes: + - xyz + timeout: 0 +''' + +# -*- -*- -*- End included fragment: doc/csr -*- -*- -*- + +# -*- -*- -*- Begin included fragment: ../../lib_utils/src/class/yedit.py -*- -*- -*- + + +class YeditException(Exception): # pragma: no cover + ''' Exception class for Yedit ''' + pass + + +# pylint: disable=too-many-public-methods +class Yedit(object): # pragma: no cover + ''' Class to modify yaml files ''' + re_valid_key = r"(((\[-?\d+\])|([0-9a-zA-Z%s/_-]+)).?)+$" + re_key = r"(?:\[(-?\d+)\])|([0-9a-zA-Z{}/_-]+)" + 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 separator ''' + return self._separator + + @separator.setter + def separator(self, inc_sep): + ''' setter method for separator ''' + self._separator = inc_sep + + @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.format(''.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.format(''.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) + 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): + raise YeditException("Unexpected item type found while going through key " + + "path: {} (at key: {})".format(key, dict_key)) + + 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: + raise YeditException("Unexpected item type found while going through key path: {}".format(key)) + + 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 + + # didn't add/update to an existing list, nor add/update key to a dict + # so we must have been provided some syntax like a.b.c[<int>] = "data" for a + # non-existent array + else: + raise YeditException("Error adding to object at path: {}".format(key)) + + 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) + 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') + + # Try to set format attributes if supported + try: + self.yaml_dict.fa.set_block_style() + except AttributeError: + pass + + # Try to use RoundTripDumper if supported. + try: + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + except AttributeError: + 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: + # Try to set format attributes if supported + try: + self.yaml_dict.fa.set_block_style() + except AttributeError: + pass + + # Try to use RoundTripLoader if supported. + try: + self.yaml_dict = yaml.safe_load(contents, yaml.RoundTripLoader) + except AttributeError: + self.yaml_dict = yaml.safe_load(contents) + + # Try to set format attributes if supported + try: + self.yaml_dict.fa.set_block_style() + except AttributeError: + pass + + 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. {}'.format(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): + # AUDIT:maybe-no-member makes sense due to fuzzy types + # pylint: disable=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): + # AUDIT:maybe-no-member makes sense due to fuzzy types + # pylint: disable=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) + + # AUDIT:maybe-no-member makes sense due to loading data from + # a serialized format. + # pylint: disable=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): + # AUDIT:maybe-no-member makes sense due to fuzzy types + # pylint: disable=maybe-no-member + if not isinstance(value, dict): + raise YeditException('Cannot replace key, value entry in dict with non-dict type. ' + + 'value=[{}] type=[{}]'.format(value, type(value))) + + entry.update(value) + return (True, self.yaml_dict) + + elif isinstance(entry, list): + # AUDIT:maybe-no-member makes sense due to fuzzy types + # pylint: disable=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 + # Try to use ruamel.yaml and fallback to pyyaml + try: + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + except AttributeError: + tmp_copy = copy.deepcopy(self.yaml_dict) + + # set the format attributes if available + try: + tmp_copy.fa.set_block_style() + except AttributeError: + pass + + result = Yedit.add_entry(tmp_copy, path, value, self.separator) + if result is None: + return (False, self.yaml_dict) + + # When path equals "" it is a special case. + # "" refers to the root of the document + # Only update the root path (entire document) when its a list or dict + if path == '': + if isinstance(result, list) or isinstance(result, dict): + self.yaml_dict = result + return (True, self.yaml_dict) + + 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 + # Try to use ruamel.yaml and fallback to pyyaml + try: + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + except AttributeError: + tmp_copy = copy.deepcopy(self.yaml_dict) + + # set the format attributes if available + try: + tmp_copy.fa.set_block_style() + except AttributeError: + pass + + result = Yedit.add_entry(tmp_copy, path, value, self.separator) + if result is not None: + 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=[{}] vtype=[{}]'.format(inc_value, vtype)) + elif isinstance(inc_value, bool) and 'str' in vtype: + inc_value = str(inc_value) + + # There is a special case where '' will turn into None after yaml loading it so skip + if isinstance(inc_value, str) and inc_value == '': + pass + # If vtype is not str then go ahead and attempt to yaml load it. + elif isinstance(inc_value, str) and 'str' not in vtype: + try: + inc_value = yaml.safe_load(inc_value) + except Exception: + raise YeditException('Could not determine type of incoming value. ' + + 'value=[{}] vtype=[{}]'.format(type(inc_value), vtype)) + + return inc_value + + @staticmethod + def process_edits(edits, yamlfile): + '''run through a list of edits and process them one-by-one''' + results = [] + for edit in edits: + value = Yedit.parse_value(edit['value'], edit.get('value_type', '')) + if edit.get('action') == 'update': + # pylint: disable=line-too-long + curr_value = Yedit.get_curr_value( + Yedit.parse_value(edit.get('curr_value')), + edit.get('curr_value_format')) + + rval = yamlfile.update(edit['key'], + value, + edit.get('index'), + curr_value) + + elif edit.get('action') == 'append': + rval = yamlfile.append(edit['key'], value) + + else: + rval = yamlfile.put(edit['key'], value) + + if rval[0]: + results.append({'key': edit['key'], 'edit': rval[1]}) + + return {'changed': len(results) > 0, 'results': results} + + # pylint: disable=too-many-return-statements,too-many-branches + @staticmethod + def run_ansible(params): + '''perform the idempotent crud operations''' + yamlfile = Yedit(filename=params['src'], + backup=params['backup'], + separator=params['separator']) + + state = params['state'] + + if params['src']: + rval = yamlfile.load() + + if yamlfile.yaml_dict is None and state != 'present': + return {'failed': True, + 'msg': 'Error opening file [{}]. Verify that the '.format(params['src']) + + 'file exists, that it is has correct permissions, and is valid yaml.'} + + if state == 'list': + if params['content']: + content = Yedit.parse_value(params['content'], params['content_type']) + yamlfile.yaml_dict = content + + if params['key']: + rval = yamlfile.get(params['key']) or {} + + return {'changed': False, 'result': rval, 'state': state} + + elif state == 'absent': + if params['content']: + content = Yedit.parse_value(params['content'], params['content_type']) + yamlfile.yaml_dict = content + + if params['update']: + rval = yamlfile.pop(params['key'], params['value']) + else: + rval = yamlfile.delete(params['key']) + + if rval[0] and params['src']: + yamlfile.write() + + return {'changed': rval[0], 'result': rval[1], 'state': state} + + elif state == 'present': + # check if content is different than what is in the file + if params['content']: + content = Yedit.parse_value(params['content'], params['content_type']) + + # We had no edits to make and the contents are the same + if yamlfile.yaml_dict == content and \ + params['value'] is None: + return {'changed': False, 'result': yamlfile.yaml_dict, 'state': state} + + yamlfile.yaml_dict = content + + # If we were passed a key, value then + # we enapsulate it in a list and process it + # Key, Value passed to the module : Converted to Edits list # + edits = [] + _edit = {} + if params['value'] is not None: + _edit['value'] = params['value'] + _edit['value_type'] = params['value_type'] + _edit['key'] = params['key'] + + if params['update']: + _edit['action'] = 'update' + _edit['curr_value'] = params['curr_value'] + _edit['curr_value_format'] = params['curr_value_format'] + _edit['index'] = params['index'] + + elif params['append']: + _edit['action'] = 'append' + + edits.append(_edit) + + elif params['edits'] is not None: + edits = params['edits'] + + if edits: + results = Yedit.process_edits(edits, yamlfile) + + # if there were changes and a src provided to us we need to write + if results['changed'] and params['src']: + yamlfile.write() + + return {'changed': results['changed'], 'result': results['results'], 'state': state} + + # no edits to make + if params['src']: + # pylint: disable=redefined-variable-type + rval = yamlfile.write() + return {'changed': rval[0], + 'result': rval[1], + 'state': state} + + # We were passed content but no src, key or value, or edits. Return contents in memory + return {'changed': False, 'result': yamlfile.yaml_dict, 'state': state} + 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 + + +ADDITIONAL_PATH_LOOKUPS = ['/usr/local/bin', os.path.expanduser('~/bin')] + + +def locate_oc_binary(): + ''' Find and return oc binary file ''' + # https://github.com/openshift/openshift-ansible/issues/3410 + # oc can be in /usr/local/bin in some cases, but that may not + # be in $PATH due to ansible/sudo + paths = os.environ.get("PATH", os.defpath).split(os.pathsep) + ADDITIONAL_PATH_LOOKUPS + + oc_binary = 'oc' + + # Use shutil.which if it is available, otherwise fallback to a naive path search + try: + which_result = shutil.which(oc_binary, path=os.pathsep.join(paths)) + if which_result is not None: + oc_binary = which_result + except AttributeError: + for path in paths: + if os.path.exists(os.path.join(path, oc_binary)): + oc_binary = os.path.join(path, oc_binary) + break + + return oc_binary + + +# 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 + self.oc_binary = locate_oc_binary() + + # 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''' + # We are removing the 'resourceVersion' to handle + # a race condition when modifying oc objects + yed = Yedit(fname) + results = yed.delete('metadata.resourceVersion') + if results[0]: + yed.write() + + 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, name=None, selector=None): + '''call oc delete on a resource''' + cmd = ['delete', resource] + if selector is not None: + cmd.append('--selector={}'.format(selector)) + elif name is not None: + cmd.append(name) + else: + raise OpenShiftCLIError('Either name or selector is required when calling delete.') + + 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 = ["{}={}".format(key, str(value).replace("'", r'"')) 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, name=None, selector=None): + '''return a resource by name ''' + cmd = ['get', resource] + if selector is not None: + cmd.append('--selector={}'.format(selector)) + elif name is not None: + cmd.append(name) + + 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={}'.format(selector)) + + cmd.append('--schedulable={}'.format(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={}'.format(selector)) + + if pod_selector: + cmd.append('--pod-selector={}'.format(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={}'.format(selector)) + + if dry_run: + cmd.append('--dry-run') + + if pod_selector: + cmd.append('--pod-selector={}'.format(pod_selector)) + + if grace_period: + cmd.append('--grace-period={}'.format(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('utf-8'), stderr.decode('utf-8') + + # 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 = [self.oc_binary] + + if oadm: + cmds.append('adm') + + cmds.extend(cmd) + + 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]) + + if self.verbose: + print(' '.join(cmds)) + + try: + returncode, stdout, stderr = self._run(cmds, input_data) + except OSError as ex: + returncode, stdout, stderr = 1, '', 'Failed to execute {}: {}'.format(subprocess.list2cmdline(cmds), ex) + + rval = {"returncode": returncode, + "cmd": ' '.join(cmds)} + + if output_type == 'json': + rval['results'] = {} + if output and stdout: + try: + rval['results'] = json.loads(stdout) + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + rval['err'] = verr.args + elif output_type == 'raw': + rval['results'] = stdout if output else '' + + if self.verbose: + print("STDOUT: {0}".format(stdout)) + print("STDERR: {0}".format(stderr)) + + if 'err' in rval or returncode != 0: + rval.update({"stderr": stderr, + "stdout": stdout}) + + return rval + + +class Utils(object): # pragma: no cover + ''' 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': + # AUDIT:no-member makes sense here due to ruamel.YAML/PyYAML usage + # 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': + # AUDIT:no-member makes sense here due to ruamel.YAML/PyYAML usage + # 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 rpm + + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") + + return rpmquery.count() > 0 + + # 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(user_def[key]) + print(value) + 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(user_values) + print(api_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, ascommalist=''): + '''return all options as a string + if ascommalist is set to the name of a key, and + the value of that key is a dict, format the dict + as a list of comma delimited key=value pairs''' + return self.stringify(ascommalist) + + def stringify(self, ascommalist=''): + ''' return the options hash as cli params in a string + if ascommalist is set to the name of a key, and + the value of that key is a dict, format the dict + as a list of comma delimited key=value pairs ''' + rval = [] + for key in sorted(self.config_options.keys()): + data = self.config_options[key] + if data['include'] \ + and (data['value'] or isinstance(data['value'], int)): + if key == ascommalist: + val = ','.join(['{}={}'.format(kk, vv) for kk, vv in sorted(data['value'].items())]) + else: + val = data['value'] + rval.append('--{}={}'.format(key.replace('_', '-'), val)) + + return rval + + +# -*- -*- -*- End included fragment: lib/base.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: class/oc_adm_csr.py -*- -*- -*- + + +class OCcsr(OpenShiftCLI): + ''' Class to wrap the oc adm certificate command line''' + kind = 'csr' + + # pylint: disable=too-many-arguments + def __init__(self, + nodes=None, + approve_all=False, + service_account=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for oc adm certificate ''' + super(OCcsr, self).__init__(None, kubeconfig, verbose) + self.service_account = service_account + self.nodes = self.create_nodes(nodes) + self._csrs = [] + self.approve_all = approve_all + self.verbose = verbose + + @property + def csrs(self): + '''property for managing csrs''' + # any processing needed?? + self._csrs = self._get(resource=self.kind)['results'][0]['items'] + return self._csrs + + def create_nodes(self, nodes): + '''create a node object to track csr signing status''' + nodes_list = [] + + if nodes is None: + return nodes_list + + results = self._get(resource='nodes')['results'][0]['items'] + + for node in nodes: + nodes_list.append(dict(name=node, csrs={}, accepted=False, denied=False)) + + for ocnode in results: + if node in ocnode['metadata']['name']: + nodes_list[-1]['accepted'] = True + + return nodes_list + + def get(self): + '''get the current certificate signing requests''' + return self.csrs + + @staticmethod + def action_needed(csr, action): + '''check to see if csr is in desired state''' + if csr['status'] == {}: + return True + + state = csr['status']['conditions'][0]['type'] + + if action == 'approve' and state != 'Approved': + return True + + elif action == 'deny' and state != 'Denied': + return True + + return False + + def match_node(self, csr): + '''match an inc csr to a node in self.nodes''' + for node in self.nodes: + # we have a match + if node['name'] in csr['metadata']['name']: + node['csrs'][csr['metadata']['name']] = csr + + # check that the username is the node and type is 'Approved' + if node['name'] in csr['spec']['username'] and csr['status']: + if csr['status']['conditions'][0]['type'] == 'Approved': + node['accepted'] = True + # check type is 'Denied' and mark node as such + if csr['status'] and csr['status']['conditions'][0]['type'] == 'Denied': + node['denied'] = True + + return node + + return None + + def finished(self): + '''determine if there are more csrs to sign''' + # if nodes is set and we have nodes then return if all nodes are 'accepted' + if self.nodes is not None and len(self.nodes) > 0: + return all([node['accepted'] or node['denied'] for node in self.nodes]) + + # we are approving everything or we still have nodes outstanding + return False + + def manage(self, action): + '''run openshift oc adm ca create-server-cert cmd and store results into self.nodes + + we attempt to verify if the node is one that was given to us to accept. + + action - (allow | deny) + ''' + + results = [] + # There are 2 types of requests: + # - node-bootstrapper-client-ip-172-31-51-246-ec2-internal + # The client request allows the client to talk to the api/controller + # - node-bootstrapper-server-ip-172-31-51-246-ec2-internal + # The server request allows the server to join the cluster + # Here we need to determine how to approve/deny + # we should query the csrs and verify they are from the nodes we thought + for csr in self.csrs: + node = self.match_node(csr) + # oc adm certificate <approve|deny> csr + # there are 3 known states: Denied, Aprroved, {} + # verify something is needed by OCcsr.action_needed + # if approve_all, then do it + # if you passed in nodes, you must have a node that matches + if self.approve_all or (node and OCcsr.action_needed(csr, action)): + result = self.openshift_cmd(['certificate', action, csr['metadata']['name']], oadm=True) + # client should have service account name in username field + # server should have node name in username field + if node and csr['metadata']['name'] not in node['csrs']: + node['csrs'][csr['metadata']['name']] = csr + + # accept node in cluster + if node['name'] in csr['spec']['username']: + node['accepted'] = True + + results.append(result) + + return results + + @staticmethod + def run_ansible(params, check_mode=False): + '''run the idempotent ansible code''' + + client = OCcsr(params['nodes'], + params['approve_all'], + params['service_account'], + params['kubeconfig'], + params['debug']) + + state = params['state'] + + api_rval = client.get() + + if state == 'list': + return {'changed': False, 'results': api_rval, 'state': state} + + if state in ['approve', 'deny']: + if check_mode: + return {'changed': True, + 'msg': "CHECK_MODE: Would have {} the certificate.".format(params['state']), + 'state': state} + + all_results = [] + finished = False + timeout = False + import time + # loop for timeout or block until all nodes pass + ctr = 0 + while True: + + all_results.extend(client.manage(params['state'])) + if client.finished(): + finished = True + break + + if params['timeout'] == 0: + if not params['approve_all']: + ctr = 0 + + if ctr * 2 > params['timeout']: + timeout = True + break + + # This provides time for the nodes to send their csr requests between approvals + time.sleep(2) + + ctr += 1 + + for result in all_results: + if result['returncode'] != 0: + return {'failed': True, 'msg': all_results} + + return dict(changed=len(all_results) > 0, + results=all_results, + nodes=client.nodes, + state=state, + finished=finished, + timeout=timeout) + + return {'failed': True, + 'msg': 'Unknown state passed. %s' % state} + + +# -*- -*- -*- End included fragment: class/oc_adm_csr.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: ansible/oc_adm_csr.py -*- -*- -*- + +def main(): + ''' + ansible oc module for approving certificate signing requests + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='approve', type='str', + choices=['approve', 'deny', 'list']), + debug=dict(default=False, type='bool'), + nodes=dict(default=None, type='list'), + timeout=dict(default=30, type='int'), + approve_all=dict(default=False, type='bool'), + service_account=dict(default='node-bootstrapper', type='str'), + ), + supports_check_mode=True, + mutually_exclusive=[['approve_all', 'nodes']], + ) + + if module.params['nodes'] == []: + module.fail_json(**dict(failed=True, msg='Please specify hosts.')) + + rval = OCcsr.run_ansible(module.params, module.check_mode) + + if 'failed' in rval: + return module.fail_json(**rval) + + return module.exit_json(**rval) + + +if __name__ == '__main__': + main() + +# -*- -*- -*- End included fragment: ansible/oc_adm_csr.py -*- -*- -*- diff --git a/roles/lib_openshift/library/oc_adm_manage_node.py b/roles/lib_openshift/library/oc_adm_manage_node.py index b09321a5b..44f3f57d8 100644 --- a/roles/lib_openshift/library/oc_adm_manage_node.py +++ b/roles/lib_openshift/library/oc_adm_manage_node.py @@ -1274,13 +1274,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_adm_policy_group.py b/roles/lib_openshift/library/oc_adm_policy_group.py index 7154fd839..687cff579 100644 --- a/roles/lib_openshift/library/oc_adm_policy_group.py +++ b/roles/lib_openshift/library/oc_adm_policy_group.py @@ -1260,13 +1260,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_adm_policy_user.py b/roles/lib_openshift/library/oc_adm_policy_user.py index 3fcf49799..ddf5d90b7 100644 --- a/roles/lib_openshift/library/oc_adm_policy_user.py +++ b/roles/lib_openshift/library/oc_adm_policy_user.py @@ -1260,13 +1260,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_adm_registry.py b/roles/lib_openshift/library/oc_adm_registry.py index bf2650460..c00eee381 100644 --- a/roles/lib_openshift/library/oc_adm_registry.py +++ b/roles/lib_openshift/library/oc_adm_registry.py @@ -1378,13 +1378,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_adm_router.py b/roles/lib_openshift/library/oc_adm_router.py index a2b7d12c0..0c925ab0b 100644 --- a/roles/lib_openshift/library/oc_adm_router.py +++ b/roles/lib_openshift/library/oc_adm_router.py @@ -1403,13 +1403,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_clusterrole.py b/roles/lib_openshift/library/oc_clusterrole.py index d101eac1c..567ecfd4e 100644 --- a/roles/lib_openshift/library/oc_clusterrole.py +++ b/roles/lib_openshift/library/oc_clusterrole.py @@ -1252,13 +1252,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_configmap.py b/roles/lib_openshift/library/oc_configmap.py index 7cd29215f..9515de569 100644 --- a/roles/lib_openshift/library/oc_configmap.py +++ b/roles/lib_openshift/library/oc_configmap.py @@ -1258,13 +1258,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_edit.py b/roles/lib_openshift/library/oc_edit.py index 5b11f45ba..d461e5ae9 100644 --- a/roles/lib_openshift/library/oc_edit.py +++ b/roles/lib_openshift/library/oc_edit.py @@ -1302,13 +1302,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_env.py b/roles/lib_openshift/library/oc_env.py index d3834ce0c..22ad58725 100644 --- a/roles/lib_openshift/library/oc_env.py +++ b/roles/lib_openshift/library/oc_env.py @@ -1269,13 +1269,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_group.py b/roles/lib_openshift/library/oc_group.py index 0d751fe28..b6c6e47d9 100644 --- a/roles/lib_openshift/library/oc_group.py +++ b/roles/lib_openshift/library/oc_group.py @@ -1242,13 +1242,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_image.py b/roles/lib_openshift/library/oc_image.py index 3a6ba3e56..f7fc286e0 100644 --- a/roles/lib_openshift/library/oc_image.py +++ b/roles/lib_openshift/library/oc_image.py @@ -1261,13 +1261,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_label.py b/roles/lib_openshift/library/oc_label.py index 5db036b23..2206878a4 100644 --- a/roles/lib_openshift/library/oc_label.py +++ b/roles/lib_openshift/library/oc_label.py @@ -1278,13 +1278,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_obj.py b/roles/lib_openshift/library/oc_obj.py index 9b0c0e0e4..126d7a617 100644 --- a/roles/lib_openshift/library/oc_obj.py +++ b/roles/lib_openshift/library/oc_obj.py @@ -1281,13 +1281,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements @@ -1478,7 +1477,16 @@ class OCObject(OpenShiftCLI): if files: return self._create(files[0]) - content['data'] = yaml.dump(content['data']) + # pylint: disable=no-member + # The purpose of this change is twofold: + # - we need a check to only use the ruamel specific dumper if ruamel is loaded + # - the dumper or the flow style change is needed so openshift is able to parse + # the resulting yaml, at least until gopkg.in/yaml.v2 is updated + if hasattr(yaml, 'RoundTripDumper'): + content['data'] = yaml.dump(content['data'], Dumper=yaml.RoundTripDumper) + else: + content['data'] = yaml.safe_dump(content['data'], default_flow_style=False) + content_file = Utils.create_tmp_files_from_contents(content)[0] return self._create(content_file['path']) diff --git a/roles/lib_openshift/library/oc_objectvalidator.py b/roles/lib_openshift/library/oc_objectvalidator.py index 130521761..d20904d0d 100644 --- a/roles/lib_openshift/library/oc_objectvalidator.py +++ b/roles/lib_openshift/library/oc_objectvalidator.py @@ -1213,13 +1213,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_process.py b/roles/lib_openshift/library/oc_process.py index c6568d520..91199d093 100644 --- a/roles/lib_openshift/library/oc_process.py +++ b/roles/lib_openshift/library/oc_process.py @@ -1270,13 +1270,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_project.py b/roles/lib_openshift/library/oc_project.py index a78bc06d2..f9b2d81fa 100644 --- a/roles/lib_openshift/library/oc_project.py +++ b/roles/lib_openshift/library/oc_project.py @@ -1267,13 +1267,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements @@ -1657,6 +1656,9 @@ class OCProject(OpenShiftCLI): # Create it here api_rval = oadm_project.create() + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + # return the created object api_rval = oadm_project.get() diff --git a/roles/lib_openshift/library/oc_pvc.py b/roles/lib_openshift/library/oc_pvc.py index a21540962..895322ba5 100644 --- a/roles/lib_openshift/library/oc_pvc.py +++ b/roles/lib_openshift/library/oc_pvc.py @@ -1274,13 +1274,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_route.py b/roles/lib_openshift/library/oc_route.py index 0c0bc9386..8f8e46e1e 100644 --- a/roles/lib_openshift/library/oc_route.py +++ b/roles/lib_openshift/library/oc_route.py @@ -1312,13 +1312,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements @@ -1666,9 +1665,6 @@ class OCRoute(OpenShiftCLI): @staticmethod def get_cert_data(path, content): '''get the data for a particular value''' - if not path and not content: - return None - rval = None if path and os.path.exists(path) and os.access(path, os.R_OK): rval = open(path).read() @@ -1707,14 +1703,14 @@ class OCRoute(OpenShiftCLI): if params['tls_termination'] and params['tls_termination'].lower() != 'passthrough': # E501 for key, option in files.items(): - if key == 'destcacert' and params['tls_termination'] != 'reencrypt': + if not option['path'] and not option['content']: continue option['value'] = OCRoute.get_cert_data(option['path'], option['content']) # E501 if not option['value']: return {'failed': True, - 'msg': 'Verify that you pass a value for %s' % key} + 'msg': 'Verify that you pass a correct value for %s' % key} rconfig = RouteConfig(params['name'], params['namespace'], diff --git a/roles/lib_openshift/library/oc_scale.py b/roles/lib_openshift/library/oc_scale.py index f112b6dd0..7130cc5fc 100644 --- a/roles/lib_openshift/library/oc_scale.py +++ b/roles/lib_openshift/library/oc_scale.py @@ -1256,13 +1256,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_secret.py b/roles/lib_openshift/library/oc_secret.py index d762e0c38..0c4b99e30 100644 --- a/roles/lib_openshift/library/oc_secret.py +++ b/roles/lib_openshift/library/oc_secret.py @@ -1308,13 +1308,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_service.py b/roles/lib_openshift/library/oc_service.py index 769b75e15..7ab139e85 100644 --- a/roles/lib_openshift/library/oc_service.py +++ b/roles/lib_openshift/library/oc_service.py @@ -1315,13 +1315,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_serviceaccount.py b/roles/lib_openshift/library/oc_serviceaccount.py index 446987eff..5d539ced4 100644 --- a/roles/lib_openshift/library/oc_serviceaccount.py +++ b/roles/lib_openshift/library/oc_serviceaccount.py @@ -1254,13 +1254,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_serviceaccount_secret.py b/roles/lib_openshift/library/oc_serviceaccount_secret.py index c7eb1986a..97e213f46 100644 --- a/roles/lib_openshift/library/oc_serviceaccount_secret.py +++ b/roles/lib_openshift/library/oc_serviceaccount_secret.py @@ -1254,13 +1254,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_storageclass.py b/roles/lib_openshift/library/oc_storageclass.py index 686119c65..9339a85e5 100644 --- a/roles/lib_openshift/library/oc_storageclass.py +++ b/roles/lib_openshift/library/oc_storageclass.py @@ -1272,13 +1272,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_user.py b/roles/lib_openshift/library/oc_user.py index 3a98693b7..2fa349547 100644 --- a/roles/lib_openshift/library/oc_user.py +++ b/roles/lib_openshift/library/oc_user.py @@ -1314,13 +1314,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_version.py b/roles/lib_openshift/library/oc_version.py index 939261526..55e1054e7 100644 --- a/roles/lib_openshift/library/oc_version.py +++ b/roles/lib_openshift/library/oc_version.py @@ -1226,13 +1226,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/library/oc_volume.py b/roles/lib_openshift/library/oc_volume.py index 41e7d0ab8..63bad57b4 100644 --- a/roles/lib_openshift/library/oc_volume.py +++ b/roles/lib_openshift/library/oc_volume.py @@ -1303,13 +1303,12 @@ class Utils(object): # pragma: no cover @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/src/ansible/oc_adm_csr.py b/roles/lib_openshift/src/ansible/oc_adm_csr.py new file mode 100644 index 000000000..9e43a810b --- /dev/null +++ b/roles/lib_openshift/src/ansible/oc_adm_csr.py @@ -0,0 +1,36 @@ +# pylint: skip-file +# flake8: noqa + +def main(): + ''' + ansible oc module for approving certificate signing requests + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='approve', type='str', + choices=['approve', 'deny', 'list']), + debug=dict(default=False, type='bool'), + nodes=dict(default=None, type='list'), + timeout=dict(default=30, type='int'), + approve_all=dict(default=False, type='bool'), + service_account=dict(default='node-bootstrapper', type='str'), + ), + supports_check_mode=True, + mutually_exclusive=[['approve_all', 'nodes']], + ) + + if module.params['nodes'] == []: + module.fail_json(**dict(failed=True, msg='Please specify hosts.')) + + rval = OCcsr.run_ansible(module.params, module.check_mode) + + if 'failed' in rval: + return module.fail_json(**rval) + + return module.exit_json(**rval) + + +if __name__ == '__main__': + main() diff --git a/roles/lib_openshift/src/class/oc_adm_csr.py b/roles/lib_openshift/src/class/oc_adm_csr.py new file mode 100644 index 000000000..ea11c6ca9 --- /dev/null +++ b/roles/lib_openshift/src/class/oc_adm_csr.py @@ -0,0 +1,197 @@ +# pylint: skip-file +# flake8: noqa + + +class OCcsr(OpenShiftCLI): + ''' Class to wrap the oc adm certificate command line''' + kind = 'csr' + + # pylint: disable=too-many-arguments + def __init__(self, + nodes=None, + approve_all=False, + service_account=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for oc adm certificate ''' + super(OCcsr, self).__init__(None, kubeconfig, verbose) + self.service_account = service_account + self.nodes = self.create_nodes(nodes) + self._csrs = [] + self.approve_all = approve_all + self.verbose = verbose + + @property + def csrs(self): + '''property for managing csrs''' + # any processing needed?? + self._csrs = self._get(resource=self.kind)['results'][0]['items'] + return self._csrs + + def create_nodes(self, nodes): + '''create a node object to track csr signing status''' + nodes_list = [] + + if nodes is None: + return nodes_list + + results = self._get(resource='nodes')['results'][0]['items'] + + for node in nodes: + nodes_list.append(dict(name=node, csrs={}, accepted=False, denied=False)) + + for ocnode in results: + if node in ocnode['metadata']['name']: + nodes_list[-1]['accepted'] = True + + return nodes_list + + def get(self): + '''get the current certificate signing requests''' + return self.csrs + + @staticmethod + def action_needed(csr, action): + '''check to see if csr is in desired state''' + if csr['status'] == {}: + return True + + state = csr['status']['conditions'][0]['type'] + + if action == 'approve' and state != 'Approved': + return True + + elif action == 'deny' and state != 'Denied': + return True + + return False + + def match_node(self, csr): + '''match an inc csr to a node in self.nodes''' + for node in self.nodes: + # we have a match + if node['name'] in csr['metadata']['name']: + node['csrs'][csr['metadata']['name']] = csr + + # check that the username is the node and type is 'Approved' + if node['name'] in csr['spec']['username'] and csr['status']: + if csr['status']['conditions'][0]['type'] == 'Approved': + node['accepted'] = True + # check type is 'Denied' and mark node as such + if csr['status'] and csr['status']['conditions'][0]['type'] == 'Denied': + node['denied'] = True + + return node + + return None + + def finished(self): + '''determine if there are more csrs to sign''' + # if nodes is set and we have nodes then return if all nodes are 'accepted' + if self.nodes is not None and len(self.nodes) > 0: + return all([node['accepted'] or node['denied'] for node in self.nodes]) + + # we are approving everything or we still have nodes outstanding + return False + + def manage(self, action): + '''run openshift oc adm ca create-server-cert cmd and store results into self.nodes + + we attempt to verify if the node is one that was given to us to accept. + + action - (allow | deny) + ''' + + results = [] + # There are 2 types of requests: + # - node-bootstrapper-client-ip-172-31-51-246-ec2-internal + # The client request allows the client to talk to the api/controller + # - node-bootstrapper-server-ip-172-31-51-246-ec2-internal + # The server request allows the server to join the cluster + # Here we need to determine how to approve/deny + # we should query the csrs and verify they are from the nodes we thought + for csr in self.csrs: + node = self.match_node(csr) + # oc adm certificate <approve|deny> csr + # there are 3 known states: Denied, Aprroved, {} + # verify something is needed by OCcsr.action_needed + # if approve_all, then do it + # if you passed in nodes, you must have a node that matches + if self.approve_all or (node and OCcsr.action_needed(csr, action)): + result = self.openshift_cmd(['certificate', action, csr['metadata']['name']], oadm=True) + # client should have service account name in username field + # server should have node name in username field + if node and csr['metadata']['name'] not in node['csrs']: + node['csrs'][csr['metadata']['name']] = csr + + # accept node in cluster + if node['name'] in csr['spec']['username']: + node['accepted'] = True + + results.append(result) + + return results + + @staticmethod + def run_ansible(params, check_mode=False): + '''run the idempotent ansible code''' + + client = OCcsr(params['nodes'], + params['approve_all'], + params['service_account'], + params['kubeconfig'], + params['debug']) + + state = params['state'] + + api_rval = client.get() + + if state == 'list': + return {'changed': False, 'results': api_rval, 'state': state} + + if state in ['approve', 'deny']: + if check_mode: + return {'changed': True, + 'msg': "CHECK_MODE: Would have {} the certificate.".format(params['state']), + 'state': state} + + all_results = [] + finished = False + timeout = False + import time + # loop for timeout or block until all nodes pass + ctr = 0 + while True: + + all_results.extend(client.manage(params['state'])) + if client.finished(): + finished = True + break + + if params['timeout'] == 0: + if not params['approve_all']: + ctr = 0 + + if ctr * 2 > params['timeout']: + timeout = True + break + + # This provides time for the nodes to send their csr requests between approvals + time.sleep(2) + + ctr += 1 + + for result in all_results: + if result['returncode'] != 0: + return {'failed': True, 'msg': all_results} + + return dict(changed=len(all_results) > 0, + results=all_results, + nodes=client.nodes, + state=state, + finished=finished, + timeout=timeout) + + return {'failed': True, + 'msg': 'Unknown state passed. %s' % state} + diff --git a/roles/lib_openshift/src/class/oc_obj.py b/roles/lib_openshift/src/class/oc_obj.py index 5e423bea9..68f7818e4 100644 --- a/roles/lib_openshift/src/class/oc_obj.py +++ b/roles/lib_openshift/src/class/oc_obj.py @@ -50,7 +50,16 @@ class OCObject(OpenShiftCLI): if files: return self._create(files[0]) - content['data'] = yaml.dump(content['data']) + # pylint: disable=no-member + # The purpose of this change is twofold: + # - we need a check to only use the ruamel specific dumper if ruamel is loaded + # - the dumper or the flow style change is needed so openshift is able to parse + # the resulting yaml, at least until gopkg.in/yaml.v2 is updated + if hasattr(yaml, 'RoundTripDumper'): + content['data'] = yaml.dump(content['data'], Dumper=yaml.RoundTripDumper) + else: + content['data'] = yaml.safe_dump(content['data'], default_flow_style=False) + content_file = Utils.create_tmp_files_from_contents(content)[0] return self._create(content_file['path']) diff --git a/roles/lib_openshift/src/class/oc_project.py b/roles/lib_openshift/src/class/oc_project.py index 9ad8111a8..298597067 100644 --- a/roles/lib_openshift/src/class/oc_project.py +++ b/roles/lib_openshift/src/class/oc_project.py @@ -156,6 +156,9 @@ class OCProject(OpenShiftCLI): # Create it here api_rval = oadm_project.create() + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + # return the created object api_rval = oadm_project.get() diff --git a/roles/lib_openshift/src/class/oc_route.py b/roles/lib_openshift/src/class/oc_route.py index 3935525f1..3a1bd732f 100644 --- a/roles/lib_openshift/src/class/oc_route.py +++ b/roles/lib_openshift/src/class/oc_route.py @@ -68,9 +68,6 @@ class OCRoute(OpenShiftCLI): @staticmethod def get_cert_data(path, content): '''get the data for a particular value''' - if not path and not content: - return None - rval = None if path and os.path.exists(path) and os.access(path, os.R_OK): rval = open(path).read() @@ -109,14 +106,14 @@ class OCRoute(OpenShiftCLI): if params['tls_termination'] and params['tls_termination'].lower() != 'passthrough': # E501 for key, option in files.items(): - if key == 'destcacert' and params['tls_termination'] != 'reencrypt': + if not option['path'] and not option['content']: continue option['value'] = OCRoute.get_cert_data(option['path'], option['content']) # E501 if not option['value']: return {'failed': True, - 'msg': 'Verify that you pass a value for %s' % key} + 'msg': 'Verify that you pass a correct value for %s' % key} rconfig = RouteConfig(params['name'], params['namespace'], diff --git a/roles/lib_openshift/src/doc/csr b/roles/lib_openshift/src/doc/csr new file mode 100644 index 000000000..db72dbda3 --- /dev/null +++ b/roles/lib_openshift/src/doc/csr @@ -0,0 +1,80 @@ +# flake8: noqa +# pylint: skip-file + +DOCUMENTATION = ''' +--- +module: oc_adm_csr +short_description: Module to approve or deny openshift certificate signing requests +description: + - Wrapper around the openshift `oc adm certificate approve|deny <csr>` command. +options: + state: + description: + - approve|deny|list Approve, deny, and list are the only supported states for certificates + required: false + default: present + choices: + - present + 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: [] + nodes: + description: + - A list of the names of the nodes in which to accept the certificates + required: false + default: None + aliases: [] + timeout: + description: + - This flag allows for a timeout value when approving nodes. + required: false + default: 30 + aliases: [] + timeout: + description: + - This flag allows for a timeout value when doing node approvals. + - A zero value for the timeout will block until the nodes have been accepted + required: false + default: 30 + aliases: [] + approve_all: + description: + - This flag allows for the module to approve all CSRs that are found. + - This facilitates testing. + required: false + default: False + aliases: [] + service_account: + description: + - This parameter tells the approval process which service account is being used for the requests + required: false + default: node-bootstrapper + aliases: [] +author: +- "Kenny Woodson <kwoodson@redhat.com>" +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +- name: Approve certificates for node xyz + oc_adm_scr: + nodes: + - xyz + timeout: 300 + +- name: Approve certificates for node xyz + oc_adm_scr: + nodes: + - xyz + timeout: 0 +''' diff --git a/roles/lib_openshift/src/lib/base.py b/roles/lib_openshift/src/lib/base.py index 16770b22d..5a307cdb3 100644 --- a/roles/lib_openshift/src/lib/base.py +++ b/roles/lib_openshift/src/lib/base.py @@ -464,13 +464,12 @@ class Utils(object): @staticmethod def openshift_installed(): ''' check if openshift is installed ''' - import yum + import rpm - yum_base = yum.YumBase() - if yum_base.rpmdb.searchNevra(name='atomic-openshift'): - return True + transaction_set = rpm.TransactionSet() + rpmquery = transaction_set.dbMatch("name", "atomic-openshift") - return False + return rpmquery.count() > 0 # Disabling too-many-branches. This is a yaml dictionary comparison function # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml index e9b6bf261..4636f6b9b 100644 --- a/roles/lib_openshift/src/sources.yml +++ b/roles/lib_openshift/src/sources.yml @@ -9,6 +9,16 @@ oc_adm_ca_server_cert.py: - class/oc_adm_ca_server_cert.py - ansible/oc_adm_ca_server_cert.py +oc_adm_csr.py: +- doc/generated +- doc/license +- lib/import.py +- doc/csr +- ../../lib_utils/src/class/yedit.py +- lib/base.py +- class/oc_adm_csr.py +- ansible/oc_adm_csr.py + oc_adm_manage_node.py: - doc/generated - doc/license diff --git a/roles/lib_openshift/src/test/integration/oc_adm_csr.yml b/roles/lib_openshift/src/test/integration/oc_adm_csr.yml new file mode 100755 index 000000000..cad8e36f5 --- /dev/null +++ b/roles/lib_openshift/src/test/integration/oc_adm_csr.yml @@ -0,0 +1,28 @@ +#!/usr/bin/ansible-playbook --module-path=../../../library/ +# ./oc_adm_csr.yml -M ../../../library -e "cli_master_test=$OPENSHIFT_MASTER +--- +- hosts: masters + gather_facts: no + user: root + tasks: + - name: list csrs + oc_adm_csr: + state: list + register: csrout + + - debug: var=csrout + + - name: list csrs + oc_adm_csr: + state: approve + nodes: + - ip-172-31-51-0-ec2-internal + - ip-172-31-51-246-ec2-internal + - ip-172-31-54-12-ec2-internal + - ip-172-31-58-173-ec2-internal + - ip-172-31-58-212-ec2-internal + - ip-172-31-51-246-ec2-internal + - ip-172-31-54-12-ec2-internal + + register: csrout + - debug: var=csrout diff --git a/roles/lib_os_firewall/README.md b/roles/lib_os_firewall/README.md new file mode 100644 index 000000000..ba8c84865 --- /dev/null +++ b/roles/lib_os_firewall/README.md @@ -0,0 +1,63 @@ +lib_os_firewall +=========== + +lib_os_firewall manages iptables firewall settings for a minimal use +case (Adding/Removing rules based on protocol and port number). + +Note: firewalld is not supported on Atomic Host +https://bugzilla.redhat.com/show_bug.cgi?id=1403331 + +Requirements +------------ + +Ansible 2.2 + +Role Variables +-------------- + +| Name | Default | | +|---------------------------|---------|----------------------------------------| +| os_firewall_allow | [] | List of service,port mappings to allow | +| os_firewall_deny | [] | List of service, port mappings to deny | + +Dependencies +------------ + +None. + +Example Playbook +---------------- + +Use iptables and open tcp ports 80 and 443: +``` +--- +- hosts: servers + vars: + os_firewall_use_firewalld: false + os_firewall_allow: + - service: httpd + port: 80/tcp + - service: https + port: 443/tcp + tasks: + - include_role: + name: lib_os_firewall + + - name: set allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + with_items: "{{ os_firewall_allow }}" +``` + + +License +------- + +Apache License, Version 2.0 + +Author Information +------------------ +Jason DeTiberus - jdetiber@redhat.com diff --git a/roles/os_firewall/library/os_firewall_manage_iptables.py b/roles/lib_os_firewall/library/os_firewall_manage_iptables.py index aeee3ede8..aeee3ede8 100755 --- a/roles/os_firewall/library/os_firewall_manage_iptables.py +++ b/roles/lib_os_firewall/library/os_firewall_manage_iptables.py diff --git a/roles/lib_utils/library/iam_cert23.py b/roles/lib_utils/library/iam_cert23.py new file mode 100644 index 000000000..07b3d3bdf --- /dev/null +++ b/roles/lib_utils/library/iam_cert23.py @@ -0,0 +1,314 @@ +#!/usr/bin/python +# pylint: skip-file +# flake8: noqa +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: iam_cert +short_description: Manage server certificates for use on ELBs and CloudFront +description: + - Allows for the management of server certificates +version_added: "2.0" +options: + name: + description: + - Name of certificate to add, update or remove. + required: true + new_name: + description: + - When state is present, this will update the name of the cert. + - The cert, key and cert_chain parameters will be ignored if this is defined. + new_path: + description: + - When state is present, this will update the path of the cert. + - The cert, key and cert_chain parameters will be ignored if this is defined. + state: + description: + - Whether to create(or update) or delete certificate. + - If new_path or new_name is defined, specifying present will attempt to make an update these. + required: true + choices: [ "present", "absent" ] + path: + description: + - When creating or updating, specify the desired path of the certificate. + default: "/" + cert_chain: + description: + - The path to, or content of the CA certificate chain in PEM encoded format. + As of 2.4 content is accepted. If the parameter is not a file, it is assumed to be content. + cert: + description: + - The path to, or content of the certificate body in PEM encoded format. + As of 2.4 content is accepted. If the parameter is not a file, it is assumed to be content. + key: + description: + - The path to, or content of the private key in PEM encoded format. + As of 2.4 content is accepted. If the parameter is not a file, it is assumed to be content. + dup_ok: + description: + - By default the module will not upload a certificate that is already uploaded into AWS. + If set to True, it will upload the certificate as long as the name is unique. + default: False + + +requirements: [ "boto" ] +author: Jonathan I. Davila +extends_documentation_fragment: + - aws + - ec2 +''' + +EXAMPLES = ''' +# Basic server certificate upload from local file +- iam_cert: + name: very_ssl + state: present + cert: "{{ lookup('file', 'path/to/cert') }}" + key: "{{ lookup('file', 'path/to/key') }}" + cert_chain: "{{ lookup('file', 'path/to/certchain') }}" + +# Basic server certificate upload +- iam_cert: + name: very_ssl + state: present + cert: path/to/cert + key: path/to/key + cert_chain: path/to/certchain + +# Server certificate upload using key string +- iam_cert: + name: very_ssl + state: present + path: "/a/cert/path/" + cert: body_of_somecert + key: vault_body_of_privcertkey + cert_chain: body_of_myverytrustedchain + +# Basic rename of existing certificate +- iam_cert: + name: very_ssl + new_name: new_very_ssl + state: present + +''' +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info, connect_to_aws +import os + +try: + import boto + import boto.iam + import boto.ec2 + HAS_BOTO = True +except ImportError: + HAS_BOTO = False + + +def boto_exception(err): + '''generic error message handler''' + if hasattr(err, 'error_message'): + error = err.error_message + elif hasattr(err, 'message'): + error = err.message + else: + error = '%s: %s' % (Exception, err) + + return error + + +def cert_meta(iam, name): + certificate = iam.get_server_certificate(name).get_server_certificate_result.server_certificate + ocert = certificate.certificate_body + opath = certificate.server_certificate_metadata.path + ocert_id = certificate.server_certificate_metadata.server_certificate_id + upload_date = certificate.server_certificate_metadata.upload_date + exp = certificate.server_certificate_metadata.expiration + arn = certificate.server_certificate_metadata.arn + return opath, ocert, ocert_id, upload_date, exp, arn + + +def dup_check(module, iam, name, new_name, cert, orig_cert_names, orig_cert_bodies, dup_ok): + update = False + + # IAM cert names are case insensitive + names_lower = [n.lower() for n in [name, new_name] if n is not None] + orig_cert_names_lower = [ocn.lower() for ocn in orig_cert_names] + + if any(ct in orig_cert_names_lower for ct in names_lower): + for i_name in names_lower: + if cert is not None: + try: + c_index = orig_cert_names_lower.index(i_name) + except NameError: + continue + else: + # NOTE: remove the carriage return to strictly compare the cert bodies. + slug_cert = cert.replace('\r', '') + slug_orig_cert_bodies = orig_cert_bodies[c_index].replace('\r', '') + if slug_orig_cert_bodies == slug_cert: + update = True + break + elif slug_cert.startswith(slug_orig_cert_bodies): + update = True + break + elif slug_orig_cert_bodies != slug_cert: + module.fail_json(changed=False, msg='A cert with the name %s already exists and' + ' has a different certificate body associated' + ' with it. Certificates cannot have the same name' % orig_cert_names[c_index]) + else: + update = True + break + elif cert in orig_cert_bodies and not dup_ok: + for crt_name, crt_body in zip(orig_cert_names, orig_cert_bodies): + if crt_body == cert: + module.fail_json(changed=False, msg='This certificate already' + ' exists under the name %s' % crt_name) + + return update + + +def cert_action(module, iam, name, cpath, new_name, new_path, state, + cert, key, cert_chain, orig_cert_names, orig_cert_bodies, dup_ok): + if state == 'present': + update = dup_check(module, iam, name, new_name, cert, orig_cert_names, + orig_cert_bodies, dup_ok) + if update: + opath, ocert, ocert_id, upload_date, exp, arn = cert_meta(iam, name) + changed = True + if new_name and new_path: + iam.update_server_cert(name, new_cert_name=new_name, new_path=new_path) + module.exit_json(changed=changed, original_name=name, new_name=new_name, + original_path=opath, new_path=new_path, cert_body=ocert, + upload_date=upload_date, expiration_date=exp, arn=arn) + elif new_name and not new_path: + iam.update_server_cert(name, new_cert_name=new_name) + module.exit_json(changed=changed, original_name=name, new_name=new_name, + cert_path=opath, cert_body=ocert, + upload_date=upload_date, expiration_date=exp, arn=arn) + elif not new_name and new_path: + iam.update_server_cert(name, new_path=new_path) + module.exit_json(changed=changed, name=new_name, + original_path=opath, new_path=new_path, cert_body=ocert, + upload_date=upload_date, expiration_date=exp, arn=arn) + else: + changed = False + module.exit_json(changed=changed, name=name, cert_path=opath, cert_body=ocert, + upload_date=upload_date, expiration_date=exp, arn=arn, + msg='No new path or name specified. No changes made') + else: + changed = True + iam.upload_server_cert(name, cert, key, cert_chain=cert_chain, path=cpath) + opath, ocert, ocert_id, upload_date, exp, arn = cert_meta(iam, name) + module.exit_json(changed=changed, name=name, cert_path=opath, cert_body=ocert, + upload_date=upload_date, expiration_date=exp, arn=arn) + elif state == 'absent': + if name in orig_cert_names: + changed = True + iam.delete_server_cert(name) + module.exit_json(changed=changed, deleted_cert=name) + else: + changed = False + module.exit_json(changed=changed, msg='Certificate with the name %s already absent' % name) + + +def load_data(cert, key, cert_chain): + # if paths are provided rather than lookups read the files and return the contents + if cert and os.path.isfile(cert): + cert = open(cert, 'r').read().rstrip() + if key and os.path.isfile(key): + key = open(key, 'r').read().rstrip() + if cert_chain and os.path.isfile(cert_chain): + cert_chain = open(cert_chain, 'r').read() + return cert, key, cert_chain + + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update(dict( + state=dict(required=True, choices=['present', 'absent']), + name=dict(), + cert=dict(), + key=dict(no_log=True), + cert_chain=dict(), + new_name=dict(), + path=dict(default='/'), + new_path=dict(), + dup_ok=dict(type='bool') + ) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=[ + ['new_path', 'key'], + ['new_path', 'cert'], + ['new_path', 'cert_chain'], + ['new_name', 'key'], + ['new_name', 'cert'], + ['new_name', 'cert_chain'], + ], + ) + + if not HAS_BOTO: + module.fail_json(msg="Boto is required for this module") + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) + + try: + if region: + iam = connect_to_aws(boto.iam, region, **aws_connect_kwargs) + else: + iam = boto.iam.connection.IAMConnection(**aws_connect_kwargs) + except boto.exception.NoAuthHandlerFound as e: + module.fail_json(msg=str(e)) + + state = module.params.get('state') + name = module.params.get('name') + path = module.params.get('path') + new_name = module.params.get('new_name') + new_path = module.params.get('new_path') + dup_ok = module.params.get('dup_ok') + if state == 'present' and not new_name and not new_path: + cert, key, cert_chain = load_data(cert=module.params.get('cert'), + key=module.params.get('key'), + cert_chain=module.params.get('cert_chain')) + else: + cert = key = cert_chain = None + + orig_cert_names = [ctb['server_certificate_name'] for ctb in + iam.get_all_server_certs().list_server_certificates_result.server_certificate_metadata_list] + orig_cert_bodies = [iam.get_server_certificate(thing).get_server_certificate_result.certificate_body + for thing in orig_cert_names] + if new_name == name: + new_name = None + if new_path == path: + new_path = None + + changed = False + try: + cert_action(module, iam, name, path, new_name, new_path, state, + cert, key, cert_chain, orig_cert_names, orig_cert_bodies, dup_ok) + except boto.exception.BotoServerError as err: + module.fail_json(changed=changed, msg=str(err), debug=[cert, key]) + + +if __name__ == '__main__': + main() diff --git a/roles/lib_utils/library/oo_iam_kms.py b/roles/lib_utils/library/oo_iam_kms.py new file mode 100644 index 000000000..c85745f01 --- /dev/null +++ b/roles/lib_utils/library/oo_iam_kms.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +''' +ansible module for creating AWS IAM KMS keys +''' +# vim: expandtab:tabstop=4:shiftwidth=4 +# +# AWS IAM KMS ansible module +# +# +# Copyright 2016 Red Hat Inc. +# +# 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. +# +# Jenkins environment doesn't have all the required libraries +# pylint: disable=import-error +import time +import boto3 +# Ansible modules need this wildcard import +# pylint: disable=unused-wildcard-import, wildcard-import, redefined-builtin +from ansible.module_utils.basic import AnsibleModule + +AWS_ALIAS_URL = "http://docs.aws.amazon.com/kms/latest/developerguide/programming-aliases.html" + + +class AwsIamKms(object): + ''' + ansible module for AWS IAM KMS + ''' + + def __init__(self): + ''' constructor ''' + self.module = None + self.kms_client = None + self.aliases = None + + @staticmethod + def valid_alias_name(user_alias): + ''' AWS KMS aliases must start with 'alias/' ''' + valid_start = 'alias/' + if user_alias.startswith(valid_start): + return True + + return False + + def get_all_kms_info(self): + '''fetch all kms info and return them + + list_keys doesn't have information regarding aliases + list_aliases doesn't have the full kms arn + + fetch both and join them on the targetKeyId + ''' + aliases = self.kms_client.list_aliases()['Aliases'] + keys = self.kms_client.list_keys()['Keys'] + + for alias in aliases: + for key in keys: + if 'TargetKeyId' in alias and 'KeyId' in key: + if alias['TargetKeyId'] == key['KeyId']: + alias.update(key) + + return aliases + + def get_kms_entry(self, user_alias, alias_list): + ''' return single alias details from list of aliases ''' + for alias in alias_list: + if user_alias == alias.get('AliasName', False): + return alias + + msg = "Did not find alias {}".format(user_alias) + self.module.exit_json(failed=True, results=msg) + + @staticmethod + def exists(user_alias, alias_list): + ''' Check if KMS alias already exists ''' + for alias in alias_list: + if user_alias == alias.get('AliasName'): + return True + + return False + + def main(self): + ''' entry point for module ''' + + self.module = AnsibleModule( + argument_spec=dict( + state=dict(default='list', choices=['list', 'present'], type='str'), + region=dict(default=None, required=True, type='str'), + alias=dict(default=None, type='str'), + # description default cannot be None + description=dict(default='', type='str'), + aws_access_key=dict(default=None, type='str'), + aws_secret_key=dict(default=None, type='str'), + ), + ) + + state = self.module.params['state'] + aws_access_key = self.module.params['aws_access_key'] + aws_secret_key = self.module.params['aws_secret_key'] + if aws_access_key and aws_secret_key: + boto3.setup_default_session(aws_access_key_id=aws_access_key, + aws_secret_access_key=aws_secret_key, + region_name=self.module.params['region']) + else: + boto3.setup_default_session(region_name=self.module.params['region']) + + self.kms_client = boto3.client('kms') + + aliases = self.get_all_kms_info() + + if state == 'list': + if self.module.params['alias'] is not None: + user_kms = self.get_kms_entry(self.module.params['alias'], + aliases) + self.module.exit_json(changed=False, results=user_kms, + state="list") + else: + self.module.exit_json(changed=False, results=aliases, + state="list") + + if state == 'present': + + # early sanity check to make sure the alias name conforms with + # AWS alias name requirements + if not self.valid_alias_name(self.module.params['alias']): + self.module.exit_json(failed=True, changed=False, + results="Alias must start with the prefix " + + "'alias/'. Please see " + AWS_ALIAS_URL, + state='present') + + if not self.exists(self.module.params['alias'], aliases): + # if we didn't find it, create it + response = self.kms_client.create_key(KeyUsage='ENCRYPT_DECRYPT', + Description=self.module.params['description']) + kid = response['KeyMetadata']['KeyId'] + response = self.kms_client.create_alias(AliasName=self.module.params['alias'], + TargetKeyId=kid) + # sleep for a bit so that the KMS data can be queried + time.sleep(10) + # get details for newly created KMS entry + new_alias_list = self.kms_client.list_aliases()['Aliases'] + user_kms = self.get_kms_entry(self.module.params['alias'], + new_alias_list) + + self.module.exit_json(changed=True, results=user_kms, + state='present') + + # already exists, normally we would check whether we need to update it + # but this module isn't written to allow changing the alias name + # or changing whether the key is enabled/disabled + user_kms = self.get_kms_entry(self.module.params['alias'], aliases) + self.module.exit_json(changed=False, results=user_kms, + state="present") + + self.module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + + +if __name__ == '__main__': + AwsIamKms().main() diff --git a/roles/nuage_common/defaults/main.yaml b/roles/nuage_common/defaults/main.yaml index a7803c0ee..919e3aa7b 100644 --- a/roles/nuage_common/defaults/main.yaml +++ b/roles/nuage_common/defaults/main.yaml @@ -10,5 +10,8 @@ nuage_ca_serial: "{{ nuage_ca_dir }}/nuageMonCA.serial.txt" nuage_master_mon_dir: /usr/share/nuage-openshift-monitor nuage_node_plugin_dir: /usr/share/vsp-openshift +nuage_node_cni_bin_dir: /opt/cni/bin +nuage_node_cni_netconf_dir: /etc/cni/net.d + nuage_mon_rest_server_port: "{{ nuage_openshift_monitor_rest_server_port | default('9443') }}" nuage_mon_cert_validity_period: "{{ nuage_cert_validity_period | default('3650') }}" diff --git a/roles/nuage_common/tasks/main.yml b/roles/nuage_common/tasks/main.yml new file mode 100644 index 000000000..6c8c9f8d2 --- /dev/null +++ b/roles/nuage_common/tasks/main.yml @@ -0,0 +1,27 @@ +--- +- name: Set the Nuage plugin openshift directory fact to handle Atomic host install + set_fact: + nuage_node_plugin_dir: /var/usr/share/vsp-openshift + when: openshift.common.is_atomic | bool + +- name: Set the Nuage CNI network config directory fact to handle Atomic host install + set_fact: + nuage_node_cni_netconf_dir: /var/etc/cni/net.d/ + when: openshift.common.is_atomic | bool + +- name: Set the Nuage CNI binary directory fact to handle Atomic host install + set_fact: + nuage_node_cni_bin_dir: /var/opt/cni/bin/ + when: openshift.common.is_atomic | bool + +- name: Assure CNI plugin config dir exists before daemon set install + become: yes + file: path="{{ nuage_node_plugin_dir }}" state=directory + +- name: Assure CNI netconf directory exists before daemon set install + become: yes + file: path="{{ nuage_node_cni_netconf_dir }}" state=directory + +- name: Assure CNI plugin binary directory exists before daemon set install + become: yes + file: path="{{ nuage_node_cni_bin_dir }}" state=directory diff --git a/roles/nuage_master/defaults/main.yml b/roles/nuage_master/defaults/main.yml new file mode 100644 index 000000000..5f1d8686a --- /dev/null +++ b/roles/nuage_master/defaults/main.yml @@ -0,0 +1,10 @@ +--- +r_nuage_master_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_nuage_master_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + +nuage_mon_rest_server_port: '9443' + +r_nuage_master_os_firewall_deny: [] +r_nuage_master_os_firewall_allow: +- service: openshift-monitor + port: "{{ nuage_mon_rest_server_port }}/tcp" diff --git a/roles/nuage_master/handlers/main.yaml b/roles/nuage_master/handlers/main.yaml index 162aaae1a..21da6b953 100644 --- a/roles/nuage_master/handlers/main.yaml +++ b/roles/nuage_master/handlers/main.yaml @@ -1,12 +1,4 @@ --- -- name: restart nuage-openshift-monitor - become: yes - systemd: name=nuage-openshift-monitor state=restarted - -- name: restart master - systemd: name={{ openshift.common.service_type }}-master state=restarted - when: (not openshift_master_ha | bool) and (not master_service_status_changed | default(false)) - - name: restart master api systemd: name={{ openshift.common.service_type }}-master-api state=restarted when: > diff --git a/roles/nuage_master/meta/main.yml b/roles/nuage_master/meta/main.yml index e3ed9ac71..3da340c85 100644 --- a/roles/nuage_master/meta/main.yml +++ b/roles/nuage_master/meta/main.yml @@ -16,8 +16,5 @@ dependencies: - role: nuage_ca - 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" +- role: lib_os_firewall diff --git a/roles/nuage_master/tasks/firewall.yml b/roles/nuage_master/tasks/firewall.yml new file mode 100644 index 000000000..0057dc9ab --- /dev/null +++ b/roles/nuage_master/tasks/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_nuage_master_firewall_enabled | bool and not r_nuage_master_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_nuage_master_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_nuage_master_os_firewall_deny }}" + +- when: r_nuage_master_firewall_enabled | bool and r_nuage_master_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_nuage_master_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_nuage_master_os_firewall_deny }}" diff --git a/roles/nuage_master/tasks/main.yaml b/roles/nuage_master/tasks/main.yaml index 4f8adb63e..f3c487132 100644 --- a/roles/nuage_master/tasks/main.yaml +++ b/roles/nuage_master/tasks/main.yaml @@ -1,15 +1,66 @@ --- +- name: setup firewall + include: firewall.yml + static: yes + +- name: Set the Nuage certificate directory fact for Atomic hosts + set_fact: + cert_output_dir: /var/usr/share/nuage-openshift-monitor + when: openshift.common.is_atomic | bool + +- name: Set the Nuage kubeconfig file path fact for Atomic hosts + set_fact: + kube_config: /var/usr/share/nuage-openshift-monitor/nuage.kubeconfig + when: openshift.common.is_atomic | bool + +- name: Set the Nuage monitor yaml location fact for Atomic hosts + set_fact: + kubemon_yaml: /var/usr/share/nuage-openshift-monitor/nuage-openshift-monitor.yaml + when: openshift.common.is_atomic | bool + +- name: Set the Nuage monitor certs location fact for Atomic hosts + set_fact: + nuage_master_crt_dir: /var/usr/share/nuage-openshift-monitor/ + when: openshift.common.is_atomic | bool + +- name: Set the Nuage master config directory for daemon sets install + set_fact: + nuage_master_config_dsets_mount_dir: /var/usr/share/ + when: master_host_type == "is_atomic" + +- name: Set the Nuage node config directory for daemon sets install + set_fact: + nuage_node_config_dsets_mount_dir: /var/usr/share/ + when: slave_host_type == "is_atomic" + +- name: Set the Nuage CNI plugin binary directory for daemon sets install + set_fact: + nuage_cni_bin_dsets_mount_dir: /var/opt/cni/bin + when: openshift.common.is_atomic | bool + - name: Create directory /usr/share/nuage-openshift-monitor become: yes file: path=/usr/share/nuage-openshift-monitor state=directory + when: not openshift.common.is_atomic | bool -- name: Create the log directory +- name: Create directory /var/usr/share/nuage-openshift-monitor become: yes - file: path={{ nuage_mon_rest_server_logdir }} state=directory + file: path=/var/usr/share/nuage-openshift-monitor state=directory + when: openshift.common.is_atomic | bool + +- name: Create directory /var/usr/bin for monitor binary on atomic + become: yes + file: path=/var/usr/bin state=directory + when: openshift.common.is_atomic | bool + +- name: Create CNI bin directory /var/opt/cni/bin + become: yes + file: path=/var/opt/cni/bin state=directory + when: openshift.common.is_atomic | bool -- name: Install Nuage Openshift Monitor +- name: Create the log directory become: yes - yum: name={{ nuage_openshift_rpm }} state=present + file: path={{ nuage_mon_rest_server_logdir }} state=directory - include: serviceaccount.yml @@ -41,11 +92,32 @@ become: yes copy: src="{{ vsd_user_key_file }}" dest="{{ cert_output_dir }}/{{ vsd_user_key_file | basename }}" -- name: Create nuage-openshift-monitor.yaml +- name: Create Nuage master daemon set yaml file become: yes - template: src=nuage-openshift-monitor.j2 dest=/usr/share/nuage-openshift-monitor/nuage-openshift-monitor.yaml owner=root mode=0644 + template: src=nuage-master-config-daemonset.j2 dest=/etc/nuage-master-config-daemonset.yaml owner=root mode=0644 + +- name: Create Nuage node daemon set yaml file + become: yes + template: src=nuage-node-config-daemonset.j2 dest=/etc/nuage-node-config-daemonset.yaml owner=root mode=0644 + +- name: Add the service account to the privileged scc to have root permissions + shell: oc adm policy add-scc-to-user privileged system:serviceaccount:openshift-infra:daemonset-controller + ignore_errors: true + when: inventory_hostname == groups.oo_first_master.0 + +- name: Spawn Nuage Master monitor daemon sets pod + shell: oc create -f /etc/nuage-master-config-daemonset.yaml + ignore_errors: true + when: inventory_hostname == groups.oo_first_master.0 + +- name: Spawn Nuage CNI daemon sets pod + shell: oc create -f /etc/nuage-node-config-daemonset.yaml + ignore_errors: true + when: inventory_hostname == groups.oo_first_master.0 + +- name: Restart daemons + command: /bin/true notify: - - restart master - restart master api - restart master controllers - - restart nuage-openshift-monitor + ignore_errors: true diff --git a/roles/nuage_master/tasks/serviceaccount.yml b/roles/nuage_master/tasks/serviceaccount.yml index eee448e2c..fbf2c4f8d 100644 --- a/roles/nuage_master/tasks/serviceaccount.yml +++ b/roles/nuage_master/tasks/serviceaccount.yml @@ -1,26 +1,6 @@ --- -- name: Create temporary directory for admin kubeconfig - command: mktemp -u /tmp/openshift-ansible-XXXXXXX.kubeconfig - register: nuage_tmp_conf_mktemp - changed_when: False - run_once: True - delegate_to: "{{ nuage_ca_master }}" - -- set_fact: - nuage_tmp_conf: "{{ nuage_tmp_conf_mktemp.stdout }}" - run_once: True - delegate_to: "{{ nuage_ca_master }}" - -- name: Copy Configuration to temporary conf - command: > - cp {{ openshift.common.config_base }}/master/admin.kubeconfig {{nuage_tmp_conf}} - changed_when: false - run_once: True - delegate_to: "{{ nuage_ca_master }}" - - name: Create Admin Service Account oc_serviceaccount: - kubeconfig: "{{ openshift_master_config_dir }}/admin.kubeconfig" name: nuage namespace: default state: present @@ -28,13 +8,12 @@ delegate_to: "{{ nuage_ca_master }}" - name: Configure role/user permissions - command: > - {{ openshift.common.client_binary }} adm {{item}} - --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" - changed_when: osnuage_perm_task.rc == 0 + oc_adm_policy_user: + namespace: default + resource_name: "{{ item.resource_name }}" + resource_kind: "{{ item.resource_kind }}" + user: "{{ item.user }}" + with_items: "{{ nuage_tasks }}" run_once: True delegate_to: "{{ nuage_ca_master }}" @@ -52,10 +31,3 @@ --user={{ nuage_service_account }} delegate_to: "{{ nuage_ca_master }}" run_once: True - -- name: Clean temporary configuration file - command: > - rm -f {{nuage_tmp_conf}} - changed_when: false - delegate_to: "{{ nuage_ca_master }}" - run_once: True diff --git a/roles/nuage_master/templates/nuage-master-config-daemonset.j2 b/roles/nuage_master/templates/nuage-master-config-daemonset.j2 new file mode 100755 index 000000000..612d689c2 --- /dev/null +++ b/roles/nuage_master/templates/nuage-master-config-daemonset.j2 @@ -0,0 +1,111 @@ +# This ConfigMap is used to configure Nuage VSP configuration on master nodes +kind: ConfigMap +apiVersion: v1 +metadata: + name: nuage-master-config + namespace: kube-system +data: + # This will generate the required Nuage configuration + # on master nodes + monitor_yaml_config: | + + # .kubeconfig that includes the nuage service account + kubeConfig: {{ nuage_master_crt_dir }}/nuage.kubeconfig + # name of the nuage service account, or another account with 'cluster-reader' + # permissions + # Openshift master config file + masterConfig: /etc/origin/master/master-config.yaml + # URL of the VSD Architect + vsdApiUrl: {{ vsd_api_url }} + # API version to query against. Usually "v3_2" + vspVersion: {{ vsp_version }} + # Name of the enterprise in which pods will reside + enterpriseName: {{ enterprise }} + # Name of the domain in which pods will reside + domainName: {{ domain }} + # VSD generated user certificate file location on master node + userCertificateFile: {{ nuage_master_crt_dir }}/{{ vsd_user }}.pem + # VSD generated user key file location on master node + userKeyFile: {{ nuage_master_crt_dir }}/{{ vsd_user }}-Key.pem + # Location where logs should be saved + log_dir: /var/log/nuage-openshift-monitor + # Monitor rest server parameters + # Logging level for the nuage openshift monitor + # allowed options are: 0 => INFO, 1 => WARNING, 2 => ERROR, 3 => FATAL + logLevel: 0 + # Parameters related to the nuage monitor REST server + nuageMonServer: + URL: 0.0.0.0:9443 + certificateDirectory: {{ nuage_master_crt_dir }} + # etcd config required for HA + etcdClientConfig: + ca: {{ nuage_master_crt_dir }}/nuageMonCA.crt + certFile: {{ nuage_master_crt_dir }}/nuageMonServer.crt + keyFile: {{ nuage_master_crt_dir }}/master.etcd-client.key + urls: + {% for etcd_url in openshift.master.etcd_urls %} + - {{ etcd_url }} + {% endfor %} + +--- + +# This manifest installs Nuage master node configuration on +# each Nuage master node in a cluster. +kind: DaemonSet +apiVersion: extensions/v1beta1 +metadata: + name: nuage-master-config + namespace: kube-system + labels: + k8s-app: nuage-master-config +spec: + selector: + matchLabels: + k8s-app: nuage-master-config + template: + metadata: + labels: + k8s-app: nuage-master-config + spec: + hostNetwork: true + tolerations: + - key: node-role.kubernetes.io/master + effect: NoSchedule + operator: Exists + nodeSelector: + install-monitor: "true" + containers: + # This container configures Nuage Master node + - name: install-nuage-master-config + image: nuage/master:{{ nuage_monitor_container_image_version }} + ports: + - containerPort: 9443 + hostPort: 9443 + command: ["/configure-master.sh"] + args: ["ose", "{{ master_host_type }}"] + securityContext: + privileged: true + env: + # nuage-openshift-monitor.yaml config to install on each slave node. + - name: NUAGE_MASTER_VSP_CONFIG + valueFrom: + configMapKeyRef: + name: nuage-master-config + key: monitor_yaml_config + volumeMounts: + - mountPath: /var/log + name: cni-log-dir + - mountPath: {{ nuage_master_config_dsets_mount_dir }} + name: usr-share-dir + - mountPath: /etc/origin/ + name: master-config-dir + volumes: + - name: cni-log-dir + hostPath: + path: /var/log + - name: usr-share-dir + hostPath: + path: {{ nuage_master_config_dsets_mount_dir }} + - name: master-config-dir + hostPath: + path: /etc/origin/ diff --git a/roles/nuage_master/templates/nuage-node-config-daemonset.j2 b/roles/nuage_master/templates/nuage-node-config-daemonset.j2 new file mode 100755 index 000000000..02e9a1563 --- /dev/null +++ b/roles/nuage_master/templates/nuage-node-config-daemonset.j2 @@ -0,0 +1,206 @@ +# This ConfigMap is used to configure Nuage VSP configuration +kind: ConfigMap +apiVersion: v1 +metadata: + name: nuage-config + namespace: kube-system +data: + # This will generate the required Nuage vsp-openshift.yaml + # config on each slave node + plugin_yaml_config: | + clientCert: {{ nuage_node_config_dsets_mount_dir }}/vsp-openshift/client.crt + # The key to the certificate in clientCert above + clientKey: {{ nuage_node_config_dsets_mount_dir }}/vsp-openshift/client.key + # The certificate authority's certificate for the local kubelet. Usually the + # same as the CA cert used to create the client Cert/Key pair. + CACert: {{ nuage_node_config_dsets_mount_dir }}/vsp-openshift/ca.crt + # Name of the enterprise in which pods will reside + enterpriseName: {{ enterprise }} + # Name of the domain in which pods will reside + domainName: {{ domain }} + # Name of the VSD user in admin group + vsdUser: {{ vsd_user }} + # IP address and port number of master API server + masterApiServer: {{ api_server_url }} + # REST server URL + nuageMonRestServer: {{ nuage_mon_rest_server_url }} + # Bridge name for the docker bridge + dockerBridgeName: docker0 + # Certificate for connecting to the openshift monitor REST api + nuageMonClientCert: {{ nuage_node_config_dsets_mount_dir }}/vsp-openshift/nuageMonClient.crt + # Key to the certificate in restClientCert + nuageMonClientKey: {{ nuage_node_config_dsets_mount_dir }}/vsp-openshift/nuageMonClient.key + # CA certificate for verifying the master's rest server + nuageMonServerCA: {{ nuage_node_config_dsets_mount_dir }}/vsp-openshift/nuageMonCA.crt + # Nuage vport mtu size + interfaceMTU: {{ nuage_vport_mtu }} + # Logging level for the plugin + # allowed options are: "dbg", "info", "warn", "err", "emer", "off" + logLevel: 3 + + # This will generate the required Nuage CNI yaml configuration + cni_yaml_config: | + vrsendpoint: "/var/run/openvswitch/db.sock" + vrsbridge: "alubr0" + monitorinterval: 60 + cniversion: 0.2.0 + loglevel: "info" + portresolvetimer: 60 + logfilesize: 1 + vrsconnectionchecktimer: 180 + mtu: 1450 + staleentrytimeout: 600 + +--- + +# This manifest installs Nuage CNI plugins and network config on +# each worker node in Openshift cluster +kind: DaemonSet +apiVersion: extensions/v1beta1 +metadata: + name: nuage-cni-ds + namespace: kube-system + labels: + k8s-app: nuage-cni-ds +spec: + selector: + matchLabels: + k8s-app: nuage-cni-ds + template: + metadata: + labels: + k8s-app: nuage-cni-ds + spec: + hostNetwork: true + tolerations: + - key: node-role.kubernetes.io/master + effect: NoSchedule + operator: Exists + containers: + # This container installs Nuage CNI binaries + # and CNI network config file on each node. + - name: install-nuage-cni + image: nuage/cni:{{ nuage_cni_container_image_version }} + command: ["/install-cni.sh"] + args: ["nuage-cni-openshift", "{{ slave_host_type }}"] + securityContext: + privileged: true + env: + # Nuage vsp-openshift.yaml config to install on each slave node. + - name: NUAGE_VSP_CONFIG + valueFrom: + configMapKeyRef: + name: nuage-config + key: plugin_yaml_config + # Nuage nuage-cni.yaml config to install on each slave node. + - name: NUAGE_CNI_YAML_CONFIG + valueFrom: + configMapKeyRef: + name: nuage-config + key: cni_yaml_config + # Nuage cluster network CIDR for iptables configuration + - name: NUAGE_CLUSTER_NW_CIDR + value: "{{ hostvars[groups.oo_first_master.0].openshift.master.sdn_cluster_network_cidr }}" + volumeMounts: + - mountPath: /host/opt/cni/bin + name: cni-bin-dir + - mountPath: /host/etc/cni/net.d + name: cni-net-dir + - mountPath: /etc/default + name: cni-yaml-dir + - mountPath: /var/run + name: var-run-dir + - mountPath: /var/log + name: cni-log-dir + - mountPath: {{ nuage_node_config_dsets_mount_dir }} + name: usr-share-dir + volumes: + - name: cni-bin-dir + hostPath: + path: {{ nuage_cni_bin_dsets_mount_dir }} + - name: cni-net-dir + hostPath: + path: {{ nuage_cni_netconf_dsets_mount_dir }} + - name: cni-yaml-dir + hostPath: + path: /etc/default + - name: var-run-dir + hostPath: + path: /var/run + - name: cni-log-dir + hostPath: + path: /var/log + - name: usr-share-dir + hostPath: + path: {{ nuage_node_config_dsets_mount_dir }} + +--- + +# This manifest installs Nuage VRS on +# each worker node in an Openshift cluster. +kind: DaemonSet +apiVersion: extensions/v1beta1 +metadata: + name: nuage-vrs-ds + namespace: kube-system + labels: + k8s-app: nuage-vrs-ds +spec: + selector: + matchLabels: + k8s-app: nuage-vrs-ds + updateStrategy: + type: RollingUpdate + template: + metadata: + labels: + k8s-app: nuage-vrs-ds + spec: + hostNetwork: true + tolerations: + - key: node-role.kubernetes.io/master + effect: NoSchedule + operator: Exists + containers: + # This container installs Nuage VRS running as a + # container on each worker node + - name: install-nuage-vrs + image: nuage/vrs:{{ nuage_vrs_container_image_version }} + securityContext: + privileged: true + env: + # Configure parameters for VRS openvswitch file + - name: NUAGE_ACTIVE_CONTROLLER + value: "{{ vsc_active_ip }}" + - name: NUAGE_STANDBY_CONTROLLER + value: "{{ vsc_standby_ip }}" + - name: NUAGE_PLATFORM + value: '"kvm, k8s"' + - name: NUAGE_K8S_SERVICE_IPV4_SUBNET + value: '192.168.0.0\/16' + - name: NUAGE_NETWORK_UPLINK_INTF + value: "eth0" + volumeMounts: + - mountPath: /var/run + name: vrs-run-dir + - mountPath: /var/log + name: vrs-log-dir + - mountPath: /sys/module + name: sys-mod-dir + readOnly: true + - mountPath: /lib/modules + name: lib-mod-dir + readOnly: true + volumes: + - name: vrs-run-dir + hostPath: + path: /var/run + - name: vrs-log-dir + hostPath: + path: /var/log + - name: sys-mod-dir + hostPath: + path: /sys/module + - name: lib-mod-dir + hostPath: + path: /lib/modules diff --git a/roles/nuage_master/templates/nuage-openshift-monitor.j2 b/roles/nuage_master/templates/nuage-openshift-monitor.j2 deleted file mode 100644 index e077128a4..000000000 --- a/roles/nuage_master/templates/nuage-openshift-monitor.j2 +++ /dev/null @@ -1,41 +0,0 @@ -# .kubeconfig that includes the nuage service account -kubeConfig: {{ kube_config }} -# name of the nuage service account, or another account with 'cluster-reader' -# permissions -# Openshift master config file -masterConfig: {{ master_config_yaml }} -# URL of the VSD Architect -vsdApiUrl: {{ vsd_api_url }} -# API version to query against. Usually "v3_2" -vspVersion: {{ vsp_version }} -# File containing a VSP license to install. Only necessary if no license has -# been installed on the VSD Architect before, only valid for standalone vsd install -# licenseFile: "/path/to/base_vsp_license.txt" -# Name of the enterprise in which pods will reside -enterpriseName: {{ enterprise }} -# Name of the domain in which pods will reside -domainName: {{ domain }} -# VSD generated user certificate file location on master node -userCertificateFile: {{ cert_output_dir }}/{{ vsd_user_cert_file | basename }} -# VSD generated user key file location on master node -userKeyFile: {{ cert_output_dir }}/{{ vsd_user_key_file | basename }} -# Location where logs should be saved -log_dir: {{ nuage_mon_rest_server_logdir }} -# Monitor rest server parameters -# Logging level for the nuage openshift monitor -# allowed options are: 0 => INFO, 1 => WARNING, 2 => ERROR, 3 => FATAL -logLevel: {{ nuage_mon_log_level }} -# Parameters related to the nuage monitor REST server -nuageMonServer: - URL: {{ nuage_mon_rest_server_url }} - certificateDirectory: {{ cert_output_dir }} -# etcd config required for HA -etcdClientConfig: - ca: {{ openshift_master_config_dir }}/{{ "ca.crt" if (openshift.master.embedded_etcd | bool) else "master.etcd-ca.crt" }} - certFile: {{ openshift_master_config_dir }}/master.etcd-client.crt - keyFile: {{ openshift_master_config_dir }}/master.etcd-client.key - urls: -{% for etcd_url in openshift.master.etcd_urls %} - - {{ etcd_url }} -{% endfor %} - diff --git a/roles/nuage_master/vars/main.yaml b/roles/nuage_master/vars/main.yaml index 651d5775c..114514d7c 100644 --- a/roles/nuage_master/vars/main.yaml +++ b/roles/nuage_master/vars/main.yaml @@ -22,5 +22,19 @@ 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_master_config_dsets_mount_dir: /usr/share/ +nuage_node_config_dsets_mount_dir: /usr/share/ +nuage_cni_bin_dsets_mount_dir: /opt/cni/bin +nuage_cni_netconf_dsets_mount_dir: /etc/cni/net.d +nuage_monitor_container_image_version: "{{ nuage_monitor_image_version | default('v5.1.1') }}" +nuage_vrs_container_image_version: "{{ nuage_vrs_image_version | default('v5.1.1') }}" +nuage_cni_container_image_version: "{{ nuage_cni_image_version | default('v5.1.1') }}" +api_server_url: "{{ hostvars[groups.oo_first_master.0].openshift.master.api_url }}" +nuage_vport_mtu: "{{ nuage_interface_mtu | default('1460') }}" +master_host_type: "{{ master_base_host_type | default('is_rhel_server') }}" +slave_host_type: "{{ slave_base_host_type | default('is_rhel_server') }}" + nuage_tasks: - - policy add-cluster-role-to-user cluster-reader {{ nuage_service_account }} +- resource_kind: cluster-role + resource_name: cluster-reader + user: "{{ nuage_service_account }}" diff --git a/roles/nuage_node/defaults/main.yml b/roles/nuage_node/defaults/main.yml new file mode 100644 index 000000000..9a2e34387 --- /dev/null +++ b/roles/nuage_node/defaults/main.yml @@ -0,0 +1,12 @@ +--- +r_nuage_node_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_nuage_node_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + +nuage_mon_rest_server_port: '9443' + +r_nuage_node_os_firewall_deny: [] +r_nuage_node_os_firewall_allow: +- service: vxlan + port: 4789/udp +- service: nuage-monitor + port: "{{ nuage_mon_rest_server_port }}/tcp" diff --git a/roles/nuage_node/handlers/main.yaml b/roles/nuage_node/handlers/main.yaml index 8384856ff..e68ae74bd 100644 --- a/roles/nuage_node/handlers/main.yaml +++ b/roles/nuage_node/handlers/main.yaml @@ -1,11 +1,7 @@ --- -- name: restart vrs - become: yes - systemd: name=openvswitch state=restarted - - name: restart node become: yes - systemd: name={{ openshift.common.service_type }}-node state=restarted + systemd: name={{ openshift.common.service_type }}-node daemon-reload=yes state=restarted - name: save iptable rules become: yes diff --git a/roles/nuage_node/meta/main.yml b/roles/nuage_node/meta/main.yml index 3e2a5e0c9..9b0315054 100644 --- a/roles/nuage_node/meta/main.yml +++ b/roles/nuage_node/meta/main.yml @@ -15,9 +15,4 @@ galaxy_info: dependencies: - role: nuage_common - role: nuage_ca -- role: os_firewall - os_firewall_allow: - - service: vxlan - port: 4789/udp - - service: nuage-monitor - port: "{{ nuage_mon_rest_server_port }}/tcp" +- role: lib_os_firewall diff --git a/roles/nuage_node/tasks/firewall.yml b/roles/nuage_node/tasks/firewall.yml new file mode 100644 index 000000000..baf600d57 --- /dev/null +++ b/roles/nuage_node/tasks/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_nuage_node_firewall_enabled | bool and not r_nuage_node_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_nuage_node_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_nuage_node_os_firewall_deny }}" + +- when: r_nuage_node_firewall_enabled | bool and r_nuage_node_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_nuage_node_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_nuage_node_os_firewall_deny }}" diff --git a/roles/nuage_node/tasks/iptables.yml b/roles/nuage_node/tasks/iptables.yml index 847c8395d..95ee8643a 100644 --- a/roles/nuage_node/tasks/iptables.yml +++ b/roles/nuage_node/tasks/iptables.yml @@ -15,3 +15,9 @@ when: "'nuage-underlay-overlay' not in iptablesrules.stdout" notify: - save iptable rules + +- name: Allow docker daemon traffic from underlay to overlay + command: /sbin/iptables -t nat -A POSTROUTING ! -s {{ hostvars[groups.oo_first_master.0].openshift.master.sdn_cluster_network_cidr }} -o svc-pat-tap -j MASQUERADE -m comment --comment "nuage-docker-underlay-overlay" + when: "'nuage-docker-underlay-overlay' not in iptablesrules.stdout" + notify: + - save iptable rules diff --git a/roles/nuage_node/tasks/main.yaml b/roles/nuage_node/tasks/main.yaml index 928f9e2e6..9db9dbb6a 100644 --- a/roles/nuage_node/tasks/main.yaml +++ b/roles/nuage_node/tasks/main.yaml @@ -1,24 +1,18 @@ --- -- name: Install Nuage VRS - become: yes - yum: name={{ vrs_rpm }} state=present - -- name: Set the uplink interface - become: yes - lineinfile: dest={{ vrs_config }} regexp=^NETWORK_UPLINK_INTF line='NETWORK_UPLINK_INTF={{ uplink_interface }}' +- name: Set the Nuage plugin openshift directory fact for Atomic hosts + set_fact: + vsp_openshift_dir: /var/usr/share/vsp-openshift + when: openshift.common.is_atomic | bool -- name: Set the Active Controller - become: yes - lineinfile: dest={{ vrs_config }} regexp=^ACTIVE_CONTROLLER line='ACTIVE_CONTROLLER={{ vsc_active_ip }}' +- name: Set the Nuage CNI binary directory fact for Atomic hosts + set_fact: + cni_bin_dir: /var/opt/cni/bin/ + when: openshift.common.is_atomic | bool -- name: Set the Standby Controller - become: yes - lineinfile: dest={{ vrs_config }} regexp=^STANDBY_CONTROLLER line='STANDBY_CONTROLLER={{ vsc_standby_ip }}' - when: vsc_standby_ip is defined - -- name: Install plugin rpm - become: yes - yum: name={{ plugin_rpm }} state=present +- name: Set the Nuage plugin certs directory fact for Atomic hosts + set_fact: + nuage_plugin_crt_dir: /var/usr/share/vsp-openshift + when: openshift.common.is_atomic | bool - name: Assure CNI conf dir exists become: yes @@ -28,13 +22,6 @@ become: yes file: path="{{ cni_bin_dir }}" state=directory -- name: Install CNI loopback plugin - become: yes - copy: - src: "{{ k8s_cni_loopback_plugin }}" - dest: "{{ cni_bin_dir }}/{{ k8s_cni_loopback_plugin | basename }}" - mode: 0755 - - name: Copy the certificates and keys become: yes copy: src="/tmp/{{ item }}" dest="{{ vsp_openshift_dir }}/{{ item }}" @@ -46,11 +33,19 @@ - include: certificates.yml -- name: Set the vsp-openshift.yaml +- name: Add additional Docker mounts for Nuage for atomic hosts become: yes - template: src=vsp-openshift.j2 dest={{ vsp_openshift_yaml }} owner=root mode=0644 + lineinfile: dest="{{ openshift_atomic_node_config_file }}" line="{{ nuage_atomic_docker_additional_mounts }}" + when: openshift.common.is_atomic | bool + +- name: Restart node services + command: /bin/true notify: - - restart vrs - restart node + ignore_errors: true - include: iptables.yml + +- name: setup firewall + include: firewall.yml + static: yes diff --git a/roles/nuage_node/templates/vsp-openshift.j2 b/roles/nuage_node/templates/vsp-openshift.j2 deleted file mode 100644 index 9fab53906..000000000 --- a/roles/nuage_node/templates/vsp-openshift.j2 +++ /dev/null @@ -1,29 +0,0 @@ -clientCert: {{ client_cert }} -# The key to the certificate in clientCert above -clientKey: {{ client_key }} -# The certificate authority's certificate for the local kubelet. Usually the -# same as the CA cert used to create the client Cert/Key pair. -CACert: {{ ca_cert }} -# Name of the enterprise in which pods will reside -enterpriseName: {{ enterprise }} -# Name of the domain in which pods will reside -domainName: {{ domain }} -# Name of the VSD user in admin group -vsdUser: {{ vsduser }} -# IP address and port number of master API server -masterApiServer: {{ api_server }} -# REST server URL -nuageMonRestServer: {{ nuage_mon_rest_server_url }} -# Bridge name for the docker bridge -dockerBridgeName: {{ docker_bridge }} -# Certificate for connecting to the kubemon REST API -nuageMonClientCert: {{ rest_client_cert }} -# Key to the certificate in restClientCert -nuageMonClientKey: {{ rest_client_key }} -# CA certificate for verifying the master's rest server -nuageMonServerCA: {{ rest_server_ca_cert }} -# Nuage vport mtu size -interfaceMTU: {{ vport_mtu }} -# Logging level for the plugin -# allowed options are: "dbg", "info", "warn", "err", "emer", "off" -logLevel: {{ plugin_log_level }} diff --git a/roles/nuage_node/vars/main.yaml b/roles/nuage_node/vars/main.yaml index 4cf68411f..d8bfca62a 100644 --- a/roles/nuage_node/vars/main.yaml +++ b/roles/nuage_node/vars/main.yaml @@ -23,3 +23,5 @@ cni_conf_dir: "/etc/cni/net.d/" cni_bin_dir: "/opt/cni/bin/" nuage_plugin_crt_dir: /usr/share/vsp-openshift +openshift_atomic_node_config_file: /etc/sysconfig/{{ openshift.common.service_type }}-node +nuage_atomic_docker_additional_mounts: "DOCKER_ADDTL_BIND_MOUNTS=-v /var/usr/share/vsp-openshift:/var/usr/share/vsp-openshift -v /etc/default:/etc/default -v /var/run:/var/run -v /opt/cni/bin:/opt/cni/bin -v /etc/cni/net.d:/etc/cni/net.d" diff --git a/roles/openshift_aws_ami_copy/README.md b/roles/openshift_aws_ami_copy/README.md new file mode 100644 index 000000000..111818451 --- /dev/null +++ b/roles/openshift_aws_ami_copy/README.md @@ -0,0 +1,50 @@ +openshift_aws_ami_perms +========= + +Ansible role for copying an AMI + +Requirements +------------ + +Ansible Modules: + + +Role Variables +-------------- + +- openshift_aws_ami_copy_src_ami: source AMI id to copy from +- openshift_aws_ami_copy_region: region where the AMI is found +- openshift_aws_ami_copy_name: name to assign to new AMI +- openshift_aws_ami_copy_kms_arn: AWS IAM KMS arn of the key to use for encryption +- openshift_aws_ami_copy_tags: dict with desired tags +- openshift_aws_ami_copy_wait: wait for the ami copy to achieve available status. This fails due to boto waiters. + +Dependencies +------------ + + +Example Playbook +---------------- +```yaml + - name: copy the ami for encrypted disks + include_role: + name: openshift_aws_ami_copy + vars: + r_openshift_aws_ami_copy_region: us-east-1 + r_openshift_aws_ami_copy_name: myami + r_openshift_aws_ami_copy_src_ami: ami-1234 + r_openshift_aws_ami_copy_kms_arn: arn:xxxx + r_openshift_aws_ami_copy_tags: {} + r_openshift_aws_ami_copy_encrypt: False + +``` + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/openshift_aws_ami_copy/tasks/main.yml b/roles/openshift_aws_ami_copy/tasks/main.yml new file mode 100644 index 000000000..bcccd4042 --- /dev/null +++ b/roles/openshift_aws_ami_copy/tasks/main.yml @@ -0,0 +1,26 @@ +--- +- fail: + msg: "{{ item }} needs to be defined" + when: item is not defined + with_items: + - r_openshift_aws_ami_copy_src_ami + - r_openshift_aws_ami_copy_name + - r_openshift_aws_ami_copy_region + +- name: "Create copied AMI image and wait: {{ r_openshift_aws_ami_copy_wait | default(False) }}" + ec2_ami_copy: + region: "{{ r_openshift_aws_ami_copy_region }}" + source_region: "{{ r_openshift_aws_ami_copy_region }}" + name: "{{ r_openshift_aws_ami_copy_name }}" + source_image_id: "{{ r_openshift_aws_ami_copy_src_ami }}" + encrypted: "{{ r_openshift_aws_ami_copy_encrypt | default(False) }}" + kms_key_id: "{{ r_openshift_aws_ami_copy_kms_arn | default(omit) }}" + wait: "{{ r_openshift_aws_ami_copy_wait | default(omit) }}" + tags: "{{ r_openshift_aws_ami_copy_tags }}" + register: copy_result + +- debug: var=copy_result + +- name: return AMI ID with setfact - openshift_aws_ami_copy_retval_custom_ami + set_fact: + r_openshift_aws_ami_copy_retval_custom_ami: "{{ copy_result.image_id }}" diff --git a/roles/openshift_aws_elb/README.md b/roles/openshift_aws_elb/README.md new file mode 100644 index 000000000..ecc45fa14 --- /dev/null +++ b/roles/openshift_aws_elb/README.md @@ -0,0 +1,75 @@ +openshift_aws_elb +========= + +Ansible role to provision and manage AWS ELB's for Openshift. + +Requirements +------------ + +Ansible Modules: + +- ec2_elb +- ec2_elb_lb + +python package: + +python-boto + +Role Variables +-------------- + +- r_openshift_aws_elb_instances: instances to put in ELB +- r_openshift_aws_elb_elb_name: name of elb +- r_openshift_aws_elb_security_group_names: list of SGs (by name) that the ELB will belong to +- r_openshift_aws_elb_region: AWS Region +- r_openshift_aws_elb_health_check: definition of the ELB health check. See ansible docs for ec2_elb +```yaml + ping_protocol: tcp + ping_port: 443 + response_timeout: 5 + interval: 30 + unhealthy_threshold: 2 + healthy_threshold: 2 +``` +- r_openshift_aws_elb_listeners: definition of the ELB listeners. See ansible docs for ec2_elb +```yaml +- protocol: tcp + load_balancer_port: 80 + instance_protocol: ssl + instance_port: 443 +- protocol: ssl + load_balancer_port: 443 + instance_protocol: ssl + instance_port: 443 + # ssl certificate required for https or ssl + ssl_certificate_id: "{{ r_openshift_aws_elb_cert_arn }}" +``` + +Dependencies +------------ + + +Example Playbook +---------------- +```yaml +- include_role: + name: openshift_aws_elb + vars: + r_openshift_aws_elb_instances: aws_instances_to_put_in_elb + r_openshift_aws_elb_elb_name: elb_name + r_openshift_aws_elb_security_groups: security_group_names + r_openshift_aws_elb_region: aws_region + r_openshift_aws_elb_health_check: "{{ elb_health_check_definition }}" + r_openshift_aws_elb_listeners: "{{ elb_listeners_definition }}" +``` + + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/openshift_aws_elb/defaults/main.yml b/roles/openshift_aws_elb/defaults/main.yml new file mode 100644 index 000000000..ed5d38079 --- /dev/null +++ b/roles/openshift_aws_elb/defaults/main.yml @@ -0,0 +1,33 @@ +--- +r_openshift_aws_elb_health_check: + ping_protocol: tcp + ping_port: 443 + response_timeout: 5 + interval: 30 + unhealthy_threshold: 2 + healthy_threshold: 2 + +r_openshift_aws_elb_cert_arn: '' + +r_openshift_aws_elb_listeners: + master: + external: + - protocol: tcp + load_balancer_port: 80 + instance_protocol: ssl + instance_port: 443 + - protocol: ssl + load_balancer_port: 443 + instance_protocol: ssl + instance_port: 443 + # ssl certificate required for https or ssl + ssl_certificate_id: "{{ r_openshift_aws_elb_cert_arn }}" + internal: + - protocol: tcp + load_balancer_port: 80 + instance_protocol: tcp + instance_port: 80 + - protocol: tcp + load_balancer_port: 443 + instance_protocol: tcp + instance_port: 443 diff --git a/roles/openshift_aws_elb/meta/main.yml b/roles/openshift_aws_elb/meta/main.yml new file mode 100644 index 000000000..58be652a5 --- /dev/null +++ b/roles/openshift_aws_elb/meta/main.yml @@ -0,0 +1,12 @@ +--- +galaxy_info: + author: OpenShift + description: Openshift ELB provisioning + company: Red Hat, Inc + license: ASL 2.0 + min_ansible_version: 1.2 + platforms: + - name: EL + versions: + - 7 +dependencies: [] diff --git a/roles/openshift_aws_elb/tasks/main.yml b/roles/openshift_aws_elb/tasks/main.yml new file mode 100644 index 000000000..64ec18545 --- /dev/null +++ b/roles/openshift_aws_elb/tasks/main.yml @@ -0,0 +1,57 @@ +--- +- name: fetch the default subnet id + ec2_remote_facts: + region: "{{ r_openshift_aws_elb_region }}" + filters: "{{ r_openshift_aws_elb_instance_filter }}" + register: instancesout + +- name: fetch the default subnet id + ec2_vpc_subnet_facts: + region: "{{ r_openshift_aws_elb_region }}" + filters: + "tag:Name": "{{ r_openshift_aws_elb_subnet_name }}" + register: subnetout + +- name: + debug: + msg: "{{ r_openshift_aws_elb_listeners[r_openshift_aws_elb_type][r_openshift_aws_elb_direction] + if 'master' in r_openshift_aws_elb_type or 'infra' in r_openshift_aws_elb_type + else r_openshift_aws_elb_listeners }}" + +- name: "Create ELB {{ r_openshift_aws_elb_name }}" + ec2_elb_lb: + name: "{{ r_openshift_aws_elb_name }}" + state: present + security_group_names: "{{ r_openshift_aws_elb_security_groups }}" + idle_timeout: "{{ r_openshift_aws_elb_idle_timout }}" + region: "{{ r_openshift_aws_elb_region }}" + subnets: + - "{{ subnetout.subnets[0].id }}" + health_check: "{{ r_openshift_aws_elb_health_check }}" + listeners: "{{ r_openshift_aws_elb_listeners[r_openshift_aws_elb_type][r_openshift_aws_elb_direction] + if 'master' in r_openshift_aws_elb_type or 'infra' in r_openshift_aws_elb_type + else r_openshift_aws_elb_listeners }}" + scheme: "{{ r_openshift_aws_elb_scheme }}" + tags: + KubernetesCluster: "{{ r_openshift_aws_elb_clusterid }}" + register: new_elb + +# It is necessary to ignore_errors here because the instances are not in 'ready' +# state when first added to ELB +- name: "Add instances to ELB {{ r_openshift_aws_elb_name }}" + ec2_elb: + instance_id: "{{ item.id }}" + ec2_elbs: "{{ r_openshift_aws_elb_name }}" + state: present + region: "{{ r_openshift_aws_elb_region }}" + wait: False + with_items: "{{ instancesout.instances }}" + ignore_errors: True + retries: 10 + register: elb_call + until: elb_call|succeeded + +- debug: + msg: "{{ item }}" + with_items: + - "{{ new_elb }}" diff --git a/roles/openshift_aws_iam_kms/README.md b/roles/openshift_aws_iam_kms/README.md new file mode 100644 index 000000000..9468e785c --- /dev/null +++ b/roles/openshift_aws_iam_kms/README.md @@ -0,0 +1,43 @@ +openshift_aws_iam_kms +========= + +Ansible role to create AWS IAM KMS keys for encryption + +Requirements +------------ + +Ansible Modules: + +oo_iam_kms + +Role Variables +-------------- + +- r_openshift_aws_iam_kms_region: AWS region to create KMS key +- r_openshift_aws_iam_kms_alias: Alias name to assign to created KMS key + +Dependencies +------------ + +lib_utils + +Example Playbook +---------------- +```yaml +- include_role: + name: openshift_aws_iam_kms + vars: + r_openshift_aws_iam_kms_region: 'us-east-1' + r_openshift_aws_iam_kms_alias: 'alias/clusterABC_kms' +``` + + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/lib_utils/tasks/main.yml b/roles/openshift_aws_iam_kms/defaults/main.yml index ed97d539c..ed97d539c 100644 --- a/roles/lib_utils/tasks/main.yml +++ b/roles/openshift_aws_iam_kms/defaults/main.yml diff --git a/roles/openshift_aws_iam_kms/meta/main.yml b/roles/openshift_aws_iam_kms/meta/main.yml new file mode 100644 index 000000000..e29aaf96b --- /dev/null +++ b/roles/openshift_aws_iam_kms/meta/main.yml @@ -0,0 +1,13 @@ +--- +galaxy_info: + author: OpenShift + description: AWS IAM KMS setup and management + company: Red Hat, Inc + license: ASL 2.0 + min_ansible_version: 1.2 + platforms: + - name: EL + versions: + - 7 +dependencies: +- lib_utils diff --git a/roles/openshift_aws_iam_kms/tasks/main.yml b/roles/openshift_aws_iam_kms/tasks/main.yml new file mode 100644 index 000000000..32aac2666 --- /dev/null +++ b/roles/openshift_aws_iam_kms/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- fail: + msg: "{{ item.name }} needs to be defined." + when: item.cond | bool + with_items: + - name: "{{ r_openshift_aws_iam_kms_alias }}" + cond: "{{ r_openshift_aws_iam_kms_alias is undefined }}" + - name: "{{ r_openshift_aws_iam_kms_region }}" + cond: "{{ r_openshift_aws_iam_kms_region is undefined }}" + +- name: Create IAM KMS key with alias + oo_iam_kms: + state: present + alias: "{{ r_openshift_aws_iam_kms_alias }}" + region: "{{ r_openshift_aws_iam_kms_region }}" + register: created_kms + +- debug: var=created_kms.results diff --git a/roles/openshift_aws_launch_config/README.md b/roles/openshift_aws_launch_config/README.md new file mode 100644 index 000000000..52b7e83b6 --- /dev/null +++ b/roles/openshift_aws_launch_config/README.md @@ -0,0 +1,72 @@ +openshift_aws_launch_config +========= + +Ansible role to create an AWS launch config for a scale group. + +This includes the AMI, volumes, user_data, etc. + +Requirements +------------ + +Ansible Modules: + + +Role Variables +-------------- +- r_openshift_aws_launch_config_name: "{{ launch_config_name }}" +- r_openshift_aws_launch_config_clusterid: "{{ clusterid }}" +- r_openshift_aws_launch_config_region: "{{ region }}" +- r_openshift_aws_launch_config: "{{ node_group_config }}" +```yaml + master: + instance_type: m4.xlarge + ami: ami-cdeec8b6 # if using an encrypted AMI this will be replaced + volumes: + - device_name: /dev/sdb + volume_size: 100 + device_type: gp2 + delete_on_termination: False + health_check: + period: 60 + type: EC2 + min_size: 3 + max_size: 3 + desired_size: 3 + tags: + host-type: master + sub-host-type: default + wait_for_instances: True +``` +- r_openshift_aws_launch_config_type: compute +- r_openshift_aws_launch_config_custom_image: ami-xxxxx +- r_openshift_aws_launch_config_bootstrap_token: <string of kubeconfig> + +Dependencies +------------ + + +Example Playbook +---------------- +```yaml + - name: create compute nodes config + include_role: + name: openshift_aws_launch_config + vars: + r_openshift_aws_launch_config_name: "{{ launch_config_name }}" + r_openshift_aws_launch_config_clusterid: "{{ clusterid }}" + r_openshift_aws_launch_config_region: "{{ region }}" + r_openshift_aws_launch_config: "{{ node_group_config }}" + r_openshift_aws_launch_config_type: compute + r_openshift_aws_launch_config_custom_image: ami-1234 + r_openshift_aws_launch_config_bootstrap_token: abcd +``` + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/openshift_aws_launch_config/defaults/main.yml b/roles/openshift_aws_launch_config/defaults/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/roles/openshift_aws_launch_config/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/openshift_aws_launch_config/meta/main.yml b/roles/openshift_aws_launch_config/meta/main.yml new file mode 100644 index 000000000..e61670cc2 --- /dev/null +++ b/roles/openshift_aws_launch_config/meta/main.yml @@ -0,0 +1,12 @@ +--- +galaxy_info: + author: OpenShift + description: Openshift AWS VPC creation + company: Red Hat, Inc + license: ASL 2.0 + min_ansible_version: 2.3 + platforms: + - name: EL + versions: + - 7 +dependencies: [] diff --git a/roles/openshift_aws_launch_config/tasks/main.yml b/roles/openshift_aws_launch_config/tasks/main.yml new file mode 100644 index 000000000..437cf1f71 --- /dev/null +++ b/roles/openshift_aws_launch_config/tasks/main.yml @@ -0,0 +1,50 @@ +--- +- name: fail when params are not set + fail: + msg: Please specify the role parameters. + when: + - r_openshift_aws_launch_config_cluseterid is undefined + - r_openshift_aws_launch_config_type is undefined + - r_openshift_aws_launch_config_region is undefined + - r_openshift_aws_launch_config is undefined + +- name: fetch the security groups for launch config + ec2_group_facts: + filters: + group-name: + - "{{ r_openshift_aws_launch_config_clusterid }}" # default sg + - "{{ r_openshift_aws_launch_config_clusterid }}_{{ r_openshift_aws_launch_config_type }}" # node type sg + - "{{ r_openshift_aws_launch_config_clusterid }}_{{ r_openshift_aws_launch_config_type }}_k8s" # node type sg k8s + region: "{{ r_openshift_aws_launch_config_region }}" + register: ec2sgs + +# Create the scale group config +- name: Create the node scale group config + ec2_lc: + name: "{{ r_openshift_aws_launch_config_name }}" + region: "{{ r_openshift_aws_launch_config_region }}" + image_id: "{{ r_openshift_aws_launch_config_custom_image if 'ami-' in r_openshift_aws_launch_config_custom_image else r_openshift_aws_launch_config[r_openshift_aws_launch_config_type].ami }}" + instance_type: "{{ r_openshift_aws_launch_config[r_openshift_aws_launch_config_type].instance_type }}" + security_groups: "{{ ec2sgs.security_groups | map(attribute='group_id')| list }}" + user_data: |- + #cloud-config + {% if r_openshift_aws_launch_config_type != 'master' %} + write_files: + - path: /root/csr_kubeconfig + owner: root:root + permissions: '0640' + content: {{ r_openshift_aws_launch_config_bootstrap_token | default('') | to_yaml }} + - path: /root/openshift_settings + owner: root:root + permissions: '0640' + content: + openshift_type: "{{ r_openshift_aws_launch_config_type }}" + runcmd: + - [ systemctl, enable, atomic-openshift-node] + - [ systemctl, start, atomic-openshift-node] + {% endif %} + key_name: "{{ r_openshift_aws_launch_config.ssh_key_name }}" + ebs_optimized: False + volumes: "{{ r_openshift_aws_launch_config[r_openshift_aws_launch_config_type].volumes }}" + assign_public_ip: True + register: test diff --git a/roles/openshift_aws_launch_config/templates/cloud-init.j2 b/roles/openshift_aws_launch_config/templates/cloud-init.j2 new file mode 100644 index 000000000..1a1e29550 --- /dev/null +++ b/roles/openshift_aws_launch_config/templates/cloud-init.j2 @@ -0,0 +1,9 @@ +{% if r_openshift_aws_launch_config_bootstrap_token is defined and r_openshift_aws_launch_config_bootstrap_token is not '' %} +#cloud-config +write_files: +- path: /root/csr_kubeconfig + owner: root:root + permissions: '0640' + content: |- + {{ r_openshift_aws_launch_config_bootstrap_token }} +{% endif %} diff --git a/roles/openshift_aws_node_group/README.md b/roles/openshift_aws_node_group/README.md new file mode 100644 index 000000000..c32c57bc5 --- /dev/null +++ b/roles/openshift_aws_node_group/README.md @@ -0,0 +1,77 @@ +openshift_aws_node_group +========= + +Ansible role to create an aws node group. + +This includes the security group, launch config, and scale group. + +Requirements +------------ + +Ansible Modules: + + +Role Variables +-------------- +```yaml +- r_openshift_aws_node_group_name: myscalegroup +- r_openshift_aws_node_group_clusterid: myclusterid +- r_openshift_aws_node_group_region: us-east-1 +- r_openshift_aws_node_group_lc_name: launch_config +- r_openshift_aws_node_group_type: master|infra|compute +- r_openshift_aws_node_group_config: "{{ node_group_config }}" +```yaml +master: + instance_type: m4.xlarge + ami: ami-cdeec8b6 # if using an encrypted AMI this will be replaced + volumes: + - device_name: /dev/sdb + volume_size: 100 + device_type: gp2 + delete_on_termination: False + health_check: + period: 60 + type: EC2 + min_size: 3 + max_size: 3 + desired_size: 3 + tags: + host-type: master + sub-host-type: default + wait_for_instances: True +``` +- r_openshift_aws_node_group_subnet_name: "{{ subnet_name }}" + +```yaml +us-east-1a # name of subnet +``` + +Dependencies +------------ + + +Example Playbook +---------------- +```yaml + - name: "create {{ openshift_build_node_type }} node groups" + include_role: + name: openshift_aws_node_group + vars: + r_openshift_aws_node_group_name: "{{ clusterid }} openshift compute" + r_openshift_aws_node_group_lc_name: "{{ launch_config_name }}" + r_openshift_aws_node_group_clusterid: "{{ clusterid }}" + r_openshift_aws_node_group_region: "{{ region }}" + r_openshift_aws_node_group_config: "{{ node_group_config }}" + r_openshift_aws_node_group_type: compute + r_openshift_aws_node_group_subnet_name: "{{ subnet_name }}" +``` + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/openshift_aws_node_group/defaults/main.yml b/roles/openshift_aws_node_group/defaults/main.yml new file mode 100644 index 000000000..44c5116a1 --- /dev/null +++ b/roles/openshift_aws_node_group/defaults/main.yml @@ -0,0 +1,58 @@ +--- +r_openshift_aws_node_group_type: master + +r_openshift_aws_node_group_config: + tags: + clusterid: "{{ r_openshift_aws_node_group_clusterid }}" + master: + instance_type: m4.xlarge + ami: "{{ r_openshift_aws_node_group_ami }}" + volumes: + - device_name: /dev/sdb + volume_size: 100 + device_type: gp2 + delete_on_termination: False + health_check: + period: 60 + type: EC2 + min_size: 3 + max_size: 3 + desired_size: 3 + tags: + host-type: master + sub-host-type: default + wait_for_instances: True + compute: + instance_type: m4.xlarge + ami: "{{ r_openshift_aws_node_group_ami }}" + volumes: + - device_name: /dev/sdb + volume_size: 100 + device_type: gp2 + delete_on_termination: True + health_check: + period: 60 + type: EC2 + min_size: 3 + max_size: 100 + desired_size: 3 + tags: + host-type: node + sub-host-type: compute + infra: + instance_type: m4.xlarge + ami: "{{ r_openshift_aws_node_group_ami }}" + volumes: + - device_name: /dev/sdb + volume_size: 100 + device_type: gp2 + delete_on_termination: True + health_check: + period: 60 + type: EC2 + min_size: 2 + max_size: 20 + desired_size: 2 + tags: + host-type: node + sub-host-type: infra diff --git a/roles/openshift_aws_node_group/tasks/main.yml b/roles/openshift_aws_node_group/tasks/main.yml new file mode 100644 index 000000000..6f5364b03 --- /dev/null +++ b/roles/openshift_aws_node_group/tasks/main.yml @@ -0,0 +1,32 @@ +--- +- name: validate role inputs + fail: + msg: Please pass in the required role variables + when: + - r_openshift_aws_node_group_clusterid is not defined + - r_openshift_aws_node_group_region is not defined + - r_openshift_aws_node_group_subnet_name is not defined + +- name: fetch the subnet to use in scale group + ec2_vpc_subnet_facts: + region: "{{ r_openshift_aws_node_group_region }}" + filters: + "tag:Name": "{{ r_openshift_aws_node_group_subnet_name }}" + register: subnetout + +- name: Create the scale group + ec2_asg: + name: "{{ r_openshift_aws_node_group_name }}" + launch_config_name: "{{ r_openshift_aws_node_group_lc_name }}" + health_check_period: "{{ r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].health_check.period }}" + health_check_type: "{{ r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].health_check.type }}" + min_size: "{{ r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].min_size }}" + max_size: "{{ r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].max_size }}" + desired_capacity: "{{ r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].desired_size }}" + region: "{{ r_openshift_aws_node_group_region }}" + termination_policies: "{{ r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].termination_policy if 'termination_policy' in r_openshift_aws_node_group_config[r_openshift_aws_node_group_type] else omit }}" + load_balancers: "{{ r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].elbs if 'elbs' in r_openshift_aws_node_group_config[r_openshift_aws_node_group_type] else omit }}" + wait_for_instances: "{{ r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].wait_for_instances | default(False)}}" + vpc_zone_identifier: "{{ subnetout.subnets[0].id }}" + tags: + - "{{ r_openshift_aws_node_group_config.tags | combine(r_openshift_aws_node_group_config[r_openshift_aws_node_group_type].tags) }}" diff --git a/roles/openshift_aws_s3/README.md b/roles/openshift_aws_s3/README.md new file mode 100644 index 000000000..afafe61cf --- /dev/null +++ b/roles/openshift_aws_s3/README.md @@ -0,0 +1,43 @@ +openshift_aws_s3 +========= + +Ansible role to create an s3 bucket + +Requirements +------------ + +Ansible Modules: + + +Role Variables +-------------- + +- r_openshift_aws_s3_clusterid: myclusterid +- r_openshift_aws_s3_region: us-east-1 +- r_openshift_aws_s3_mode: create|delete + +Dependencies +------------ + + +Example Playbook +---------------- +```yaml +- name: create an s3 bucket + include_role: + name: openshift_aws_s3 + vars: + r_openshift_aws_s3_clusterid: mycluster + r_openshift_aws_s3_region: us-east-1 + r_openshift_aws_s3_mode: create +``` + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/openshift_aws_s3/tasks/main.yml b/roles/openshift_aws_s3/tasks/main.yml new file mode 100644 index 000000000..46bd781bd --- /dev/null +++ b/roles/openshift_aws_s3/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: Create an s3 bucket + s3: + bucket: "{{ r_openshift_aws_s3_clusterid }}" + mode: "{{ r_openshift_aws_s3_mode }}" + region: "{{ r_openshift_aws_s3_region }}" diff --git a/roles/openshift_aws_sg/README.md b/roles/openshift_aws_sg/README.md new file mode 100644 index 000000000..eeb76bbb6 --- /dev/null +++ b/roles/openshift_aws_sg/README.md @@ -0,0 +1,59 @@ +openshift_aws_sg +========= + +Ansible role to create an aws security groups + +Requirements +------------ + +Ansible Modules: + + +Role Variables +-------------- + +- r_openshift_aws_sg_clusterid: myclusterid +- r_openshift_aws_sg_region: us-east-1 +- r_openshift_aws_sg_type: master|infra|compute +```yaml +# defaults/main.yml + default: + name: "{{ r_openshift_aws_sg_clusterid }}" + desc: "{{ r_openshift_aws_sg_clusterid }} default" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: all + from_port: all + to_port: all + group_name: "{{ r_openshift_aws_sg_clusterid }}" +``` + + +Dependencies +------------ + + +Example Playbook +---------------- +```yaml +- name: create security groups for master + include_role: + name: openshift_aws_sg + vars: + r_openshift_aws_sg_clusterid: mycluster + r_openshift_aws_sg_region: us-east-1 + r_openshift_aws_sg_type: master +``` + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/openshift_aws_sg/defaults/main.yml b/roles/openshift_aws_sg/defaults/main.yml new file mode 100644 index 000000000..9c480d337 --- /dev/null +++ b/roles/openshift_aws_sg/defaults/main.yml @@ -0,0 +1,48 @@ +--- +r_openshift_aws_sg_sg: + default: + name: "{{ r_openshift_aws_sg_clusterid }}" + desc: "{{ r_openshift_aws_sg_clusterid }} default" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: all + from_port: all + to_port: all + group_name: "{{ r_openshift_aws_sg_clusterid }}" + master: + name: "{{ r_openshift_aws_sg_clusterid }}_master" + desc: "{{ r_openshift_aws_sg_clusterid }} master instances" + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 443 + to_port: 443 + cidr_ip: 0.0.0.0/0 + compute: + name: "{{ r_openshift_aws_sg_clusterid }}_compute" + desc: "{{ r_openshift_aws_sg_clusterid }} compute node instances" + infra: + name: "{{ r_openshift_aws_sg_clusterid }}_infra" + desc: "{{ r_openshift_aws_sg_clusterid }} infra node instances" + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 443 + to_port: 443 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 30000 + to_port: 32000 + cidr_ip: 0.0.0.0/0 + etcd: + name: "{{ r_openshift_aws_sg_clusterid }}_etcd" + desc: "{{ r_openshift_aws_sg_clusterid }} etcd instances" diff --git a/roles/openshift_aws_sg/tasks/main.yml b/roles/openshift_aws_sg/tasks/main.yml new file mode 100644 index 000000000..2294fdcc9 --- /dev/null +++ b/roles/openshift_aws_sg/tasks/main.yml @@ -0,0 +1,53 @@ +--- +- name: Validate role inputs + fail: + msg: Please ensure to pass the correct variables + when: + - r_openshift_aws_sg_region is undefined + - r_openshift_aws_sg_region is undefined + + +- name: Fetch the VPC for vpc.id + ec2_vpc_net_facts: + region: "{{ r_openshift_aws_sg_region }}" + filters: + "tag:Name": "{{ r_openshift_aws_sg_clusterid }}" + register: vpcout + +- name: Create default security group for cluster + ec2_group: + name: "{{ r_openshift_aws_sg_sg.default.name }}" + description: "{{ r_openshift_aws_sg_sg.default.desc }}" + region: "{{ r_openshift_aws_sg_region }}" + vpc_id: "{{ vpcout.vpcs[0].id }}" + rules: "{{ r_openshift_aws_sg_sg.default.rules | default(omit, True)}}" + register: sg_default_created + +- name: create the node group sgs + ec2_group: + name: "{{ item.name}}" + description: "{{ item.desc }}" + rules: "{{ item.rules if 'rules' in item else [] }}" + region: "{{ r_openshift_aws_sg_region }}" + vpc_id: "{{ vpcout.vpcs[0].id }}" + register: sg_create + with_items: + - "{{ r_openshift_aws_sg_sg[r_openshift_aws_sg_type]}}" + +- name: create the k8s sgs for the node group + ec2_group: + name: "{{ item.name }}_k8s" + description: "{{ item.desc }} for k8s" + region: "{{ r_openshift_aws_sg_region }}" + vpc_id: "{{ vpcout.vpcs[0].id }}" + register: k8s_sg_create + with_items: + - "{{ r_openshift_aws_sg_sg[r_openshift_aws_sg_type] }}" + +- name: tag sg groups with proper tags + ec2_tag: + tags: + KubernetesCluster: "{{ r_openshift_aws_sg_clusterid }}" + resource: "{{ item.group_id }}" + region: "{{ r_openshift_aws_sg_region }}" + with_items: "{{ k8s_sg_create.results }}" diff --git a/roles/openshift_aws_ssh_keys/README.md b/roles/openshift_aws_ssh_keys/README.md new file mode 100644 index 000000000..4f8667918 --- /dev/null +++ b/roles/openshift_aws_ssh_keys/README.md @@ -0,0 +1,49 @@ +openshift_aws_ssh_keys +========= + +Ansible role for sshind SSH keys + +Requirements +------------ + +Ansible Modules: + + +Role Variables +-------------- + +- r_openshift_aws_ssh_keys_users: list of dicts of users +- r_openshift_aws_ssh_keys_region: ec2_region to install the keys + +Dependencies +------------ + + +Example Playbook +---------------- +```yaml +users: +- username: user1 + pub_key: <user1 ssh public key> +- username: user2 + pub_key: <user2 ssh public key> + +region: us-east-1 + +- include_role: + name: openshift_aws_ssh_keys + vars: + r_openshift_aws_ssh_keys_users: "{{ users }}" + r_openshift_aws_ssh_keys_region: "{{ region }}" +``` + + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/openshift_aws_ssh_keys/tasks/main.yml b/roles/openshift_aws_ssh_keys/tasks/main.yml new file mode 100644 index 000000000..232cf20ed --- /dev/null +++ b/roles/openshift_aws_ssh_keys/tasks/main.yml @@ -0,0 +1,8 @@ +--- +- name: Add the public keys for the users + ec2_key: + name: "{{ item.key_name }}" + key_material: "{{ item.pub_key }}" + region: "{{ r_openshift_aws_ssh_keys_region }}" + with_items: "{{ r_openshift_aws_ssh_keys_users }}" + no_log: True diff --git a/roles/openshift_aws_vpc/README.md b/roles/openshift_aws_vpc/README.md new file mode 100644 index 000000000..d88cf0581 --- /dev/null +++ b/roles/openshift_aws_vpc/README.md @@ -0,0 +1,62 @@ +openshift_aws_vpc +========= + +Ansible role to create a default AWS VPC + +Requirements +------------ + +Ansible Modules: + + +Role Variables +-------------- + +- r_openshift_aws_vpc_clusterid: "{{ clusterid }}" +- r_openshift_aws_vpc_cidr: 172.31.48.0/20 +- r_openshift_aws_vpc_subnets: "{{ subnets }}" +```yaml + subnets: + us-east-1: # These are us-east-1 region defaults. Ensure this matches your region + - cidr: 172.31.48.0/20 + az: "us-east-1c" + - cidr: 172.31.32.0/20 + az: "us-east-1e" + - cidr: 172.31.16.0/20 + az: "us-east-1a" +``` +- r_openshift_aws_vpc_region: "{{ region }}" +- r_openshift_aws_vpc_tags: dict of tags to apply to vpc +- r_openshift_aws_vpc_name: "{{ vpc_name | default(clusterid) }}" + +Dependencies +------------ + + +Example Playbook +---------------- + +```yaml + - name: create default vpc + include_role: + name: openshift_aws_vpc + vars: + r_openshift_aws_vpc_clusterid: mycluster + r_openshift_aws_vpc_cidr: 172.31.48.0/20 + r_openshift_aws_vpc_subnets: "{{ subnets }}" + r_openshift_aws_vpc_region: us-east-1 + r_openshift_aws_vpc_tags: {} + r_openshift_aws_vpc_name: mycluster + +``` + + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Openshift diff --git a/roles/openshift_aws_vpc/defaults/main.yml b/roles/openshift_aws_vpc/defaults/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/roles/openshift_aws_vpc/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/openshift_aws_vpc/tasks/main.yml b/roles/openshift_aws_vpc/tasks/main.yml new file mode 100644 index 000000000..cfe08dae5 --- /dev/null +++ b/roles/openshift_aws_vpc/tasks/main.yml @@ -0,0 +1,53 @@ +--- +- name: Create AWS VPC + ec2_vpc_net: + state: present + cidr_block: "{{ r_openshift_aws_vpc_cidr }}" + dns_support: True + dns_hostnames: True + region: "{{ r_openshift_aws_vpc_region }}" + name: "{{ r_openshift_aws_vpc_clusterid }}" + tags: + Name: "{{ r_openshift_aws_vpc_clusterid }}" + register: vpc + +- name: Sleep to avoid a race condition when creating the vpc + pause: + seconds: 5 + when: vpc.changed + +- name: assign the vpc igw + ec2_vpc_igw: + region: "{{ r_openshift_aws_vpc_region }}" + vpc_id: "{{ vpc.vpc.id }}" + register: igw + +- name: assign the vpc subnets + ec2_vpc_subnet: + region: "{{ r_openshift_aws_vpc_region }}" + vpc_id: "{{ vpc.vpc.id }}" + cidr: "{{ item.cidr }}" + az: "{{ item.az }}" + resource_tags: + Name: "{{ item.az }}" + with_items: "{{ r_openshift_aws_vpc_subnets[r_openshift_aws_vpc_region] }}" + +- name: Grab the route tables from our VPC + ec2_vpc_route_table_facts: + region: "{{ r_openshift_aws_vpc_region }}" + filters: + vpc-id: "{{ vpc.vpc.id }}" + register: route_table + +- name: update the route table in the vpc + ec2_vpc_route_table: + lookup: id + route_table_id: "{{ route_table.route_tables[0].id }}" + vpc_id: "{{ vpc.vpc.id }}" + region: "{{ r_openshift_aws_vpc_region }}" + tags: + Name: "{{ r_openshift_aws_vpc_name }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: igw + register: route_table_out diff --git a/roles/openshift_ca/meta/main.yml b/roles/openshift_ca/meta/main.yml index 444c5b77e..dfbdf0cc7 100644 --- a/roles/openshift_ca/meta/main.yml +++ b/roles/openshift_ca/meta/main.yml @@ -13,6 +13,5 @@ galaxy_info: - cloud - system dependencies: -- role: openshift_repos - role: openshift_cli - role: openshift_named_certificates diff --git a/roles/openshift_cfme/defaults/main.yml b/roles/openshift_cfme/defaults/main.yml index 79e59b410..8aa57e75a 100644 --- a/roles/openshift_cfme/defaults/main.yml +++ b/roles/openshift_cfme/defaults/main.yml @@ -1,6 +1,7 @@ --- -# Namespace for the CFME project -openshift_cfme_project: cfme +# Namespace for the CFME project (Note: changed post-3.6 to use +# reserved 'openshift-' namespace prefix) +openshift_cfme_project: openshift-cfme # Namespace/project description openshift_cfme_project_description: ManageIQ - CloudForms Management Engine # Basic user assigned the `admin` role for the project @@ -28,6 +29,7 @@ openshift_cfme_pv_data: openshift_cfme_maxImagesBulkImportedPerRepository: 100 # Hostname/IP of the NFS server. Currently defaults to first master openshift_cfme_nfs_server: "{{ groups.nfs.0 }}" +openshift_cfme_nfs_directory: "/exports" # TODO: Refactor '_install_app' variable. This is just for testing but # maybe in the future it should control the entire yes/no for CFME. # @@ -35,9 +37,9 @@ openshift_cfme_nfs_server: "{{ groups.nfs.0 }}" # --template=manageiq). If False everything UP TO 'new-app' is ran. openshift_cfme_install_app: False # Docker image to pull -openshift_cfme_application_img_name: "{{ 'registry.access.redhat.com/cloudforms45/cfme-openshift-app' if openshift_deployment_type == 'openshift-enterprise' else 'docker.io/manageiq/manageiq-pods:app-latest-fine' }}" -openshift_cfme_postgresql_img_name: "{{ 'registry.access.redhat.com/cloudforms45/cfme-openshift-postgresql' if openshift_deployment_type == 'openshift-enterprise' else 'docker.io/manageiq/manageiq-pods:app-latest-fine' }}" -openshift_cfme_memcached_img_name: "{{ 'registry.access.redhat.com/cloudforms45/cfme-openshift-memcached' if openshift_deployment_type == 'openshift-enterprise' else 'docker.io/manageiq/manageiq-pods:app-latest-fine' }}" +openshift_cfme_application_img_name: "{{ 'registry.access.redhat.com/cloudforms45/cfme-openshift-app' if openshift_deployment_type == 'openshift-enterprise' else 'docker.io/manageiq/manageiq-pods' }}" +openshift_cfme_postgresql_img_name: "{{ 'registry.access.redhat.com/cloudforms45/cfme-openshift-postgresql' if openshift_deployment_type == 'openshift-enterprise' else 'docker.io/manageiq/manageiq-pods' }}" +openshift_cfme_memcached_img_name: "{{ 'registry.access.redhat.com/cloudforms45/cfme-openshift-memcached' if openshift_deployment_type == 'openshift-enterprise' else 'docker.io/manageiq/manageiq-pods' }}" openshift_cfme_application_img_tag: "{{ 'latest' if openshift_deployment_type == 'openshift-enterprise' else 'app-latest-fine' }}" openshift_cfme_memcached_img_tag: "{{ 'latest' if openshift_deployment_type == 'openshift-enterprise' else 'memcached-latest-fine' }}" openshift_cfme_postgresql_img_tag: "{{ 'latest' if openshift_deployment_type == 'openshift-enterprise' else 'postgresql-latest-fine' }}" diff --git a/roles/openshift_cfme/handlers/main.yml b/roles/openshift_cfme/handlers/main.yml index 476a5e030..7e90b09a4 100644 --- a/roles/openshift_cfme/handlers/main.yml +++ b/roles/openshift_cfme/handlers/main.yml @@ -6,19 +6,14 @@ # See: https://github.com/openshift/openshift-ansible/pull/4041#discussion_r118770782 ###################################################################### -- name: restart master - systemd: name={{ openshift.common.service_type }}-master state=restarted - when: (openshift.master.ha is not defined or not openshift.master.ha | bool) and (not (master_service_status_changed | default(false) | bool)) - notify: Verify API Server - - name: restart master api systemd: name={{ openshift.common.service_type }}-master-api state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' notify: Verify API Server - name: restart master controllers systemd: name={{ openshift.common.service_type }}-master-controllers state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' - name: Verify API Server # Using curl here since the uri module requires python-httplib2 and diff --git a/roles/openshift_cfme/templates/miq-pv-db.yaml.j2 b/roles/openshift_cfme/templates/miq-pv-db.yaml.j2 index b8c3bb277..280f3e97a 100644 --- a/roles/openshift_cfme/templates/miq-pv-db.yaml.j2 +++ b/roles/openshift_cfme/templates/miq-pv-db.yaml.j2 @@ -8,6 +8,6 @@ spec: accessModes: - ReadWriteOnce nfs: - path: /exports/miq-pv01 + path: {{ openshift_cfme_nfs_directory }}/miq-pv01 server: {{ openshift_cfme_nfs_server }} persistentVolumeReclaimPolicy: Retain diff --git a/roles/openshift_cfme/templates/miq-pv-region.yaml.j2 b/roles/openshift_cfme/templates/miq-pv-region.yaml.j2 index 7218773f0..fe80dffa5 100644 --- a/roles/openshift_cfme/templates/miq-pv-region.yaml.j2 +++ b/roles/openshift_cfme/templates/miq-pv-region.yaml.j2 @@ -8,6 +8,6 @@ spec: accessModes: - ReadWriteOnce nfs: - path: /exports/miq-pv02 + path: {{ openshift_cfme_nfs_directory }}/miq-pv02 server: {{ openshift_cfme_nfs_server }} persistentVolumeReclaimPolicy: Retain diff --git a/roles/openshift_cfme/templates/miq-pv-server.yaml.j2 b/roles/openshift_cfme/templates/miq-pv-server.yaml.j2 index 7b40b6c69..f84b67ea9 100644 --- a/roles/openshift_cfme/templates/miq-pv-server.yaml.j2 +++ b/roles/openshift_cfme/templates/miq-pv-server.yaml.j2 @@ -8,6 +8,6 @@ spec: accessModes: - ReadWriteOnce nfs: - path: /exports/miq-pv03 + path: {{ openshift_cfme_nfs_directory }}/miq-pv03 server: {{ openshift_cfme_nfs_server }} persistentVolumeReclaimPolicy: Retain diff --git a/roles/openshift_cli/library/openshift_container_binary_sync.py b/roles/openshift_cli/library/openshift_container_binary_sync.py index c47203211..b40c49701 100644 --- a/roles/openshift_cli/library/openshift_container_binary_sync.py +++ b/roles/openshift_cli/library/openshift_container_binary_sync.py @@ -133,6 +133,11 @@ class BinarySyncer(object): dest_path = os.path.join(self.bin_dir, binary_name) incoming_checksum = self.module.run_command(['sha256sum', src_path])[1] if not os.path.exists(dest_path) or self.module.run_command(['sha256sum', dest_path])[1] != incoming_checksum: + + # See: https://github.com/openshift/openshift-ansible/issues/4965 + if os.path.islink(dest_path): + os.unlink(dest_path) + self.output.append('Removed old symlink {} before copying binary.'.format(dest_path)) shutil.move(src_path, dest_path) self.output.append("Moved %s to %s." % (src_path, dest_path)) self.changed = True diff --git a/roles/openshift_cli/meta/main.yml b/roles/openshift_cli/meta/main.yml index c1de367d9..04a1ce873 100644 --- a/roles/openshift_cli/meta/main.yml +++ b/roles/openshift_cli/meta/main.yml @@ -15,4 +15,4 @@ dependencies: - role: openshift_docker when: not skip_docker_role | default(False) | bool - role: openshift_common -- role: openshift_cli_facts +- role: openshift_facts diff --git a/roles/openshift_cli/tasks/main.yml b/roles/openshift_cli/tasks/main.yml index c716a0860..9e61805f9 100644 --- a/roles/openshift_cli/tasks/main.yml +++ b/roles/openshift_cli/tasks/main.yml @@ -1,6 +1,6 @@ --- - set_fact: - l_use_crio: "{{ openshift_docker_use_crio | default(false) }}" + l_use_crio: "{{ openshift_use_crio | default(false) }}" - name: Install clients package: name={{ openshift.common.service_type }}-clients state=present diff --git a/roles/openshift_cli_facts/meta/main.yml b/roles/openshift_cli_facts/meta/main.yml deleted file mode 100644 index 59acde215..000000000 --- a/roles/openshift_cli_facts/meta/main.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -galaxy_info: - author: Jason DeTiberus - description: OpenShift CLI Facts - company: Red Hat, Inc. - license: Apache License, Version 2.0 - min_ansible_version: 1.9 - platforms: - - name: EL - versions: - - 7 - categories: - - cloud -dependencies: -- role: openshift_facts diff --git a/roles/openshift_cli_facts/tasks/main.yml b/roles/openshift_cli_facts/tasks/main.yml deleted file mode 100644 index dd1ed8965..000000000 --- a/roles/openshift_cli_facts/tasks/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# TODO: move this to a new 'cli' role -- openshift_facts: - role: common - local_facts: - cli_image: "{{ osm_image | default(None) }}" diff --git a/roles/openshift_common/meta/main.yml b/roles/openshift_common/meta/main.yml index cd8c75ec5..7cc95d8fa 100644 --- a/roles/openshift_common/meta/main.yml +++ b/roles/openshift_common/meta/main.yml @@ -13,5 +13,3 @@ galaxy_info: - cloud dependencies: - role: openshift_facts -- role: openshift_repos -- role: openshift_version diff --git a/roles/openshift_common/tasks/main.yml b/roles/openshift_common/tasks/main.yml index 51313a258..a0bd6c860 100644 --- a/roles/openshift_common/tasks/main.yml +++ b/roles/openshift_common/tasks/main.yml @@ -40,8 +40,8 @@ when: openshift_use_calico | default(false) | bool and openshift_use_contiv | default(false) | bool - fail: - msg: openshift_hostname must be 64 characters or less - when: openshift_hostname is defined and openshift_hostname | length > 64 + msg: openshift_hostname must be 63 characters or less + when: openshift_hostname is defined and openshift_hostname | length > 63 - name: Set common Cluster facts openshift_facts: diff --git a/roles/openshift_docker/meta/main.yml b/roles/openshift_docker/meta/main.yml index 10131f717..60efd4e45 100644 --- a/roles/openshift_docker/meta/main.yml +++ b/roles/openshift_docker/meta/main.yml @@ -12,6 +12,5 @@ galaxy_info: categories: - cloud dependencies: -- role: openshift_version - role: openshift_docker_facts - role: docker diff --git a/roles/openshift_docker_facts/tasks/main.yml b/roles/openshift_docker_facts/tasks/main.yml index 516d7dc29..334150f63 100644 --- a/roles/openshift_docker_facts/tasks/main.yml +++ b/roles/openshift_docker_facts/tasks/main.yml @@ -17,7 +17,7 @@ hosted_registry_insecure: "{{ openshift_docker_hosted_registry_insecure | default(openshift.docker.hosted_registry_insecure | default(False)) }}" hosted_registry_network: "{{ openshift_docker_hosted_registry_network | default(None) }}" use_system_container: "{{ openshift_docker_use_system_container | default(False) }}" - use_crio: "{{ openshift_docker_use_crio | default(False) }}" + use_crio: "{{ openshift_use_crio | default(False) }}" - role: node local_facts: sdn_mtu: "{{ openshift_node_sdn_mtu | default(None) }}" diff --git a/roles/openshift_examples/examples-sync.sh b/roles/openshift_examples/examples-sync.sh index 3a8ce55c4..ca3f219d8 100755 --- a/roles/openshift_examples/examples-sync.sh +++ b/roles/openshift_examples/examples-sync.sh @@ -40,5 +40,6 @@ popd wget https://raw.githubusercontent.com/redhat-developer/s2i-dotnetcore/master/dotnet_imagestreams.json -O ${EXAMPLES_BASE}/image-streams/dotnet_imagestreams.json wget https://raw.githubusercontent.com/redhat-developer/s2i-dotnetcore/master/templates/dotnet-example.json -O ${EXAMPLES_BASE}/quickstart-templates/dotnet-example.json wget https://raw.githubusercontent.com/redhat-developer/s2i-dotnetcore/master/templates/dotnet-pgsql-persistent.json -O ${EXAMPLES_BASE}/quickstart-templates/dotnet-pgsql-persistent.json +wget https://raw.githubusercontent.com/redhat-developer/s2i-dotnetcore/master/templates/dotnet-runtime-example.json -O ${EXAMPLES_BASE}/quickstart-templates/dotnet-runtime-example.json git diff files/examples diff --git a/roles/openshift_examples/files/examples/v1.5/cfme-templates/cfme-template.yaml b/roles/openshift_examples/files/examples/v1.5/cfme-templates/cfme-template.yaml index 3bc6c5813..fd57a864c 100644 --- a/roles/openshift_examples/files/examples/v1.5/cfme-templates/cfme-template.yaml +++ b/roles/openshift_examples/files/examples/v1.5/cfme-templates/cfme-template.yaml @@ -10,6 +10,12 @@ metadata: iconClass: "icon-rails" objects: - apiVersion: v1 + kind: Secret + metadata: + name: "${NAME}-secrets" + stringData: + pg-password: "${DATABASE_PASSWORD}" +- apiVersion: v1 kind: Service metadata: annotations: @@ -148,7 +154,10 @@ objects: value: "${DATABASE_USER}" - name: "POSTGRESQL_PASSWORD" - value: "${DATABASE_PASSWORD}" + valueFrom: + secretKeyRef: + name: "${NAME}-secrets" + key: "pg-password" - name: "POSTGRESQL_DATABASE" value: "${DATABASE_NAME}" @@ -345,7 +354,10 @@ objects: value: "${DATABASE_USER}" - name: "POSTGRESQL_PASSWORD" - value: "${DATABASE_PASSWORD}" + valueFrom: + secretKeyRef: + name: "${NAME}-secrets" + key: "pg-password" - name: "POSTGRESQL_DATABASE" value: "${DATABASE_NAME}" @@ -386,7 +398,8 @@ parameters: displayName: "PostgreSQL Password" required: true description: "Password for the PostgreSQL user." - value: "smartvm" + from: "[a-zA-Z0-9]{8}" + generate: expression - name: "DATABASE_NAME" required: true diff --git a/roles/openshift_examples/files/examples/v3.6/cfme-templates/cfme-template.yaml b/roles/openshift_examples/files/examples/v3.6/cfme-templates/cfme-template.yaml index 3bc6c5813..fd57a864c 100644 --- a/roles/openshift_examples/files/examples/v3.6/cfme-templates/cfme-template.yaml +++ b/roles/openshift_examples/files/examples/v3.6/cfme-templates/cfme-template.yaml @@ -10,6 +10,12 @@ metadata: iconClass: "icon-rails" objects: - apiVersion: v1 + kind: Secret + metadata: + name: "${NAME}-secrets" + stringData: + pg-password: "${DATABASE_PASSWORD}" +- apiVersion: v1 kind: Service metadata: annotations: @@ -148,7 +154,10 @@ objects: value: "${DATABASE_USER}" - name: "POSTGRESQL_PASSWORD" - value: "${DATABASE_PASSWORD}" + valueFrom: + secretKeyRef: + name: "${NAME}-secrets" + key: "pg-password" - name: "POSTGRESQL_DATABASE" value: "${DATABASE_NAME}" @@ -345,7 +354,10 @@ objects: value: "${DATABASE_USER}" - name: "POSTGRESQL_PASSWORD" - value: "${DATABASE_PASSWORD}" + valueFrom: + secretKeyRef: + name: "${NAME}-secrets" + key: "pg-password" - name: "POSTGRESQL_DATABASE" value: "${DATABASE_NAME}" @@ -386,7 +398,8 @@ parameters: displayName: "PostgreSQL Password" required: true description: "Password for the PostgreSQL user." - value: "smartvm" + from: "[a-zA-Z0-9]{8}" + generate: expression - name: "DATABASE_NAME" required: true diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/OWNERS b/roles/openshift_examples/files/examples/v3.6/db-templates/OWNERS new file mode 100644 index 000000000..cbdc20f41 --- /dev/null +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/OWNERS @@ -0,0 +1,12 @@ +reviewers: + - bparees + - gabemontero + - mfojtik + - dinhxuanvu + - jim-minter + - spadgett +approvers: + - bparees + - mfojtik + - spadgett + - jupierce diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/mariadb-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/mariadb-ephemeral-template.json index 536f7275e..6500ed0d3 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/mariadb-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/mariadb-ephemeral-template.json @@ -27,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-root_password": "{.data['database-root-password']}" + "template.openshift.io/expose-root_password": "{.data['database-root-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MYSQL_USER}", "database-password" : "${MYSQL_PASSWORD}", - "database-root-password" : "${MYSQL_ROOT_PASSWORD}" + "database-root-password" : "${MYSQL_ROOT_PASSWORD}", + "database-name" : "${MYSQL_DATABASE}" } }, { @@ -61,7 +63,10 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "${DATABASE_SERVICE_NAME}" + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -151,7 +156,12 @@ }, { "name": "MYSQL_DATABASE", - "value": "${MYSQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/mariadb-persistent-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/mariadb-persistent-template.json index 3b7fdccce..4378fa4a0 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/mariadb-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/mariadb-persistent-template.json @@ -27,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-root_password": "{.data['database-root-password']}" + "template.openshift.io/expose-root_password": "{.data['database-root-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MYSQL_USER}", "database-password" : "${MYSQL_PASSWORD}", - "database-root-password" : "${MYSQL_ROOT_PASSWORD}" + "database-root-password" : "${MYSQL_ROOT_PASSWORD}", + "database-name" : "${MYSQL_DATABASE}" } }, { @@ -78,7 +80,10 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "${DATABASE_SERVICE_NAME}" + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -168,7 +173,12 @@ }, { "name": "MYSQL_DATABASE", - "value": "${MYSQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/mongodb-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/mongodb-ephemeral-template.json index ee274194f..7271a2c69 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/mongodb-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/mongodb-ephemeral-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "mongodb-ephemeral", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "MongoDB (Ephemeral)", "description": "MongoDB database service, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mongodb-container/blob/master/3.2/README.md.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", @@ -28,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-admin_password": "{.data['database-admin-password']}" + "template.openshift.io/expose-admin_password": "{.data['database-admin-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MONGODB_USER}", "database-password" : "${MONGODB_PASSWORD}", - "database-admin-password" : "${MONGODB_ADMIN_PASSWORD}" + "database-admin-password" : "${MONGODB_ADMIN_PASSWORD}", + "database-name" : "${MONGODB_DATABASE}" } }, { @@ -42,7 +43,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "mongodb://{.spec.clusterIP}:{.spec.ports[?(.name==\"mongo\")].port}" } @@ -72,7 +72,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -104,7 +106,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -164,7 +165,12 @@ }, { "name": "MONGODB_DATABASE", - "value": "${MONGODB_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/mongodb-persistent-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/mongodb-persistent-template.json index e5ba43669..d70d2263f 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/mongodb-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/mongodb-persistent-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "mongodb-persistent", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "MongoDB (Persistent)", "description": "MongoDB database service, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mongodb-container/blob/master/3.2/README.md.\n\nNOTE: Scaling to more than one replica is not supported. You must have persistent volumes available in your cluster to use this template.", @@ -28,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-admin_password": "{.data['database-admin-password']}" + "template.openshift.io/expose-admin_password": "{.data['database-admin-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MONGODB_USER}", "database-password" : "${MONGODB_PASSWORD}", - "database-admin-password" : "${MONGODB_ADMIN_PASSWORD}" + "database-admin-password" : "${MONGODB_ADMIN_PASSWORD}", + "database-name" : "${MONGODB_DATABASE}" } }, { @@ -42,7 +43,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "mongodb://{.spec.clusterIP}:{.spec.ports[?(.name==\"mongo\")].port}" } @@ -89,7 +89,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -121,7 +123,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -181,7 +182,12 @@ }, { "name": "MONGODB_DATABASE", - "value": "${MONGODB_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/mysql-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/mysql-ephemeral-template.json index 969e62ac5..54785993c 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/mysql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/mysql-ephemeral-template.json @@ -19,7 +19,7 @@ "template": "mysql-ephemeral-template" }, "objects": [ - { + { "kind": "Secret", "apiVersion": "v1", "metadata": { @@ -27,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-root_password": "{.data['database-root-password']}" + "template.openshift.io/expose-root_password": "{.data['database-root-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MYSQL_USER}", "database-password" : "${MYSQL_PASSWORD}", - "database-root-password" : "${MYSQL_ROOT_PASSWORD}" + "database-root-password" : "${MYSQL_ROOT_PASSWORD}", + "database-name" : "${MYSQL_DATABASE}" } }, { @@ -41,7 +43,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "mysql://{.spec.clusterIP}:{.spec.ports[?(.name==\"mysql\")].port}" } @@ -71,7 +72,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -103,7 +106,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -164,7 +166,12 @@ }, { "name": "MYSQL_DATABASE", - "value": "${MYSQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/mysql-persistent-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/mysql-persistent-template.json index 4f39d41a5..2bd84b106 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/mysql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/mysql-persistent-template.json @@ -27,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-root_password": "{.data['database-root-password']}" + "template.openshift.io/expose-root_password": "{.data['database-root-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MYSQL_USER}", "database-password" : "${MYSQL_PASSWORD}", - "database-root-password" : "${MYSQL_ROOT_PASSWORD}" + "database-root-password" : "${MYSQL_ROOT_PASSWORD}", + "database-name" : "${MYSQL_DATABASE}" } }, { @@ -78,7 +80,10 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "${DATABASE_SERVICE_NAME}" + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -168,7 +173,12 @@ }, { "name": "MYSQL_DATABASE", - "value": "${MYSQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/postgresql-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/postgresql-ephemeral-template.json index c37102cb0..849c9d83f 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/postgresql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/postgresql-ephemeral-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "postgresql-ephemeral", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "PostgreSQL (Ephemeral)", "description": "PostgreSQL database service, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/postgresql-container/blob/master/9.5.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", @@ -27,12 +26,14 @@ "name": "${DATABASE_SERVICE_NAME}", "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", - "template.openshift.io/expose-password": "{.data['database-password']}" + "template.openshift.io/expose-password": "{.data['database-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${POSTGRESQL_USER}", - "database-password" : "${POSTGRESQL_PASSWORD}" + "database-password" : "${POSTGRESQL_PASSWORD}", + "database-name" : "${POSTGRESQL_DATABASE}" } }, { @@ -40,7 +41,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "postgres://{.spec.clusterIP}:{.spec.ports[?(.name==\"postgresql\")].port}" } @@ -70,7 +70,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -102,7 +104,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -153,7 +154,12 @@ }, { "name": "POSTGRESQL_DATABASE", - "value": "${POSTGRESQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/postgresql-persistent-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/postgresql-persistent-template.json index 32dc93a95..b622baa01 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/postgresql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/postgresql-persistent-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "postgresql-persistent", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "PostgreSQL (Persistent)", "description": "PostgreSQL database service, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/postgresql-container/blob/master/9.5.\n\nNOTE: Scaling to more than one replica is not supported. You must have persistent volumes available in your cluster to use this template.", @@ -27,12 +26,14 @@ "name": "${DATABASE_SERVICE_NAME}", "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", - "template.openshift.io/expose-password": "{.data['database-password']}" + "template.openshift.io/expose-password": "{.data['database-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${POSTGRESQL_USER}", - "database-password" : "${POSTGRESQL_PASSWORD}" + "database-password" : "${POSTGRESQL_PASSWORD}", + "database-name" : "${POSTGRESQL_DATABASE}" } }, { @@ -40,7 +41,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "postgres://{.spec.clusterIP}:{.spec.ports[?(.name==\"postgresql\")].port}" } @@ -87,7 +87,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -119,7 +121,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -170,7 +171,12 @@ }, { "name": "POSTGRESQL_DATABASE", - "value": "${POSTGRESQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/redis-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/redis-ephemeral-template.json index 6bb683e52..15bdd079b 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/redis-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/redis-ephemeral-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "redis-ephemeral", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "Redis (Ephemeral)", "description": "Redis in-memory data structure store, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/3.2.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", @@ -38,7 +37,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" } @@ -68,7 +66,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -100,7 +100,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } diff --git a/roles/openshift_examples/files/examples/v3.6/db-templates/redis-persistent-template.json b/roles/openshift_examples/files/examples/v3.6/db-templates/redis-persistent-template.json index 9e8be2309..1e31b02e0 100644 --- a/roles/openshift_examples/files/examples/v3.6/db-templates/redis-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.6/db-templates/redis-persistent-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "redis-persistent", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "Redis (Persistent)", "description": "Redis in-memory data structure store, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/3.2.\n\nNOTE: You must have persistent volumes available in your cluster to use this template.", @@ -38,7 +37,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" } @@ -85,7 +83,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -117,7 +117,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } diff --git a/roles/openshift_examples/files/examples/v3.6/image-streams/OWNERS b/roles/openshift_examples/files/examples/v3.6/image-streams/OWNERS new file mode 100644 index 000000000..6ddf77f12 --- /dev/null +++ b/roles/openshift_examples/files/examples/v3.6/image-streams/OWNERS @@ -0,0 +1,14 @@ +reviewers: + - bparees + - sspeiche + - mfojtik + - liggitt + - jcantrill + - hhorak + - csrwng +approvers: + - bparees + - mfojtik + - liggitt + - jcantrill + - csrwng diff --git a/roles/openshift_examples/files/examples/v3.6/image-streams/dotnet_imagestreams.json b/roles/openshift_examples/files/examples/v3.6/image-streams/dotnet_imagestreams.json index 857ffa980..ee753966f 100644 --- a/roles/openshift_examples/files/examples/v3.6/image-streams/dotnet_imagestreams.json +++ b/roles/openshift_examples/files/examples/v3.6/image-streams/dotnet_imagestreams.json @@ -14,7 +14,7 @@ "metadata": { "name": "dotnet", "annotations": { - "openshift.io/display-name": ".NET Core" + "openshift.io/display-name": ".NET Core Builder Images" } }, "spec": { @@ -23,17 +23,35 @@ "name": "latest", "annotations": { "openshift.io/display-name": ".NET Core (Latest)", - "description": "Build and run .NET Core applications on RHEL 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/1.1/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of .NET Core available on OpenShift, including major versions updates.", + "description": "Build and run .NET Core applications on RHEL 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/2.0/build/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of .NET Core available on OpenShift, including major versions updates.", "iconClass": "icon-dotnet", "tags": "builder,.net,dotnet,dotnetcore", "supports":"dotnet", "sampleRepo": "https://github.com/redhat-developer/s2i-dotnetcore-ex.git", "sampleContextDir": "app", - "sampleRef": "dotnetcore-1.1" + "sampleRef": "dotnetcore-2.0" }, "from": { "kind": "ImageStreamTag", - "name": "1.1" + "name": "2.0" + } + }, + { + "name": "2.0", + "annotations": { + "openshift.io/display-name": ".NET Core 2.0", + "description": "Build and run .NET Core 2.0 applications on RHEL 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/2.0/build/README.md.", + "iconClass": "icon-dotnet", + "tags": "builder,.net,dotnet,dotnetcore,rh-dotnet20", + "supports":"dotnet:2.0,dotnet", + "sampleRepo": "https://github.com/redhat-developer/s2i-dotnetcore-ex.git", + "sampleContextDir": "app", + "sampleRef": "dotnetcore-2.0", + "version": "2.0" + }, + "from": { + "kind": "DockerImage", + "name": "registry.access.redhat.com/dotnet/dotnet-20-rhel7:2.0" } }, { @@ -74,6 +92,49 @@ } ] } + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "dotnet-runtime", + "annotations": { + "openshift.io/display-name": ".NET Core Runtime Images" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": { + "openshift.io/display-name": ".NET Core Runtime (Latest)", + "description": "Run .NET Core applications on RHEL 7. For more information about using this image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/2.0/runtime/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of .NET Core Runtime available on OpenShift, including major versions updates.", + "iconClass": "icon-dotnet", + "tags": "runtime,.net-runtime,dotnet-runtime,dotnetcore-runtime", + "supports":"dotnet-runtime" + }, + "from": { + "kind": "ImageStreamTag", + "name": "2.0" + } + }, + { + "name": "2.0", + "annotations": { + "openshift.io/display-name": ".NET Core 2.0 Runtime", + "description": "Run .NET Core applications on RHEL 7. For more information about using this image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/2.0/runtime/README.md.", + "iconClass": "icon-dotnet", + "tags": "runtime,.net-runtime,dotnet-runtime,dotnetcore-runtime", + "supports":"dotnet-runtime", + "version": "2.0" + }, + "from": { + "kind": "DockerImage", + "name": "registry.access.redhat.com/dotnet/dotnet-20-runtime-rhel7:2.0" + } + } + ] + } } ] } diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/OWNERS b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/OWNERS new file mode 100644 index 000000000..a26e484d6 --- /dev/null +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/OWNERS @@ -0,0 +1,12 @@ +reviewers: + - bparees + - gabemontero + - coreydaley + - dinhxuanvu + - sspeiche + - mfojtik + - jupierce +approvers: + - bparees + - mfojtik + - jupierce diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/cakephp-mysql-persistent.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/cakephp-mysql-persistent.json index 6d987ee33..289f809fa 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/cakephp-mysql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/cakephp-mysql-persistent.json @@ -89,7 +89,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -148,7 +149,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/cakephp-mysql.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/cakephp-mysql.json index fb2ef206e..0562982b3 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/cakephp-mysql.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/cakephp-mysql.json @@ -89,7 +89,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -148,7 +149,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dancer-mysql-persistent.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dancer-mysql-persistent.json index 7ffb25e14..7a3875d09 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dancer-mysql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dancer-mysql-persistent.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -146,7 +147,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dancer-mysql.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dancer-mysql.json index d787e376b..399ec72a8 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dancer-mysql.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dancer-mysql.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -146,7 +147,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/django-postgresql-persistent.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/django-postgresql-persistent.json index a2070207b..e37f7a492 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/django-postgresql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/django-postgresql-persistent.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -146,7 +147,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/django-postgresql.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/django-postgresql.json index 0d33c6e0e..965c2ebfe 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/django-postgresql.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/django-postgresql.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -146,7 +147,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-example.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-example.json index af46579c8..f1fef3093 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-example.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-example.json @@ -249,7 +249,7 @@ "displayName": ".NET builder", "required": true, "description": "The image stream tag which is used to build the code.", - "value": "dotnet:1.0" + "value": "dotnet:2.0" }, { "name": "NAMESPACE", @@ -269,7 +269,7 @@ "name": "SOURCE_REPOSITORY_REF", "displayName": "Git Reference", "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch.", - "value": "dotnetcore-1.0" + "value": "dotnetcore-2.0" }, { "name": "CONTEXT_DIR", @@ -299,7 +299,7 @@ { "name": "DOTNET_STARTUP_PROJECT", "displayName": "Startup Project", - "description": "Set this to the folder containing your startup project.", + "description": "Set this to a project file (e.g. csproj) or a folder containing a single project file.", "value": "app" }, { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-pgsql-persistent.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-pgsql-persistent.json index a2b59c2d3..c83132152 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-pgsql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-pgsql-persistent.json @@ -455,7 +455,7 @@ "displayName": ".NET builder", "required": true, "description": "The image stream tag which is used to build the code.", - "value": "dotnet:1.1" + "value": "dotnet:2.0" }, { "name": "NAMESPACE", @@ -475,7 +475,7 @@ "name": "SOURCE_REPOSITORY_REF", "displayName": "Git Reference", "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch.", - "value": "rel/1.1-example" + "value": "rel/2.0-example" }, { "name": "CONTEXT_DIR", @@ -485,7 +485,7 @@ { "name": "DOTNET_STARTUP_PROJECT", "displayName": "Startup Project", - "description": "Set this to the folder containing your startup project.", + "description": "Set this to a project file (e.g. csproj) or a folder containing a single project file.", "value": "samples/MusicStore" }, { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-runtime-example.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-runtime-example.json new file mode 100644 index 000000000..e1dccf290 --- /dev/null +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/dotnet-runtime-example.json @@ -0,0 +1,412 @@ +{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "name": "dotnet-runtime-example", + "annotations": { + "openshift.io/display-name": ".NET Core Runtime Example", + "description": "An example .NET Core Runtime example application.", + "tags": "quickstart,dotnet,.net", + "iconClass": "icon-dotnet", + "template.openshift.io/provider-display-name": "Red Hat, Inc.", + "template.openshift.io/documentation-url": "https://github.com/redhat-developer/s2i-dotnetcore", + "template.openshift.io/support-url": "https://access.redhat.com" + } + }, + "objects": [ + { + "kind": "Route", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}" + }, + "spec": { + "host": "${APPLICATION_DOMAIN}", + "to": { + "kind": "Service", + "name": "${NAME}" + } + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}", + "annotations": { + "description": "Exposes and load balances the application pods" + } + }, + "spec": { + "ports": [ + { + "name": "web", + "port": 8080, + "targetPort": 8080 + } + ], + "selector": { + "name": "${NAME}" + } + } + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}", + "annotations": { + "description": "Keeps track of changes in the application runtime image" + } + } + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-build", + "annotations": { + "description": "Keeps track of changes in the application builder image" + } + } + }, + { + "kind": "BuildConfig", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-build", + "annotations": { + "description": "Defines how to build the application" + } + }, + "spec": { + "source": { + "type": "Git", + "git": { + "uri": "${SOURCE_REPOSITORY_URL}", + "ref": "${SOURCE_REPOSITORY_REF}" + }, + "contextDir": "${CONTEXT_DIR}" + }, + "strategy": { + "type": "Source", + "sourceStrategy": { + "from": { + "kind": "ImageStreamTag", + "namespace": "${NAMESPACE}", + "name": "${DOTNET_BUILD_IMAGE_STREAM_TAG}" + }, + "env": [ + { + "name": "DOTNET_STARTUP_PROJECT", + "value": "${DOTNET_STARTUP_PROJECT}" + }, + { + "name": "DOTNET_ASSEMBLY_NAME", + "value": "${DOTNET_ASSEMBLY_NAME}" + }, + { + "name": "DOTNET_NPM_TOOLS", + "value": "${DOTNET_NPM_TOOLS}" + }, + { + "name": "DOTNET_TEST_PROJECTS", + "value": "${DOTNET_TEST_PROJECTS}" + }, + { + "name": "DOTNET_CONFIGURATION", + "value": "${DOTNET_CONFIGURATION}" + }, + { + "name": "DOTNET_RESTORE_SOURCES", + "value": "${DOTNET_RESTORE_SOURCES}" + }, + { + "name": "DOTNET_PACK", + "value": "true" + } + ] + } + }, + "output": { + "to": { + "kind": "ImageStreamTag", + "name": "${NAME}-build:latest" + } + }, + "triggers": [ + { + "type": "ImageChange" + }, + { + "type": "ConfigChange" + }, + { + "type": "GitHub", + "github": { + "secret": "${GITHUB_WEBHOOK_SECRET}" + } + }, + { + "type": "Generic", + "generic": { + "secret": "${GENERIC_WEBHOOK_SECRET}" + } + } + ] + } + }, + { + "kind": "BuildConfig", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-runtime", + "annotations": { + "description": "Defines how to chain the runtime image from the source build image" + } + }, + "spec": { + "source": { + "dockerfile": "FROM ${DOTNET_RUNTIME_IMAGE_STREAM_TAG}\nADD app.tar.gz .", + "images": [ + { + "from": { + "kind": "ImageStreamTag", + "name": "${NAME}-build:latest" + }, + "paths": [ + { + "sourcePath": "/opt/app-root/app.tar.gz", + "destinationDir": "." + } + ] + } + ] + }, + "strategy": { + "type": "Docker", + "dockerStrategy": { + "from": { + "kind": "ImageStreamTag", + "namespace": "${NAMESPACE}", + "name": "${DOTNET_RUNTIME_IMAGE_STREAM_TAG}" + } + } + }, + "output": { + "to": { + "kind": "ImageStreamTag", + "name": "${NAME}:latest" + } + }, + "triggers": [ + { + "type": "ImageChange" + }, + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChange": { + "from": { + "kind": "ImageStreamTag", + "name": "${NAME}-build:latest" + } + } + } + ] + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-runtime", + "annotations": { + "description": "Defines how to deploy the application server" + } + }, + "spec": { + "strategy": { + "type": "Rolling" + }, + "triggers": [ + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "dotnet-runtime-app" + ], + "from": { + "kind": "ImageStreamTag", + "name": "${NAME}:latest" + } + } + }, + { + "type": "ConfigChange" + } + ], + "replicas": 1, + "selector": { + "name": "${NAME}" + }, + "template": { + "metadata": { + "name": "${NAME}", + "labels": { + "name": "${NAME}" + } + }, + "spec": { + "containers": [ + { + "name": "dotnet-runtime-app", + "image": " ", + "ports": [ + { + "containerPort": 8080 + } + ], + "livenessProbe": { + "httpGet": { + "path": "/", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 40, + "timeoutSeconds": 15 + }, + "readinessProbe": { + "httpGet": { + "path": "/", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 10, + "timeoutSeconds": 30 + }, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, + "env": [] + } + ] + } + } + } + } + ], + "parameters": [ + { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "dotnet-runtime-example" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "required": true, + "value": "512Mi" + }, + { + "name": "DOTNET_RUNTIME_IMAGE_STREAM_TAG", + "displayName": ".NET Runtime Imagestream Tag", + "description": "The image stream tag which is used to run the application.", + "required": true, + "value": "dotnet-runtime:2.0" + }, + { + "name": "DOTNET_BUILD_IMAGE_STREAM_TAG", + "displayName": ".NET builder", + "required": true, + "description": "The image stream tag which is used to build the application.", + "value": "dotnet:2.0" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "required": true, + "value": "openshift" + }, + { + "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", + "description": "The exposed hostname that will route to the .NET Core service, if left blank a value will be defaulted.", + "value": "" + }, + { + "name": "SOURCE_REPOSITORY_URL", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", + "required": true, + "value": "https://github.com/redhat-developer/s2i-dotnetcore-ex.git" + }, + { + "name": "SOURCE_REPOSITORY_REF", + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch.", + "value": "dotnetcore-2.0" + }, + { + "name": "CONTEXT_DIR", + "displayName": "Context Directory", + "description": "Set this to use a subdirectory of the source code repository" + }, + { + "name": "GITHUB_WEBHOOK_SECRET", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", + "generate": "expression", + "from": "[a-zA-Z0-9]{40}" + }, + { + "name": "GENERIC_WEBHOOK_SECRET", + "displayName": "Generic Webhook Secret", + "description": "A secret string used to configure the Generic webhook.", + "generate": "expression", + "from": "[a-zA-Z0-9]{40}" + }, + { + "name": "DOTNET_STARTUP_PROJECT", + "displayName": "Startup Project", + "description": "Set this to the folder containing your startup project.", + "value": "app" + }, + { + "name": "DOTNET_ASSEMBLY_NAME", + "displayName": "Startup Assembly", + "description": "Set this when the assembly name is overridden in the project file." + }, + { + "name": "DOTNET_NPM_TOOLS", + "displayName": "Npm Tools", + "description": "Set this to a space separated list of npm tools needed to publish.", + "value": "bower gulp" + }, + { + "name": "DOTNET_TEST_PROJECTS", + "displayName": "Test projects", + "description": "Set this to a space separated list of test projects to run before publishing." + }, + { + "name": "DOTNET_CONFIGURATION", + "displayName": "Configuration", + "description": "Set this to configuration (Release/Debug).", + "value": "Release" + }, + { + "name": "DOTNET_RESTORE_SOURCES", + "displayName": "NuGet package sources", + "description": "Set this to override the NuGet.config sources." + } + ] +} diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/httpd.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/httpd.json index ac671cc06..6cf9d76eb 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/httpd.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/httpd.json @@ -74,7 +74,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -130,7 +131,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/jenkins-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/jenkins-ephemeral-template.json index ce96684a9..62f43bc0b 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/jenkins-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/jenkins-ephemeral-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "jenkins-ephemeral", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "Jenkins (Ephemeral)", "description": "Jenkins service, without persistent storage.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing.", @@ -22,7 +21,6 @@ "apiVersion": "v1", "metadata": { "name": "${JENKINS_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "http://{.spec.host}{.spec.path}" } @@ -43,7 +41,9 @@ "apiVersion": "v1", "metadata": { "name": "${JENKINS_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -75,7 +75,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${JENKINS_SERVICE_NAME}" } @@ -221,8 +220,7 @@ "annotations": { "service.alpha.openshift.io/dependencies": "[{\"name\": \"${JNLP_SERVICE_NAME}\", \"namespace\": \"\", \"kind\": \"Service\"}]", "service.openshift.io/infrastructure": "true" - }, - "creationTimestamp": null + } }, "spec": { "ports": [ diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/jenkins-persistent-template.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/jenkins-persistent-template.json index 34b2b920b..e9068e455 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/jenkins-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/jenkins-persistent-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "jenkins-persistent", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "Jenkins (Persistent)", "description": "Jenkins service, with persistent storage.\n\nNOTE: You must have persistent volumes available in your cluster to use this template.", @@ -22,7 +21,6 @@ "apiVersion": "v1", "metadata": { "name": "${JENKINS_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "http://{.spec.host}{.spec.path}" } @@ -60,7 +58,9 @@ "apiVersion": "v1", "metadata": { "name": "${JENKINS_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -92,7 +92,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${JENKINS_SERVICE_NAME}" } @@ -238,8 +237,7 @@ "annotations": { "service.alpha.openshift.io/dependencies": "[{\"name\": \"${JNLP_SERVICE_NAME}\", \"namespace\": \"\", \"kind\": \"Service\"}]", "service.openshift.io/infrastructure": "true" - }, - "creationTimestamp": null + } }, "spec": { "ports": [ diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/nodejs-mongodb-persistent.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/nodejs-mongodb-persistent.json index a9c365361..df3704b9f 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/nodejs-mongodb-persistent.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/nodejs-mongodb-persistent.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -152,7 +153,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/nodejs-mongodb.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/nodejs-mongodb.json index 53a6147d5..eb6ab33d9 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/nodejs-mongodb.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/nodejs-mongodb.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -152,7 +153,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/rails-postgresql-persistent.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/rails-postgresql-persistent.json index f07a43071..59e2e41ea 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/rails-postgresql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/rails-postgresql-persistent.json @@ -93,7 +93,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -152,7 +153,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/rails-postgresql.json b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/rails-postgresql.json index a7992c988..b3d080a91 100644 --- a/roles/openshift_examples/files/examples/v3.6/quickstart-templates/rails-postgresql.json +++ b/roles/openshift_examples/files/examples/v3.6/quickstart-templates/rails-postgresql.json @@ -93,7 +93,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -152,7 +153,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/mariadb-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/mariadb-ephemeral-template.json index 536f7275e..6500ed0d3 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/mariadb-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/mariadb-ephemeral-template.json @@ -27,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-root_password": "{.data['database-root-password']}" + "template.openshift.io/expose-root_password": "{.data['database-root-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MYSQL_USER}", "database-password" : "${MYSQL_PASSWORD}", - "database-root-password" : "${MYSQL_ROOT_PASSWORD}" + "database-root-password" : "${MYSQL_ROOT_PASSWORD}", + "database-name" : "${MYSQL_DATABASE}" } }, { @@ -61,7 +63,10 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "${DATABASE_SERVICE_NAME}" + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -151,7 +156,12 @@ }, { "name": "MYSQL_DATABASE", - "value": "${MYSQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/mariadb-persistent-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/mariadb-persistent-template.json index 3b7fdccce..4378fa4a0 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/mariadb-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/mariadb-persistent-template.json @@ -27,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-root_password": "{.data['database-root-password']}" + "template.openshift.io/expose-root_password": "{.data['database-root-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MYSQL_USER}", "database-password" : "${MYSQL_PASSWORD}", - "database-root-password" : "${MYSQL_ROOT_PASSWORD}" + "database-root-password" : "${MYSQL_ROOT_PASSWORD}", + "database-name" : "${MYSQL_DATABASE}" } }, { @@ -78,7 +80,10 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "${DATABASE_SERVICE_NAME}" + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -168,7 +173,12 @@ }, { "name": "MYSQL_DATABASE", - "value": "${MYSQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/mongodb-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/mongodb-ephemeral-template.json index ee274194f..7271a2c69 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/mongodb-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/mongodb-ephemeral-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "mongodb-ephemeral", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "MongoDB (Ephemeral)", "description": "MongoDB database service, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mongodb-container/blob/master/3.2/README.md.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", @@ -28,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-admin_password": "{.data['database-admin-password']}" + "template.openshift.io/expose-admin_password": "{.data['database-admin-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MONGODB_USER}", "database-password" : "${MONGODB_PASSWORD}", - "database-admin-password" : "${MONGODB_ADMIN_PASSWORD}" + "database-admin-password" : "${MONGODB_ADMIN_PASSWORD}", + "database-name" : "${MONGODB_DATABASE}" } }, { @@ -42,7 +43,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "mongodb://{.spec.clusterIP}:{.spec.ports[?(.name==\"mongo\")].port}" } @@ -72,7 +72,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -104,7 +106,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -164,7 +165,12 @@ }, { "name": "MONGODB_DATABASE", - "value": "${MONGODB_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/mongodb-persistent-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/mongodb-persistent-template.json index e5ba43669..d70d2263f 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/mongodb-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/mongodb-persistent-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "mongodb-persistent", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "MongoDB (Persistent)", "description": "MongoDB database service, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mongodb-container/blob/master/3.2/README.md.\n\nNOTE: Scaling to more than one replica is not supported. You must have persistent volumes available in your cluster to use this template.", @@ -28,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-admin_password": "{.data['database-admin-password']}" + "template.openshift.io/expose-admin_password": "{.data['database-admin-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MONGODB_USER}", "database-password" : "${MONGODB_PASSWORD}", - "database-admin-password" : "${MONGODB_ADMIN_PASSWORD}" + "database-admin-password" : "${MONGODB_ADMIN_PASSWORD}", + "database-name" : "${MONGODB_DATABASE}" } }, { @@ -42,7 +43,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "mongodb://{.spec.clusterIP}:{.spec.ports[?(.name==\"mongo\")].port}" } @@ -89,7 +89,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -121,7 +123,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -181,7 +182,12 @@ }, { "name": "MONGODB_DATABASE", - "value": "${MONGODB_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/mysql-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/mysql-ephemeral-template.json index 969e62ac5..54785993c 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/mysql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/mysql-ephemeral-template.json @@ -19,7 +19,7 @@ "template": "mysql-ephemeral-template" }, "objects": [ - { + { "kind": "Secret", "apiVersion": "v1", "metadata": { @@ -27,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-root_password": "{.data['database-root-password']}" + "template.openshift.io/expose-root_password": "{.data['database-root-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MYSQL_USER}", "database-password" : "${MYSQL_PASSWORD}", - "database-root-password" : "${MYSQL_ROOT_PASSWORD}" + "database-root-password" : "${MYSQL_ROOT_PASSWORD}", + "database-name" : "${MYSQL_DATABASE}" } }, { @@ -41,7 +43,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "mysql://{.spec.clusterIP}:{.spec.ports[?(.name==\"mysql\")].port}" } @@ -71,7 +72,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -103,7 +106,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -164,7 +166,12 @@ }, { "name": "MYSQL_DATABASE", - "value": "${MYSQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/mysql-persistent-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/mysql-persistent-template.json index 4f39d41a5..2bd84b106 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/mysql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/mysql-persistent-template.json @@ -27,13 +27,15 @@ "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", "template.openshift.io/expose-password": "{.data['database-password']}", - "template.openshift.io/expose-root_password": "{.data['database-root-password']}" + "template.openshift.io/expose-root_password": "{.data['database-root-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${MYSQL_USER}", "database-password" : "${MYSQL_PASSWORD}", - "database-root-password" : "${MYSQL_ROOT_PASSWORD}" + "database-root-password" : "${MYSQL_ROOT_PASSWORD}", + "database-name" : "${MYSQL_DATABASE}" } }, { @@ -78,7 +80,10 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "${DATABASE_SERVICE_NAME}" + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -168,7 +173,12 @@ }, { "name": "MYSQL_DATABASE", - "value": "${MYSQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/postgresql-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/postgresql-ephemeral-template.json index c37102cb0..849c9d83f 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/postgresql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/postgresql-ephemeral-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "postgresql-ephemeral", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "PostgreSQL (Ephemeral)", "description": "PostgreSQL database service, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/postgresql-container/blob/master/9.5.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", @@ -27,12 +26,14 @@ "name": "${DATABASE_SERVICE_NAME}", "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", - "template.openshift.io/expose-password": "{.data['database-password']}" + "template.openshift.io/expose-password": "{.data['database-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${POSTGRESQL_USER}", - "database-password" : "${POSTGRESQL_PASSWORD}" + "database-password" : "${POSTGRESQL_PASSWORD}", + "database-name" : "${POSTGRESQL_DATABASE}" } }, { @@ -40,7 +41,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "postgres://{.spec.clusterIP}:{.spec.ports[?(.name==\"postgresql\")].port}" } @@ -70,7 +70,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -102,7 +104,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -153,7 +154,12 @@ }, { "name": "POSTGRESQL_DATABASE", - "value": "${POSTGRESQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/postgresql-persistent-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/postgresql-persistent-template.json index 32dc93a95..b622baa01 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/postgresql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/postgresql-persistent-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "postgresql-persistent", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "PostgreSQL (Persistent)", "description": "PostgreSQL database service, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/postgresql-container/blob/master/9.5.\n\nNOTE: Scaling to more than one replica is not supported. You must have persistent volumes available in your cluster to use this template.", @@ -27,12 +26,14 @@ "name": "${DATABASE_SERVICE_NAME}", "annotations": { "template.openshift.io/expose-username": "{.data['database-user']}", - "template.openshift.io/expose-password": "{.data['database-password']}" + "template.openshift.io/expose-password": "{.data['database-password']}", + "template.openshift.io/expose-database_name": "{.data['database-name']}" } }, "stringData" : { "database-user" : "${POSTGRESQL_USER}", - "database-password" : "${POSTGRESQL_PASSWORD}" + "database-password" : "${POSTGRESQL_PASSWORD}", + "database-name" : "${POSTGRESQL_DATABASE}" } }, { @@ -40,7 +41,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "postgres://{.spec.clusterIP}:{.spec.ports[?(.name==\"postgresql\")].port}" } @@ -87,7 +87,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -119,7 +121,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } @@ -170,7 +171,12 @@ }, { "name": "POSTGRESQL_DATABASE", - "value": "${POSTGRESQL_DATABASE}" + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-name" + } + } } ], "resources": { diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/redis-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/redis-ephemeral-template.json index 6bb683e52..15bdd079b 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/redis-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/redis-ephemeral-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "redis-ephemeral", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "Redis (Ephemeral)", "description": "Redis in-memory data structure store, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/3.2.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", @@ -38,7 +37,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" } @@ -68,7 +66,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -100,7 +100,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } diff --git a/roles/openshift_examples/files/examples/v3.7/db-templates/redis-persistent-template.json b/roles/openshift_examples/files/examples/v3.7/db-templates/redis-persistent-template.json index 9e8be2309..1e31b02e0 100644 --- a/roles/openshift_examples/files/examples/v3.7/db-templates/redis-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.7/db-templates/redis-persistent-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "redis-persistent", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "Redis (Persistent)", "description": "Redis in-memory data structure store, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/3.2.\n\nNOTE: You must have persistent volumes available in your cluster to use this template.", @@ -38,7 +37,6 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" } @@ -85,7 +83,9 @@ "apiVersion": "v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -117,7 +117,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${DATABASE_SERVICE_NAME}" } diff --git a/roles/openshift_examples/files/examples/v3.7/image-streams/OWNERS b/roles/openshift_examples/files/examples/v3.7/image-streams/OWNERS index 4ccb64c74..6ddf77f12 100644 --- a/roles/openshift_examples/files/examples/v3.7/image-streams/OWNERS +++ b/roles/openshift_examples/files/examples/v3.7/image-streams/OWNERS @@ -1,7 +1,6 @@ reviewers: - bparees - sspeiche - - oatmealraisin - mfojtik - liggitt - jcantrill diff --git a/roles/openshift_examples/files/examples/v3.7/image-streams/dotnet_imagestreams.json b/roles/openshift_examples/files/examples/v3.7/image-streams/dotnet_imagestreams.json index 857ffa980..ee753966f 100644 --- a/roles/openshift_examples/files/examples/v3.7/image-streams/dotnet_imagestreams.json +++ b/roles/openshift_examples/files/examples/v3.7/image-streams/dotnet_imagestreams.json @@ -14,7 +14,7 @@ "metadata": { "name": "dotnet", "annotations": { - "openshift.io/display-name": ".NET Core" + "openshift.io/display-name": ".NET Core Builder Images" } }, "spec": { @@ -23,17 +23,35 @@ "name": "latest", "annotations": { "openshift.io/display-name": ".NET Core (Latest)", - "description": "Build and run .NET Core applications on RHEL 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/1.1/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of .NET Core available on OpenShift, including major versions updates.", + "description": "Build and run .NET Core applications on RHEL 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/2.0/build/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of .NET Core available on OpenShift, including major versions updates.", "iconClass": "icon-dotnet", "tags": "builder,.net,dotnet,dotnetcore", "supports":"dotnet", "sampleRepo": "https://github.com/redhat-developer/s2i-dotnetcore-ex.git", "sampleContextDir": "app", - "sampleRef": "dotnetcore-1.1" + "sampleRef": "dotnetcore-2.0" }, "from": { "kind": "ImageStreamTag", - "name": "1.1" + "name": "2.0" + } + }, + { + "name": "2.0", + "annotations": { + "openshift.io/display-name": ".NET Core 2.0", + "description": "Build and run .NET Core 2.0 applications on RHEL 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/2.0/build/README.md.", + "iconClass": "icon-dotnet", + "tags": "builder,.net,dotnet,dotnetcore,rh-dotnet20", + "supports":"dotnet:2.0,dotnet", + "sampleRepo": "https://github.com/redhat-developer/s2i-dotnetcore-ex.git", + "sampleContextDir": "app", + "sampleRef": "dotnetcore-2.0", + "version": "2.0" + }, + "from": { + "kind": "DockerImage", + "name": "registry.access.redhat.com/dotnet/dotnet-20-rhel7:2.0" } }, { @@ -74,6 +92,49 @@ } ] } + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "dotnet-runtime", + "annotations": { + "openshift.io/display-name": ".NET Core Runtime Images" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": { + "openshift.io/display-name": ".NET Core Runtime (Latest)", + "description": "Run .NET Core applications on RHEL 7. For more information about using this image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/2.0/runtime/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of .NET Core Runtime available on OpenShift, including major versions updates.", + "iconClass": "icon-dotnet", + "tags": "runtime,.net-runtime,dotnet-runtime,dotnetcore-runtime", + "supports":"dotnet-runtime" + }, + "from": { + "kind": "ImageStreamTag", + "name": "2.0" + } + }, + { + "name": "2.0", + "annotations": { + "openshift.io/display-name": ".NET Core 2.0 Runtime", + "description": "Run .NET Core applications on RHEL 7. For more information about using this image, including OpenShift considerations, see https://github.com/redhat-developer/s2i-dotnetcore/tree/master/2.0/runtime/README.md.", + "iconClass": "icon-dotnet", + "tags": "runtime,.net-runtime,dotnet-runtime,dotnetcore-runtime", + "supports":"dotnet-runtime", + "version": "2.0" + }, + "from": { + "kind": "DockerImage", + "name": "registry.access.redhat.com/dotnet/dotnet-20-runtime-rhel7:2.0" + } + } + ] + } } ] } diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/cakephp-mysql-persistent.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/cakephp-mysql-persistent.json index 6d987ee33..289f809fa 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/cakephp-mysql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/cakephp-mysql-persistent.json @@ -89,7 +89,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -148,7 +149,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/cakephp-mysql.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/cakephp-mysql.json index fb2ef206e..0562982b3 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/cakephp-mysql.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/cakephp-mysql.json @@ -89,7 +89,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -148,7 +149,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dancer-mysql-persistent.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dancer-mysql-persistent.json index 7ffb25e14..7a3875d09 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dancer-mysql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dancer-mysql-persistent.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -146,7 +147,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dancer-mysql.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dancer-mysql.json index d787e376b..399ec72a8 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dancer-mysql.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dancer-mysql.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -146,7 +147,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/django-postgresql-persistent.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/django-postgresql-persistent.json index a2070207b..e37f7a492 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/django-postgresql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/django-postgresql-persistent.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -146,7 +147,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/django-postgresql.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/django-postgresql.json index 0d33c6e0e..965c2ebfe 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/django-postgresql.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/django-postgresql.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -146,7 +147,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-example.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-example.json index af46579c8..f1fef3093 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-example.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-example.json @@ -249,7 +249,7 @@ "displayName": ".NET builder", "required": true, "description": "The image stream tag which is used to build the code.", - "value": "dotnet:1.0" + "value": "dotnet:2.0" }, { "name": "NAMESPACE", @@ -269,7 +269,7 @@ "name": "SOURCE_REPOSITORY_REF", "displayName": "Git Reference", "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch.", - "value": "dotnetcore-1.0" + "value": "dotnetcore-2.0" }, { "name": "CONTEXT_DIR", @@ -299,7 +299,7 @@ { "name": "DOTNET_STARTUP_PROJECT", "displayName": "Startup Project", - "description": "Set this to the folder containing your startup project.", + "description": "Set this to a project file (e.g. csproj) or a folder containing a single project file.", "value": "app" }, { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-pgsql-persistent.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-pgsql-persistent.json index a2b59c2d3..c83132152 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-pgsql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-pgsql-persistent.json @@ -455,7 +455,7 @@ "displayName": ".NET builder", "required": true, "description": "The image stream tag which is used to build the code.", - "value": "dotnet:1.1" + "value": "dotnet:2.0" }, { "name": "NAMESPACE", @@ -475,7 +475,7 @@ "name": "SOURCE_REPOSITORY_REF", "displayName": "Git Reference", "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch.", - "value": "rel/1.1-example" + "value": "rel/2.0-example" }, { "name": "CONTEXT_DIR", @@ -485,7 +485,7 @@ { "name": "DOTNET_STARTUP_PROJECT", "displayName": "Startup Project", - "description": "Set this to the folder containing your startup project.", + "description": "Set this to a project file (e.g. csproj) or a folder containing a single project file.", "value": "samples/MusicStore" }, { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-runtime-example.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-runtime-example.json new file mode 100644 index 000000000..e1dccf290 --- /dev/null +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/dotnet-runtime-example.json @@ -0,0 +1,412 @@ +{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "name": "dotnet-runtime-example", + "annotations": { + "openshift.io/display-name": ".NET Core Runtime Example", + "description": "An example .NET Core Runtime example application.", + "tags": "quickstart,dotnet,.net", + "iconClass": "icon-dotnet", + "template.openshift.io/provider-display-name": "Red Hat, Inc.", + "template.openshift.io/documentation-url": "https://github.com/redhat-developer/s2i-dotnetcore", + "template.openshift.io/support-url": "https://access.redhat.com" + } + }, + "objects": [ + { + "kind": "Route", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}" + }, + "spec": { + "host": "${APPLICATION_DOMAIN}", + "to": { + "kind": "Service", + "name": "${NAME}" + } + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}", + "annotations": { + "description": "Exposes and load balances the application pods" + } + }, + "spec": { + "ports": [ + { + "name": "web", + "port": 8080, + "targetPort": 8080 + } + ], + "selector": { + "name": "${NAME}" + } + } + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}", + "annotations": { + "description": "Keeps track of changes in the application runtime image" + } + } + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-build", + "annotations": { + "description": "Keeps track of changes in the application builder image" + } + } + }, + { + "kind": "BuildConfig", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-build", + "annotations": { + "description": "Defines how to build the application" + } + }, + "spec": { + "source": { + "type": "Git", + "git": { + "uri": "${SOURCE_REPOSITORY_URL}", + "ref": "${SOURCE_REPOSITORY_REF}" + }, + "contextDir": "${CONTEXT_DIR}" + }, + "strategy": { + "type": "Source", + "sourceStrategy": { + "from": { + "kind": "ImageStreamTag", + "namespace": "${NAMESPACE}", + "name": "${DOTNET_BUILD_IMAGE_STREAM_TAG}" + }, + "env": [ + { + "name": "DOTNET_STARTUP_PROJECT", + "value": "${DOTNET_STARTUP_PROJECT}" + }, + { + "name": "DOTNET_ASSEMBLY_NAME", + "value": "${DOTNET_ASSEMBLY_NAME}" + }, + { + "name": "DOTNET_NPM_TOOLS", + "value": "${DOTNET_NPM_TOOLS}" + }, + { + "name": "DOTNET_TEST_PROJECTS", + "value": "${DOTNET_TEST_PROJECTS}" + }, + { + "name": "DOTNET_CONFIGURATION", + "value": "${DOTNET_CONFIGURATION}" + }, + { + "name": "DOTNET_RESTORE_SOURCES", + "value": "${DOTNET_RESTORE_SOURCES}" + }, + { + "name": "DOTNET_PACK", + "value": "true" + } + ] + } + }, + "output": { + "to": { + "kind": "ImageStreamTag", + "name": "${NAME}-build:latest" + } + }, + "triggers": [ + { + "type": "ImageChange" + }, + { + "type": "ConfigChange" + }, + { + "type": "GitHub", + "github": { + "secret": "${GITHUB_WEBHOOK_SECRET}" + } + }, + { + "type": "Generic", + "generic": { + "secret": "${GENERIC_WEBHOOK_SECRET}" + } + } + ] + } + }, + { + "kind": "BuildConfig", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-runtime", + "annotations": { + "description": "Defines how to chain the runtime image from the source build image" + } + }, + "spec": { + "source": { + "dockerfile": "FROM ${DOTNET_RUNTIME_IMAGE_STREAM_TAG}\nADD app.tar.gz .", + "images": [ + { + "from": { + "kind": "ImageStreamTag", + "name": "${NAME}-build:latest" + }, + "paths": [ + { + "sourcePath": "/opt/app-root/app.tar.gz", + "destinationDir": "." + } + ] + } + ] + }, + "strategy": { + "type": "Docker", + "dockerStrategy": { + "from": { + "kind": "ImageStreamTag", + "namespace": "${NAMESPACE}", + "name": "${DOTNET_RUNTIME_IMAGE_STREAM_TAG}" + } + } + }, + "output": { + "to": { + "kind": "ImageStreamTag", + "name": "${NAME}:latest" + } + }, + "triggers": [ + { + "type": "ImageChange" + }, + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChange": { + "from": { + "kind": "ImageStreamTag", + "name": "${NAME}-build:latest" + } + } + } + ] + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-runtime", + "annotations": { + "description": "Defines how to deploy the application server" + } + }, + "spec": { + "strategy": { + "type": "Rolling" + }, + "triggers": [ + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "dotnet-runtime-app" + ], + "from": { + "kind": "ImageStreamTag", + "name": "${NAME}:latest" + } + } + }, + { + "type": "ConfigChange" + } + ], + "replicas": 1, + "selector": { + "name": "${NAME}" + }, + "template": { + "metadata": { + "name": "${NAME}", + "labels": { + "name": "${NAME}" + } + }, + "spec": { + "containers": [ + { + "name": "dotnet-runtime-app", + "image": " ", + "ports": [ + { + "containerPort": 8080 + } + ], + "livenessProbe": { + "httpGet": { + "path": "/", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 40, + "timeoutSeconds": 15 + }, + "readinessProbe": { + "httpGet": { + "path": "/", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 10, + "timeoutSeconds": 30 + }, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, + "env": [] + } + ] + } + } + } + } + ], + "parameters": [ + { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "dotnet-runtime-example" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "required": true, + "value": "512Mi" + }, + { + "name": "DOTNET_RUNTIME_IMAGE_STREAM_TAG", + "displayName": ".NET Runtime Imagestream Tag", + "description": "The image stream tag which is used to run the application.", + "required": true, + "value": "dotnet-runtime:2.0" + }, + { + "name": "DOTNET_BUILD_IMAGE_STREAM_TAG", + "displayName": ".NET builder", + "required": true, + "description": "The image stream tag which is used to build the application.", + "value": "dotnet:2.0" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "required": true, + "value": "openshift" + }, + { + "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", + "description": "The exposed hostname that will route to the .NET Core service, if left blank a value will be defaulted.", + "value": "" + }, + { + "name": "SOURCE_REPOSITORY_URL", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", + "required": true, + "value": "https://github.com/redhat-developer/s2i-dotnetcore-ex.git" + }, + { + "name": "SOURCE_REPOSITORY_REF", + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch.", + "value": "dotnetcore-2.0" + }, + { + "name": "CONTEXT_DIR", + "displayName": "Context Directory", + "description": "Set this to use a subdirectory of the source code repository" + }, + { + "name": "GITHUB_WEBHOOK_SECRET", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", + "generate": "expression", + "from": "[a-zA-Z0-9]{40}" + }, + { + "name": "GENERIC_WEBHOOK_SECRET", + "displayName": "Generic Webhook Secret", + "description": "A secret string used to configure the Generic webhook.", + "generate": "expression", + "from": "[a-zA-Z0-9]{40}" + }, + { + "name": "DOTNET_STARTUP_PROJECT", + "displayName": "Startup Project", + "description": "Set this to the folder containing your startup project.", + "value": "app" + }, + { + "name": "DOTNET_ASSEMBLY_NAME", + "displayName": "Startup Assembly", + "description": "Set this when the assembly name is overridden in the project file." + }, + { + "name": "DOTNET_NPM_TOOLS", + "displayName": "Npm Tools", + "description": "Set this to a space separated list of npm tools needed to publish.", + "value": "bower gulp" + }, + { + "name": "DOTNET_TEST_PROJECTS", + "displayName": "Test projects", + "description": "Set this to a space separated list of test projects to run before publishing." + }, + { + "name": "DOTNET_CONFIGURATION", + "displayName": "Configuration", + "description": "Set this to configuration (Release/Debug).", + "value": "Release" + }, + { + "name": "DOTNET_RESTORE_SOURCES", + "displayName": "NuGet package sources", + "description": "Set this to override the NuGet.config sources." + } + ] +} diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/httpd.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/httpd.json index ac671cc06..6cf9d76eb 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/httpd.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/httpd.json @@ -74,7 +74,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -130,7 +131,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/jenkins-ephemeral-template.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/jenkins-ephemeral-template.json index ce96684a9..62f43bc0b 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/jenkins-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/jenkins-ephemeral-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "jenkins-ephemeral", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "Jenkins (Ephemeral)", "description": "Jenkins service, without persistent storage.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing.", @@ -22,7 +21,6 @@ "apiVersion": "v1", "metadata": { "name": "${JENKINS_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "http://{.spec.host}{.spec.path}" } @@ -43,7 +41,9 @@ "apiVersion": "v1", "metadata": { "name": "${JENKINS_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -75,7 +75,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${JENKINS_SERVICE_NAME}" } @@ -221,8 +220,7 @@ "annotations": { "service.alpha.openshift.io/dependencies": "[{\"name\": \"${JNLP_SERVICE_NAME}\", \"namespace\": \"\", \"kind\": \"Service\"}]", "service.openshift.io/infrastructure": "true" - }, - "creationTimestamp": null + } }, "spec": { "ports": [ diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/jenkins-persistent-template.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/jenkins-persistent-template.json index 34b2b920b..e9068e455 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/jenkins-persistent-template.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/jenkins-persistent-template.json @@ -3,7 +3,6 @@ "apiVersion": "v1", "metadata": { "name": "jenkins-persistent", - "creationTimestamp": null, "annotations": { "openshift.io/display-name": "Jenkins (Persistent)", "description": "Jenkins service, with persistent storage.\n\nNOTE: You must have persistent volumes available in your cluster to use this template.", @@ -22,7 +21,6 @@ "apiVersion": "v1", "metadata": { "name": "${JENKINS_SERVICE_NAME}", - "creationTimestamp": null, "annotations": { "template.openshift.io/expose-uri": "http://{.spec.host}{.spec.path}" } @@ -60,7 +58,9 @@ "apiVersion": "v1", "metadata": { "name": "${JENKINS_SERVICE_NAME}", - "creationTimestamp": null + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } }, "spec": { "strategy": { @@ -92,7 +92,6 @@ }, "template": { "metadata": { - "creationTimestamp": null, "labels": { "name": "${JENKINS_SERVICE_NAME}" } @@ -238,8 +237,7 @@ "annotations": { "service.alpha.openshift.io/dependencies": "[{\"name\": \"${JNLP_SERVICE_NAME}\", \"namespace\": \"\", \"kind\": \"Service\"}]", "service.openshift.io/infrastructure": "true" - }, - "creationTimestamp": null + } }, "spec": { "ports": [ diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/nodejs-mongodb-persistent.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/nodejs-mongodb-persistent.json index a9c365361..df3704b9f 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/nodejs-mongodb-persistent.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/nodejs-mongodb-persistent.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -152,7 +153,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/nodejs-mongodb.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/nodejs-mongodb.json index 53a6147d5..eb6ab33d9 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/nodejs-mongodb.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/nodejs-mongodb.json @@ -87,7 +87,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -152,7 +153,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/rails-postgresql-persistent.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/rails-postgresql-persistent.json index f07a43071..59e2e41ea 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/rails-postgresql-persistent.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/rails-postgresql-persistent.json @@ -93,7 +93,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -152,7 +153,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/rails-postgresql.json b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/rails-postgresql.json index a7992c988..b3d080a91 100644 --- a/roles/openshift_examples/files/examples/v3.7/quickstart-templates/rails-postgresql.json +++ b/roles/openshift_examples/files/examples/v3.7/quickstart-templates/rails-postgresql.json @@ -93,7 +93,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to build the application" + "description": "Defines how to build the application", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { @@ -152,7 +153,8 @@ "metadata": { "name": "${NAME}", "annotations": { - "description": "Defines how to deploy the application server" + "description": "Defines how to deploy the application server", + "template.alpha.openshift.io/wait-for-ready": "true" } }, "spec": { diff --git a/roles/openshift_facts/library/openshift_facts.py b/roles/openshift_facts/library/openshift_facts.py index 844d77255..251d1dfb4 100755 --- a/roles/openshift_facts/library/openshift_facts.py +++ b/roles/openshift_facts/library/openshift_facts.py @@ -193,7 +193,9 @@ def hostname_valid(hostname): """ if (not hostname or hostname.startswith('localhost') or - hostname.endswith('localdomain')): + hostname.endswith('localdomain') or + # OpenShift will not allow a node with more than 63 chars in name. + len(hostname) > 63): return False return True diff --git a/roles/openshift_health_checker/action_plugins/openshift_health_check.py b/roles/openshift_health_checker/action_plugins/openshift_health_check.py index 05e53333d..8d35db6b5 100644 --- a/roles/openshift_health_checker/action_plugins/openshift_health_check.py +++ b/roles/openshift_health_checker/action_plugins/openshift_health_check.py @@ -1,76 +1,74 @@ """ Ansible action plugin to execute health checks in OpenShift clusters. """ -# pylint: disable=wrong-import-position,missing-docstring,invalid-name import sys import os +import traceback from collections import defaultdict +from ansible.plugins.action import ActionBase +from ansible.module_utils.six import string_types + try: from __main__ import display except ImportError: + # pylint: disable=ungrouped-imports; this is the standard way how to import + # the default display object in Ansible action plugins. from ansible.utils.display import Display display = Display() -from ansible.plugins.action import ActionBase -from ansible.module_utils.six import string_types - # 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__))) +# pylint: disable=wrong-import-position; the import statement must come after +# the manipulation of sys.path. from openshift_checks import OpenShiftCheck, OpenShiftCheckException, load_checks # noqa: E402 class ActionModule(ActionBase): + """Action plugin to execute health checks.""" def run(self, tmp=None, task_vars=None): result = super(ActionModule, self).run(tmp, task_vars) task_vars = task_vars or {} - # vars are not supportably available in the callback plugin, - # so record any it will need in the result. + # callback plugins cannot read Ansible vars, but we would like + # zz_failure_summary to have access to certain values. We do so by + # storing the information we need in the result. result['playbook_context'] = task_vars.get('r_openshift_health_checker_playbook_context') - 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(tmp, task_vars) args = self._task.args requested_checks = normalize(args.get('checks', [])) + + if not requested_checks: + result['failed'] = True + result['msg'] = list_known_checks(known_checks) + return result + resolved_checks = resolve_checks(requested_checks, known_checks.values()) - except OpenShiftCheckException as e: + except OpenShiftCheckException as exc: result["failed"] = True - result["msg"] = str(e) + result["msg"] = str(exc) + return result + + if "openshift" not in task_vars: + result["failed"] = True + result["msg"] = "'openshift' is undefined, did 'openshift_facts' run?" return result result["checks"] = check_results = {} user_disabled_checks = normalize(task_vars.get('openshift_disable_check', [])) - for check_name in resolved_checks: - display.banner("CHECK [{} : {}]".format(check_name, task_vars["ansible_host"])) - check = known_checks[check_name] - - if not check.is_active(): - r = dict(skipped=True, skipped_reason="Not active for this host") - elif check_name in user_disabled_checks: - r = dict(skipped=True, skipped_reason="Disabled by user request") - else: - try: - r = check.run() - except OpenShiftCheckException as e: - r = dict( - failed=True, - msg=str(e), - ) - + for name in resolved_checks: + display.banner("CHECK [{} : {}]".format(name, task_vars["ansible_host"])) + check = known_checks[name] + check_results[name] = run_check(name, check, user_disabled_checks) if check.changed: - r["changed"] = True - check_results[check_name] = r + check_results[name]["changed"] = True result["changed"] = any(r.get("changed") for r in check_results.values()) if any(r.get("failed") for r in check_results.values()): @@ -80,22 +78,55 @@ class ActionModule(ActionBase): return result def load_known_checks(self, tmp, task_vars): + """Find all existing checks and return a mapping of names to instances.""" load_checks() known_checks = {} for cls in OpenShiftCheck.subclasses(): - check_name = cls.name - if check_name in known_checks: - other_cls = known_checks[check_name].__class__ + name = cls.name + if name in known_checks: + other_cls = known_checks[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(execute_module=self._execute_module, tmp=tmp, task_vars=task_vars) + "duplicate check name '{}' in: '{}' and '{}'" + "".format(name, full_class_name(cls), full_class_name(other_cls)) + ) + known_checks[name] = cls(execute_module=self._execute_module, tmp=tmp, task_vars=task_vars) return known_checks +def list_known_checks(known_checks): + """Return text listing the existing checks and tags.""" + # TODO: we could include a description of each check by taking it from a + # check class attribute (e.g., __doc__) when building the message below. + msg = ( + 'This playbook is meant to run health checks, but no checks were ' + 'requested. Set the `openshift_checks` variable to a comma-separated ' + 'list of check names or a YAML list. Available checks:\n {}' + ).format('\n '.join(sorted(known_checks))) + + tags = describe_tags(known_checks.values()) + + msg += ( + '\n\nTags can be used as a shortcut to select multiple ' + 'checks. Available tags and the checks they select:\n {}' + ).format('\n '.join(tags)) + + return msg + + +def describe_tags(check_classes): + """Return a sorted list of strings describing tags and the checks they include.""" + tag_checks = defaultdict(list) + for cls in check_classes: + for tag in cls.tags: + tag_checks[tag].append(cls.name) + tags = [ + '@{} = {}'.format(tag, ','.join(sorted(checks))) + for tag, checks in tag_checks.items() + ] + return sorted(tags) + + def resolve_checks(names, all_checks): """Returns a set of resolved check names. @@ -123,6 +154,12 @@ def resolve_checks(names, all_checks): if unknown_tag_names: msg.append('Unknown tag names: {}.'.format(', '.join(sorted(unknown_tag_names)))) msg.append('Make sure there is no typo in the playbook and no files are missing.') + # TODO: implement a "Did you mean ...?" when the input is similar to a + # valid check or tag. + msg.append('Known checks:') + msg.append(' {}'.format('\n '.join(sorted(known_check_names)))) + msg.append('Known tags:') + msg.append(' {}'.format('\n '.join(describe_tags(all_checks)))) raise OpenShiftCheckException('\n'.join(msg)) tag_to_checks = defaultdict(set) @@ -146,3 +183,32 @@ def normalize(checks): if isinstance(checks, string_types): checks = checks.split(',') return [name.strip() for name in checks if name.strip()] + + +def run_check(name, check, user_disabled_checks): + """Run a single check if enabled and return a result dict.""" + if name in user_disabled_checks: + return dict(skipped=True, skipped_reason="Disabled by user request") + + # pylint: disable=broad-except; capturing exceptions broadly is intentional, + # to isolate arbitrary failures in one check from others. + try: + is_active = check.is_active() + except Exception as exc: + reason = "Could not determine if check should be run, exception: {}".format(exc) + return dict(skipped=True, skipped_reason=reason, exception=traceback.format_exc()) + + if not is_active: + return dict(skipped=True, skipped_reason="Not active for this host") + + try: + return check.run() + except OpenShiftCheckException as exc: + return dict(failed=True, msg=str(exc)) + except Exception as exc: + return dict(failed=True, msg=str(exc), exception=traceback.format_exc()) + + +def full_class_name(cls): + """Return the name of a class prefixed with its module name.""" + return '{}.{}'.format(cls.__module__, cls.__name__) diff --git a/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py b/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py index d10200719..349655966 100644 --- a/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py +++ b/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py @@ -1,161 +1,223 @@ -""" -Ansible callback plugin to give a nicely formatted summary of failures. -""" +"""Ansible callback plugin to print a nicely formatted summary of failures. -# Reason: In several locations below we disable pylint protected-access -# for Ansible objects that do not give us any public way -# to access the full details we need to report check failures. -# Status: disabled permanently or until Ansible object has a public API. -# This does leave the code more likely to be broken by future Ansible changes. +The file / module name is prefixed with `zz_` to make this plugin be loaded last +by Ansible, thus making its output the last thing that users see. +""" -from pprint import pformat +from collections import defaultdict +import traceback from ansible.plugins.callback import CallbackBase from ansible import constants as C from ansible.utils.color import stringc +FAILED_NO_MSG = u'Failed without returning a message.' + + class CallbackModule(CallbackBase): - """ - This callback plugin stores task results and summarizes failures. - The file name is prefixed with `zz_` to make this plugin be loaded last by - Ansible, thus making its output the last thing that users see. - """ + """This callback plugin stores task results and summarizes failures.""" CALLBACK_VERSION = 2.0 CALLBACK_TYPE = 'aggregate' CALLBACK_NAME = 'failure_summary' CALLBACK_NEEDS_WHITELIST = False - _playbook_file = None def __init__(self): super(CallbackModule, self).__init__() self.__failures = [] + self.__playbook_file = '' def v2_playbook_on_start(self, playbook): super(CallbackModule, self).v2_playbook_on_start(playbook) - # re: playbook attrs see top comment # pylint: disable=protected-access - self._playbook_file = playbook._file_name + # pylint: disable=protected-access; Ansible gives us no public API to + # get the file name of the current playbook from a callback plugin. + self.__playbook_file = playbook._file_name def v2_runner_on_failed(self, result, ignore_errors=False): super(CallbackModule, self).v2_runner_on_failed(result, ignore_errors) if not ignore_errors: - self.__failures.append(dict(result=result, ignore_errors=ignore_errors)) + self.__failures.append(result) def v2_playbook_on_stats(self, stats): super(CallbackModule, self).v2_playbook_on_stats(stats) - if self.__failures: - self._print_failure_details(self.__failures) - - def _print_failure_details(self, failures): - """Print a summary of failed tasks or checks.""" - self._display.display(u'\nFailure summary:\n') - - width = len(str(len(failures))) - initial_indent_format = u' {{:>{width}}}. '.format(width=width) - initial_indent_len = len(initial_indent_format.format(0)) - subsequent_indent = u' ' * initial_indent_len - subsequent_extra_indent = u' ' * (initial_indent_len + 10) - - for i, failure in enumerate(failures, 1): - entries = _format_failure(failure) - self._display.display(u'\n{}{}'.format(initial_indent_format.format(i), entries[0])) - for entry in entries[1:]: - entry = entry.replace(u'\n', u'\n' + subsequent_extra_indent) - indented = u'{}{}'.format(subsequent_indent, entry) - self._display.display(indented) - - failed_checks = set() - playbook_context = None - # re: result attrs see top comment # pylint: disable=protected-access - for failure in failures: - # Get context from check task result since callback plugins cannot access task vars. - # NOTE: thus context is not known unless checks run. Failures prior to checks running - # don't have playbook_context in the results. But we only use it now when checks fail. - playbook_context = playbook_context or failure['result']._result.get('playbook_context') - failed_checks.update( - name - for name, result in failure['result']._result.get('checks', {}).items() - if result.get('failed') - ) - if failed_checks: - self._print_check_failure_summary(failed_checks, playbook_context) - - def _print_check_failure_summary(self, failed_checks, context): - checks = ','.join(sorted(failed_checks)) - # The purpose of specifying context is to vary the output depending on what the user was - # expecting to happen (based on which playbook they ran). The only use currently is to - # vary the message depending on whether the user was deliberately running checks or was - # trying to install/upgrade and checks are just included. Other use cases may arise. - summary = ( # default to explaining what checks are in the first place - '\n' - 'The execution of "{playbook}"\n' - 'includes checks designed to fail early if the requirements\n' - 'of the playbook are not met. One or more of these checks\n' - 'failed. To disregard these results, you may choose to\n' - 'disable failing checks by setting an Ansible variable:\n\n' - ' openshift_disable_check={checks}\n\n' - 'Failing check names are shown in the failure details above.\n' - 'Some checks may be configurable by variables if your requirements\n' - 'are different from the defaults; consult check documentation.\n' - 'Variables can be set in the inventory or passed on the\n' - 'command line using the -e flag to ansible-playbook.\n\n' - ).format(playbook=self._playbook_file, checks=checks) - if context in ['pre-install', 'health']: - summary = ( # user was expecting to run checks, less explanation needed - '\n' - 'You may choose to configure or disable failing checks by\n' - 'setting Ansible variables. To disable those above:\n\n' - ' openshift_disable_check={checks}\n\n' - 'Consult check documentation for configurable variables.\n' - 'Variables can be set in the inventory or passed on the\n' - 'command line using the -e flag to ansible-playbook.\n\n' - ).format(checks=checks) - self._display.display(summary) - - -# re: result attrs see top comment # pylint: disable=protected-access -def _format_failure(failure): + # pylint: disable=broad-except; capturing exceptions broadly is + # intentional, to isolate arbitrary failures in this callback plugin. + try: + if self.__failures: + self._display.display(failure_summary(self.__failures, self.__playbook_file)) + except Exception: + msg = stringc( + u'An error happened while generating a summary of failures:\n' + u'{}'.format(traceback.format_exc()), C.COLOR_WARN) + self._display.v(msg) + + +def failure_summary(failures, playbook): + """Return a summary of failed tasks, including details on health checks.""" + if not failures: + return u'' + + # NOTE: because we don't have access to task_vars from callback plugins, we + # store the playbook context in the task result when the + # openshift_health_check action plugin is used, and we use this context to + # customize the error message. + # pylint: disable=protected-access; Ansible gives us no sufficient public + # API on TaskResult objects. + context = next(( + context for context in + (failure._result.get('playbook_context') for failure in failures) + if context + ), None) + + failures = [failure_to_dict(failure) for failure in failures] + failures = deduplicate_failures(failures) + + summary = [u'', u'', u'Failure summary:', u''] + + width = len(str(len(failures))) + initial_indent_format = u' {{:>{width}}}. '.format(width=width) + initial_indent_len = len(initial_indent_format.format(0)) + subsequent_indent = u' ' * initial_indent_len + subsequent_extra_indent = u' ' * (initial_indent_len + 10) + + for i, failure in enumerate(failures, 1): + entries = format_failure(failure) + summary.append(u'\n{}{}'.format(initial_indent_format.format(i), entries[0])) + for entry in entries[1:]: + entry = entry.replace(u'\n', u'\n' + subsequent_extra_indent) + indented = u'{}{}'.format(subsequent_indent, entry) + summary.append(indented) + + failed_checks = set() + for failure in failures: + failed_checks.update(name for name, message in failure['checks']) + if failed_checks: + summary.append(check_failure_footer(failed_checks, context, playbook)) + + return u'\n'.join(summary) + + +def failure_to_dict(failed_task_result): + """Extract information out of a failed TaskResult into a dict. + + The intent is to transform a TaskResult object into something easier to + manipulate. TaskResult is ansible.executor.task_result.TaskResult. + """ + # pylint: disable=protected-access; Ansible gives us no sufficient public + # API on TaskResult objects. + _result = failed_task_result._result + return { + 'host': failed_task_result._host.get_name(), + 'play': play_name(failed_task_result._task), + 'task': failed_task_result.task_name, + 'msg': _result.get('msg', FAILED_NO_MSG), + 'checks': tuple( + (name, result.get('msg', FAILED_NO_MSG)) + for name, result in sorted(_result.get('checks', {}).items()) + if result.get('failed') + ), + } + + +def play_name(obj): + """Given a task or block, return the name of its parent play. + + This is loosely inspired by ansible.playbook.base.Base.dump_me. + """ + # pylint: disable=protected-access; Ansible gives us no sufficient public + # API to implement this. + if not obj: + return '' + if hasattr(obj, '_play'): + return obj._play.get_name() + return play_name(getattr(obj, '_parent')) + + +def deduplicate_failures(failures): + """Group together similar failures from different hosts. + + Returns a new list of failures such that identical failures from different + hosts are grouped together in a single entry. The relative order of failures + is preserved. + """ + groups = defaultdict(list) + for failure in failures: + group_key = tuple(sorted((key, value) for key, value in failure.items() if key != 'host')) + groups[group_key].append(failure) + result = [] + for failure in failures: + group_key = tuple(sorted((key, value) for key, value in failure.items() if key != 'host')) + if group_key not in groups: + continue + failure['host'] = tuple(sorted(g_failure['host'] for g_failure in groups.pop(group_key))) + result.append(failure) + return result + + +def format_failure(failure): """Return a list of pretty-formatted text entries describing a failure, including relevant information about it. Expect that the list of text entries will be joined by a newline separator when output to the user.""" - result = failure['result'] - host = result._host.get_name() - play = _get_play(result._task) - if play: - play = play.get_name() - task = result._task.get_name() - msg = result._result.get('msg', u'???') + host = u', '.join(failure['host']) + play = failure['play'] + task = failure['task'] + msg = failure['msg'] + checks = failure['checks'] fields = ( - (u'Host', host), + (u'Hosts', host), (u'Play', play), (u'Task', task), (u'Message', stringc(msg, C.COLOR_ERROR)), ) - if 'checks' in result._result: - fields += ((u'Details', _format_failed_checks(result._result['checks'])),) + if checks: + fields += ((u'Details', format_failed_checks(checks)),) row_format = '{:10}{}' return [row_format.format(header + u':', body) for header, body in fields] -def _format_failed_checks(checks): +def format_failed_checks(checks): """Return pretty-formatted text describing checks that failed.""" - failed_check_msgs = [] - for check, body in checks.items(): - if body.get('failed', False): # only show the failed checks - msg = body.get('msg', u"Failed without returning a message") - failed_check_msgs.append('check "%s":\n%s' % (check, msg)) - if failed_check_msgs: - return stringc("\n\n".join(failed_check_msgs), C.COLOR_ERROR) - else: # something failed but no checks will admit to it, so dump everything - return stringc(pformat(checks), C.COLOR_ERROR) - - -# This is inspired by ansible.playbook.base.Base.dump_me. -# re: play/task/block attrs see top comment # pylint: disable=protected-access -def _get_play(obj): - """Given a task or block, recursively try to find its parent play.""" - if hasattr(obj, '_play'): - return obj._play - if getattr(obj, '_parent'): - return _get_play(obj._parent) + messages = [] + for name, message in checks: + messages.append(u'check "{}":\n{}'.format(name, message)) + return stringc(u'\n\n'.join(messages), C.COLOR_ERROR) + + +def check_failure_footer(failed_checks, context, playbook): + """Return a textual explanation about checks depending on context. + + The purpose of specifying context is to vary the output depending on what + the user was expecting to happen (based on which playbook they ran). The + only use currently is to vary the message depending on whether the user was + deliberately running checks or was trying to install/upgrade and checks are + just included. Other use cases may arise. + """ + checks = ','.join(sorted(failed_checks)) + summary = [u''] + if context in ['pre-install', 'health', 'adhoc']: + # User was expecting to run checks, less explanation needed. + summary.extend([ + u'You may configure or disable checks by setting Ansible ' + u'variables. To disable those above, set:', + u' openshift_disable_check={checks}'.format(checks=checks), + u'Consult check documentation for configurable variables.', + ]) + else: + # User may not be familiar with the checks, explain what checks are in + # the first place. + summary.extend([ + u'The execution of "{playbook}" includes checks designed to fail ' + u'early if the requirements of the playbook are not met. One or ' + u'more of these checks failed. To disregard these results,' + u'explicitly disable checks by setting an Ansible variable:'.format(playbook=playbook), + u' openshift_disable_check={checks}'.format(checks=checks), + u'Failing check names are shown in the failure details above. ' + u'Some checks may be configurable by variables if your requirements ' + u'are different from the defaults; consult check documentation.', + ]) + summary.append( + u'Variables can be set in the inventory or passed on the command line ' + u'using the -e flag to ansible-playbook.' + ) + return u'\n'.join(summary) diff --git a/roles/openshift_health_checker/library/aos_version.py b/roles/openshift_health_checker/library/aos_version.py index f9babebb9..c8769b511 100644 --- a/roles/openshift_health_checker/library/aos_version.py +++ b/roles/openshift_health_checker/library/aos_version.py @@ -24,11 +24,19 @@ from ansible.module_utils.basic import AnsibleModule # Python 3, we use six for cross compatibility in this module alone: from ansible.module_utils.six import string_types -IMPORT_EXCEPTION = None +YUM_IMPORT_EXCEPTION = None +DNF_IMPORT_EXCEPTION = None +PKG_MGR = None try: import yum # pylint: disable=import-error + PKG_MGR = "yum" except ImportError as err: - IMPORT_EXCEPTION = err + YUM_IMPORT_EXCEPTION = err +try: + import dnf # pylint: disable=import-error + PKG_MGR = "dnf" +except ImportError as err: + DNF_IMPORT_EXCEPTION = err class AosVersionException(Exception): @@ -47,8 +55,11 @@ def main(): supports_check_mode=True ) - if IMPORT_EXCEPTION: - module.fail_json(msg="aos_version module could not import yum: %s" % IMPORT_EXCEPTION) + if YUM_IMPORT_EXCEPTION and DNF_IMPORT_EXCEPTION: + module.fail_json( + msg="aos_version module could not import yum or dnf: %s %s" % + (YUM_IMPORT_EXCEPTION, DNF_IMPORT_EXCEPTION) + ) # determine the packages we will look for package_list = module.params['package_list'] @@ -83,9 +94,6 @@ def _to_dict(pkg_list): def _retrieve_available_packages(expected_pkgs): - # search for package versions available for openshift pkgs - yb = yum.YumBase() # pylint: disable=invalid-name - # The openshift excluder prevents unintended updates to openshift # packages by setting yum excludes on those packages. See: # https://wiki.centos.org/SpecialInterestGroup/PaaS/OpenShift-Origin-Control-Updates @@ -94,17 +102,41 @@ def _retrieve_available_packages(expected_pkgs): # attempt to determine what packages are available via yum they may # be excluded. So, for our purposes here, disable excludes to see # what will really be available during an install or upgrade. - yb.conf.disable_excludes = ['all'] - try: - pkgs = yb.pkgSack.returnPackages(patterns=expected_pkgs) - except yum.Errors.PackageSackError as excinfo: - # you only hit this if *none* of the packages are available - raise AosVersionException('\n'.join([ - 'Unable to find any OpenShift packages.', - 'Check your subscription and repo settings.', - str(excinfo), - ])) + if PKG_MGR == "yum": + # search for package versions available for openshift pkgs + yb = yum.YumBase() # pylint: disable=invalid-name + + yb.conf.disable_excludes = ['all'] + + try: + pkgs = yb.pkgSack.returnPackages(patterns=expected_pkgs) + except yum.Errors.PackageSackError as excinfo: + # you only hit this if *none* of the packages are available + raise AosVersionException('\n'.join([ + 'Unable to find any OpenShift packages.', + 'Check your subscription and repo settings.', + str(excinfo), + ])) + elif PKG_MGR == "dnf": + dbase = dnf.Base() # pyling: disable=invalid-name + + dbase.conf.disable_excludes = ['all'] + dbase.read_all_repos() + dbase.fill_sack(load_system_repo=False, load_available_repos=True) + + dquery = dbase.sack.query() + aquery = dquery.available() + + pkgs = list(aquery.filter(name=expected_pkgs)) + + if not pkgs: + # pkgs list is empty, raise because no expected packages found + raise AosVersionException('\n'.join([ + 'Unable to find any OpenShift packages.', + 'Check your subscription and repo settings.', + ])) + return pkgs diff --git a/roles/openshift_health_checker/library/rpm_version.py b/roles/openshift_health_checker/library/rpm_version.py index 8ea223055..c24fbba3b 100644 --- a/roles/openshift_health_checker/library/rpm_version.py +++ b/roles/openshift_health_checker/library/rpm_version.py @@ -4,6 +4,7 @@ Ansible module for rpm-based systems determining existing package version inform """ from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six import string_types IMPORT_EXCEPTION = None try: @@ -82,11 +83,16 @@ def _check_pkg_versions(found_pkgs_dict, expected_pkgs_dict): continue found_versions = [_parse_version(version) for version in found_pkgs_dict[pkg_name]] - expected_version = _parse_version(pkg["version"]) - if expected_version not in found_versions: + + if isinstance(pkg["version"], string_types): + expected_versions = [_parse_version(pkg["version"])] + else: + expected_versions = [_parse_version(version) for version in pkg["version"]] + + if not set(expected_versions) & set(found_versions): invalid_pkg_versions[pkg_name] = { "found_versions": found_versions, - "required_version": expected_version, + "required_versions": expected_versions, } if not_found_pkgs: @@ -106,7 +112,7 @@ def _check_pkg_versions(found_pkgs_dict, expected_pkgs_dict): "The following packages were found to be installed with an incorrect version: {}".format('\n'.join([ " \n{}\n Required version: {}\n Found versions: {}".format( pkg_name, - pkg["required_version"], + ', '.join(pkg["required_versions"]), ', '.join([version for version in pkg["found_versions"]])) for pkg_name, pkg in invalid_pkg_versions.items() ])) diff --git a/roles/openshift_health_checker/meta/main.yml b/roles/openshift_health_checker/meta/main.yml index ed97d539c..bc8e7bdcf 100644 --- a/roles/openshift_health_checker/meta/main.yml +++ b/roles/openshift_health_checker/meta/main.yml @@ -1 +1,3 @@ --- +dependencies: +- role: openshift_facts diff --git a/roles/openshift_health_checker/openshift_checks/__init__.py b/roles/openshift_health_checker/openshift_checks/__init__.py index 29faa9c5b..02ee1d0f9 100644 --- a/roles/openshift_health_checker/openshift_checks/__init__.py +++ b/roles/openshift_health_checker/openshift_checks/__init__.py @@ -10,6 +10,7 @@ from importlib import import_module from ansible.module_utils import six from ansible.module_utils.six.moves import reduce # pylint: disable=import-error,redefined-builtin +from ansible.plugins.filter.core import to_bool as ansible_to_bool class OpenShiftCheckException(Exception): @@ -119,16 +120,59 @@ class OpenShiftCheck(object): 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 or returning a default value - provided as a keyword argument. + OpenShiftCheckException when a key is not found. + + Keyword args: + default: + On missing key, return this as default value instead of raising exception. + convert: + Supply a function to apply to normalize the value before returning it. + None is the default (return as-is). + This function should raise ValueError if the user has provided a value + that cannot be converted, or OpenShiftCheckException if some other + problem needs to be described to the user. """ + if len(keys) == 1: + keys = keys[0].split(".") + try: value = reduce(operator.getitem, keys, self.task_vars) except (KeyError, TypeError): - if "default" in kwargs: - return kwargs["default"] - raise OpenShiftCheckException("'{}' is undefined".format(".".join(map(str, keys)))) - return value + if "default" not in kwargs: + raise OpenShiftCheckException( + "This check expects the '{}' inventory variable to be defined\n" + "in order to proceed, but it is undefined. There may be a bug\n" + "in Ansible, the checks, or their dependencies." + "".format(".".join(map(str, keys))) + ) + value = kwargs["default"] + + convert = kwargs.get("convert", None) + try: + if convert is None: + return value + elif convert is bool: # interpret bool as Ansible does, instead of python truthiness + return ansible_to_bool(value) + else: + return convert(value) + + except ValueError as error: # user error in specifying value + raise OpenShiftCheckException( + 'Cannot convert inventory variable to expected type:\n' + ' "{var}={value}"\n' + '{error}'.format(var=".".join(keys), value=value, error=error) + ) + + except OpenShiftCheckException: # some other check-specific problem + raise + + except Exception as error: # probably a bug in the function + raise OpenShiftCheckException( + 'There is a bug in this check. While trying to convert variable \n' + ' "{var}={value}"\n' + 'the given converter cannot be used or failed unexpectedly:\n' + '{error}'.format(var=".".join(keys), value=value, error=error) + ) @staticmethod def get_major_minor_version(openshift_image_tag): @@ -153,6 +197,31 @@ class OpenShiftCheck(object): components = tuple(int(x) for x in components[:2]) return components + def find_ansible_mount(self, path): + """Return the mount point for path from ansible_mounts.""" + + # reorganize list of mounts into dict by path + mount_for_path = { + mount['mount']: mount + for mount + in self.get_var('ansible_mounts') + } + + # NOTE: including base cases '/' and '' to ensure the loop ends + mount_targets = set(mount_for_path.keys()) | {'/', ''} + mount_point = path + while mount_point not in mount_targets: + mount_point = os.path.dirname(mount_point) + + try: + return mount_for_path[mount_point] + except KeyError: + known_mounts = ', '.join('"{}"'.format(mount) for mount in sorted(mount_for_path)) + raise OpenShiftCheckException( + 'Unable to determine mount point for path "{}".\n' + 'Known mount points: {}.'.format(path, known_mounts or 'none') + ) + LOADER_EXCLUDES = ( "__init__.py", diff --git a/roles/openshift_health_checker/openshift_checks/disk_availability.py b/roles/openshift_health_checker/openshift_checks/disk_availability.py index 39ac0e4ec..f302fd14b 100644 --- a/roles/openshift_health_checker/openshift_checks/disk_availability.py +++ b/roles/openshift_health_checker/openshift_checks/disk_availability.py @@ -1,6 +1,5 @@ """Check that there is enough disk space in predefined paths.""" -import os.path import tempfile from openshift_checks import OpenShiftCheck, OpenShiftCheckException @@ -55,9 +54,6 @@ class DiskAvailability(OpenShiftCheck): def run(self): group_names = self.get_var("group_names") - ansible_mounts = self.get_var("ansible_mounts") - ansible_mounts = {mount['mount']: mount for mount in ansible_mounts} - user_config = self.get_var("openshift_check_min_host_disk_gb", default={}) try: # For backwards-compatibility, if openshift_check_min_host_disk_gb @@ -80,7 +76,7 @@ class DiskAvailability(OpenShiftCheck): # not part of the official recommendation but present in the user # configuration. for path, recommendation in self.recommended_disk_space_bytes.items(): - free_bytes = self.free_bytes(path, ansible_mounts) + free_bytes = self.free_bytes(path) recommended_bytes = max(recommendation.get(name, 0) for name in group_names) config = user_config.get(path, {}) @@ -119,30 +115,22 @@ class DiskAvailability(OpenShiftCheck): return { 'failed': True, - 'msg': ( - 'Available disk space in "{}" ({:.1f} GB) ' - 'is below minimum recommended ({:.1f} GB)' - ).format(path, free_gb, recommended_gb) + 'msg': msg, } return {} - @staticmethod - def free_bytes(path, ansible_mounts): + def free_bytes(self, path): """Return the size available in path based on ansible_mounts.""" - mount_point = path - # arbitry value to prevent an infinite loop, in the unlike case that '/' - # is not in ansible_mounts. - max_depth = 32 - while mount_point not in ansible_mounts and max_depth > 0: - mount_point = os.path.dirname(mount_point) - max_depth -= 1 - + mount = self.find_ansible_mount(path) try: - free_bytes = ansible_mounts[mount_point]['size_available'] + return mount['size_available'] except KeyError: - known_mounts = ', '.join('"{}"'.format(mount) for mount in sorted(ansible_mounts)) or 'none' - msg = 'Unable to determine disk availability for "{}". Known mount points: {}.' - raise OpenShiftCheckException(msg.format(path, known_mounts)) - - return free_bytes + raise OpenShiftCheckException( + 'Unable to retrieve disk availability for "{path}".\n' + 'Ansible facts included a matching mount point for this path:\n' + ' {mount}\n' + 'however it is missing the size_available field.\n' + 'To investigate, you can inspect the output of `ansible -m setup <host>`' + ''.format(path=path, mount=mount) + ) diff --git a/roles/openshift_health_checker/openshift_checks/docker_image_availability.py b/roles/openshift_health_checker/openshift_checks/docker_image_availability.py index 85a922f86..857a80c74 100644 --- a/roles/openshift_health_checker/openshift_checks/docker_image_availability.py +++ b/roles/openshift_health_checker/openshift_checks/docker_image_availability.py @@ -168,7 +168,10 @@ class DockerImageAvailability(DockerHostMixin, OpenShiftCheck): registries = [registry] for registry in registries: - args = {"_raw_params": "skopeo inspect --tls-verify=false docker://{}/{}".format(registry, image)} + args = { + "_raw_params": "timeout 10 skopeo inspect --tls-verify=false " + "docker://{}/{}".format(registry, image) + } result = self.execute_module("command", args) if result.get("rc", 0) == 0 and not result.get("failed"): return True diff --git a/roles/openshift_health_checker/openshift_checks/docker_storage.py b/roles/openshift_health_checker/openshift_checks/docker_storage.py index 7ae384bd7..0558ddf14 100644 --- a/roles/openshift_health_checker/openshift_checks/docker_storage.py +++ b/roles/openshift_health_checker/openshift_checks/docker_storage.py @@ -1,6 +1,5 @@ """Check Docker storage driver and usage.""" import json -import os.path import re from openshift_checks import OpenShiftCheck, OpenShiftCheckException from openshift_checks.mixins import DockerHostMixin @@ -252,7 +251,7 @@ class DockerStorage(DockerHostMixin, OpenShiftCheck): "msg": "Specified 'max_overlay_usage_percent' is not a percentage: {}".format(threshold), } - mount = self.find_ansible_mount(path, self.get_var("ansible_mounts")) + mount = self.find_ansible_mount(path) try: free_bytes = mount['size_available'] total_bytes = mount['size_total'] @@ -275,22 +274,3 @@ class DockerStorage(DockerHostMixin, OpenShiftCheck): } return {} - - # TODO(lmeyer): migrate to base class - @staticmethod - def find_ansible_mount(path, ansible_mounts): - """Return the mount point for path from ansible_mounts.""" - - mount_for_path = {mount['mount']: mount for mount in ansible_mounts} - mount_point = path - while mount_point not in mount_for_path: - if mount_point in ["/", ""]: # "/" not in ansible_mounts??? - break - mount_point = os.path.dirname(mount_point) - - try: - return mount_for_path[mount_point] - except KeyError: - known_mounts = ', '.join('"{}"'.format(mount) for mount in sorted(mount_for_path)) or 'none' - msg = 'Unable to determine mount point for path "{}". Known mount points: {}.' - raise OpenShiftCheckException(msg.format(path, known_mounts)) diff --git a/roles/openshift_health_checker/openshift_checks/etcd_imagedata_size.py b/roles/openshift_health_checker/openshift_checks/etcd_imagedata_size.py index ae8460b7e..f4296753a 100644 --- a/roles/openshift_health_checker/openshift_checks/etcd_imagedata_size.py +++ b/roles/openshift_health_checker/openshift_checks/etcd_imagedata_size.py @@ -2,7 +2,7 @@ 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 +from openshift_checks import OpenShiftCheck class EtcdImageDataSize(OpenShiftCheck): @@ -12,7 +12,7 @@ class EtcdImageDataSize(OpenShiftCheck): tags = ["etcd"] def run(self): - etcd_mountpath = self._get_etcd_mountpath(self.get_var("ansible_mounts")) + etcd_mountpath = self.find_ansible_mount("/var/lib/etcd") etcd_avail_diskspace = etcd_mountpath["size_available"] etcd_total_diskspace = etcd_mountpath["size_total"] @@ -68,18 +68,5 @@ class EtcdImageDataSize(OpenShiftCheck): return {} @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/openshift_checks/etcd_traffic.py b/roles/openshift_health_checker/openshift_checks/etcd_traffic.py index cc1b14d8a..b4c8957e9 100644 --- a/roles/openshift_health_checker/openshift_checks/etcd_traffic.py +++ b/roles/openshift_health_checker/openshift_checks/etcd_traffic.py @@ -14,8 +14,8 @@ class EtcdTraffic(OpenShiftCheck): group_names = self.get_var("group_names", default=[]) valid_group_names = "etcd" in group_names - version = self.get_var("openshift", "common", "short_version") - valid_version = version in ("3.4", "3.5", "1.4", "1.5") + version = self.get_major_minor_version(self.get_var("openshift_image_tag")) + valid_version = version in ((3, 4), (3, 5)) return super(EtcdTraffic, self).is_active() and valid_group_names and valid_version diff --git a/roles/openshift_health_checker/openshift_checks/etcd_volume.py b/roles/openshift_health_checker/openshift_checks/etcd_volume.py index e55d55e91..e5d93ff3f 100644 --- a/roles/openshift_health_checker/openshift_checks/etcd_volume.py +++ b/roles/openshift_health_checker/openshift_checks/etcd_volume.py @@ -1,6 +1,6 @@ """A health check for OpenShift clusters.""" -from openshift_checks import OpenShiftCheck, OpenShiftCheckException +from openshift_checks import OpenShiftCheck class EtcdVolume(OpenShiftCheck): @@ -11,8 +11,8 @@ class EtcdVolume(OpenShiftCheck): # Default device usage threshold. Value should be in the range [0, 100]. default_threshold_percent = 90 - # Where to find ectd data, higher priority first. - supported_mount_paths = ["/var/lib/etcd", "/var/lib", "/var", "/"] + # Where to find etcd data + etcd_mount_path = "/var/lib/etcd" def is_active(self): etcd_hosts = self.get_var("groups", "etcd", default=[]) or self.get_var("groups", "masters", default=[]) or [] @@ -20,7 +20,7 @@ class EtcdVolume(OpenShiftCheck): return super(EtcdVolume, self).is_active() and is_etcd_host def run(self): - mount_info = self._etcd_mount_info() + mount_info = self.find_ansible_mount(self.etcd_mount_path) available = mount_info["size_available"] total = mount_info["size_total"] used = total - available @@ -41,15 +41,3 @@ class EtcdVolume(OpenShiftCheck): return {"failed": True, "msg": msg} return {} - - def _etcd_mount_info(self): - ansible_mounts = self.get_var("ansible_mounts") - mounts = {mnt.get("mount"): mnt for mnt in ansible_mounts} - - for path in self.supported_mount_paths: - if path in mounts: - return mounts[path] - - paths = ', '.join(sorted(mounts)) or 'none' - msg = "Unable to find etcd storage mount point. Paths mounted: {}.".format(paths) - raise OpenShiftCheckException(msg) diff --git a/roles/openshift_health_checker/openshift_checks/logging/logging.py b/roles/openshift_health_checker/openshift_checks/logging/logging.py index 3b7c39760..ecd8adb64 100644 --- a/roles/openshift_health_checker/openshift_checks/logging/logging.py +++ b/roles/openshift_health_checker/openshift_checks/logging/logging.py @@ -27,7 +27,7 @@ class LoggingCheck(OpenShiftCheck): name = "logging" def is_active(self): - logging_deployed = self.get_var("openshift_hosted_logging_deploy", default=False) + logging_deployed = self.get_var("openshift_hosted_logging_deploy", convert=bool, default=False) return logging_deployed and super(LoggingCheck, self).is_active() and self.is_first_master() def is_first_master(self): diff --git a/roles/openshift_health_checker/openshift_checks/ovs_version.py b/roles/openshift_health_checker/openshift_checks/ovs_version.py index d5e55bc25..363c12def 100644 --- a/roles/openshift_health_checker/openshift_checks/ovs_version.py +++ b/roles/openshift_health_checker/openshift_checks/ovs_version.py @@ -16,8 +16,8 @@ class OvsVersion(NotContainerizedMixin, OpenShiftCheck): tags = ["health"] openshift_to_ovs_version = { - "3.6": "2.6", - "3.5": "2.6", + "3.6": ["2.6", "2.7"], + "3.5": ["2.6", "2.7"], "3.4": "2.4", } diff --git a/roles/openshift_health_checker/test/action_plugin_test.py b/roles/openshift_health_checker/test/action_plugin_test.py index f5161d6f5..c109ebd24 100644 --- a/roles/openshift_health_checker/test/action_plugin_test.py +++ b/roles/openshift_health_checker/test/action_plugin_test.py @@ -80,7 +80,8 @@ def skipped(result): None, {}, ]) -def test_action_plugin_missing_openshift_facts(plugin, task_vars): +def test_action_plugin_missing_openshift_facts(plugin, task_vars, monkeypatch): + monkeypatch.setattr('openshift_health_check.resolve_checks', lambda *args: ['fake_check']) result = plugin.run(tmp=None, task_vars=task_vars) assert failed(result, msg_has=['openshift_facts']) @@ -94,7 +95,7 @@ def test_action_plugin_cannot_load_checks_with_the_same_name(plugin, task_vars, result = plugin.run(tmp=None, task_vars=task_vars) - assert failed(result, msg_has=['unique', 'duplicate_name', 'FakeCheck']) + assert failed(result, msg_has=['duplicate', 'duplicate_name', 'FakeCheck']) def test_action_plugin_skip_non_active_checks(plugin, task_vars, monkeypatch): @@ -217,24 +218,21 @@ def test_resolve_checks_ok(names, all_checks, expected): assert resolve_checks(names, all_checks) == expected -@pytest.mark.parametrize('names,all_checks,words_in_exception,words_not_in_exception', [ +@pytest.mark.parametrize('names,all_checks,words_in_exception', [ ( ['testA', 'testB'], [], ['check', 'name', 'testA', 'testB'], - ['tag', 'group', '@'], ), ( ['@group'], [], ['tag', 'name', 'group'], - ['check', '@'], ), ( ['testA', 'testB', '@group'], [], ['check', 'name', 'testA', 'testB', 'tag', 'group'], - ['@'], ), ( ['testA', 'testB', '@group'], @@ -244,13 +242,10 @@ def test_resolve_checks_ok(names, all_checks, expected): fake_check('from_group_2', ['preflight', 'group']), ], ['check', 'name', 'testA', 'testB'], - ['tag', 'group', '@'], ), ]) -def test_resolve_checks_failure(names, all_checks, words_in_exception, words_not_in_exception): +def test_resolve_checks_failure(names, all_checks, words_in_exception): with pytest.raises(Exception) as excinfo: resolve_checks(names, all_checks) for word in words_in_exception: assert word in str(excinfo.value) - for word in words_not_in_exception: - assert word not in str(excinfo.value) diff --git a/roles/openshift_health_checker/test/conftest.py b/roles/openshift_health_checker/test/conftest.py index 3cbd65507..244a1f0fa 100644 --- a/roles/openshift_health_checker/test/conftest.py +++ b/roles/openshift_health_checker/test/conftest.py @@ -7,5 +7,6 @@ openshift_health_checker_path = os.path.dirname(os.path.dirname(__file__)) sys.path[1:1] = [ openshift_health_checker_path, os.path.join(openshift_health_checker_path, 'action_plugins'), + os.path.join(openshift_health_checker_path, 'callback_plugins'), os.path.join(openshift_health_checker_path, 'library'), ] diff --git a/roles/openshift_health_checker/test/disk_availability_test.py b/roles/openshift_health_checker/test/disk_availability_test.py index 5720eeacf..f4fd2dfed 100644 --- a/roles/openshift_health_checker/test/disk_availability_test.py +++ b/roles/openshift_health_checker/test/disk_availability_test.py @@ -20,12 +20,24 @@ def test_is_active(group_names, is_active): assert DiskAvailability(None, task_vars).is_active() == is_active -@pytest.mark.parametrize('ansible_mounts,extra_words', [ - ([], ['none']), # empty ansible_mounts - ([{'mount': '/mnt'}], ['/mnt']), # missing relevant mount paths - ([{'mount': '/var'}], ['/var']), # missing size_available +@pytest.mark.parametrize('desc, ansible_mounts, expect_chunks', [ + ( + 'empty ansible_mounts', + [], + ['determine mount point', 'none'], + ), + ( + 'missing relevant mount paths', + [{'mount': '/mnt'}], + ['determine mount point', '/mnt'], + ), + ( + 'missing size_available', + [{'mount': '/var'}, {'mount': '/usr'}, {'mount': '/tmp'}], + ['missing', 'size_available'], + ), ]) -def test_cannot_determine_available_disk(ansible_mounts, extra_words): +def test_cannot_determine_available_disk(desc, ansible_mounts, expect_chunks): task_vars = dict( group_names=['masters'], ansible_mounts=ansible_mounts, @@ -34,8 +46,8 @@ def test_cannot_determine_available_disk(ansible_mounts, extra_words): with pytest.raises(OpenShiftCheckException) as excinfo: DiskAvailability(fake_execute_module, task_vars).run() - for word in 'determine disk availability'.split() + extra_words: - assert word in str(excinfo.value) + for chunk in expect_chunks: + assert chunk in str(excinfo.value) @pytest.mark.parametrize('group_names,configured_min,ansible_mounts', [ @@ -97,7 +109,7 @@ def test_succeeds_with_recommended_disk_space(group_names, configured_min, ansib assert not result.get('failed', False) -@pytest.mark.parametrize('name,group_names,configured_min,ansible_mounts,extra_words', [ +@pytest.mark.parametrize('name,group_names,configured_min,ansible_mounts,expect_chunks', [ ( 'test with no space available', ['masters'], @@ -164,7 +176,7 @@ def test_succeeds_with_recommended_disk_space(group_names, configured_min, ansib ['0.0 GB'], ), ], ids=lambda argval: argval[0]) -def test_fails_with_insufficient_disk_space(name, group_names, configured_min, ansible_mounts, extra_words): +def test_fails_with_insufficient_disk_space(name, group_names, configured_min, ansible_mounts, expect_chunks): task_vars = dict( group_names=group_names, openshift_check_min_host_disk_gb=configured_min, @@ -174,8 +186,8 @@ def test_fails_with_insufficient_disk_space(name, group_names, configured_min, a result = DiskAvailability(fake_execute_module, task_vars).run() assert result['failed'] - for word in 'below recommended'.split() + extra_words: - assert word in result.get('msg', '') + for chunk in 'below recommended'.split() + expect_chunks: + assert chunk in result.get('msg', '') @pytest.mark.parametrize('name,group_names,context,ansible_mounts,failed,extra_words', [ diff --git a/roles/openshift_health_checker/test/etcd_imagedata_size_test.py b/roles/openshift_health_checker/test/etcd_imagedata_size_test.py index e3d6706fa..d3aae98f2 100644 --- a/roles/openshift_health_checker/test/etcd_imagedata_size_test.py +++ b/roles/openshift_health_checker/test/etcd_imagedata_size_test.py @@ -1,7 +1,8 @@ import pytest from collections import namedtuple -from openshift_checks.etcd_imagedata_size import EtcdImageDataSize, OpenShiftCheckException +from openshift_checks.etcd_imagedata_size import EtcdImageDataSize +from openshift_checks import OpenShiftCheckException from etcdkeysize import check_etcd_key_size @@ -56,7 +57,7 @@ def test_cannot_determine_available_mountpath(ansible_mounts, extra_words): with pytest.raises(OpenShiftCheckException) as excinfo: check.run() - for word in 'determine valid etcd mountpath'.split() + extra_words: + for word in ['Unable to determine mount point'] + extra_words: assert word in str(excinfo.value) diff --git a/roles/openshift_health_checker/test/etcd_traffic_test.py b/roles/openshift_health_checker/test/etcd_traffic_test.py index f4316c423..fae3e578d 100644 --- a/roles/openshift_health_checker/test/etcd_traffic_test.py +++ b/roles/openshift_health_checker/test/etcd_traffic_test.py @@ -8,7 +8,7 @@ from openshift_checks.etcd_traffic import EtcdTraffic (['masters'], "3.6", False), (['nodes'], "3.4", False), (['etcd'], "3.4", True), - (['etcd'], "3.5", True), + (['etcd'], "1.5", True), (['etcd'], "3.1", False), (['masters', 'nodes'], "3.5", False), (['masters', 'etcd'], "3.5", True), @@ -17,9 +17,7 @@ from openshift_checks.etcd_traffic import EtcdTraffic def test_is_active(group_names, version, is_active): task_vars = dict( group_names=group_names, - openshift=dict( - common=dict(short_version=version), - ), + openshift_image_tag=version, ) assert EtcdTraffic(task_vars=task_vars).is_active() == is_active diff --git a/roles/openshift_health_checker/test/etcd_volume_test.py b/roles/openshift_health_checker/test/etcd_volume_test.py index 0b255136e..077cea3ea 100644 --- a/roles/openshift_health_checker/test/etcd_volume_test.py +++ b/roles/openshift_health_checker/test/etcd_volume_test.py @@ -1,6 +1,7 @@ import pytest -from openshift_checks.etcd_volume import EtcdVolume, OpenShiftCheckException +from openshift_checks.etcd_volume import EtcdVolume +from openshift_checks import OpenShiftCheckException @pytest.mark.parametrize('ansible_mounts,extra_words', [ @@ -15,7 +16,7 @@ def test_cannot_determine_available_disk(ansible_mounts, extra_words): with pytest.raises(OpenShiftCheckException) as excinfo: EtcdVolume(fake_execute_module, task_vars).run() - for word in 'Unable to find etcd storage mount point'.split() + extra_words: + for word in ['Unable to determine mount point'] + extra_words: assert word in str(excinfo.value) diff --git a/roles/openshift_health_checker/test/openshift_check_test.py b/roles/openshift_health_checker/test/openshift_check_test.py index 43aa875f4..789784c77 100644 --- a/roles/openshift_health_checker/test/openshift_check_test.py +++ b/roles/openshift_health_checker/test/openshift_check_test.py @@ -81,6 +81,7 @@ def dummy_check(task_vars): @pytest.mark.parametrize("keys,expected", [ (("foo",), 42), (("bar", "baz"), "openshift"), + (("bar.baz",), "openshift"), ]) def test_get_var_ok(task_vars, keys, expected): assert dummy_check(task_vars).get_var(*keys) == expected @@ -94,3 +95,24 @@ def test_get_var_error(task_vars, missing_keys): def test_get_var_default(task_vars, missing_keys): default = object() assert dummy_check(task_vars).get_var(*missing_keys, default=default) == default + + +@pytest.mark.parametrize("keys, convert, expected", [ + (("foo",), str, "42"), + (("foo",), float, 42.0), + (("bar", "baz"), bool, False), +]) +def test_get_var_convert(task_vars, keys, convert, expected): + assert dummy_check(task_vars).get_var(*keys, convert=convert) == expected + + +@pytest.mark.parametrize("keys, convert", [ + (("bar", "baz"), int), + (("bar.baz"), float), + (("foo"), "bogus"), + (("foo"), lambda a, b: 1), + (("foo"), lambda a: 1 / 0), +]) +def test_get_var_convert_error(task_vars, keys, convert): + with pytest.raises(OpenShiftCheckException): + dummy_check(task_vars).get_var(*keys, convert=convert) diff --git a/roles/openshift_health_checker/test/ovs_version_test.py b/roles/openshift_health_checker/test/ovs_version_test.py index b6acef5a6..e1bf29d2a 100644 --- a/roles/openshift_health_checker/test/ovs_version_test.py +++ b/roles/openshift_health_checker/test/ovs_version_test.py @@ -38,8 +38,8 @@ def test_invalid_openshift_release_format(): @pytest.mark.parametrize('openshift_release,expected_ovs_version', [ - ("3.5", "2.6"), - ("3.6", "2.6"), + ("3.5", ["2.6", "2.7"]), + ("3.6", ["2.6", "2.7"]), ("3.4", "2.4"), ("3.3", "2.4"), ("1.0", "2.4"), diff --git a/roles/openshift_health_checker/test/rpm_version_test.py b/roles/openshift_health_checker/test/rpm_version_test.py index 2f09ef965..2c1bcf876 100644 --- a/roles/openshift_health_checker/test/rpm_version_test.py +++ b/roles/openshift_health_checker/test/rpm_version_test.py @@ -49,7 +49,7 @@ def test_check_pkg_found(pkgs, expect_not_found): }, { "eggs": { - "required_version": "3.2", + "required_versions": ["3.2"], "found_versions": ["3.3"], } }, # not the right version @@ -61,11 +61,11 @@ def test_check_pkg_found(pkgs, expect_not_found): }, { "eggs": { - "required_version": "3.2", + "required_versions": ["3.2"], "found_versions": ["3.3", "1.2"], }, "spam": { - "required_version": "3.2", + "required_versions": ["3.2"], "found_versions": ["3.1", "3.3"], } }, # not the right version diff --git a/roles/openshift_health_checker/test/zz_failure_summary_test.py b/roles/openshift_health_checker/test/zz_failure_summary_test.py new file mode 100644 index 000000000..0fc258133 --- /dev/null +++ b/roles/openshift_health_checker/test/zz_failure_summary_test.py @@ -0,0 +1,70 @@ +from zz_failure_summary import deduplicate_failures + +import pytest + + +@pytest.mark.parametrize('failures,deduplicated', [ + ( + [ + { + 'host': 'master1', + 'msg': 'One or more checks failed', + }, + ], + [ + { + 'host': ('master1',), + 'msg': 'One or more checks failed', + }, + ], + ), + ( + [ + { + 'host': 'master1', + 'msg': 'One or more checks failed', + }, + { + 'host': 'node1', + 'msg': 'One or more checks failed', + }, + ], + [ + { + 'host': ('master1', 'node1'), + 'msg': 'One or more checks failed', + }, + ], + ), + ( + [ + { + 'host': 'node1', + 'msg': 'One or more checks failed', + 'checks': (('test_check', 'error message'),), + }, + { + 'host': 'master2', + 'msg': 'Some error happened', + }, + { + 'host': 'master1', + 'msg': 'One or more checks failed', + 'checks': (('test_check', 'error message'),), + }, + ], + [ + { + 'host': ('master1', 'node1'), + 'msg': 'One or more checks failed', + 'checks': (('test_check', 'error message'),), + }, + { + 'host': ('master2',), + 'msg': 'Some error happened', + }, + ], + ), +]) +def test_deduplicate_failures(failures, deduplicated): + assert deduplicate_failures(failures) == deduplicated diff --git a/roles/openshift_hosted/defaults/main.yml b/roles/openshift_hosted/defaults/main.yml index 0391e5602..c26df3afa 100644 --- a/roles/openshift_hosted/defaults/main.yml +++ b/roles/openshift_hosted/defaults/main.yml @@ -1,4 +1,13 @@ --- +r_openshift_hosted_router_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_openshift_hosted_router_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + +r_openshift_hosted_registry_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_openshift_hosted_registry_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + +openshift_hosted_router_wait: "{{ not openshift_master_bootstrap_enabled | default(True) }}" +openshift_hosted_registry_wait: "{{ not openshift_master_bootstrap_enabled | default(True) }}" + registry_volume_claim: 'registry-claim' openshift_hosted_router_edits: @@ -26,12 +35,15 @@ openshift_hosted_routers: - 443:443 certificate: "{{ openshift_hosted_router_certificate | default({}) }}" - openshift_hosted_router_certificate: {} openshift_hosted_registry_cert_expire_days: 730 openshift_hosted_router_create_certificate: True -os_firewall_allow: +r_openshift_hosted_router_os_firewall_deny: [] +r_openshift_hosted_router_os_firewall_allow: [] + +r_openshift_hosted_registry_os_firewall_deny: [] +r_openshift_hosted_registry_os_firewall_allow: - service: Docker Registry Port port: 5000/tcp - when: openshift.common.use_calico | bool + cond: "{{ r_openshift_hosted_use_calico }}" diff --git a/roles/openshift_hosted/meta/main.yml b/roles/openshift_hosted/meta/main.yml index 9e3f37130..28fd396d6 100644 --- a/roles/openshift_hosted/meta/main.yml +++ b/roles/openshift_hosted/meta/main.yml @@ -15,8 +15,4 @@ dependencies: - role: openshift_cli - role: openshift_hosted_facts - role: lib_openshift -- role: os_firewall - os_firewall_allow: - - service: Docker Registry Port - port: 5000/tcp - when: openshift.common.use_calico | bool +- role: lib_os_firewall diff --git a/roles/openshift_hosted/tasks/registry/firewall.yml b/roles/openshift_hosted/tasks/registry/firewall.yml new file mode 100644 index 000000000..775b7d6d7 --- /dev/null +++ b/roles/openshift_hosted/tasks/registry/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_openshift_hosted_registry_firewall_enabled | bool and not r_openshift_hosted_registry_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_hosted_registry_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_hosted_registry_os_firewall_deny }}" + +- when: r_openshift_hosted_registry_firewall_enabled | bool and r_openshift_hosted_registry_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_openshift_hosted_registry_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_openshift_hosted_registry_os_firewall_deny }}" diff --git a/roles/openshift_hosted/tasks/registry/registry.yml b/roles/openshift_hosted/tasks/registry/registry.yml index b946ec8ca..3e424da12 100644 --- a/roles/openshift_hosted/tasks/registry/registry.yml +++ b/roles/openshift_hosted/tasks/registry/registry.yml @@ -1,6 +1,10 @@ --- -- block: +- name: setup firewall + include: firewall.yml + static: yes +- when: openshift.hosted.registry.replicas | default(none) is none + block: - name: Retrieve list of openshift nodes matching registry selector oc_obj: state: list @@ -28,7 +32,6 @@ l_default_replicas: "{{ l_node_count if openshift.hosted.registry.storage.kind | default(none) is not none else 1 }}" when: l_node_count | int > 0 - when: openshift.hosted.registry.replicas | default(none) is none - name: set openshift_hosted facts set_fact: @@ -40,9 +43,6 @@ openshift_hosted_registry_images: "{{ openshift.hosted.registry.registryurl | default('openshift3/ose-${component}:${version}')}}" openshift_hosted_registry_volumes: [] openshift_hosted_registry_env_vars: {} - openshift_hosted_registry_routecertificates: "{{ ('routecertificates' in openshift.hosted.registry.keys()) | ternary(openshift.hosted.registry.routecertificates, {}) }}" - openshift_hosted_registry_routehost: "{{ ('routehost' in openshift.hosted.registry.keys()) | ternary(openshift.hosted.registry.routehost, False) }}" - openshift_hosted_registry_routetermination: "{{ ('routetermination' in openshift.hosted.registry.keys()) | ternary(openshift.hosted.registry.routetermination, 'passthrough') }}" openshift_hosted_registry_edits: # These edits are being specified only to prevent 'changed' on rerun - key: spec.strategy.rollingParams @@ -129,34 +129,36 @@ edits: "{{ openshift_hosted_registry_edits }}" force: "{{ True|bool in openshift_hosted_registry_force }}" -- name: Ensure OpenShift registry correctly rolls out (best-effort today) - command: | - oc rollout status deploymentconfig {{ openshift_hosted_registry_name }} \ - --namespace {{ openshift_hosted_registry_namespace }} \ - --config {{ openshift.common.config_base }}/master/admin.kubeconfig - async: 600 - poll: 15 - failed_when: false - -- name: Determine the latest version of the OpenShift registry deployment - command: | - {{ openshift.common.client_binary }} get deploymentconfig {{ openshift_hosted_registry_name }} \ - --namespace {{ openshift_hosted_registry_namespace }} \ - --config {{ openshift.common.config_base }}/master/admin.kubeconfig \ - -o jsonpath='{ .status.latestVersion }' - register: openshift_hosted_registry_latest_version - -- name: Sanity-check that the OpenShift registry rolled out correctly - command: | - {{ openshift.common.client_binary }} get replicationcontroller {{ openshift_hosted_registry_name }}-{{ openshift_hosted_registry_latest_version.stdout }} \ - --namespace {{ openshift_hosted_registry_namespace }} \ - --config {{ openshift.common.config_base }}/master/admin.kubeconfig \ - -o jsonpath='{ .metadata.annotations.openshift\.io/deployment\.phase }' - register: openshift_hosted_registry_rc_phase - until: "'Running' not in openshift_hosted_registry_rc_phase.stdout" - delay: 15 - retries: 40 - failed_when: "'Failed' in openshift_hosted_registry_rc_phase.stdout" +- when: openshift_hosted_registry_wait + block: + - name: Ensure OpenShift registry correctly rolls out (best-effort today) + command: | + oc rollout status deploymentconfig {{ openshift_hosted_registry_name }} \ + --namespace {{ openshift_hosted_registry_namespace }} \ + --config {{ openshift.common.config_base }}/master/admin.kubeconfig + async: 600 + poll: 15 + failed_when: false + + - name: Determine the latest version of the OpenShift registry deployment + command: | + {{ openshift.common.client_binary }} get deploymentconfig {{ openshift_hosted_registry_name }} \ + --namespace {{ openshift_hosted_registry_namespace }} \ + --config {{ openshift.common.config_base }}/master/admin.kubeconfig \ + -o jsonpath='{ .status.latestVersion }' + register: openshift_hosted_registry_latest_version + + - name: Sanity-check that the OpenShift registry rolled out correctly + command: | + {{ openshift.common.client_binary }} get replicationcontroller {{ openshift_hosted_registry_name }}-{{ openshift_hosted_registry_latest_version.stdout }} \ + --namespace {{ openshift_hosted_registry_namespace }} \ + --config {{ openshift.common.config_base }}/master/admin.kubeconfig \ + -o jsonpath='{ .metadata.annotations.openshift\.io/deployment\.phase }' + register: openshift_hosted_registry_rc_phase + until: "'Running' not in openshift_hosted_registry_rc_phase.stdout" + delay: 15 + retries: 40 + failed_when: "'Failed' in openshift_hosted_registry_rc_phase.stdout" - include: storage/glusterfs.yml when: diff --git a/roles/openshift_hosted/tasks/registry/secure.yml b/roles/openshift_hosted/tasks/registry/secure.yml index 29c164f52..a8a6f6fc8 100644 --- a/roles/openshift_hosted/tasks/registry/secure.yml +++ b/roles/openshift_hosted/tasks/registry/secure.yml @@ -1,65 +1,71 @@ --- -- name: Set fact docker_registry_route_hostname +- name: Configure facts for docker-registry set_fact: - docker_registry_route_hostname: "{{ 'docker-registry-default.' ~ (openshift_master_default_subdomain | default('router.default.svc.cluster.local', true)) }}" + openshift_hosted_registry_routecertificates: "{{ ('routecertificates' in openshift.hosted.registry.keys()) | ternary(openshift.hosted.registry.routecertificates, {}) }}" + openshift_hosted_registry_routehost: "{{ ('routehost' in openshift.hosted.registry.keys()) | ternary(openshift.hosted.registry.routehost, False) }}" + openshift_hosted_registry_routetermination: "{{ ('routetermination' in openshift.hosted.registry.keys()) | ternary(openshift.hosted.registry.routetermination, 'passthrough') }}" -- name: Get the certificate contents for registry - copy: - backup: True - dest: "/etc/origin/master/named_certificates/{{ item.value | basename }}" - src: "{{ item.value }}" - when: item.key in ['certfile', 'keyfile', 'cafile'] and item.value - with_dict: "{{ openshift_hosted_registry_routecertificates }}" +- name: Include reencrypt route configuration + include: secure/reencrypt.yml + static: no + when: openshift_hosted_registry_routetermination == 'reencrypt' -# When certificates are defined we will create the reencrypt -# docker-registry route -- name: Create a reencrypt route for docker-registry - oc_route: - name: docker-registry - namespace: "{{ openshift_hosted_registry_namespace }}" - service_name: docker-registry - tls_termination: "{{ openshift_hosted_registry_routetermination }}" - host: "{{ openshift_hosted_registry_routehost | default(docker_registry_route_hostname) }}" - cert_path: "/etc/origin/master/named_certificates/{{ openshift_hosted_registry_routecertificates['certfile'] | basename }}" - key_path: "/etc/origin/master/named_certificates/{{ openshift_hosted_registry_routecertificates['keyfile'] | basename }}" - cacert_path: "/etc/origin/master/named_certificates/{{ openshift_hosted_registry_routecertificates['cafile'] | basename }}" - dest_cacert_path: /etc/origin/master/ca.crt - when: - - "'cafile' in openshift_hosted_registry_routecertificates" - - "'certfile' in openshift_hosted_registry_routecertificates" - - "'keyfile' in openshift_hosted_registry_routecertificates" +- name: Include passthrough route configuration + include: secure/passthrough.yml + static: no + when: openshift_hosted_registry_routetermination == 'passthrough' -# When routetermination is passthrough we will create the route -- name: Create passthrough route for docker-registry +- name: Fetch the docker-registry route oc_route: name: docker-registry - namespace: "{{ openshift_hosted_registry_namespace }}" - service_name: docker-registry - tls_termination: "{{ openshift_hosted_registry_routetermination }}" - host: "{{ openshift_hosted_registry_routehost | ternary(openshift_hosted_registry_routehost, docker_registry_route_hostname) }}" - when: openshift_hosted_registry_routetermination == 'passthrough' + namespace: default + state: list + register: docker_registry_route -- name: Retrieve registry service IP +- name: Retrieve registry service for the clusterip oc_service: namespace: "{{ openshift_hosted_registry_namespace }}" name: docker-registry state: list - register: docker_registry_service_ip + register: docker_registry_service -- name: Create registry certificates +- name: Generate self-signed docker-registry certificates oc_adm_ca_server_cert: 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.results.clusterip }}" + - "{{ docker_registry_service.results.clusterip }}" + - "{{ docker_registry_route.results[0].spec.host }}" - "{{ openshift_hosted_registry_name }}.default.svc" - "{{ openshift_hosted_registry_name }}.default.svc.{{ openshift.common.dns_domain }}" - - "{{ docker_registry_route_hostname }}" - cert: "{{ openshift_master_config_dir }}/registry.crt" - key: "{{ openshift_master_config_dir }}/registry.key" + - "{{ openshift_hosted_registry_routehost }}" + cert: "{{ docker_registry_cert_path }}" + key: "{{ docker_registry_key_path }}" expire_days: "{{ openshift_hosted_registry_cert_expire_days if openshift_version | oo_version_gte_3_5_or_1_5(openshift.common.deployment_type) | bool else omit }}" - register: server_cert_out + register: registry_self_cert + when: docker_registry_self_signed + +# Setting up REGISTRY_HTTP_TLS_CLIENTCAS as the cacert doesn't seem to work. +# If we need to set up a cacert, bundle it with the cert. +- when: docker_registry_cacert_path is defined + block: + - name: Retrieve certificate files to generate certificate bundle + slurp: + src: "{{ item }}" + with_items: + - "{{Â docker_registry_cert_path }}" + - "{{ docker_registry_cacert_path }}" + register: certificate_files + + - name: Generate certificate bundle + copy: + content: "{{ certificate_files.results | map(attribute='content') | map('b64decode') | join('') }}" + dest: "{{ openshift_master_config_dir }}/named_certificates/docker-registry.pem" + + - name: Reset the certificate path to use the bundle + set_fact: + docker_registry_cert_path: "{{ openshift_master_config_dir }}/named_certificates/docker-registry.pem" - name: Create the secret for the registry certificates oc_secret: @@ -67,9 +73,9 @@ namespace: "{{ openshift_hosted_registry_namespace }}" files: - name: registry.crt - path: "{{ openshift_master_config_dir }}/registry.crt" + path: "{{ docker_registry_cert_path }}" - name: registry.key - path: "{{ openshift_master_config_dir }}/registry.key" + path: "{{ docker_registry_key_path }}" register: create_registry_certificates_secret_out - name: Add the secret to the registry's pod service accounts @@ -99,9 +105,15 @@ value: HTTPS action: put +- name: Detect if there has been certificate changes + set_fact: + registry_cert_changed: true + when: ( registry_self_cert is defined and registry_self_cert.changed ) or + create_registry_certificates_secret_out.changed + - name: Update openshift_hosted facts with secure registry variables set_fact: openshift_hosted_registry_volumes: "{{ openshift_hosted_registry_volumes | union(registry_secure_volume_mounts) }}" openshift_hosted_registry_env_vars: "{{ openshift_hosted_registry_env_vars | combine(registry_secure_env_vars) }}" openshift_hosted_registry_edits: "{{ openshift_hosted_registry_edits | union(registry_secure_edits) }}" - openshift_hosted_registry_force: "{{ openshift_hosted_registry_force | union([server_cert_out.changed]) | union([create_registry_certificates_secret_out.changed]) }}" + openshift_hosted_registry_force: "{{ openshift_hosted_registry_force | union([registry_cert_changed | default(false)]) }}" diff --git a/roles/openshift_hosted/tasks/registry/secure/passthrough.yml b/roles/openshift_hosted/tasks/registry/secure/passthrough.yml new file mode 100644 index 000000000..5b44fda10 --- /dev/null +++ b/roles/openshift_hosted/tasks/registry/secure/passthrough.yml @@ -0,0 +1,45 @@ +--- +# Generate a self-signed certificate when there is no user-supplied certificate +- name: Configure self-signed certificate file paths + set_fact: + docker_registry_cert_path: "{{ openshift_master_config_dir }}/registry.crt" + docker_registry_key_path: "{{ openshift_master_config_dir }}/registry.key" + docker_registry_cacert_path: "{{ openshift_master_config_dir }}/ca.crt" + docker_registry_self_signed: true + when: + - "'certfile' not in openshift_hosted_registry_routecertificates" + - "'keyfile' not in openshift_hosted_registry_routecertificates" + +# Retrieve user supplied certificate files if they are provided +- when: + - "'certfile' in openshift_hosted_registry_routecertificates" + - "'keyfile' in openshift_hosted_registry_routecertificates" + block: + - name: Configure provided certificate file paths + set_fact: + docker_registry_cert_path: "{{ openshift_master_config_dir }}/named_certificates/{{ openshift_hosted_registry_routecertificates['certfile'] | basename }}" + docker_registry_key_path: "{{ openshift_master_config_dir }}/named_certificates/{{ openshift_hosted_registry_routecertificates['keyfile'] | basename }}" + docker_registry_self_signed: false + + # Since we end up bundling the cert, cacert and key in a .pem file, the 'cafile' + # is optional + - name: Configure provided ca certificate file path + set_fact: + docker_registry_cacert_path: "{{ openshift_master_config_dir }}/named_certificates/{{ openshift_hosted_registry_routecertificates['cafile'] | basename }}" + when: "'cafile' in openshift_hosted_registry_routecertificates" + + - name: Retrieve provided certificate files + copy: + backup: True + dest: "{{ openshift_master_config_dir }}/named_certificates/{{ item.value | basename }}" + src: "{{ item.value }}" + when: item.key in ['certfile', 'keyfile', 'cafile'] and item.value + with_dict: "{{ openshift_hosted_registry_routecertificates }}" + +- name: Configure a passthrough route for docker-registry + oc_route: + name: docker-registry + namespace: "{{ openshift_hosted_registry_namespace }}" + service_name: docker-registry + tls_termination: "{{ openshift_hosted_registry_routetermination }}" + host: "{{ openshift_hosted_registry_routehost | default(omit, true) }}" diff --git a/roles/openshift_hosted/tasks/registry/secure/reencrypt.yml b/roles/openshift_hosted/tasks/registry/secure/reencrypt.yml new file mode 100644 index 000000000..48e5b0fba --- /dev/null +++ b/roles/openshift_hosted/tasks/registry/secure/reencrypt.yml @@ -0,0 +1,38 @@ +--- +- name: Validate route termination configuration + fail: + msg: > + When 'openshift_hosted_registry_routetermination' is 'reencrypt', you must + provide certificate files with 'openshift_hosted_registry_routecertificates' + when: ('certfile' not in openshift_hosted_registry_routecertificates) or + ('keyfile' not in openshift_hosted_registry_routecertificates) or + ('cafile' not in openshift_hosted_registry_routecertificates) + +- name: Configure self-signed certificate file paths + set_fact: + docker_registry_cert_path: "{{ openshift_master_config_dir }}/registry.crt" + docker_registry_key_path: "{{ openshift_master_config_dir }}/registry.key" + docker_registry_cacert_path: "{{ openshift_master_config_dir }}/ca.crt" + docker_registry_self_signed: true + +- name: Retrieve provided certificate files + copy: + backup: True + dest: "{{ openshift_master_config_dir }}/named_certificates/{{ item.value | basename }}" + src: "{{ item.value }}" + when: item.key in ['certfile', 'keyfile', 'cafile'] and item.value + with_dict: "{{ openshift_hosted_registry_routecertificates }}" + +# Encrypt with the provided certificate and provide the dest_cacert for the +# self-signed certificate at the endpoint +- name: Configure a reencrypt route for docker-registry + oc_route: + name: docker-registry + namespace: "{{ openshift_hosted_registry_namespace }}" + service_name: docker-registry + tls_termination: "{{ openshift_hosted_registry_routetermination }}" + host: "{{ openshift_hosted_registry_routehost | default(omit, true) }}" + cert_path: "{{ openshift_master_config_dir }}/named_certificates/{{ openshift_hosted_registry_routecertificates['certfile'] | basename }}" + key_path: "{{ openshift_master_config_dir }}/named_certificates/{{ openshift_hosted_registry_routecertificates['keyfile'] | basename }}" + cacert_path: "{{ openshift_master_config_dir }}/named_certificates/{{ openshift_hosted_registry_routecertificates['cafile'] | basename }}" + dest_cacert_path: "{{ openshift_master_config_dir }}/ca.crt" diff --git a/roles/openshift_hosted/tasks/registry/storage/object_storage.yml b/roles/openshift_hosted/tasks/registry/storage/object_storage.yml index 8aaba0f3c..8553a8098 100644 --- a/roles/openshift_hosted/tasks/registry/storage/object_storage.yml +++ b/roles/openshift_hosted/tasks/registry/storage/object_storage.yml @@ -2,7 +2,7 @@ - include: s3.yml when: openshift.hosted.registry.storage.provider == 's3' -- name: Ensure the resgistry secret exists +- name: Ensure the registry secret exists oc_secret: name: "{{ registry_config_secret_name }}" state: present @@ -10,6 +10,19 @@ - path: /tmp/config.yml data: "{{ lookup('template', 'registry_config.j2') }}" register: registry_config_out + when: openshift_hosted_registry_storage_gcs_keyfile is not defined + +- name: Ensure the registry secret exists for GCS + oc_secret: + name: "{{ registry_config_secret_name }}" + state: present + contents: + - path: /tmp/config.yml + data: "{{ lookup('template', 'registry_config.j2') }}" + - path: /tmp/gcs.json + data: "{{ lookup('file', openshift_hosted_registry_storage_gcs_keyfile) | string }}" + register: registry_config_out + when: openshift_hosted_registry_storage_gcs_keyfile is defined - name: Add secrets to registry service account oc_serviceaccount_secret: diff --git a/roles/openshift_hosted/tasks/registry/storage/registry_config_secret.j2 b/roles/openshift_hosted/tasks/registry/storage/registry_config_secret.j2 deleted file mode 120000 index b9e82c1ea..000000000 --- a/roles/openshift_hosted/tasks/registry/storage/registry_config_secret.j2 +++ /dev/null @@ -1 +0,0 @@ -../../../templates/registry_config_secret.j2
\ No newline at end of file diff --git a/roles/openshift_hosted/tasks/router/firewall.yml b/roles/openshift_hosted/tasks/router/firewall.yml new file mode 100644 index 000000000..ff90f3372 --- /dev/null +++ b/roles/openshift_hosted/tasks/router/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_openshift_hosted_router_firewall_enabled | bool and not r_openshift_hosted_router_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_hosted_router_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_hosted_router_os_firewall_deny }}" + +- when: r_openshift_hosted_router_firewall_enabled | bool and r_openshift_hosted_router_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_openshift_hosted_router_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_openshift_hosted_router_os_firewall_deny }}" diff --git a/roles/openshift_hosted/tasks/router/router.yml b/roles/openshift_hosted/tasks/router/router.yml index dd485a64a..e57ed733e 100644 --- a/roles/openshift_hosted/tasks/router/router.yml +++ b/roles/openshift_hosted/tasks/router/router.yml @@ -1,4 +1,8 @@ --- +- name: setup firewall + include: firewall.yml + static: yes + - name: Retrieve list of openshift nodes matching router selector oc_obj: state: list @@ -90,36 +94,38 @@ stats_port: "{{ item.stats_port }}" with_items: "{{ openshift_hosted_routers }}" -- name: Ensure OpenShift router correctly rolls out (best-effort today) - command: | - {{ openshift.common.client_binary }} rollout status deploymentconfig {{ item.name }} \ - --namespace {{ item.namespace | default('default') }} \ - --config {{ openshift.common.config_base }}/master/admin.kubeconfig - async: 600 - poll: 15 - with_items: "{{ openshift_hosted_routers }}" - failed_when: false +- when: openshift_hosted_router_wait + block: + - name: Ensure OpenShift router correctly rolls out (best-effort today) + command: | + {{ openshift.common.client_binary }} rollout status deploymentconfig {{ item.name }} \ + --namespace {{ item.namespace | default('default') }} \ + --config {{ openshift.common.config_base }}/master/admin.kubeconfig + async: 600 + poll: 15 + with_items: "{{ openshift_hosted_routers }}" + failed_when: false -- name: Determine the latest version of the OpenShift router deployment - command: | - {{ openshift.common.client_binary }} get deploymentconfig {{ item.name }} \ - --namespace {{ item.namespace }} \ - --config {{ openshift.common.config_base }}/master/admin.kubeconfig \ - -o jsonpath='{ .status.latestVersion }' - register: openshift_hosted_routers_latest_version - with_items: "{{ openshift_hosted_routers }}" + - name: Determine the latest version of the OpenShift router deployment + command: | + {{ openshift.common.client_binary }} get deploymentconfig {{ item.name }} \ + --namespace {{ item.namespace }} \ + --config {{ openshift.common.config_base }}/master/admin.kubeconfig \ + -o jsonpath='{ .status.latestVersion }' + register: openshift_hosted_routers_latest_version + with_items: "{{ openshift_hosted_routers }}" -- name: Poll for OpenShift router deployment success - command: | - {{ openshift.common.client_binary }} get replicationcontroller {{ item.0.name }}-{{ item.1.stdout }} \ - --namespace {{ item.0.namespace }} \ - --config {{ openshift.common.config_base }}/master/admin.kubeconfig \ - -o jsonpath='{ .metadata.annotations.openshift\.io/deployment\.phase }' - register: openshift_hosted_router_rc_phase - until: "'Running' not in openshift_hosted_router_rc_phase.stdout" - delay: 15 - retries: 40 - failed_when: "'Failed' in openshift_hosted_router_rc_phase.stdout" - with_together: - - "{{ openshift_hosted_routers }}" - - "{{ openshift_hosted_routers_latest_version.results }}" + - name: Poll for OpenShift router deployment success + command: | + {{ openshift.common.client_binary }} get replicationcontroller {{ item.0.name }}-{{ item.1.stdout }} \ + --namespace {{ item.0.namespace }} \ + --config {{ openshift.common.config_base }}/master/admin.kubeconfig \ + -o jsonpath='{ .metadata.annotations.openshift\.io/deployment\.phase }' + register: openshift_hosted_router_rc_phase + until: "'Running' not in openshift_hosted_router_rc_phase.stdout" + delay: 15 + retries: 40 + failed_when: "'Failed' in openshift_hosted_router_rc_phase.stdout" + with_together: + - "{{ openshift_hosted_routers }}" + - "{{ openshift_hosted_routers_latest_version.results }}" diff --git a/roles/openshift_hosted/templates/registry_config.j2 b/roles/openshift_hosted/templates/registry_config.j2 index fc9272679..61da452de 100644 --- a/roles/openshift_hosted/templates/registry_config.j2 +++ b/roles/openshift_hosted/templates/registry_config.j2 @@ -60,7 +60,7 @@ storage: gcs: bucket: {{ openshift_hosted_registry_storage_gcs_bucket }} {% if openshift_hosted_registry_storage_gcs_keyfile is defined %} - keyfile: {{ openshift_hosted_registry_storage_gcs_keyfile }} + keyfile: /etc/registry/gcs.json {% endif -%} {% if openshift_hosted_registry_storage_gcs_rootdirectory is defined %} rootdirectory: {{ openshift_hosted_registry_storage_gcs_rootdirectory }} diff --git a/roles/openshift_hosted/templates/registry_config_secret.j2 b/roles/openshift_hosted/templates/registry_config_secret.j2 deleted file mode 100644 index ca68544ec..000000000 --- a/roles/openshift_hosted/templates/registry_config_secret.j2 +++ /dev/null @@ -1,9 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: registry-config - annotations: - provider: {{ openshift.hosted.registry.storage.provider }} -data: - config.yml: {{ registry_config }} diff --git a/roles/openshift_hosted_logging/handlers/main.yml b/roles/openshift_hosted_logging/handlers/main.yml index ffb812271..d7e83fe9a 100644 --- a/roles/openshift_hosted_logging/handlers/main.yml +++ b/roles/openshift_hosted_logging/handlers/main.yml @@ -1,9 +1,4 @@ --- -- name: restart master - systemd: name={{ openshift.common.service_type }}-master state=restarted - when: (openshift.master.ha is not defined or not openshift.master.ha | bool) and (not (master_service_status_changed | default(false) | bool)) - notify: Verify API Server - - name: Verify API Server # Using curl here since the uri module requires python-httplib2 and # wait_for port doesn't provide health information. diff --git a/roles/openshift_hosted_metrics/handlers/main.yml b/roles/openshift_hosted_metrics/handlers/main.yml index 69c5a1663..ce7688581 100644 --- a/roles/openshift_hosted_metrics/handlers/main.yml +++ b/roles/openshift_hosted_metrics/handlers/main.yml @@ -1,17 +1,12 @@ --- -- name: restart master - systemd: name={{ openshift.common.service_type }}-master state=restarted - when: (openshift.master.ha is not defined or not openshift.master.ha | bool) and (not (master_service_status_changed | default(false) | bool)) - notify: Verify API Server - - name: restart master api systemd: name={{ openshift.common.service_type }}-master-api state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' notify: Verify API Server - name: restart master controllers systemd: name={{ openshift.common.service_type }}-master-controllers state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' - name: Verify API Server # Using curl here since the uri module requires python-httplib2 and diff --git a/roles/openshift_loadbalancer/defaults/main.yml b/roles/openshift_loadbalancer/defaults/main.yml index 6190383b6..41a2b12a2 100644 --- a/roles/openshift_loadbalancer/defaults/main.yml +++ b/roles/openshift_loadbalancer/defaults/main.yml @@ -1,4 +1,7 @@ --- +r_openshift_loadbalancer_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_openshift_loadbalancer_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + haproxy_frontends: - name: main binds: @@ -12,3 +15,13 @@ haproxy_backends: - name: web01 address: 127.0.0.1:9000 opts: check + +r_openshift_loadbalancer_os_firewall_deny: [] +r_openshift_loadbalancer_os_firewall_allow: +- service: haproxy stats + port: "9000/tcp" +- service: haproxy balance + port: "{{ openshift_master_api_port | default(8443) }}/tcp" +- service: nuage mon + port: "{{ nuage_mon_rest_server_port | default(9443) }}/tcp" + cond: "{{ openshift_use_nuage | default(false) | bool }}" diff --git a/roles/openshift_loadbalancer/meta/main.yml b/roles/openshift_loadbalancer/meta/main.yml index 0dffb545f..72298b599 100644 --- a/roles/openshift_loadbalancer/meta/main.yml +++ b/roles/openshift_loadbalancer/meta/main.yml @@ -10,16 +10,5 @@ galaxy_info: versions: - 7 dependencies: +- role: lib_os_firewall - role: openshift_facts -- role: os_firewall - os_firewall_allow: - - service: haproxy stats - port: "9000/tcp" - - service: haproxy balance - port: "{{ openshift_master_api_port | default(8443) }}/tcp" -- role: os_firewall - os_firewall_allow: - - service: nuage mon - port: "{{ nuage_mon_rest_server_port | default(9443) }}/tcp" - when: openshift_use_nuage | default(false) | bool -- role: openshift_repos diff --git a/roles/openshift_loadbalancer/tasks/firewall.yml b/roles/openshift_loadbalancer/tasks/firewall.yml new file mode 100644 index 000000000..7d6e8ff36 --- /dev/null +++ b/roles/openshift_loadbalancer/tasks/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_openshift_loadbalancer_firewall_enabled | bool and not r_openshift_loadbalancer_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_loadbalancer_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_loadbalancer_os_firewall_deny }}" + +- when: r_openshift_loadbalancer_firewall_enabled | bool and r_openshift_loadbalancer_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_openshift_loadbalancer_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_openshift_loadbalancer_os_firewall_deny }}" diff --git a/roles/openshift_loadbalancer/tasks/main.yml b/roles/openshift_loadbalancer/tasks/main.yml index 68bb4ace8..69b061fc5 100644 --- a/roles/openshift_loadbalancer/tasks/main.yml +++ b/roles/openshift_loadbalancer/tasks/main.yml @@ -1,4 +1,8 @@ --- +- name: setup firewall + include: firewall.yml + static: yes + - name: Install haproxy package: name=haproxy state=present when: not openshift.common.is_containerized | bool diff --git a/roles/openshift_logging/README.md b/roles/openshift_logging/README.md index 84ead3548..f283261c4 100644 --- a/roles/openshift_logging/README.md +++ b/roles/openshift_logging/README.md @@ -15,14 +15,25 @@ to the list of persisted [node labels](https://docs.openshift.org/latest/install ###Required vars: - `openshift_logging_install_logging`: When `True` the `openshift_logging` role will install Aggregated Logging. -- `openshift_logging_upgrade_logging`: When `True` the `openshift_logging` role will upgrade Aggregated Logging. -When both `openshift_logging_install_logging` and `openshift_logging_upgrade_logging` are `False` the `openshift_logging` role will uninstall Aggregated Logging. +When `openshift_logging_install_logging` is set to `False` the `openshift_logging` role will uninstall Aggregated Logging. ###Optional vars: - +- `openshift_logging_purge_logging`: When `openshift_logging_install_logging` is set to 'False' to trigger uninstalation and `openshift_logging_purge_logging` is set to 'True', it will completely and irreversibly remove all logging persistent data including PVC. Defaults to 'False'. - `openshift_logging_image_prefix`: The prefix for the logging images to use. Defaults to 'docker.io/openshift/origin-'. +- `openshift_logging_curator_image_prefix`: Setting the image prefix for Curator image. Defaults to `openshift_logging_image_prefix`. +- `openshift_logging_elasticsearch_image_prefix`: Setting the image prefix for Elasticsearch image. Defaults to `openshift_logging_image_prefix`. +- `openshift_logging_fluentd_image_prefix`: Setting the image prefix for Fluentd image. Defaults to `openshift_logging_image_prefix`. +- `openshift_logging_kibana_image_prefix`: Setting the image prefix for Kibana image. Defaults to `openshift_logging_image_prefix`. +- `openshift_logging_kibana_proxy_image_prefix`: Setting the image prefix for Kibana proxy image. Defaults to `openshift_logging_image_prefix`. +- `openshift_logging_mux_image_prefix`: Setting the image prefix for Mux image. Defaults to `openshift_logging_image_prefix`. - `openshift_logging_image_version`: The image version for the logging images to use. Defaults to 'latest'. +- `openshift_logging_curator_image_version`: Setting the image version for Curator image. Defaults to `openshift_logging_image_version`. +- `openshift_logging_elasticsearch_image_version`: Setting the image version for Elasticsearch image. Defaults to `openshift_logging_image_version`. +- `openshift_logging_fluentd_image_version`: Setting the image version for Fluentd image. Defaults to `openshift_logging_image_version`. +- `openshift_logging_kibana_image_version`: Setting the image version for Kibana image. Defaults to `openshift_logging_image_version`. +- `openshift_logging_kibana_proxy_image_version`: Setting the image version for Kibana proxy image. Defaults to `openshift_logging_image_version`. +- `openshift_logging_mux_image_version`: Setting the image version for Mux image. Defaults to `openshift_logging_image_version`. - `openshift_logging_use_ops`: If 'True', set up a second ES and Kibana cluster for infrastructure logs. Defaults to 'False'. - `openshift_logging_master_url`: The URL for the Kubernetes master, this does not need to be public facing but should be accessible from within the cluster. Defaults to 'https://kubernetes.default.svc.{{openshift.common.dns_domain}}'. - `openshift_logging_master_public_url`: The public facing URL for the Kubernetes master, this is used for Authentication redirection. Defaults to 'https://{{openshift.common.public_hostname}}:{{openshift.master.api_port}}'. diff --git a/roles/openshift_logging/defaults/main.yml b/roles/openshift_logging/defaults/main.yml index 8b0f4cb62..716f0e002 100644 --- a/roles/openshift_logging/defaults/main.yml +++ b/roles/openshift_logging/defaults/main.yml @@ -8,6 +8,7 @@ openshift_logging_labels: {} openshift_logging_label_key: "" openshift_logging_label_value: "" openshift_logging_install_logging: True +openshift_logging_purge_logging: False openshift_logging_image_pull_secret: "{{ openshift_hosted_logging_image_pull_secret | default('') }}" openshift_logging_curator_default_days: 30 @@ -84,7 +85,7 @@ openshift_logging_es_ca: /etc/fluent/keys/ca 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_cpu_limit: 1000m # the logging appenders for the root loggers to write ES logs. Valid values: 'file', 'console' openshift_logging_es_log_appenders: ['file'] openshift_logging_es_memory_limit: "{{ openshift_hosted_logging_elasticsearch_instance_ram | default('8Gi') }}" @@ -125,7 +126,7 @@ openshift_logging_es_ops_ca: /etc/fluent/keys/ca 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_cpu_limit: 1000m openshift_logging_es_ops_memory_limit: "{{ openshift_hosted_logging_elasticsearch_ops_instance_ram | default('8Gi') }}" openshift_logging_es_ops_pv_selector: "{{ openshift_hosted_loggingops_storage_labels | default('') }}" openshift_logging_es_ops_pvc_dynamic: "{{ openshift_hosted_logging_elasticsearch_ops_pvc_dynamic | default(False) }}" diff --git a/roles/openshift_logging/handlers/main.yml b/roles/openshift_logging/handlers/main.yml index 69c5a1663..ce7688581 100644 --- a/roles/openshift_logging/handlers/main.yml +++ b/roles/openshift_logging/handlers/main.yml @@ -1,17 +1,12 @@ --- -- name: restart master - systemd: name={{ openshift.common.service_type }}-master state=restarted - when: (openshift.master.ha is not defined or not openshift.master.ha | bool) and (not (master_service_status_changed | default(false) | bool)) - notify: Verify API Server - - name: restart master api systemd: name={{ openshift.common.service_type }}-master-api state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' notify: Verify API Server - name: restart master controllers systemd: name={{ openshift.common.service_type }}-master-controllers state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' - name: Verify API Server # Using curl here since the uri module requires python-httplib2 and diff --git a/roles/openshift_logging/tasks/annotate_ops_projects.yaml b/roles/openshift_logging/tasks/annotate_ops_projects.yaml new file mode 100644 index 000000000..d4b33616a --- /dev/null +++ b/roles/openshift_logging/tasks/annotate_ops_projects.yaml @@ -0,0 +1,17 @@ +--- +- oc_obj: + state: list + kind: project + name: "{{ item }}" + with_items: "{{ __default_logging_ops_projects }}" + register: __logging_ops_projects + +- name: Annotate Operations Projects + oc_edit: + kind: ns + name: "{{ item.item }}" + separator: '#' + content: + metadata#annotations#openshift.io/logging.ui.hostname: "{{ openshift_logging_kibana_ops_hostname }}" + with_items: "{{ __logging_ops_projects.results }}" + when: "{{ item.results.stderr is not defined }}" diff --git a/roles/openshift_logging/tasks/delete_logging.yaml b/roles/openshift_logging/tasks/delete_logging.yaml index 6d023a02d..45298e345 100644 --- a/roles/openshift_logging/tasks/delete_logging.yaml +++ b/roles/openshift_logging/tasks/delete_logging.yaml @@ -14,6 +14,16 @@ - templates - ds +# return all persistent volume claims as well if purge is set +- name: delete logging pvc objects + oc_obj: + state: absent + kind: pvc + namespace: "{{ openshift_logging_namespace }}" + selector: "logging-infra" + when: + - openshift_logging_purge_logging | default(false) | bool + # delete the oauthclient - name: delete oauthclient kibana-proxy oc_obj: diff --git a/roles/openshift_logging/tasks/install_logging.yaml b/roles/openshift_logging/tasks/install_logging.yaml index 464e8594f..a77df9986 100644 --- a/roles/openshift_logging/tasks/install_logging.yaml +++ b/roles/openshift_logging/tasks/install_logging.yaml @@ -132,6 +132,8 @@ openshift_logging_elasticsearch_pvc_size: "{{ openshift_logging_es_ops_pvc_size }}" openshift_logging_elasticsearch_pvc_dynamic: "{{ openshift_logging_es_ops_pvc_dynamic }}" openshift_logging_elasticsearch_pvc_pv_selector: "{{ openshift_logging_es_ops_pv_selector }}" + openshift_logging_elasticsearch_memory_limit: "{{ openshift_logging_es_ops_memory_limit }}" + openshift_logging_elasticsearch_cpu_limit: "{{ openshift_logging_es_ops_cpu_limit }}" openshift_logging_es_key: "{{ openshift_logging_es_ops_key }}" openshift_logging_es_cert: "{{ openshift_logging_es_ops_cert }}" openshift_logging_es_ca_ext: "{{ openshift_logging_es_ops_ca_ext }}" @@ -161,6 +163,8 @@ openshift_logging_elasticsearch_pvc_size: "{{ openshift_logging_es_ops_pvc_size }}" openshift_logging_elasticsearch_pvc_dynamic: "{{ openshift_logging_es_ops_pvc_dynamic }}" openshift_logging_elasticsearch_pvc_pv_selector: "{{ openshift_logging_es_ops_pv_selector }}" + openshift_logging_elasticsearch_memory_limit: "{{ openshift_logging_es_ops_memory_limit }}" + openshift_logging_elasticsearch_cpu_limit: "{{ openshift_logging_es_ops_cpu_limit }}" openshift_logging_es_key: "{{ openshift_logging_es_ops_key }}" openshift_logging_es_cert: "{{ openshift_logging_es_ops_cert }}" openshift_logging_es_ca_ext: "{{ openshift_logging_es_ops_ca_ext }}" @@ -181,8 +185,6 @@ openshift_logging_kibana_namespace: "{{ openshift_logging_namespace }}" openshift_logging_kibana_master_url: "{{ openshift_logging_master_url }}" openshift_logging_kibana_master_public_url: "{{ openshift_logging_master_public_url }}" - openshift_logging_kibana_image_prefix: "{{ openshift_logging_image_prefix }}" - openshift_logging_kibana_image_version: "{{ openshift_logging_image_version }}" openshift_logging_kibana_replicas: "{{ openshift_logging_kibana_replica_count }}" openshift_logging_kibana_es_host: "{{ openshift_logging_es_host }}" openshift_logging_kibana_es_port: "{{ openshift_logging_es_port }}" @@ -197,8 +199,6 @@ openshift_logging_kibana_namespace: "{{ openshift_logging_namespace }}" openshift_logging_kibana_master_url: "{{ openshift_logging_master_url }}" openshift_logging_kibana_master_public_url: "{{ openshift_logging_master_public_url }}" - openshift_logging_kibana_image_prefix: "{{ openshift_logging_image_prefix }}" - openshift_logging_kibana_image_version: "{{ openshift_logging_image_version }}" openshift_logging_kibana_image_pull_secret: "{{ openshift_logging_image_pull_secret }}" openshift_logging_kibana_es_host: "{{ openshift_logging_es_ops_host }}" openshift_logging_kibana_es_port: "{{ openshift_logging_es_ops_port }}" @@ -216,6 +216,7 @@ when: - openshift_logging_use_ops | bool +- include: annotate_ops_projects.yaml ## Curator - include_role: @@ -226,8 +227,6 @@ openshift_logging_curator_es_host: "{{ openshift_logging_es_host }}" openshift_logging_curator_es_port: "{{ openshift_logging_es_port }}" openshift_logging_curator_master_url: "{{ openshift_logging_master_url }}" - openshift_logging_curator_image_prefix: "{{ openshift_logging_image_prefix }}" - openshift_logging_curator_image_version: "{{ openshift_logging_image_version }}" openshift_logging_curator_image_pull_secret: "{{ openshift_logging_image_pull_secret }}" - include_role: @@ -239,8 +238,6 @@ openshift_logging_curator_es_port: "{{ openshift_logging_es_ops_port }}" openshift_logging_curator_namespace: "{{ openshift_logging_namespace }}" openshift_logging_curator_master_url: "{{ openshift_logging_master_url }}" - openshift_logging_curator_image_prefix: "{{ openshift_logging_image_prefix }}" - openshift_logging_curator_image_version: "{{ openshift_logging_image_version }}" openshift_logging_curator_image_pull_secret: "{{ openshift_logging_image_pull_secret }}" openshift_logging_curator_cpu_limit: "{{ openshift_logging_curator_ops_cpu_limit }}" openshift_logging_curator_memory_limit: "{{ openshift_logging_curator_ops_memory_limit }}" @@ -256,8 +253,6 @@ openshift_logging_mux_ops_host: "{{ ( openshift_logging_use_ops | bool ) | ternary('logging-es-ops', 'logging-es') }}" openshift_logging_mux_namespace: "{{ openshift_logging_namespace }}" openshift_logging_mux_master_url: "{{ openshift_logging_master_url }}" - openshift_logging_mux_image_prefix: "{{ openshift_logging_image_prefix }}" - openshift_logging_mux_image_version: "{{ openshift_logging_image_version }}" openshift_logging_mux_image_pull_secret: "{{ openshift_logging_image_pull_secret }}" when: - openshift_logging_use_mux | bool @@ -269,8 +264,6 @@ vars: generated_certs_dir: "{{openshift.common.config_base}}/logging" openshift_logging_fluentd_ops_host: "{{ ( openshift_logging_use_ops | bool ) | ternary('logging-es-ops', 'logging-es') }}" - openshift_logging_fluentd_image_prefix: "{{ openshift_logging_image_prefix }}" - openshift_logging_fluentd_image_version: "{{ openshift_logging_image_version }}" openshift_logging_fluentd_image_pull_secret: "{{ openshift_logging_image_pull_secret }}" openshift_logging_fluentd_master_url: "{{ openshift_logging_master_url }}" openshift_logging_fluentd_namespace: "{{ openshift_logging_namespace }}" diff --git a/roles/openshift_logging/tasks/update_master_config.yaml b/roles/openshift_logging/tasks/update_master_config.yaml index 10f522b61..b96b8e29d 100644 --- a/roles/openshift_logging/tasks/update_master_config.yaml +++ b/roles/openshift_logging/tasks/update_master_config.yaml @@ -5,7 +5,6 @@ yaml_key: assetConfig.loggingPublicURL yaml_value: "https://{{ openshift_logging_kibana_hostname }}" notify: - - restart master - restart master api - restart master controllers tags: diff --git a/roles/openshift_logging/vars/main.yaml b/roles/openshift_logging/vars/main.yaml index e561b41e2..01809fddf 100644 --- a/roles/openshift_logging/vars/main.yaml +++ b/roles/openshift_logging/vars/main.yaml @@ -6,3 +6,5 @@ es_ops_node_quorum: "{{ (openshift_logging_es_ops_cluster_size | int/2 | round(0 es_ops_recover_expected_nodes: "{{openshift_logging_es_ops_cluster_size | int}}" es_log_appenders: ['file', 'console'] + +__default_logging_ops_projects: ['default', 'openshift', 'openshift-infra', 'kube-system'] diff --git a/roles/openshift_logging_curator/defaults/main.yml b/roles/openshift_logging_curator/defaults/main.yml index 82ffb2f93..17807b644 100644 --- a/roles/openshift_logging_curator/defaults/main.yml +++ b/roles/openshift_logging_curator/defaults/main.yml @@ -1,7 +1,7 @@ --- ### General logging settings -openshift_logging_curator_image_prefix: "{{ openshift_hosted_logging_deployer_prefix | default('docker.io/openshift/origin-') }}" -openshift_logging_curator_image_version: "{{ openshift_hosted_logging_deployer_version | default('latest') }}" +openshift_logging_curator_image_prefix: "{{ openshift_logging_image_prefix | default(__openshift_logging_image_prefix) }}" +openshift_logging_curator_image_version: "{{ openshift_logging_image_version | default('latest') }}" openshift_logging_curator_image_pull_secret: "{{ openshift_hosted_logging_image_pull_secret | default('') }}" openshift_logging_curator_master_url: "https://kubernetes.default.svc.cluster.local" diff --git a/roles/openshift_logging_curator/tasks/main.yaml b/roles/openshift_logging_curator/tasks/main.yaml index 3113fb3c9..6e8fab2b5 100644 --- a/roles/openshift_logging_curator/tasks/main.yaml +++ b/roles/openshift_logging_curator/tasks/main.yaml @@ -86,7 +86,7 @@ component: "{{ curator_component }}" logging_component: curator deploy_name: "{{ curator_name }}" - image: "{{openshift_logging_image_prefix}}logging-curator:{{openshift_logging_image_version}}" + image: "{{openshift_logging_curator_image_prefix}}logging-curator:{{openshift_logging_curator_image_version}}" es_host: "{{ openshift_logging_curator_es_host }}" es_port: "{{ openshift_logging_curator_es_port }}" curator_cpu_limit: "{{ openshift_logging_curator_cpu_limit }}" diff --git a/roles/openshift_logging_elasticsearch/defaults/main.yml b/roles/openshift_logging_elasticsearch/defaults/main.yml index c0b5d394e..75bd479be 100644 --- a/roles/openshift_logging_elasticsearch/defaults/main.yml +++ b/roles/openshift_logging_elasticsearch/defaults/main.yml @@ -1,7 +1,7 @@ --- ### Common settings -openshift_logging_elasticsearch_image_prefix: "{{ openshift_hosted_logging_deployer_prefix | default('docker.io/openshift/origin-') }}" -openshift_logging_elasticsearch_image_version: "{{ openshift_hosted_logging_deployer_version | default('latest') }}" +openshift_logging_elasticsearch_image_prefix: "{{ openshift_logging_image_prefix | default(__openshift_logging_image_prefix) }}" +openshift_logging_elasticsearch_image_version: "{{ openshift_logging_image_version | default('latest') }}" openshift_logging_elasticsearch_image_pull_secret: "{{ openshift_hosted_logging_image_pull_secret | default('') }}" openshift_logging_elasticsearch_namespace: logging @@ -37,6 +37,9 @@ openshift_logging_elasticsearch_storage_group: '65534' openshift_logging_es_pvc_prefix: "{{ openshift_hosted_logging_elasticsearch_pvc_prefix | default('logging-es') }}" +# config the es plugin to write kibana index based on the index mode +openshift_logging_elasticsearch_kibana_index_mode: 'unique' + # this is used to determine if this is an operations deployment or a non-ops deployment # simply used for naming purposes openshift_logging_elasticsearch_ops_deployment: false diff --git a/roles/openshift_logging_elasticsearch/tasks/main.yaml b/roles/openshift_logging_elasticsearch/tasks/main.yaml index aae23668a..1e800b1d6 100644 --- a/roles/openshift_logging_elasticsearch/tasks/main.yaml +++ b/roles/openshift_logging_elasticsearch/tasks/main.yaml @@ -102,9 +102,21 @@ delete_after: true # configmap +- assert: + that: + - openshift_logging_elasticsearch_kibana_index_mode in __kibana_index_modes + msg: "The openshift_logging_elasticsearch_kibana_index_mode '{{ openshift_logging_elasticsearch_kibana_index_mode }}' only supports one of: {{ __kibana_index_modes | join(', ') }}" + +- assert: + that: + - "{{ openshift_logging_es_log_appenders | length > 0 }}" + msg: "The openshift_logging_es_log_appenders '{{ openshift_logging_es_log_appenders }}' has an unrecognized option and only supports the following as a list: {{ __es_log_appenders | join(', ') }}" + - template: src: elasticsearch-logging.yml.j2 dest: "{{ tempdir }}/elasticsearch-logging.yml" + vars: + root_logger: "{{openshift_logging_es_log_appenders | join(', ')}}" when: es_logging_contents is undefined changed_when: no @@ -115,6 +127,8 @@ allow_cluster_reader: "{{ openshift_logging_elasticsearch_ops_allow_cluster_reader | lower | default('false') }}" es_number_of_shards: "{{ openshift_logging_es_number_of_shards | default(1) }}" es_number_of_replicas: "{{ openshift_logging_es_number_of_replicas | default(0) }}" + es_kibana_index_mode: "{{ openshift_logging_elasticsearch_kibana_index_mode | default('unique') }}" + when: es_config_contents is undefined changed_when: no @@ -215,7 +229,7 @@ dest: "{{ tempdir }}/templates/logging-es-pvc.yml" vars: obj_name: "{{ openshift_logging_elasticsearch_pvc_name }}" - size: "{{ openshift_logging_elasticsearch_pvc_size }}" + size: "{{ (openshift_logging_elasticsearch_pvc_size | trim | length == 0) | ternary('10Gi', openshift_logging_elasticsearch_pvc_size) }}" access_modes: "{{ openshift_logging_elasticsearch_pvc_access_modes | list }}" pv_selector: "{{ openshift_logging_elasticsearch_pvc_pv_selector }}" storage_class_name: "{{ openshift_logging_elasticsearch_pvc_storage_class_name | default('', true) }}" @@ -229,7 +243,7 @@ dest: "{{ tempdir }}/templates/logging-es-pvc.yml" vars: obj_name: "{{ openshift_logging_elasticsearch_pvc_name }}" - size: "{{ openshift_logging_elasticsearch_pvc_size }}" + size: "{{ (openshift_logging_elasticsearch_pvc_size | trim | length == 0) | ternary('10Gi', openshift_logging_elasticsearch_pvc_size) }}" access_modes: "{{ openshift_logging_elasticsearch_pvc_access_modes | list }}" pv_selector: "{{ openshift_logging_elasticsearch_pvc_pv_selector }}" when: @@ -263,7 +277,7 @@ component: "{{ es_component }}" logging_component: elasticsearch deploy_name: "{{ es_deploy_name }}" - image: "{{ openshift_logging_image_prefix }}logging-elasticsearch:{{ openshift_logging_image_version }}" + image: "{{ openshift_logging_elasticsearch_image_prefix }}logging-elasticsearch:{{ openshift_logging_elasticsearch_image_version }}" es_cpu_limit: "{{ openshift_logging_elasticsearch_cpu_limit }}" es_memory_limit: "{{ openshift_logging_elasticsearch_memory_limit }}" es_node_selector: "{{ openshift_logging_elasticsearch_nodeselector | default({}) }}" diff --git a/roles/openshift_logging_elasticsearch/templates/elasticsearch-logging.yml.j2 b/roles/openshift_logging_elasticsearch/templates/elasticsearch-logging.yml.j2 index 38948ba2f..c7b2b2721 100644 --- a/roles/openshift_logging_elasticsearch/templates/elasticsearch-logging.yml.j2 +++ b/roles/openshift_logging_elasticsearch/templates/elasticsearch-logging.yml.j2 @@ -1,14 +1,26 @@ # you can override this using by setting a system property, for example -Des.logger.level=DEBUG es.logger.level: INFO -rootLogger: ${es.logger.level}, console, file +rootLogger: ${es.logger.level}, {{root_logger}} logger: # log action execution errors for easier debugging action: WARN + + # + # deprecation logging, turn to DEBUG to see them + deprecation: WARN, deprecation_log_file + # reduce the logging for aws, too much is logged under the default INFO com.amazonaws: WARN + io.fabric8.elasticsearch: ${PLUGIN_LOGLEVEL} io.fabric8.kubernetes: ${PLUGIN_LOGLEVEL} + # aws will try to do some sketchy JMX stuff, but its not needed. + com.amazonaws.jmx.SdkMBeanRegistrySupport: ERROR + com.amazonaws.metrics.AwsSdkMetrics: ERROR + + org.apache.http: INFO + # gateway #gateway: DEBUG #index.gateway: DEBUG @@ -28,13 +40,14 @@ logger: additivity: index.search.slowlog: false index.indexing.slowlog: false + deprecation: false appender: console: type: console layout: type: consolePattern - conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %.1000m%n" # need this filter until https://github.com/openshift/origin/issues/14515 is fixed filter: 1: @@ -67,6 +80,14 @@ appender: #type: pattern #conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + deprecation_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_deprecation.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + index_search_slow_log_file: type: dailyRollingFile file: ${path.logs}/${cluster.name}_index_search_slowlog.log diff --git a/roles/openshift_logging_elasticsearch/templates/elasticsearch.yml.j2 b/roles/openshift_logging_elasticsearch/templates/elasticsearch.yml.j2 index 141967c33..0c06a7677 100644 --- a/roles/openshift_logging_elasticsearch/templates/elasticsearch.yml.j2 +++ b/roles/openshift_logging_elasticsearch/templates/elasticsearch.yml.j2 @@ -53,6 +53,8 @@ openshift.searchguard: openshift.operations.allow_cluster_reader: {{allow_cluster_reader | default (false)}} +openshift.kibana.index.mode: {{es_kibana_index_mode | default('unique')}} + path: data: /elasticsearch/persistent/${CLUSTER_NAME}/data logs: /elasticsearch/${CLUSTER_NAME}/logs diff --git a/roles/openshift_logging_elasticsearch/vars/main.yml b/roles/openshift_logging_elasticsearch/vars/main.yml index 7a1f5048b..20fa63543 100644 --- a/roles/openshift_logging_elasticsearch/vars/main.yml +++ b/roles/openshift_logging_elasticsearch/vars/main.yml @@ -2,6 +2,8 @@ __latest_es_version: "3_5" __allowed_es_versions: ["3_5", "3_6"] __allowed_es_types: ["data-master", "data-client", "master", "client"] +__es_log_appenders: ['file', 'console'] +__kibana_index_modes: ["unique", "shared_ops"] # TODO: integrate these openshift_master_config_dir: "{{ openshift.common.config_base }}/master" diff --git a/roles/openshift_logging_fluentd/defaults/main.yml b/roles/openshift_logging_fluentd/defaults/main.yml index a53bbd2df..30d3d854a 100644 --- a/roles/openshift_logging_fluentd/defaults/main.yml +++ b/roles/openshift_logging_fluentd/defaults/main.yml @@ -1,7 +1,7 @@ --- ### General logging settings -openshift_logging_fluentd_image_prefix: "{{ openshift_hosted_logging_deployer_prefix | default('docker.io/openshift/origin-') }}" -openshift_logging_fluentd_image_version: "{{ openshift_hosted_logging_deployer_version | default('latest') }}" +openshift_logging_fluentd_image_prefix: "{{ openshift_logging_image_prefix | default(__openshift_logging_image_prefix) }}" +openshift_logging_fluentd_image_version: "{{ openshift_logging_image_version | default('latest') }}" openshift_logging_fluentd_image_pull_secret: "{{ openshift_hosted_logging_image_pull_secret | default('') }}" openshift_logging_fluentd_master_url: "https://kubernetes.default.svc.{{ openshift.common.dns_domain }}" openshift_logging_fluentd_namespace: logging diff --git a/roles/openshift_logging_fluentd/tasks/main.yaml b/roles/openshift_logging_fluentd/tasks/main.yaml index 9dfc6fc86..74b4d7db4 100644 --- a/roles/openshift_logging_fluentd/tasks/main.yaml +++ b/roles/openshift_logging_fluentd/tasks/main.yaml @@ -1,7 +1,7 @@ --- - fail: msg: Only one Fluentd nodeselector key pair should be provided - when: "{{ openshift_logging_fluentd_nodeselector.keys() | count }} > 1" + when: openshift_logging_fluentd_nodeselector.keys() | count > 1 - fail: msg: Application logs destination is required diff --git a/roles/openshift_logging_fluentd/templates/fluentd.j2 b/roles/openshift_logging_fluentd/templates/fluentd.j2 index 39dffba19..88e039e3f 100644 --- a/roles/openshift_logging_fluentd/templates/fluentd.j2 +++ b/roles/openshift_logging_fluentd/templates/fluentd.j2 @@ -28,7 +28,7 @@ spec: {{ fluentd_nodeselector_key }}: "{{ fluentd_nodeselector_value }}" containers: - name: "{{ daemonset_container_name }}" - image: "{{ openshift_logging_image_prefix }}{{ daemonset_name }}:{{ openshift_logging_image_version }}" + image: "{{ openshift_logging_fluentd_image_prefix }}{{ daemonset_name }}:{{ openshift_logging_fluentd_image_version }}" imagePullPolicy: Always securityContext: privileged: true diff --git a/roles/openshift_logging_kibana/defaults/main.yml b/roles/openshift_logging_kibana/defaults/main.yml index b2556fd71..ee265bb14 100644 --- a/roles/openshift_logging_kibana/defaults/main.yml +++ b/roles/openshift_logging_kibana/defaults/main.yml @@ -2,8 +2,8 @@ ### Common settings openshift_logging_kibana_master_url: "https://kubernetes.default.svc.cluster.local" openshift_logging_kibana_master_public_url: "https://kubernetes.default.svc.cluster.local" -openshift_logging_kibana_image_prefix: "{{ openshift_hosted_logging_deployer_prefix | default('docker.io/openshift/origin-') }}" -openshift_logging_kibana_image_version: "{{ openshift_hosted_logging_deployer_version | default('latest') }}" +openshift_logging_kibana_image_prefix: "{{ openshift_logging_image_prefix | default(__openshift_logging_image_prefix) }}" +openshift_logging_kibana_image_version: "{{ openshift_logging_image_version | default('latest') }}" openshift_logging_kibana_image_pull_secret: "{{ openshift_hosted_logging_image_pull_secret | default('') }}" openshift_logging_kibana_namespace: logging @@ -24,9 +24,11 @@ openshift_logging_kibana_edge_term_policy: Redirect openshift_logging_kibana_ops_deployment: false # Proxy settings +openshift_logging_kibana_proxy_image_prefix: "{{ openshift_logging_image_prefix | default(__openshift_logging_image_prefix) }}" +openshift_logging_kibana_proxy_image_version: "{{ openshift_logging_image_version | default('latest') }}" openshift_logging_kibana_proxy_debug: false openshift_logging_kibana_proxy_cpu_limit: null -openshift_logging_kibana_proxy_memory_limit: 96Mi +openshift_logging_kibana_proxy_memory_limit: 256Mi #The absolute path on the control node to the cert file to use #for the public facing kibana certs diff --git a/roles/openshift_logging_kibana/tasks/main.yaml b/roles/openshift_logging_kibana/tasks/main.yaml index 62bc26e37..e17e8c1f2 100644 --- a/roles/openshift_logging_kibana/tasks/main.yaml +++ b/roles/openshift_logging_kibana/tasks/main.yaml @@ -99,17 +99,17 @@ # TODO: set up these certs differently? - set_fact: kibana_key: "{{ lookup('file', openshift_logging_kibana_key) | b64encode }}" - when: "{{ openshift_logging_kibana_key | trim | length > 0 }}" + when: openshift_logging_kibana_key | trim | length > 0 changed_when: false - set_fact: kibana_cert: "{{ lookup('file', openshift_logging_kibana_cert) | b64encode }}" - when: "{{ openshift_logging_kibana_cert | trim | length > 0 }}" + when: openshift_logging_kibana_cert | trim | length > 0 changed_when: false - set_fact: kibana_ca: "{{ lookup('file', openshift_logging_kibana_ca) | b64encode }}" - when: "{{ openshift_logging_kibana_ca | trim | length > 0 }}" + when: openshift_logging_kibana_ca | trim | length > 0 changed_when: false - set_fact: @@ -225,8 +225,8 @@ component: "{{ kibana_component }}" logging_component: kibana deploy_name: "{{ kibana_name }}" - image: "{{ openshift_logging_image_prefix }}logging-kibana:{{ openshift_logging_image_version }}" - proxy_image: "{{ openshift_logging_image_prefix }}logging-auth-proxy:{{ openshift_logging_image_version }}" + image: "{{ openshift_logging_kibana_image_prefix }}logging-kibana:{{ openshift_logging_kibana_image_version }}" + proxy_image: "{{ openshift_logging_kibana_proxy_image_prefix }}logging-auth-proxy:{{ openshift_logging_kibana_proxy_image_version }}" es_host: "{{ openshift_logging_kibana_es_host }}" es_port: "{{ openshift_logging_kibana_es_port }}" kibana_cpu_limit: "{{ openshift_logging_kibana_cpu_limit }}" diff --git a/roles/openshift_logging_mux/defaults/main.yml b/roles/openshift_logging_mux/defaults/main.yml index 7a3da9b4c..68412aec8 100644 --- a/roles/openshift_logging_mux/defaults/main.yml +++ b/roles/openshift_logging_mux/defaults/main.yml @@ -1,7 +1,7 @@ --- ### General logging settings -openshift_logging_mux_image_prefix: "{{ openshift_hosted_logging_deployer_prefix | default('docker.io/openshift/origin-') }}" -openshift_logging_mux_image_version: "{{ openshift_hosted_logging_deployer_version | default('latest') }}" +openshift_logging_mux_image_prefix: "{{ openshift_logging_image_prefix | default(__openshift_logging_image_prefix) }}" +openshift_logging_mux_image_version: "{{ openshift_logging_image_version | default('latest') }}" openshift_logging_mux_image_pull_secret: "{{ openshift_hosted_logging_image_pull_secret | default('') }}" openshift_logging_mux_master_url: "https://kubernetes.default.svc.{{ openshift.common.dns_domain }}" openshift_logging_mux_master_public_url: "{{ openshift_hosted_logging_master_public_url | default('https://' + openshift.common.public_hostname + ':' ~ (openshift_master_api_port | default('8443', true))) }}" diff --git a/roles/openshift_logging_mux/tasks/main.yaml b/roles/openshift_logging_mux/tasks/main.yaml index 8ec93de7d..2ec863afa 100644 --- a/roles/openshift_logging_mux/tasks/main.yaml +++ b/roles/openshift_logging_mux/tasks/main.yaml @@ -165,7 +165,7 @@ component: mux logging_component: mux deploy_name: "logging-{{ component }}" - image: "{{ openshift_logging_image_prefix }}logging-fluentd:{{ openshift_logging_image_version }}" + image: "{{ openshift_logging_mux_image_prefix }}logging-fluentd:{{ openshift_logging_mux_image_version }}" es_host: "{{ openshift_logging_mux_app_host }}" es_port: "{{ openshift_logging_mux_app_port }}" ops_host: "{{ openshift_logging_mux_ops_host }}" diff --git a/roles/openshift_master/defaults/main.yml b/roles/openshift_master/defaults/main.yml index 2d3ce5bcd..d70106276 100644 --- a/roles/openshift_master/defaults/main.yml +++ b/roles/openshift_master/defaults/main.yml @@ -1,4 +1,26 @@ --- +r_openshift_master_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_openshift_master_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + openshift_node_ips: [] r_openshift_master_clean_install: false r_openshift_master_etcd3_storage: false +r_openshift_master_os_firewall_enable: true +r_openshift_master_os_firewall_deny: [] +r_openshift_master_os_firewall_allow: +- service: api server https + port: "{{ openshift.master.api_port }}/tcp" +- service: api controllers https + port: "{{ openshift.master.controllers_port }}/tcp" +- service: skydns tcp + port: "{{ openshift.master.dns_port }}/tcp" +- service: skydns udp + port: "{{ openshift.master.dns_port }}/udp" +- service: etcd embedded + port: 4001/tcp + cond: "{{ groups.oo_etcd_to_config | default([]) | length == 0 }}" + +oreg_url: '' +oreg_host: "{{ oreg_url.split('/')[0] if '.' in oreg_url.split('/')[0] else '' }}" +oreg_auth_credentials_path: "{{ openshift.common.data_dir }}/.docker" +oreg_auth_credentials_replace: False diff --git a/roles/openshift_master/files/atomic-openshift-master.service b/roles/openshift_master/files/atomic-openshift-master.service deleted file mode 100644 index 02af4dd16..000000000 --- a/roles/openshift_master/files/atomic-openshift-master.service +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Description=Atomic OpenShift Master -Documentation=https://github.com/openshift/origin -After=network-online.target -After=etcd.service -Before=atomic-openshift-node.service -Requires=network-online.target - -[Service] -Type=notify -EnvironmentFile=/etc/sysconfig/atomic-openshift-master -Environment=GOTRACEBACK=crash -ExecStart=/usr/bin/openshift start master --config=${CONFIG_FILE} $OPTIONS -LimitNOFILE=131072 -LimitCORE=infinity -WorkingDirectory=/var/lib/origin/ -SyslogIdentifier=atomic-openshift-master -Restart=always -RestartSec=5s - -[Install] -WantedBy=multi-user.target -WantedBy=atomic-openshift-node.service diff --git a/roles/openshift_master/files/origin-master.service b/roles/openshift_master/files/origin-master.service deleted file mode 100644 index cf79dda02..000000000 --- a/roles/openshift_master/files/origin-master.service +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Description=Origin Master Service -Documentation=https://github.com/openshift/origin -After=network-online.target -After=etcd.service -Before=origin-node.service -Requires=network-online.target - -[Service] -Type=notify -EnvironmentFile=/etc/sysconfig/origin-master -Environment=GOTRACEBACK=crash -ExecStart=/usr/bin/openshift start master --config=${CONFIG_FILE} $OPTIONS -LimitNOFILE=131072 -LimitCORE=infinity -WorkingDirectory=/var/lib/origin/ -SyslogIdentifier=origin-master -Restart=always -RestartSec=5s - -[Install] -WantedBy=multi-user.target -WantedBy=origin-node.service diff --git a/roles/openshift_master/handlers/main.yml b/roles/openshift_master/handlers/main.yml index 69c5a1663..d5094c2c9 100644 --- a/roles/openshift_master/handlers/main.yml +++ b/roles/openshift_master/handlers/main.yml @@ -1,17 +1,21 @@ --- -- name: restart master - systemd: name={{ openshift.common.service_type }}-master state=restarted - when: (openshift.master.ha is not defined or not openshift.master.ha | bool) and (not (master_service_status_changed | default(false) | bool)) - notify: Verify API Server - - name: restart master api - systemd: name={{ openshift.common.service_type }}-master-api state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' - notify: Verify API Server + systemd: + name: "{{ openshift.common.service_type }}-master-api" + state: restarted + when: + - not (master_api_service_status_changed | default(false) | bool) + - openshift.master.cluster_method == 'native' + notify: + - Verify API Server - name: restart master controllers - systemd: name={{ openshift.common.service_type }}-master-controllers state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + systemd: + name: "{{ openshift.common.service_type }}-master-controllers" + state: restarted + when: + - not (master_controllers_service_status_changed | default(false) | bool) + - openshift.master.cluster_method == 'native' - name: Verify API Server # Using curl here since the uri module requires python-httplib2 and @@ -28,8 +32,8 @@ # Disables the following warning: # Consider using get_url or uri module rather than running curl warn: no - register: api_available_output - until: api_available_output.stdout == 'ok' + register: l_api_available_output + until: l_api_available_output.stdout == 'ok' retries: 120 delay: 1 changed_when: false diff --git a/roles/openshift_master/meta/main.yml b/roles/openshift_master/meta/main.yml index 907f25bc5..bd2383f61 100644 --- a/roles/openshift_master/meta/main.yml +++ b/roles/openshift_master/meta/main.yml @@ -13,6 +13,7 @@ galaxy_info: - cloud dependencies: - role: lib_openshift +- role: lib_os_firewall - role: openshift_master_facts - role: openshift_hosted_facts - role: openshift_master_certificates @@ -25,21 +26,6 @@ dependencies: - role: openshift_cloud_provider - role: openshift_builddefaults - role: openshift_buildoverrides -- role: os_firewall - os_firewall_allow: - - service: api server https - port: "{{ openshift.master.api_port }}/tcp" - - service: api controllers https - port: "{{ openshift.master.controllers_port }}/tcp" - - service: skydns tcp - port: "{{ openshift.master.dns_port }}/tcp" - - service: skydns udp - port: "{{ openshift.master.dns_port }}/udp" -- role: os_firewall - os_firewall_allow: - - service: etcd embedded - port: 4001/tcp - when: groups.oo_etcd_to_config | default([]) | length == 0 - role: nickhammond.logrotate - role: contiv contiv_role: netmaster diff --git a/roles/openshift_master/tasks/bootstrap.yml b/roles/openshift_master/tasks/bootstrap.yml new file mode 100644 index 000000000..0013f5289 --- /dev/null +++ b/roles/openshift_master/tasks/bootstrap.yml @@ -0,0 +1,28 @@ +--- + +- name: ensure the node-bootstrap service account exists + oc_serviceaccount: + name: node-bootstrapper + namespace: openshift-infra + state: present + run_once: true + +- name: grant node-bootstrapper the correct permissions to bootstrap + oc_adm_policy_user: + namespace: openshift-infra + user: system:serviceaccount:openshift-infra:node-bootstrapper + resource_kind: cluster-role + resource_name: system:node-bootstrapper + state: present + run_once: true + +# TODO: create a module for this command. +# oc_serviceaccounts_kubeconfig +- name: create service account kubeconfig with csr rights + command: "oc serviceaccounts create-kubeconfig node-bootstrapper -n openshift-infra" + register: kubeconfig_out + +- name: put service account kubeconfig into a file on disk for bootstrap + copy: + content: "{{ kubeconfig_out.stdout }}" + dest: "{{ openshift_master_config_dir }}/bootstrap.kubeconfig" diff --git a/roles/openshift_master/tasks/clean_systemd_units.yml b/roles/openshift_master/tasks/clean_systemd_units.yml new file mode 100644 index 000000000..e641f84d4 --- /dev/null +++ b/roles/openshift_master/tasks/clean_systemd_units.yml @@ -0,0 +1,9 @@ +--- + +- name: Disable master service + systemd: + name: "{{ openshift.common.service_type }}-master" + state: stopped + enabled: no + masked: yes + ignore_errors: true diff --git a/roles/openshift_master/tasks/files b/roles/openshift_master/tasks/files deleted file mode 120000 index feb122881..000000000 --- a/roles/openshift_master/tasks/files +++ /dev/null @@ -1 +0,0 @@ -../files
\ No newline at end of file diff --git a/roles/openshift_master/tasks/firewall.yml b/roles/openshift_master/tasks/firewall.yml new file mode 100644 index 000000000..38afb6764 --- /dev/null +++ b/roles/openshift_master/tasks/firewall.yml @@ -0,0 +1,44 @@ +--- +- when: r_openshift_master_firewall_enabled | bool and not r_openshift_master_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: + - item.cond | default(True) + with_items: "{{ r_openshift_master_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: + - item.cond | default(True) + with_items: "{{ r_openshift_master_os_firewall_deny }}" + +- when: r_openshift_master_firewall_enabled | bool and r_openshift_master_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: + - item.cond | default(True) + with_items: "{{ r_openshift_master_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: + - item.cond | default(True) + with_items: "{{ r_openshift_master_os_firewall_deny }}" diff --git a/roles/openshift_master/tasks/main.yml b/roles/openshift_master/tasks/main.yml index 1f182a25c..a06defdb9 100644 --- a/roles/openshift_master/tasks/main.yml +++ b/roles/openshift_master/tasks/main.yml @@ -7,27 +7,45 @@ - fail: msg: > Invalid OAuth grant method: {{ openshift_master_oauth_grant_method }} - when: openshift_master_oauth_grant_method is defined and openshift_master_oauth_grant_method not in openshift_master_valid_grant_methods + when: + - openshift_master_oauth_grant_method is defined + - openshift_master_oauth_grant_method not in openshift_master_valid_grant_methods # HA Variable Validation - fail: msg: "openshift_master_cluster_method must be set to either 'native' or 'pacemaker' for multi-master installations" - when: openshift_master_ha | bool and ((openshift_master_cluster_method is not defined) or (openshift_master_cluster_method is defined and openshift_master_cluster_method not in ["native", "pacemaker"])) + when: + - openshift.master.ha | bool + - (openshift.master.cluster_method is not defined) or (openshift.master.cluster_method is defined and openshift.master.cluster_method not in ["native", "pacemaker"]) - fail: msg: "'native' high availability is not supported for the requested OpenShift version" - when: openshift_master_ha | bool and openshift_master_cluster_method == "native" and not openshift.common.version_gte_3_1_or_1_1 | bool + when: + - openshift.master.ha | bool + - openshift.master.cluster_method == "native" + - not openshift.common.version_gte_3_1_or_1_1 | bool - fail: msg: "openshift_master_cluster_password must be set for multi-master installations" - when: openshift_master_ha | bool and openshift_master_cluster_method == "pacemaker" and (openshift_master_cluster_password is not defined or not openshift_master_cluster_password) + when: + - openshift.master.ha | bool + - openshift.master.cluster_method == "pacemaker" + - openshift_master_cluster_password is not defined or not openshift_master_cluster_password - fail: msg: "Pacemaker based HA is not supported at this time when used with containerized installs" - when: openshift_master_ha | bool and openshift_master_cluster_method == "pacemaker" and openshift.common.is_containerized | bool + when: + - openshift.master.ha | bool + - openshift.master.cluster_method == "pacemaker" + - openshift.common.is_containerized | bool + +- name: Open up firewall ports + include: firewall.yml + static: yes - name: Install Master package package: name: "{{ openshift.common.service_type }}-master{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) }}" state: present - when: not openshift.common.is_containerized | bool + when: + - not openshift.common.is_containerized | bool - name: Create openshift.common.data_dir file: @@ -36,11 +54,13 @@ mode: 0755 owner: root group: root - when: openshift.common.is_containerized | bool + when: + - openshift.common.is_containerized | bool - name: Reload systemd units command: systemctl daemon-reload - when: openshift.common.is_containerized | bool and install_result | changed + when: + - openshift.common.is_containerized | bool - name: Re-gather package dependent master facts openshift_facts: @@ -57,9 +77,8 @@ args: creates: "{{ openshift_master_policy }}" notify: - - restart master - - restart master api - - restart master controllers + - restart master api + - restart master controllers - name: Create the scheduler config copy: @@ -67,21 +86,22 @@ dest: "{{ openshift_master_scheduler_conf }}" backup: true notify: - - restart master - - restart master api - - restart master controllers + - restart master api + - restart master controllers - name: Install httpd-tools if needed package: name=httpd-tools state=present - when: (item.kind == 'HTPasswdPasswordIdentityProvider') and - not openshift.common.is_atomic | bool + when: + - item.kind == 'HTPasswdPasswordIdentityProvider' + - not openshift.common.is_atomic | bool with_items: "{{ openshift.master.identity_providers }}" - name: Ensure htpasswd directory exists file: path: "{{ item.filename | dirname }}" state: directory - when: item.kind == 'HTPasswdPasswordIdentityProvider' + when: + - item.kind == 'HTPasswdPasswordIdentityProvider' with_items: "{{ openshift.master.identity_providers }}" - name: Create the htpasswd file if needed @@ -89,7 +109,9 @@ dest: "{{ item.filename }}" src: htpasswd.j2 backup: yes - when: item.kind == 'HTPasswdPasswordIdentityProvider' and openshift.master.manage_htpasswd | bool + when: + - item.kind == 'HTPasswdPasswordIdentityProvider' + - openshift.master.manage_htpasswd | bool with_items: "{{ openshift.master.identity_providers }}" - name: Ensure htpasswd file exists @@ -98,7 +120,8 @@ force: no content: "" mode: 0600 - when: item.kind == 'HTPasswdPasswordIdentityProvider' + when: + - item.kind == 'HTPasswdPasswordIdentityProvider' with_items: "{{ openshift.master.identity_providers }}" - name: Create the ldap ca file if needed @@ -107,7 +130,9 @@ content: "{{ openshift.master.ldap_ca }}" mode: 0600 backup: yes - when: openshift.master.ldap_ca is defined and item.kind == 'LDAPPasswordIdentityProvider' + when: + - openshift.master.ldap_ca is defined + - item.kind == 'LDAPPasswordIdentityProvider' with_items: "{{ openshift.master.identity_providers }}" - name: Create the openid ca file if needed @@ -116,7 +141,10 @@ content: "{{ openshift.master.openid_ca }}" mode: 0600 backup: yes - when: openshift.master.openid_ca is defined and item.kind == 'OpenIDIdentityProvider' and item.ca | default('') != '' + when: + - openshift.master.openid_ca is defined + - item.kind == 'OpenIDIdentityProvider' + - item.ca | default('') != '' with_items: "{{ openshift.master.identity_providers }}" - name: Create the request header ca file if needed @@ -125,20 +153,23 @@ content: "{{ openshift.master.request_header_ca }}" mode: 0600 backup: yes - when: openshift.master.request_header_ca is defined and item.kind == 'RequestHeaderIdentityProvider' and item.clientCA | default('') != '' + when: + - openshift.master.request_header_ca is defined + - item.kind == 'RequestHeaderIdentityProvider' + - item.clientCA | default('') != '' with_items: "{{ openshift.master.identity_providers }}" # This is an ugly hack to verify settings are in a file without modifying them with lineinfile. # The template file will stomp any other settings made. - block: - - name: check whether our docker-registry setting exists in the env file - command: "awk '/^OPENSHIFT_DEFAULT_REGISTRY=docker-registry.default.svc:5000/' /etc/sysconfig/{{ openshift.common.service_type }}-master" - failed_when: false - changed_when: false - register: already_set + - name: check whether our docker-registry setting exists in the env file + command: "awk '/^OPENSHIFT_DEFAULT_REGISTRY=docker-registry.default.svc:5000/' /etc/sysconfig/{{ openshift.common.service_type }}-master" + failed_when: false + changed_when: false + register: l_already_set - - set_fact: - openshift_push_via_dns: "{{ (openshift_use_dnsmasq | default(true) and openshift.common.version_gte_3_6) or (already_set.stdout is defined and already_set.stdout | match('OPENSHIFT_DEFAULT_REGISTRY=docker-registry.default.svc:5000')) }}" + - set_fact: + openshift_push_via_dns: "{{ (openshift_use_dnsmasq | default(true) and openshift.common.version_gte_3_6) or (l_already_set.stdout is defined and l_already_set.stdout | match('OPENSHIFT_DEFAULT_REGISTRY=docker-registry.default.svc:5000')) }}" - name: Set fact of all etcd host IPs openshift_facts: @@ -151,7 +182,9 @@ - name: Install Master system container include: system_container.yml - when: openshift.common.is_containerized | bool and openshift.common.is_master_system_container | bool + when: + - openshift.common.is_containerized | bool + - openshift.common.is_master_system_container | bool - name: Create session secrets file template: @@ -160,10 +193,11 @@ owner: root group: root mode: 0600 - when: openshift.master.session_auth_secrets is defined and openshift.master.session_encryption_secrets is defined + when: + - openshift.master.session_auth_secrets is defined + - openshift.master.session_encryption_secrets is defined notify: - - restart master - - restart master api + - restart master api - set_fact: translated_identity_providers: "{{ openshift.master.identity_providers | translate_idps('v1', openshift.common.version, openshift.common.deployment_type) }}" @@ -178,101 +212,96 @@ group: root mode: 0600 notify: - - restart master - - restart master api - - restart master controllers + - restart master api + - restart master controllers + +- name: modify controller args + yedit: + src: /etc/origin/master/master-config.yaml + edits: + - key: kubernetesMasterConfig.controllerArguments.cluster-signing-cert-file + value: + - /etc/origin/master/ca.crt + - key: kubernetesMasterConfig.controllerArguments.cluster-signing-key-file + value: + - /etc/origin/master/ca.key + notify: + - restart master controllers + when: openshift_master_bootstrap_enabled | default(False) + +- name: Check for credentials file for registry auth + stat: + path: "{{oreg_auth_credentials_path }}" + when: + - oreg_auth_user is defined + register: master_oreg_auth_credentials_stat + +- name: Create credentials for registry auth + command: "docker --config={{ oreg_auth_credentials_path }} login -u {{ oreg_auth_user }} -p {{ oreg_auth_password }} {{ oreg_host }}" + when: + - oreg_auth_user is defined + - (not master_oreg_auth_credentials_stat.stat.exists or oreg_auth_credentials_replace) | bool + notify: + - restart master api + - restart master controllers - include: set_loopback_context.yml - when: openshift.common.version_gte_3_2_or_1_2 - -# TODO: Master startup can fail when ec2 transparently reallocates the block -# storage, causing etcd writes to temporarily fail. Retry failures blindly just -# once to allow time for this transient condition to to resolve and for systemd -# to restart the master (which will eventually succeed). -# -# https://github.com/coreos/etcd/issues/3864 -# https://github.com/openshift/origin/issues/6065 -# https://github.com/openshift/origin/issues/6447 -- name: Start and enable master - systemd: - daemon_reload: yes - name: "{{ openshift.common.service_type }}-master" - enabled: yes - state: started - when: not openshift_master_ha | bool - register: start_result - until: not start_result | failed - retries: 1 - delay: 60 - notify: Verify API Server - -- name: Dump logs from master service if it failed - command: journalctl --no-pager -n 100 -u {{ openshift.common.service_type }}-master - when: start_result | failed - -- name: Stop and disable non-HA master when running HA - systemd: - name: "{{ openshift.common.service_type }}-master" - enabled: no - state: stopped - when: openshift_master_ha | bool - register: task_result - failed_when: task_result|failed and 'could not' not in task_result.msg|lower - -- set_fact: - master_service_status_changed: "{{ start_result | changed }}" - when: not openshift_master_ha | bool - -- name: Mask master service - systemd: - name: "{{ openshift.common.service_type }}-master" - masked: yes - when: > - openshift_master_ha | bool and - openshift.master.cluster_method == 'native' and - not openshift.common.is_containerized | bool + when: + - openshift.common.version_gte_3_2_or_1_2 - name: Start and enable master api on first master systemd: name: "{{ openshift.common.service_type }}-master-api" enabled: yes state: started - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' and inventory_hostname == openshift_master_hosts[0] - register: start_result - until: not start_result | failed + when: + - openshift.master.cluster_method == 'native' + - inventory_hostname == openshift_master_hosts[0] + register: l_start_result + until: not l_start_result | failed retries: 1 delay: 60 - name: Dump logs from master-api if it failed command: journalctl --no-pager -n 100 -u {{ openshift.common.service_type }}-master-api - when: start_result | failed + when: + - l_start_result | failed - set_fact: - master_api_service_status_changed: "{{ start_result | changed }}" - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' and inventory_hostname == openshift_master_hosts[0] + master_api_service_status_changed: "{{ l_start_result | changed }}" + when: + - openshift.master.cluster_method == 'native' + - inventory_hostname == openshift_master_hosts[0] - pause: seconds: 15 - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' + when: + - openshift.master.ha | bool + - openshift.master.cluster_method == 'native' - name: Start and enable master api all masters systemd: name: "{{ openshift.common.service_type }}-master-api" enabled: yes state: started - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' and inventory_hostname != openshift_master_hosts[0] - register: start_result - until: not start_result | failed + when: + - openshift.master.cluster_method == 'native' + - inventory_hostname != openshift_master_hosts[0] + register: l_start_result + until: not l_start_result | failed retries: 1 delay: 60 - name: Dump logs from master-api if it failed command: journalctl --no-pager -n 100 -u {{ openshift.common.service_type }}-master-api - when: start_result | failed + when: + - l_start_result | failed - set_fact: - master_api_service_status_changed: "{{ start_result | changed }}" - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' and inventory_hostname != openshift_master_hosts[0] + master_api_service_status_changed: "{{ l_start_result | changed }}" + when: + - openshift.master.cluster_method == 'native' + - inventory_hostname != openshift_master_hosts[0] # A separate wait is required here for native HA since notifies will # be resolved after all tasks in the role. @@ -287,67 +316,84 @@ --cacert {{ openshift.common.config_base }}/master/ca.crt {% endif %} {{ openshift.master.api_url }}/healthz/ready - register: api_available_output - until: api_available_output.stdout == 'ok' + register: l_api_available_output + until: l_api_available_output.stdout == 'ok' retries: 120 delay: 1 run_once: true changed_when: false - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' and master_api_service_status_changed | bool + when: + - openshift.master.cluster_method == 'native' + - master_api_service_status_changed | bool - name: Start and enable master controller on first master systemd: name: "{{ openshift.common.service_type }}-master-controllers" enabled: yes state: started - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' and inventory_hostname == openshift_master_hosts[0] - register: start_result - until: not start_result | failed + when: + - openshift.master.cluster_method == 'native' + - inventory_hostname == openshift_master_hosts[0] + register: l_start_result + until: not l_start_result | failed retries: 1 delay: 60 - name: Dump logs from master-controllers if it failed command: journalctl --no-pager -n 100 -u {{ openshift.common.service_type }}-master-controllers - when: start_result | failed + when: + - l_start_result | failed - name: Wait for master controller service to start on first master pause: seconds: 15 - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' + when: + - openshift.master.cluster_method == 'native' - name: Start and enable master controller on all masters systemd: name: "{{ openshift.common.service_type }}-master-controllers" enabled: yes state: started - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' and inventory_hostname != openshift_master_hosts[0] - register: start_result - until: not start_result | failed + when: + - openshift.master.cluster_method == 'native' + - inventory_hostname != openshift_master_hosts[0] + register: l_start_result + until: not l_start_result | failed retries: 1 delay: 60 - name: Dump logs from master-controllers if it failed command: journalctl --no-pager -n 100 -u {{ openshift.common.service_type }}-master-controllers - when: start_result | failed + when: + - l_start_result | failed - set_fact: - master_controllers_service_status_changed: "{{ start_result | changed }}" - when: openshift_master_ha | bool and openshift.master.cluster_method == 'native' + master_controllers_service_status_changed: "{{ l_start_result | changed }}" + when: + - openshift.master.cluster_method == 'native' - name: Install cluster packages package: name=pcs state=present - when: openshift_master_ha | bool and openshift.master.cluster_method == 'pacemaker' - and not openshift.common.is_containerized | bool - register: install_result + when: + - openshift.master.cluster_method == 'pacemaker' + - not openshift.common.is_containerized | bool + register: l_install_result - name: Start and enable cluster service systemd: name: pcsd enabled: yes state: started - when: openshift_master_ha | bool and openshift.master.cluster_method == 'pacemaker' - and not openshift.common.is_containerized | bool + when: + - openshift.master.cluster_method == 'pacemaker' + - not openshift.common.is_containerized | bool - name: Set the cluster user password shell: echo {{ openshift_master_cluster_password | quote }} | passwd --stdin hacluster - when: install_result | changed + when: + - l_install_result | changed + +- name: node bootstrap settings + include: bootstrap.yml + when: openshift_master_bootstrap_enabled | default(False) diff --git a/roles/openshift_master/tasks/set_loopback_context.yml b/roles/openshift_master/tasks/set_loopback_context.yml index 9c3fb31dc..308b2f4cd 100644 --- a/roles/openshift_master/tasks/set_loopback_context.yml +++ b/roles/openshift_master/tasks/set_loopback_context.yml @@ -4,7 +4,7 @@ {{ openshift.common.client_binary }} config view --config={{ openshift_master_loopback_config }} changed_when: false - register: loopback_config + register: l_loopback_config - command: > {{ openshift.common.client_binary }} config set-cluster @@ -12,7 +12,8 @@ --embed-certs=true --server={{ openshift.master.loopback_api_url }} {{ openshift.master.loopback_cluster_name }} --config={{ openshift_master_loopback_config }} - when: loopback_context_string not in loopback_config.stdout + when: + - loopback_context_string not in l_loopback_config.stdout register: set_loopback_cluster - command: > @@ -21,11 +22,13 @@ --namespace=default --user={{ openshift.master.loopback_user }} {{ openshift.master.loopback_context_name }} --config={{ openshift_master_loopback_config }} - when: set_loopback_cluster | changed - register: set_loopback_context + when: + - set_loopback_cluster | changed + register: l_set_loopback_context - command: > {{ openshift.common.client_binary }} config use-context {{ openshift.master.loopback_context_name }} --config={{ openshift_master_loopback_config }} - when: set_loopback_context | changed + when: + - l_set_loopback_context | changed register: set_current_context diff --git a/roles/openshift_master/tasks/system_container.yml b/roles/openshift_master/tasks/system_container.yml index 9944682cc..91332acfb 100644 --- a/roles/openshift_master/tasks/system_container.yml +++ b/roles/openshift_master/tasks/system_container.yml @@ -2,21 +2,12 @@ - name: Pre-pull master system container image command: > atomic pull --storage=ostree {{ 'docker:' if openshift.common.system_images_registry == 'docker' else openshift.common.system_images_registry + '/' }}{{ openshift.master.master_system_image }}:{{ openshift_image_tag }} - register: pull_result - changed_when: "'Pulling layer' in pull_result.stdout" + register: l_pull_result + changed_when: "'Pulling layer' in l_pull_result.stdout" - name: Check Master system container package command: > atomic containers list --no-trunc -a -f container={{ openshift.common.service_type }}-master - register: result - -- name: Install or Update master system container - oc_atomic_container: - name: "{{ openshift.common.service_type }}-master" - image: "{{ 'docker:' if openshift.common.system_images_registry == 'docker' else openshift.common.system_images_registry + '/' }}{{ openshift.master.master_system_image }}:{{ openshift_image_tag }}" - state: latest - when: - - not l_is_ha # HA - name: Install or Update HA api master system container @@ -25,16 +16,12 @@ image: "{{ 'docker:' if openshift.common.system_images_registry == 'docker' else openshift.common.system_images_registry + '/' }}{{ openshift.master.master_system_image }}:{{ openshift_image_tag }}" state: latest values: - - COMMAND=api - when: - - l_is_ha + - COMMAND=api - name: Install or Update HA controller master system container oc_atomic_container: name: "{{ openshift.common.service_type }}-master-controllers" - image: "{{{ 'docker:' if openshift.common.system_images_registry == 'docker' else openshift.common.system_images_registry + '/' }}{ openshift.master.master_system_image }}:{{ openshift_image_tag }}" + image: "{{ 'docker:' if openshift.common.system_images_registry == 'docker' else openshift.common.system_images_registry + '/' }}{{ openshift.master.master_system_image }}:{{ openshift_image_tag }}" state: latest values: - - COMMAND=controllers - when: - - l_is_ha + - COMMAND=controllers diff --git a/roles/openshift_master/tasks/systemd_units.yml b/roles/openshift_master/tasks/systemd_units.yml index d71ad3459..782a35abe 100644 --- a/roles/openshift_master/tasks/systemd_units.yml +++ b/roles/openshift_master/tasks/systemd_units.yml @@ -3,6 +3,16 @@ # playbooks. For that reason the ha_svc variables are use set_fact instead of # the vars directory on the role. +# This play may be consumed outside the role, we need to ensure that +# openshift_master_config_dir is set. +- name: Set openshift_master_config_dir if unset + set_fact: + openshift_master_config_dir: '/var/lib/origin' + when: openshift_master_config_dir is not defined + +- name: Remove the legacy master service if it exists + include: clean_systemd_units.yml + - name: Init HA Service Info set_fact: containerized_svc_dir: "/usr/lib/systemd/system" @@ -12,65 +22,49 @@ set_fact: containerized_svc_dir: "/etc/systemd/system" ha_svc_template_path: "docker-cluster" - when: openshift.common.is_containerized | bool + when: + - openshift.common.is_containerized | bool # This is the image used for both HA and non-HA clusters: - name: Pre-pull master image command: > docker pull {{ openshift.master.master_image }}:{{ openshift_image_tag }} - register: pull_result - changed_when: "'Downloaded newer image' in pull_result.stdout" - when: openshift.common.is_containerized | bool and not openshift.common.is_master_system_container | bool - -# workaround for missing systemd unit files -- name: "Create the {{ openshift.common.service_type }} systemd unit file" - template: - src: "master_docker/master.docker.service.j2" - dest: "{{ containerized_svc_dir }}/{{ openshift.common.service_type }}-master.service" + register: l_pull_result + changed_when: "'Downloaded newer image' in l_pull_result.stdout" when: - - openshift.common.is_containerized | bool and (openshift.master.ha is not defined or not openshift.master.ha) | bool + - openshift.common.is_containerized | bool - not openshift.common.is_master_system_container | bool - register: create_master_unit_file - -- name: "Install {{ openshift.common.service_type }} systemd unit file" - copy: - dest: "/etc/systemd/system/{{ openshift.common.service_type }}-master.service" - src: "{{ openshift.common.service_type }}-master.service" - register: create_master_unit_file - when: - - not openshift.common.is_containerized | bool - - (openshift.master.ha is not defined or not openshift.master.ha) | bool -- command: systemctl daemon-reload - when: create_master_unit_file | changed - -- name: Create the ha systemd unit files for api and controller services +- name: Create the ha systemd unit files 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" + - openshift.master.cluster_method == "native" - not openshift.common.is_master_system_container | bool with_items: - api - controllers - register: create_ha_unit_files + register: l_create_ha_unit_files - command: systemctl daemon-reload - when: create_ha_unit_files | changed + when: + - l_create_ha_unit_files | changed # end workaround for missing systemd unit files - name: Preserve Master API Proxy Config options command: grep PROXY /etc/sysconfig/{{ openshift.common.service_type }}-master-api - register: master_api_proxy - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + register: l_master_api_proxy + when: + - openshift.master.cluster_method == "native" failed_when: false changed_when: false - name: Preserve Master API AWS options command: grep AWS_ /etc/sysconfig/{{ openshift.common.service_type }}-master-api register: master_api_aws - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + when: + - openshift.master.cluster_method == "native" failed_when: false changed_when: false @@ -79,22 +73,27 @@ src: "{{ ha_svc_template_path }}/atomic-openshift-master-api.j2" dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-api backup: true - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + when: + - openshift.master.cluster_method == "native" notify: - restart master api - name: Restore Master API Proxy Config Options - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" - and master_api_proxy.rc == 0 and 'http_proxy' not in openshift.common and 'https_proxy' not in openshift.common + when: + - openshift.master.cluster_method == "native" + - l_master_api_proxy.rc == 0 + - "'http_proxy' not in openshift.common" + - "'https_proxy' not in openshift.common" lineinfile: dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-api line: "{{ item }}" - with_items: "{{ master_api_proxy.stdout_lines | default([]) }}" + with_items: "{{ l_master_api_proxy.stdout_lines | default([]) }}" - name: Restore Master API AWS Options - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" - and master_api_aws.rc == 0 and - not (openshift_cloudprovider_kind is defined and openshift_cloudprovider_kind == 'aws' and openshift_cloudprovider_aws_access_key is defined and openshift_cloudprovider_aws_secret_key is defined) + when: + - openshift.master.cluster_method == "native" + - master_api_aws.rc == 0 + - not (openshift_cloudprovider_kind is defined and openshift_cloudprovider_kind == 'aws' and openshift_cloudprovider_aws_access_key is defined and openshift_cloudprovider_aws_secret_key is defined) lineinfile: dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-api line: "{{ item }}" @@ -104,14 +103,16 @@ - name: Preserve Master Controllers Proxy Config options command: grep PROXY /etc/sysconfig/{{ openshift.common.service_type }}-master-controllers register: master_controllers_proxy - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + when: + - openshift.master.cluster_method == "native" failed_when: false changed_when: false - name: Preserve Master Controllers AWS options command: grep AWS_ /etc/sysconfig/{{ openshift.common.service_type }}-master-controllers register: master_controllers_aws - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + when: + - openshift.master.cluster_method == "native" failed_when: false changed_when: false @@ -120,7 +121,8 @@ src: "{{ ha_svc_template_path }}/atomic-openshift-master-controllers.j2" dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-controllers backup: true - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + when: + - openshift.master.cluster_method == "native" notify: - restart master controllers @@ -129,47 +131,18 @@ dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-controllers line: "{{ item }}" with_items: "{{ master_controllers_proxy.stdout_lines | default([]) }}" - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" - and master_controllers_proxy.rc == 0 and 'http_proxy' not in openshift.common and 'https_proxy' not in openshift.common + when: + - openshift.master.cluster_method == "native" + - master_controllers_proxy.rc == 0 + - "'http_proxy' not in openshift.common" + - "'https_proxy' not in openshift.common" - name: Restore Master Controllers AWS Options lineinfile: dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-controllers line: "{{ item }}" with_items: "{{ master_controllers_aws.stdout_lines | default([]) }}" - when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" - and master_controllers_aws.rc == 0 and - not (openshift_cloudprovider_kind is defined and openshift_cloudprovider_kind == 'aws' and openshift_cloudprovider_aws_access_key is defined and openshift_cloudprovider_aws_secret_key is defined) - -- name: Install Master docker service file - template: - dest: "/etc/systemd/system/{{ openshift.common.service_type }}-master.service" - src: master_docker/master.docker.service.j2 - register: install_result - when: openshift.common.is_containerized | bool and openshift.master.ha is defined and not openshift.master.ha | bool and not openshift.common.is_master_system_container | bool - -- name: Preserve Master Proxy Config options - command: grep PROXY /etc/sysconfig/{{ openshift.common.service_type }}-master - register: master_proxy_result - failed_when: false - changed_when: false - -- set_fact: - master_proxy: "{{ master_proxy_result.stdout_lines | default([]) }}" - -- name: Preserve Master AWS options - command: grep AWS_ /etc/sysconfig/{{ openshift.common.service_type }}-master - register: master_aws_result - failed_when: false - changed_when: false - -- set_fact: - master_aws: "{{ master_aws_result.stdout_lines | default([]) }}" - -- name: Create the master service env file - template: - src: "atomic-openshift-master.j2" - dest: /etc/sysconfig/{{ openshift.common.service_type }}-master - backup: true - notify: - - restart master + when: + - openshift.master.cluster_method == "native" + - master_controllers_aws.rc == 0 + - not (openshift_cloudprovider_kind is defined and openshift_cloudprovider_kind == 'aws' and openshift_cloudprovider_aws_access_key is defined and openshift_cloudprovider_aws_secret_key is defined) diff --git a/roles/openshift_master/templates/master.yaml.v1.j2 b/roles/openshift_master/templates/master.yaml.v1.j2 index 7964bbb48..c14579435 100644 --- a/roles/openshift_master/templates/master.yaml.v1.j2 +++ b/roles/openshift_master/templates/master.yaml.v1.j2 @@ -47,11 +47,10 @@ assetConfig: {% if openshift.master.audit_config | default(none) is not none and openshift.common.version_gte_3_2_or_1_2 | bool %} auditConfig:{{ openshift.master.audit_config | to_padded_yaml(level=1) }} {% endif %} -{% if openshift_master_ha | bool %} -controllerLeaseTTL: {{ openshift.master.controller_lease_ttl | default('30') }} -{% endif %} {% if openshift.common.version_gte_3_3_or_1_3 | bool %} controllerConfig: + election: + lockName: openshift-master-controllers serviceServingCert: signer: certFile: service-signer.crt diff --git a/roles/openshift_master/templates/master_docker/master.docker.service.j2 b/roles/openshift_master/templates/master_docker/master.docker.service.j2 deleted file mode 100644 index 31c1dfc33..000000000 --- a/roles/openshift_master/templates/master_docker/master.docker.service.j2 +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -After={{ openshift.docker.service_name }}.service -Requires={{ openshift.docker.service_name }}.service -PartOf={{ openshift.docker.service_name }}.service -After=etcd_container.service -Wants=etcd_container.service - -[Service] -EnvironmentFile=/etc/sysconfig/{{ openshift.common.service_type }}-master -ExecStartPre=-/usr/bin/docker rm -f {{ openshift.common.service_type }}-master -ExecStart=/usr/bin/docker run --rm --privileged --net=host --name {{ openshift.common.service_type }}-master --env-file=/etc/sysconfig/{{ openshift.common.service_type }}-master -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v /var/log:/var/log -v /var/run/docker.sock:/var/run/docker.sock -v {{ openshift.common.config_base }}:{{ openshift.common.config_base }} {% if openshift_cloudprovider_kind | default('') != '' -%} -v {{ openshift.common.config_base }}/cloudprovider:{{ openshift.common.config_base}}/cloudprovider {% endif -%} -v /etc/pki:/etc/pki:ro {{ openshift.master.master_image }}:${IMAGE_VERSION} start master --config=${CONFIG_FILE} $OPTIONS -ExecStartPost=/usr/bin/sleep 10 -ExecStop=/usr/bin/docker stop {{ openshift.common.service_type }}-master -Restart=always -RestartSec=5s - -[Install] -WantedBy={{ openshift.docker.service_name }}.service diff --git a/roles/openshift_master/vars/main.yml b/roles/openshift_master/vars/main.yml index 7745d014f..cf39b73f6 100644 --- a/roles/openshift_master/vars/main.yml +++ b/roles/openshift_master/vars/main.yml @@ -19,5 +19,4 @@ openshift_master_valid_grant_methods: - prompt - deny -l_is_ha: "{{ openshift.master.ha is defined and openshift.master.ha | bool }}" openshift_master_is_scaleup_host: False diff --git a/roles/openshift_master_cluster/meta/main.yml b/roles/openshift_master_cluster/meta/main.yml index f2a67bc54..c452b165e 100644 --- a/roles/openshift_master_cluster/meta/main.yml +++ b/roles/openshift_master_cluster/meta/main.yml @@ -12,5 +12,4 @@ galaxy_info: categories: - cloud - system -dependencies: -- { role: openshift_repos } +dependencies: [] diff --git a/roles/openshift_master_facts/tasks/main.yml b/roles/openshift_master_facts/tasks/main.yml index ef8dcd5fd..fa228af2a 100644 --- a/roles/openshift_master_facts/tasks/main.yml +++ b/roles/openshift_master_facts/tasks/main.yml @@ -32,7 +32,7 @@ openshift_facts: role: master local_facts: - cluster_method: "{{ openshift_master_cluster_method | default(None) }}" + cluster_method: "{{ openshift_master_cluster_method | default('native') }}" cluster_hostname: "{{ openshift_master_cluster_hostname | default(None) }}" cluster_public_hostname: "{{ openshift_master_cluster_public_hostname | default(None) }}" debug_level: "{{ openshift_master_debug_level | default(openshift.common.debug_level) }}" diff --git a/roles/openshift_metrics/handlers/main.yml b/roles/openshift_metrics/handlers/main.yml index 69c5a1663..ce7688581 100644 --- a/roles/openshift_metrics/handlers/main.yml +++ b/roles/openshift_metrics/handlers/main.yml @@ -1,17 +1,12 @@ --- -- name: restart master - systemd: name={{ openshift.common.service_type }}-master state=restarted - when: (openshift.master.ha is not defined or not openshift.master.ha | bool) and (not (master_service_status_changed | default(false) | bool)) - notify: Verify API Server - - name: restart master api systemd: name={{ openshift.common.service_type }}-master-api state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' notify: Verify API Server - name: restart master controllers systemd: name={{ openshift.common.service_type }}-master-controllers state=restarted - when: (openshift.master.ha is defined and openshift.master.ha | bool) and (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' - name: Verify API Server # Using curl here since the uri module requires python-httplib2 and diff --git a/roles/openshift_metrics/tasks/update_master_config.yaml b/roles/openshift_metrics/tasks/update_master_config.yaml index be1e3c3a0..5059d8d94 100644 --- a/roles/openshift_metrics/tasks/update_master_config.yaml +++ b/roles/openshift_metrics/tasks/update_master_config.yaml @@ -5,7 +5,6 @@ yaml_key: assetConfig.metricsPublicURL yaml_value: "https://{{ openshift_metrics_hawkular_hostname}}/hawkular/metrics" notify: - - restart master - restart master api - restart master controllers tags: diff --git a/roles/openshift_node/defaults/main.yml b/roles/openshift_node/defaults/main.yml index 47073ee0f..a7dad5b1f 100644 --- a/roles/openshift_node/defaults/main.yml +++ b/roles/openshift_node/defaults/main.yml @@ -1,5 +1,66 @@ --- -os_firewall_allow: +r_openshift_node_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_openshift_node_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + +openshift_service_type: "{{ openshift.common.service_type }}" + +openshift_image_tag: '' + +openshift_node_ami_prep_packages: +- "{{ openshift_service_type }}-master" +- "{{ openshift_service_type }}-node" +- "{{ openshift_service_type }}-docker-excluder" +- "{{ openshift_service_type }}-sdn-ovs" +- ansible +- openvswitch +- docker +- etcd +#- pcs +- haproxy +- dnsmasq +- ntp +- logrotate +- httpd-tools +- bind +- firewalld +- libselinux-python +- conntrack-tools +- openssl +- cloud-init +- iproute +- python-dbus +- PyYAML +- yum-utils +- python2-boto +- python2-boto3 +- cloud-utils-growpart +# gluster +- glusterfs-fuse +- heketi-client +# nfs +- nfs-utils +- flannel +- bash-completion +# cockpit +- cockpit-ws +- cockpit-system +- cockpit-bridge +- cockpit-docker +# iscsi +- iscsi-initiator-utils +# ceph +- ceph-common +# systemcontainer +# - runc +# - container-selinux +# - atomic +# +openshift_deployment_type: origin + +openshift_node_bootstrap: False + +r_openshift_node_os_firewall_deny: [] +r_openshift_node_os_firewall_allow: - service: Kubernetes kubelet port: 10250/tcp - service: http @@ -8,7 +69,18 @@ os_firewall_allow: port: 443/tcp - service: OpenShift OVS sdn port: 4789/udp - when: openshift.common.use_openshift_sdn | default(true) | bool + cond: openshift.common.use_openshift_sdn | default(true) | bool - service: Calico BGP Port port: 179/tcp - when: openshift.common.use_calico | bool + cond: "{{ openshift.common.use_calico | bool }}" +- service: Kubernetes service NodePort TCP + port: "{{ openshift_node_port_range | default('') }}/tcp" + cond: "{{ openshift_node_port_range is defined }}" +- service: Kubernetes service NodePort UDP + port: "{{ openshift_node_port_range | default('') }}/udp" + cond: "{{ openshift_node_port_range is defined }}" + +oreg_url: '' +oreg_host: "{{ oreg_url.split('/')[0] if '.' in oreg_url.split('/')[0] else '' }}" +oreg_auth_credentials_path: "{{ openshift.common.data_dir }}/.docker" +oreg_auth_credentials_replace: False diff --git a/roles/openshift_node/handlers/main.yml b/roles/openshift_node/handlers/main.yml index f2c45a4bd..14ba48aba 100644 --- a/roles/openshift_node/handlers/main.yml +++ b/roles/openshift_node/handlers/main.yml @@ -27,6 +27,7 @@ when: - (not skip_node_svc_handlers | default(False) | bool) - not (node_service_status_changed | default(false) | bool) + - not openshift_node_bootstrap - name: reload sysctl.conf command: /sbin/sysctl -p diff --git a/roles/openshift_node/meta/main.yml b/roles/openshift_node/meta/main.yml index 4fb841add..3db980514 100644 --- a/roles/openshift_node/meta/main.yml +++ b/roles/openshift_node/meta/main.yml @@ -14,36 +14,12 @@ galaxy_info: dependencies: - role: openshift_node_facts - role: lib_openshift +- role: lib_os_firewall - role: openshift_common - role: openshift_clock - role: openshift_docker - role: openshift_node_certificates + when: not openshift_node_bootstrap - role: openshift_cloud_provider -- role: os_firewall - os_firewall_allow: - - service: Kubernetes kubelet - port: 10250/tcp - - service: http - port: 80/tcp - - service: https - port: 443/tcp -- role: os_firewall - os_firewall_allow: - - service: OpenShift OVS sdn - port: 4789/udp - when: openshift.common.use_openshift_sdn | default(true) | 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 - port: "{{ openshift_node_port_range | default('') }}/udp" - when: openshift_node_port_range is defined - role: openshift_node_dnsmasq when: openshift.common.use_dnsmasq | bool diff --git a/roles/openshift_node/tasks/bootstrap.yml b/roles/openshift_node/tasks/bootstrap.yml new file mode 100644 index 000000000..cb1440283 --- /dev/null +++ b/roles/openshift_node/tasks/bootstrap.yml @@ -0,0 +1,55 @@ +--- +- name: install needed rpm(s) + package: + name: "{{ item }}" + state: present + with_items: "{{ openshift_node_ami_prep_packages }}" + +- name: create the directory for node + file: + state: directory + path: "/etc/systemd/system/{{ openshift_service_type }}-node.service.d" + +- name: laydown systemd override + copy: + dest: "/etc/systemd/system/{{ openshift_service_type }}-node.service.d/override.conf" + content: | + [Unit] + After=cloud-init.service + +- name: update the sysconfig to have KUBECONFIG + lineinfile: + dest: "/etc/sysconfig/{{ openshift_service_type }}-node" + line: "KUBECONFIG=/root/csr_kubeconfig" + regexp: "^KUBECONFIG=.*" + +- name: update the ExecStart to have bootstrap + lineinfile: + dest: "/usr/lib/systemd/system/{{ openshift_service_type }}-node.service" + line: "{% raw %}ExecStart=/usr/bin/openshift start node --bootstrap --kubeconfig=${KUBECONFIG} $OPTIONS{% endraw %}" + regexp: "^ExecStart=.*" + +- name: "systemctl enable {{ openshift_service_type }}-node" + systemd: + name: "{{ item }}" + enabled: no + with_items: + - "{{ openshift_service_type }}-node.service" + - "{{ openshift_service_type }}-master.service" + +- name: Check for RPM generated config marker file .config_managed + stat: + path: /etc/origin/.config_managed + register: rpmgenerated_config + +- name: Remove RPM generated config files if present + file: + path: "/etc/origin/{{ item }}" + state: absent + when: + - rpmgenerated_config.stat.exists + - openshift_deployment_type in ['openshift-enterprise', 'atomic-enterprise'] + with_items: + - master + - node + - .config_managed diff --git a/roles/openshift_node/tasks/config.yml b/roles/openshift_node/tasks/config.yml new file mode 100644 index 000000000..8210fd881 --- /dev/null +++ b/roles/openshift_node/tasks/config.yml @@ -0,0 +1,111 @@ +--- +- name: Install the systemd units + include: systemd_units.yml + +- name: Check for tuned package + command: rpm -q tuned + args: + warn: no + register: tuned_installed + changed_when: false + failed_when: false + +- name: Set atomic-guest tuned profile + command: "tuned-adm profile atomic-guest" + when: tuned_installed.rc == 0 and openshift.common.is_atomic | bool + +- name: Start and enable openvswitch service + systemd: + name: openvswitch.service + enabled: yes + state: started + daemon_reload: yes + when: + - openshift.common.is_containerized | bool + - openshift.common.use_openshift_sdn | default(true) | bool + register: ovs_start_result + until: not ovs_start_result | failed + retries: 3 + delay: 30 + +- set_fact: + ovs_service_status_changed: "{{ ovs_start_result | changed }}" + +- file: + dest: "{{ (openshift_node_kubelet_args|default({'config':None})).config}}" + state: directory + when: openshift_node_kubelet_args is defined and 'config' in openshift_node_kubelet_args + +# TODO: add the validate parameter when there is a validation command to run +- name: Create the Node config + template: + dest: "{{ openshift.common.config_base }}/node/node-config.yaml" + src: node.yaml.v1.j2 + backup: true + owner: root + group: root + mode: 0600 + notify: + - restart node + +- name: Configure Node Environment Variables + lineinfile: + dest: /etc/sysconfig/{{ openshift.common.service_type }}-node + regexp: "^{{ item.key }}=" + line: "{{ item.key }}={{ item.value }}" + create: true + with_dict: "{{ openshift.node.env_vars | default({}) }}" + notify: + - restart node + +# 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 +- when: openshift.common.is_containerized | bool + block: + - name: Wait for master API to become available before proceeding + # Using curl here since the uri module requires python-httplib2 and + # wait_for port doesn't provide health information. + command: > + curl --silent --tlsv1.2 --cacert {{ openshift.common.config_base }}/node/ca.crt + {{ openshift_node_master_api_url }}/healthz/ready + args: + # Disables the following warning: + # Consider using get_url or uri module rather than running curl + warn: no + register: api_available_output + until: api_available_output.stdout == 'ok' + retries: 120 + delay: 1 + changed_when: false + + - name: Start and enable node dep + systemd: + daemon_reload: yes + name: "{{ openshift.common.service_type }}-node-dep" + enabled: yes + state: started + +- name: Start and enable node + systemd: + name: "{{ openshift.common.service_type }}-node" + enabled: yes + state: started + daemon_reload: yes + register: node_start_result + until: not node_start_result | failed + retries: 1 + delay: 30 + ignore_errors: true + +- name: Dump logs from node service if it failed + command: journalctl --no-pager -n 100 -u {{ openshift.common.service_type }}-node + when: node_start_result | failed + +- name: Abort if node failed to start + fail: + msg: Node failed to start please inspect the logs and try again + when: node_start_result | failed + +- set_fact: + node_service_status_changed: "{{ node_start_result | changed }}" diff --git a/roles/openshift_node/tasks/firewall.yml b/roles/openshift_node/tasks/firewall.yml new file mode 100644 index 000000000..255aa886a --- /dev/null +++ b/roles/openshift_node/tasks/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_openshift_node_firewall_enabled | bool and not r_openshift_node_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_node_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_node_os_firewall_deny }}" + +- when: r_openshift_node_firewall_enabled | bool and r_openshift_node_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_openshift_node_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_openshift_node_os_firewall_deny }}" diff --git a/roles/openshift_node/tasks/install.yml b/roles/openshift_node/tasks/install.yml new file mode 100644 index 000000000..9bf4ed879 --- /dev/null +++ b/roles/openshift_node/tasks/install.yml @@ -0,0 +1,33 @@ +--- +# We have to add tuned-profiles in the same transaction otherwise we run into depsolving +# problems because the rpms don't pin the version properly. This was fixed in 3.1 packaging. +- when: not openshift.common.is_containerized | bool + block: + - name: Install Node package + package: + name: "{{ openshift.common.service_type }}-node{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) }},tuned-profiles-{{ openshift.common.service_type }}-node{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) }}" + state: present + + - name: Install sdn-ovs package + package: + name: "{{ openshift.common.service_type }}-sdn-ovs{{ openshift_pkg_version | oo_image_tag_to_rpm_version(include_dash=True) }}" + state: present + when: + - openshift.common.use_openshift_sdn | default(true) | bool + + - name: Install conntrack-tools package + package: + name: "conntrack-tools" + state: present + +- when: + - openshift.common.is_containerized | bool + - not openshift.common.is_node_system_container | bool + block: + - name: Pre-pull node image when containerized + command: > + docker pull {{ openshift.node.node_image }}:{{ openshift_image_tag }} + register: pull_result + changed_when: "'Downloaded newer image' in pull_result.stdout" + + - include: config/install-node-docker-service-file.yml diff --git a/roles/openshift_node/tasks/main.yml b/roles/openshift_node/tasks/main.yml index ca4fef360..60a25dcc6 100644 --- a/roles/openshift_node/tasks/main.yml +++ b/roles/openshift_node/tasks/main.yml @@ -1,11 +1,15 @@ --- -# TODO: allow for overriding default ports where possible - fail: msg: "SELinux is disabled, This deployment type requires that SELinux is enabled." when: - (not ansible_selinux or ansible_selinux.status != 'enabled') and deployment_type in ['enterprise', 'online', 'atomic-enterprise', 'openshift-enterprise'] - - not openshift_docker_use_crio | default(false) + - not openshift_use_crio | default(false) +- name: setup firewall + include: firewall.yml + static: yes + +#### Disable SWAP ##### # https://docs.openshift.com/container-platform/3.4/admin_guide/overcommit.html#disabling-swap-memory - name: Check for swap usage command: grep "^[^#].*swap" /etc/fstab @@ -14,9 +18,10 @@ failed_when: false register: swap_result -# Disable Swap Block -- block: - +- when: + - swap_result.stdout_lines | length > 0 + - openshift_disable_swap | default(true) | bool + block: - name: Disable swap command: swapoff --all @@ -32,55 +37,17 @@ dest: /etc/fstab line: '# OpenShift-Ansible Installer disabled swap per overcommit guidelines' state: present +#### End Disable Swap Block #### - when: - - swap_result.stdout_lines | length > 0 - - openshift_disable_swap | default(true) | bool -# End Disable Swap Block - -# We have to add tuned-profiles in the same transaction otherwise we run into depsolving -# problems because the rpms don't pin the version properly. This was fixed in 3.1 packaging. -- name: Install Node package - package: - name: "{{ openshift.common.service_type }}-node{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) }},tuned-profiles-{{ openshift.common.service_type }}-node{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) }}" - state: present - when: not openshift.common.is_containerized | bool - -- name: Check for tuned package - command: rpm -q tuned - args: - warn: no - register: tuned_installed - changed_when: false - failed_when: false - -- name: Set atomic-guest tuned profile - command: "tuned-adm profile atomic-guest" - when: tuned_installed.rc == 0 and openshift.common.is_atomic | bool - -- name: Install sdn-ovs package - package: - name: "{{ openshift.common.service_type }}-sdn-ovs{{ openshift_pkg_version | oo_image_tag_to_rpm_version(include_dash=True) }}" - state: present - when: - - openshift.common.use_openshift_sdn | default(true) | bool - - not openshift.common.is_containerized | bool +- name: include node installer + include: install.yml - name: Restart cri-o systemd: name: cri-o enabled: yes state: restarted - when: openshift_docker_use_crio | default(false) - -- name: Install conntrack-tools package - package: - name: "conntrack-tools" - state: present - when: not openshift.common.is_containerized | bool - -- name: Install the systemd units - include: systemd_units.yml + when: openshift_use_crio | default(false) # The atomic-openshift-node service will set this parameter on # startup, but if the network service is restarted this setting is @@ -94,37 +61,26 @@ notify: - reload sysctl.conf -- name: Start and enable openvswitch service - systemd: - name: openvswitch.service - enabled: yes - state: started - daemon_reload: yes +- name: include bootstrap node config + include: bootstrap.yml + when: openshift_node_bootstrap + +- name: include standard node config + include: config.yml + when: not openshift_node_bootstrap + +- name: Check for credentials file for registry auth + stat: + path: "{{oreg_auth_credentials_path }}" + when: + - oreg_auth_user is defined + register: node_oreg_auth_credentials_stat + +- name: Create credentials for registry auth + command: "docker --config={{ oreg_auth_credentials_path }} login -u {{ oreg_auth_user }} -p {{ oreg_auth_password }} {{ oreg_host }}" when: - - openshift.common.is_containerized | bool - - openshift.common.use_openshift_sdn | default(true) | bool - register: ovs_start_result - until: not ovs_start_result | failed - retries: 3 - delay: 30 - -- set_fact: - ovs_service_status_changed: "{{ ovs_start_result | changed }}" - -- file: - dest: "{{ (openshift_node_kubelet_args|default({'config':None})).config}}" - state: directory - when: openshift_node_kubelet_args is defined and 'config' in openshift_node_kubelet_args - -# TODO: add the validate parameter when there is a validation command to run -- name: Create the Node config - template: - dest: "{{ openshift.common.config_base }}/node/node-config.yaml" - src: node.yaml.v1.j2 - backup: true - owner: root - group: root - mode: 0600 + - oreg_auth_user is defined + - (not node_oreg_auth_credentials_stat.stat.exists or oreg_auth_credentials_replace) | bool notify: - restart node @@ -144,16 +100,7 @@ notify: - restart node -- name: Configure Node Environment Variables - lineinfile: - dest: /etc/sysconfig/{{ openshift.common.service_type }}-node - regexp: "^{{ item.key }}=" - line: "{{ item.key }}={{ item.value }}" - create: true - with_dict: "{{ openshift.node.env_vars | default({}) }}" - notify: - - restart node - +#### Storage class plugins here #### - name: NFS storage plugin configuration include: storage_plugins/nfs.yml tags: @@ -171,55 +118,7 @@ include: storage_plugins/iscsi.yml when: "'iscsi' in openshift.node.storage_plugin_deps" -# 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 -- name: Wait for master API to become available before proceeding - # Using curl here since the uri module requires python-httplib2 and - # wait_for port doesn't provide health information. - command: > - curl --silent --tlsv1.2 --cacert {{ openshift.common.config_base }}/node/ca.crt - {{ openshift_node_master_api_url }}/healthz/ready - args: - # Disables the following warning: - # Consider using get_url or uri module rather than running curl - warn: no - register: api_available_output - until: api_available_output.stdout == 'ok' - retries: 120 - delay: 1 - changed_when: false - when: openshift.common.is_containerized | bool - -- name: Start and enable node dep - systemd: - daemon_reload: yes - name: "{{ openshift.common.service_type }}-node-dep" - enabled: yes - state: started - when: openshift.common.is_containerized | bool - +##### END Storage ##### -- name: Start and enable node - systemd: - name: "{{ openshift.common.service_type }}-node" - enabled: yes - state: started - daemon_reload: yes - register: node_start_result - until: not node_start_result | failed - retries: 1 - delay: 30 - ignore_errors: true - -- name: Dump logs from node service if it failed - command: journalctl --no-pager -n 100 -u {{ openshift.common.service_type }}-node - when: node_start_result | failed - -- name: Abort if node failed to start - fail: - msg: Node failed to start please inspect the logs and try again - when: node_start_result | failed - -- set_fact: - node_service_status_changed: "{{ node_start_result | changed }}" +- include: config/workaround-bz1331590-ovs-oom-fix.yml + when: openshift.common.use_openshift_sdn | default(true) | bool diff --git a/roles/openshift_node/tasks/openvswitch_system_container.yml b/roles/openshift_node/tasks/openvswitch_system_container.yml index dc1df9185..e09063aa5 100644 --- a/roles/openshift_node/tasks/openvswitch_system_container.yml +++ b/roles/openshift_node/tasks/openvswitch_system_container.yml @@ -1,6 +1,6 @@ --- - set_fact: - l_use_crio: "{{ openshift_docker_use_crio | default(false) }}" + l_use_crio: "{{ openshift_use_crio | default(false) }}" - set_fact: l_service_name: "cri-o" diff --git a/roles/openshift_node/tasks/systemd_units.yml b/roles/openshift_node/tasks/systemd_units.yml index b86bb1549..4687400cd 100644 --- a/roles/openshift_node/tasks/systemd_units.yml +++ b/roles/openshift_node/tasks/systemd_units.yml @@ -1,22 +1,6 @@ --- # This file is included both in the openshift_master role and in the upgrade # playbooks. - -- include: config/install-node-deps-docker-service-file.yml - when: openshift.common.is_containerized | bool - -- block: - - name: Pre-pull node image - command: > - docker pull {{ openshift.node.node_image }}:{{ openshift_image_tag }} - register: pull_result - changed_when: "'Downloaded newer image' in pull_result.stdout" - - - include: config/install-node-docker-service-file.yml - when: - - openshift.common.is_containerized | bool - - not openshift.common.is_node_system_container | bool - - name: Install Node service file template: dest: "/etc/systemd/system/{{ openshift.common.service_type }}-node.service" @@ -26,24 +10,24 @@ - reload systemd units - restart node -- include: config/install-ovs-service-env-file.yml - when: openshift.common.is_containerized | bool +- when: openshift.common.is_containerized | bool + block: + - name: include node deps docker service file + include: config/install-node-deps-docker-service-file.yml -- name: Install Node system container - include: node_system_container.yml - when: - - openshift.common.is_containerized | bool - - openshift.common.is_node_system_container | bool + - name: include ovs service environment file + include: config/install-ovs-service-env-file.yml -- name: Install OpenvSwitch system containers - include: openvswitch_system_container.yml - when: - - openshift.common.use_openshift_sdn | default(true) | bool - - openshift.common.is_containerized | bool - - openshift.common.is_openvswitch_system_container | bool + - name: Install Node system container + include: node_system_container.yml + when: + - openshift.common.is_node_system_container | bool -- include: config/workaround-bz1331590-ovs-oom-fix.yml - when: openshift.common.use_openshift_sdn | default(true) | bool + - name: Install OpenvSwitch system containers + include: openvswitch_system_container.yml + when: + - openshift.common.use_openshift_sdn | default(true) | bool + - openshift.common.is_openvswitch_system_container | bool - block: - name: Pre-pull openvswitch image diff --git a/roles/openshift_node/tasks/tuned.yml b/roles/openshift_node/tasks/tuned.yml new file mode 100644 index 000000000..425bf6a26 --- /dev/null +++ b/roles/openshift_node/tasks/tuned.yml @@ -0,0 +1,41 @@ +--- +- name: Check for tuned package + command: rpm -q tuned + args: + warn: no + register: tuned_installed + changed_when: false + failed_when: false + +- name: Tuned service setup + block: + - name: Set tuned OpenShift variables + set_fact: + openshift_tuned_guest_profile: "{{ 'atomic-guest' if openshift.common.is_atomic else 'virtual-guest' }}" + tuned_etc_directory: '/etc/tuned' + tuned_templates_source: '../templates/tuned' + + - name: Ensure directory structure exists + file: + state: directory + dest: '{{ tuned_etc_directory }}/{{ item.path }}' + with_filetree: '{{ tuned_templates_source }}' + when: item.state == 'directory' + + - name: Ensure files are populated from templates + template: + src: '{{ item.src }}' + dest: '{{ tuned_etc_directory }}/{{ item.path }}' + with_filetree: '{{ tuned_templates_source }}' + when: item.state == 'file' + + - name: Make tuned use the recommended tuned profile on restart + file: path=/etc/tuned/active_profile state=absent + + - name: Restart tuned service + systemd: + state: restarted + daemon_reload: yes + name: tuned + + when: tuned_installed.rc == 0 | bool diff --git a/roles/openshift_node/templates/node.service.j2 b/roles/openshift_node/templates/node.service.j2 index 3d0ae3bbd..0856737f6 100644 --- a/roles/openshift_node/templates/node.service.j2 +++ b/roles/openshift_node/templates/node.service.j2 @@ -8,7 +8,7 @@ Wants={{ openshift.docker.service_name }}.service Documentation=https://github.com/openshift/origin Requires=dnsmasq.service After=dnsmasq.service -{% if openshift.docker.use_crio %}Wants=cri-o.service{% endif %} +{% if openshift_use_crio|default(false) %}Wants=cri-o.service{% endif %} [Service] Type=notify diff --git a/roles/openshift_node/templates/node.yaml.v1.j2 b/roles/openshift_node/templates/node.yaml.v1.j2 index 93f8658b4..711afcadb 100644 --- a/roles/openshift_node/templates/node.yaml.v1.j2 +++ b/roles/openshift_node/templates/node.yaml.v1.j2 @@ -16,7 +16,7 @@ imageConfig: latest: false kind: NodeConfig kubeletArguments: {{ openshift.node.kubelet_args | default(None) | to_padded_yaml(level=1) }} -{% if openshift.docker.use_crio | default(False) %} +{% if openshift_use_crio | default(False) %} container-runtime: - remote container-runtime-endpoint: diff --git a/roles/openshift_node/templates/openshift.docker.node.dep.service b/roles/openshift_node/templates/openshift.docker.node.dep.service index c4580be1f..8734e7443 100644 --- a/roles/openshift_node/templates/openshift.docker.node.dep.service +++ b/roles/openshift_node/templates/openshift.docker.node.dep.service @@ -3,7 +3,7 @@ Requires={{ openshift.docker.service_name }}.service After={{ openshift.docker.service_name }}.service PartOf={{ openshift.common.service_type }}-node.service Before={{ openshift.common.service_type }}-node.service -{% if openshift.docker.use_crio %}Wants=cri-o.service{% endif %} +{% if openshift_use_crio|default(false) %}Wants=cri-o.service{% endif %} [Service] ExecStart=/bin/bash -c "if [[ -f /usr/bin/docker-current ]]; then echo \"DOCKER_ADDTL_BIND_MOUNTS=--volume=/usr/bin/docker-current:/usr/bin/docker-current:ro --volume=/etc/sysconfig/docker:/etc/sysconfig/docker:ro\" > /etc/sysconfig/{{ openshift.common.service_type }}-node-dep; else echo \"#DOCKER_ADDTL_BIND_MOUNTS=\" > /etc/sysconfig/{{ openshift.common.service_type }}-node-dep; fi" diff --git a/roles/openshift_node/templates/tuned/openshift-control-plane/tuned.conf b/roles/openshift_node/templates/tuned/openshift-control-plane/tuned.conf new file mode 100644 index 000000000..f22f21065 --- /dev/null +++ b/roles/openshift_node/templates/tuned/openshift-control-plane/tuned.conf @@ -0,0 +1,25 @@ +# +# tuned configuration +# + +[main] +summary=Optimize systems running OpenShift control plane +include=openshift + +[sysctl] +# ktune sysctl settings, maximizing i/o throughput +# +# Minimal preemption granularity for CPU-bound tasks: +# (default: 1 msec# (1 + ilog(ncpus)), units: nanoseconds) +kernel.sched_min_granularity_ns=10000000 + +# The total time the scheduler will consider a migrated process +# "cache hot" and thus less likely to be re-migrated +# (system default is 500000, i.e. 0.5 ms) +kernel.sched_migration_cost_ns=5000000 + +# SCHED_OTHER wake-up granularity. +# +# Preemption granularity when tasks wake up. Lower the value to improve +# wake-up latency and throughput for latency critical tasks. +kernel.sched_wakeup_granularity_ns = 4000000 diff --git a/roles/openshift_node/templates/tuned/openshift-node/tuned.conf b/roles/openshift_node/templates/tuned/openshift-node/tuned.conf new file mode 100644 index 000000000..78c7d19c9 --- /dev/null +++ b/roles/openshift_node/templates/tuned/openshift-node/tuned.conf @@ -0,0 +1,10 @@ +# +# tuned configuration +# + +[main] +summary=Optimize systems running OpenShift nodes +include=openshift + +[sysctl] +net.ipv4.tcp_fastopen=3 diff --git a/roles/openshift_node/templates/tuned/openshift/tuned.conf b/roles/openshift_node/templates/tuned/openshift/tuned.conf new file mode 100644 index 000000000..68ac5dadb --- /dev/null +++ b/roles/openshift_node/templates/tuned/openshift/tuned.conf @@ -0,0 +1,24 @@ +# +# tuned configuration +# + +[main] +summary=Optimize systems running OpenShift (parent profile) +include=${f:virt_check:{{ openshift_tuned_guest_profile }}:throughput-performance} + +[selinux] +avc_cache_threshold=65536 + +[net] +nf_conntrack_hashsize=131072 + +[sysctl] +kernel.pid_max=131072 +net.netfilter.nf_conntrack_max=1048576 +fs.inotify.max_user_watches=65536 +net.ipv4.neigh.default.gc_thresh1=8192 +net.ipv4.neigh.default.gc_thresh2=32768 +net.ipv4.neigh.default.gc_thresh3=65536 +net.ipv6.neigh.default.gc_thresh1=8192 +net.ipv6.neigh.default.gc_thresh2=32768 +net.ipv6.neigh.default.gc_thresh3=65536 diff --git a/roles/openshift_node/templates/tuned/recommend.conf b/roles/openshift_node/templates/tuned/recommend.conf new file mode 100644 index 000000000..5fa765798 --- /dev/null +++ b/roles/openshift_node/templates/tuned/recommend.conf @@ -0,0 +1,8 @@ +[openshift-node] +/etc/origin/node/node-config.yaml=.*region=primary + +[openshift-control-plane,master] +/etc/origin/master/master-config.yaml=.* + +[openshift-control-plane,node] +/etc/origin/node/node-config.yaml=.*region=infra diff --git a/roles/openshift_node_certificates/defaults/main.yml b/roles/openshift_node_certificates/defaults/main.yml index 70a38b844..455f26f30 100644 --- a/roles/openshift_node_certificates/defaults/main.yml +++ b/roles/openshift_node_certificates/defaults/main.yml @@ -1,2 +1,3 @@ --- openshift_node_cert_expire_days: 730 +openshift_ca_host: '' diff --git a/roles/openshift_node_dnsmasq/files/networkmanager/99-origin-dns.sh b/roles/openshift_node_dnsmasq/files/networkmanager/99-origin-dns.sh index 4aab8f2e9..61d2a5b51 100755 --- a/roles/openshift_node_dnsmasq/files/networkmanager/99-origin-dns.sh +++ b/roles/openshift_node_dnsmasq/files/networkmanager/99-origin-dns.sh @@ -46,9 +46,7 @@ if [[ $2 =~ ^(up|dhcp4-change|dhcp6-change)$ ]]; then def_route=$(/sbin/ip route list match 0.0.0.0/0 | awk '{print $3 }') def_route_int=$(/sbin/ip route get to ${def_route} | awk '{print $3}') def_route_ip=$(/sbin/ip route get to ${def_route} | awk '{print $5}') - if [[ ${DEVICE_IFACE} == ${def_route_int} && \ - -n "${IP4_NAMESERVERS}" && \ - "${IP4_NAMESERVERS}" != "${def_route_ip}" ]]; then + if [[ ${DEVICE_IFACE} == ${def_route_int} ]]; then if [ ! -f /etc/dnsmasq.d/origin-dns.conf ]; then cat << EOF > /etc/dnsmasq.d/origin-dns.conf no-resolv @@ -61,35 +59,40 @@ EOF NEEDS_RESTART=1 fi - ###################################################################### - # Write out default nameservers for /etc/dnsmasq.d/origin-upstream-dns.conf - # and /etc/origin/node/resolv.conf in their respective formats - for ns in ${IP4_NAMESERVERS}; do - if [[ ! -z $ns ]]; then - echo "server=${ns}" >> $UPSTREAM_DNS_TMP - echo "nameserver ${ns}" >> $NEW_NODE_RESOLV_CONF + # If network manager doesn't know about the nameservers then the best + # we can do is grab them from /etc/resolv.conf but only if we've got no + # watermark + if ! grep -q '99-origin-dns.sh' /etc/resolv.conf; then + if [[ -z "${IP4_NAMESERVERS}" || "${IP4_NAMESERVERS}" == "${def_route_ip}" ]]; then + IP4_NAMESERVERS=`grep '^nameserver ' /etc/resolv.conf | awk '{ print $2 }'` + fi + ###################################################################### + # Write out default nameservers for /etc/dnsmasq.d/origin-upstream-dns.conf + # and /etc/origin/node/resolv.conf in their respective formats + for ns in ${IP4_NAMESERVERS}; do + if [[ ! -z $ns ]]; then + echo "server=${ns}" >> $UPSTREAM_DNS_TMP + echo "nameserver ${ns}" >> $NEW_NODE_RESOLV_CONF + fi + done + # Sort it in case DNS servers arrived in a different order + sort $UPSTREAM_DNS_TMP > $UPSTREAM_DNS_TMP_SORTED + sort $UPSTREAM_DNS > $CURRENT_UPSTREAM_DNS_SORTED + # Compare to the current config file (sorted) + NEW_DNS_SUM=`md5sum ${UPSTREAM_DNS_TMP_SORTED} | awk '{print $1}'` + CURRENT_DNS_SUM=`md5sum ${CURRENT_UPSTREAM_DNS_SORTED} | awk '{print $1}'` + if [ "${NEW_DNS_SUM}" != "${CURRENT_DNS_SUM}" ]; then + # DNS has changed, copy the temp file to the proper location (-Z + # sets default selinux context) and set the restart flag + cp -Z $UPSTREAM_DNS_TMP $UPSTREAM_DNS + NEEDS_RESTART=1 + fi + # compare /etc/origin/node/resolv.conf checksum and replace it if different + NEW_NODE_RESOLV_CONF_MD5=`md5sum ${NEW_NODE_RESOLV_CONF}` + OLD_NODE_RESOLV_CONF_MD5=`md5sum /etc/origin/node/resolv.conf` + if [ "${NEW_NODE_RESOLV_CONF_MD5}" != "${OLD_NODE_RESOLV_CONF_MD5}" ]; then + cp -Z $NEW_NODE_RESOLV_CONF /etc/origin/node/resolv.conf fi - done - - # Sort it in case DNS servers arrived in a different order - sort $UPSTREAM_DNS_TMP > $UPSTREAM_DNS_TMP_SORTED - sort $UPSTREAM_DNS > $CURRENT_UPSTREAM_DNS_SORTED - - # Compare to the current config file (sorted) - NEW_DNS_SUM=`md5sum ${UPSTREAM_DNS_TMP_SORTED} | awk '{print $1}'` - CURRENT_DNS_SUM=`md5sum ${CURRENT_UPSTREAM_DNS_SORTED} | awk '{print $1}'` - if [ "${NEW_DNS_SUM}" != "${CURRENT_DNS_SUM}" ]; then - # DNS has changed, copy the temp file to the proper location (-Z - # sets default selinux context) and set the restart flag - cp -Z $UPSTREAM_DNS_TMP $UPSTREAM_DNS - NEEDS_RESTART=1 - fi - - # compare /etc/origin/node/resolv.conf checksum and replace it if different - NEW_NODE_RESOLV_CONF_MD5=`md5sum ${NEW_NODE_RESOLV_CONF}` - OLD_NODE_RESOLV_CONF_MD5=`md5sum /etc/origin/node/resolv.conf` - if [ "${NEW_NODE_RESOLV_CONF_MD5}" != "${OLD_NODE_RESOLV_CONF_MD5}" ]; then - cp -Z $NEW_NODE_RESOLV_CONF /etc/origin/node/resolv.conf fi if ! `systemctl -q is-active dnsmasq.service`; then diff --git a/roles/openshift_node_dnsmasq/meta/main.yml b/roles/openshift_node_dnsmasq/meta/main.yml index 18e04e06d..84035b88c 100644 --- a/roles/openshift_node_dnsmasq/meta/main.yml +++ b/roles/openshift_node_dnsmasq/meta/main.yml @@ -13,3 +13,4 @@ galaxy_info: - cloud dependencies: - role: openshift_common +- role: openshift_node_facts diff --git a/roles/openshift_node_dnsmasq/tasks/main.yml b/roles/openshift_node_dnsmasq/tasks/main.yml index d0221a94b..9bbaafc29 100644 --- a/roles/openshift_node_dnsmasq/tasks/main.yml +++ b/roles/openshift_node_dnsmasq/tasks/main.yml @@ -14,6 +14,17 @@ package: name=dnsmasq state=installed when: not openshift.common.is_atomic | bool +- name: ensure origin/node directory exists + file: + state: directory + path: "{{ item }}" + owner: root + group: root + mode: '0700' + with_items: + - /etc/origin + - /etc/origin/node + # this file is copied to /etc/dnsmasq.d/ when the node starts and is removed # when the node stops. A dbus-message is sent to dnsmasq to add the same entries # so that dnsmasq doesn't need to be restarted. Once we can use dnsmasq 2.77 or diff --git a/roles/openshift_node_dnsmasq/templates/origin-dns.conf.j2 b/roles/openshift_node_dnsmasq/templates/origin-dns.conf.j2 index 779b4d2f5..ef3ba2880 100644 --- a/roles/openshift_node_dnsmasq/templates/origin-dns.conf.j2 +++ b/roles/openshift_node_dnsmasq/templates/origin-dns.conf.j2 @@ -4,4 +4,4 @@ no-negcache max-cache-ttl=1 enable-dbus bind-interfaces -listen-address={{ ansible_default_ipv4.address }} +listen-address={{ openshift.node.dns_ip }} diff --git a/roles/openshift_node_upgrade/tasks/main.yml b/roles/openshift_node_upgrade/tasks/main.yml index f984a04b2..bc092c26c 100644 --- a/roles/openshift_node_upgrade/tasks/main.yml +++ b/roles/openshift_node_upgrade/tasks/main.yml @@ -24,7 +24,6 @@ name: "{{ item }}" state: stopped with_items: - - "{{ openshift.common.service_type }}-master" - "{{ openshift.common.service_type }}-master-controllers" - "{{ openshift.common.service_type }}-master-api" - etcd_container @@ -81,7 +80,6 @@ with_items: - etcd_container - openvswitch - - "{{ openshift.common.service_type }}-master" - "{{ openshift.common.service_type }}-master-api" - "{{ openshift.common.service_type }}-master-controllers" - "{{ openshift.common.service_type }}-node" diff --git a/roles/openshift_node_upgrade/tasks/restart.yml b/roles/openshift_node_upgrade/tasks/restart.yml index f228b6e08..a4fa51172 100644 --- a/roles/openshift_node_upgrade/tasks/restart.yml +++ b/roles/openshift_node_upgrade/tasks/restart.yml @@ -31,7 +31,6 @@ with_items: - etcd_container - openvswitch - - "{{ openshift.common.service_type }}-master" - "{{ openshift.common.service_type }}-master-api" - "{{ openshift.common.service_type }}-master-controllers" - "{{ openshift.common.service_type }}-node" diff --git a/roles/openshift_provisioners/tasks/install_efs.yaml b/roles/openshift_provisioners/tasks/install_efs.yaml index b53b6afa1..4a6e00513 100644 --- a/roles/openshift_provisioners/tasks/install_efs.yaml +++ b/roles/openshift_provisioners/tasks/install_efs.yaml @@ -67,4 +67,4 @@ register: efs_output failed_when: efs_output.rc == 1 and 'exists' not in efs_output.stderr check_mode: no - when: efs_anyuid.stdout.find("system:serviceaccount:{{openshift_provisioners_project}}:provisioners-efs") == -1 + when: efs_anyuid.stdout.find("system:serviceaccount:" + openshift_provisioners_project + ":provisioners-efs") == -1 diff --git a/roles/openshift_repos/meta/main.yml b/roles/openshift_repos/meta/main.yml deleted file mode 100644 index 1b043863b..000000000 --- a/roles/openshift_repos/meta/main.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -galaxy_info: - author: TODO - description: OpenShift Repositories - company: Red Hat, Inc. - license: Apache License, Version 2.0 - min_ansible_version: 1.7 - platforms: - - name: EL - versions: - - 7 - categories: - - cloud -dependencies: -- role: openshift_sanitize_inventory diff --git a/roles/openshift_repos/tasks/main.yaml b/roles/openshift_repos/tasks/main.yaml index 7458db87e..6b3de4dba 100644 --- a/roles/openshift_repos/tasks/main.yaml +++ b/roles/openshift_repos/tasks/main.yaml @@ -6,23 +6,24 @@ - when: not ostree_booted.stat.exists block: + # TODO: This needs to be removed and placed into a role - name: Ensure libselinux-python is installed package: name=libselinux-python state=present - name: Create any additional repos that are defined - template: - src: yum_repo.j2 - dest: /etc/yum.repos.d/openshift_additional.repo - when: - - openshift_additional_repos | length > 0 - notify: refresh cache - - - name: Remove the additional repos if no longer defined - file: - dest: /etc/yum.repos.d/openshift_additional.repo - state: absent - when: - - openshift_additional_repos | length == 0 + yum_repository: + description: "{{ item.description | default(item.name) }}" + name: "{{ item.name | default(item.id) }}" + baseurl: "{{ item.baseurl }}" + gpgkey: "{{ item.gpgkey | default(omit)}}" + gpgcheck: "{{ item.gpgcheck | default(1) }}" + sslverify: "{{ item.sslverify | default(1) }}" + sslclientkey: "{{ item.sslclientkey | default(omit) }}" + sslclientcert: "{{ item.sslclientcert | default(omit) }}" + file: "{{ item.name }}" + enabled: "{{ item.enabled | default('no')}}" + with_items: "{{ openshift_additional_repos }}" + when: openshift_additional_repos | length > 0 notify: refresh cache # Singleton block diff --git a/roles/openshift_repos/templates/yum_repo.j2 b/roles/openshift_repos/templates/yum_repo.j2 deleted file mode 100644 index ef2cd6603..000000000 --- a/roles/openshift_repos/templates/yum_repo.j2 +++ /dev/null @@ -1,14 +0,0 @@ -{% for repo in openshift_additional_repos %} -[{{ repo.id }}] -name={{ repo.name | default(repo.id) }} -baseurl={{ repo.baseurl }} -{% set enable_repo = repo.enabled | default(1) %} -enabled={{ 1 if ( enable_repo == 1 or enable_repo == True ) else 0 }} -{% set enable_gpg_check = repo.gpgcheck | default(1) %} -gpgcheck={{ 1 if ( enable_gpg_check == 1 or enable_gpg_check == True ) else 0 }} -{% for key, value in repo.iteritems() %} -{% if key not in ['id', 'name', 'baseurl', 'enabled', 'gpgcheck'] and value is defined %} -{{ key }}={{ value }} -{% endif %} -{% endfor %} -{% endfor %} diff --git a/roles/openshift_service_catalog/tasks/install.yml b/roles/openshift_service_catalog/tasks/install.yml index 686857d94..64f94347b 100644 --- a/roles/openshift_service_catalog/tasks/install.yml +++ b/roles/openshift_service_catalog/tasks/install.yml @@ -168,19 +168,19 @@ - "{{ mktemp.stdout }}/service_catalog_api_server.yml" delete_after: yes -- template: - src: api_server_service.j2 - dest: "{{ mktemp.stdout }}/service_catalog_api_service.yml" - - name: Set Service Catalog API Server service - oc_obj: - state: present - namespace: "kube-service-catalog" - kind: service + oc_service: name: apiserver - files: - - "{{ mktemp.stdout }}/service_catalog_api_service.yml" - delete_after: yes + namespace: kube-service-catalog + state: present + ports: + - name: secure + port: 443 + protocol: TCP + targetPort: 6443 + selector: + app: apiserver + session_affinity: None - template: src: api_server_route.j2 @@ -216,19 +216,19 @@ - "{{ mktemp.stdout }}/controller_manager.yml" delete_after: yes -- template: - src: controller_manager_service.j2 - dest: "{{ mktemp.stdout }}/controller_manager_service.yml" - - name: Set Controller Manager service - oc_obj: - state: present - namespace: "kube-service-catalog" - kind: service + oc_service: name: controller-manager - files: - - "{{ mktemp.stdout }}/controller_manager_service.yml" - delete_after: yes + namespace: kube-service-catalog + state: present + ports: + - port: 6443 + protocol: TCP + targetPort: 6443 + selector: + app: controller-manager + session_affinity: None + service_type: ClusterIP - include: start_api_server.yml diff --git a/roles/openshift_service_catalog/tasks/wire_aggregator.yml b/roles/openshift_service_catalog/tasks/wire_aggregator.yml index d5291a99a..1c788470a 100644 --- a/roles/openshift_service_catalog/tasks/wire_aggregator.yml +++ b/roles/openshift_service_catalog/tasks/wire_aggregator.yml @@ -156,24 +156,16 @@ register: yedit_output #restart master serially here -- name: restart master - systemd: name={{ openshift.common.service_type }}-master state=restarted - when: - - yedit_output.changed - - openshift.master.ha is not defined or not openshift.master.ha | bool - - name: restart master api systemd: name={{ openshift.common.service_type }}-master-api state=restarted when: - yedit_output.changed - - openshift.master.ha is defined and openshift.master.ha | bool - openshift.master.cluster_method == 'native' - name: restart master controllers systemd: name={{ openshift.common.service_type }}-master-controllers state=restarted when: - yedit_output.changed - - openshift.master.ha is defined and openshift.master.ha | bool - openshift.master.cluster_method == 'native' - name: Verify API Server diff --git a/roles/openshift_service_catalog/templates/api_server_service.j2 b/roles/openshift_service_catalog/templates/api_server_service.j2 deleted file mode 100644 index bae337201..000000000 --- a/roles/openshift_service_catalog/templates/api_server_service.j2 +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: apiserver -spec: - ports: - - name: secure - port: 443 - protocol: TCP - targetPort: 6443 - selector: - app: apiserver - sessionAffinity: None diff --git a/roles/openshift_service_catalog/templates/controller_manager_service.j2 b/roles/openshift_service_catalog/templates/controller_manager_service.j2 deleted file mode 100644 index 2bac645fc..000000000 --- a/roles/openshift_service_catalog/templates/controller_manager_service.j2 +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: controller-manager -spec: - ports: - - port: 6443 - protocol: TCP - targetPort: 6443 - selector: - app: controller-manager - sessionAffinity: None - type: ClusterIP diff --git a/roles/openshift_storage_glusterfs/README.md b/roles/openshift_storage_glusterfs/README.md index d3de2165a..a059745a6 100644 --- a/roles/openshift_storage_glusterfs/README.md +++ b/roles/openshift_storage_glusterfs/README.md @@ -55,7 +55,7 @@ defined: | Name | Default value | Description | |-------------------|---------------|-----------------------------------------| -| glusterfs_devices | None | A list of block devices that will be completely managed as part of a GlusterFS cluster. There must be at least one device listed. Each device must be bare, e.g. no partitions or LVM PVs. **Example:** '[ "/dev/sdb" ]' +| glusterfs_devices | None | A list of block devices that will be completely managed as part of a GlusterFS cluster. There must be at least one device listed. Each device must be bare, e.g. no partitions or LVM PVs. **Example:** '[ "/dev/sdb" ]' **NOTE:** You MUST set this as a host variable on each node host. For some reason, if you set this as a group variable it gets interpreted as a string rather than an array. See https://github.com/openshift/openshift-ansible/issues/5071 In addition, each host may specify the following variables to further control their configuration as GlusterFS nodes: diff --git a/roles/openshift_storage_glusterfs/defaults/main.yml b/roles/openshift_storage_glusterfs/defaults/main.yml index ff2c18812..8d21a3f27 100644 --- a/roles/openshift_storage_glusterfs/defaults/main.yml +++ b/roles/openshift_storage_glusterfs/defaults/main.yml @@ -52,3 +52,15 @@ openshift_storage_glusterfs_registry_heketi_ssh_port: "{{ openshift_storage_glus openshift_storage_glusterfs_registry_heketi_ssh_user: "{{ openshift_storage_glusterfs_heketi_ssh_user }}" openshift_storage_glusterfs_registry_heketi_ssh_sudo: "{{ openshift_storage_glusterfs_heketi_ssh_sudo }}" openshift_storage_glusterfs_registry_heketi_ssh_keyfile: "{{ openshift_storage_glusterfs_heketi_ssh_keyfile | default(omit) }}" +r_openshift_master_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_openshift_master_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" +r_openshift_storage_glusterfs_os_firewall_deny: [] +r_openshift_storage_glusterfs_os_firewall_allow: +- service: glusterfs_sshd + port: "2222/tcp" +- service: glusterfs_daemon + port: "24007/tcp" +- service: glusterfs_management + port: "24008/tcp" +- service: glusterfs_bricks + port: "49152-49251/tcp" diff --git a/roles/openshift_storage_glusterfs/meta/main.yml b/roles/openshift_storage_glusterfs/meta/main.yml index aab9851f9..0cdd33880 100644 --- a/roles/openshift_storage_glusterfs/meta/main.yml +++ b/roles/openshift_storage_glusterfs/meta/main.yml @@ -11,5 +11,5 @@ galaxy_info: - 7 dependencies: - role: openshift_hosted_facts -- role: openshift_repos - role: lib_openshift +- role: lib_os_firewall diff --git a/roles/openshift_storage_glusterfs/tasks/firewall.yml b/roles/openshift_storage_glusterfs/tasks/firewall.yml new file mode 100644 index 000000000..09dcf1ef9 --- /dev/null +++ b/roles/openshift_storage_glusterfs/tasks/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_openshift_storage_glusterfs_firewall_enabled | bool and not r_openshift_storage_glusterfs_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_storage_glusterfs_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_storage_glusterfs_os_firewall_deny }}" + +- when: r_openshift_storage_glusterfs_firewall_enabled | bool and r_openshift_storage_glusterfs_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_openshift_storage_glusterfs_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_openshift_storage_glusterfs_os_firewall_deny }}" diff --git a/roles/openshift_storage_nfs/defaults/main.yml b/roles/openshift_storage_nfs/defaults/main.yml index 7f3c054e7..e7e0b331b 100644 --- a/roles/openshift_storage_nfs/defaults/main.yml +++ b/roles/openshift_storage_nfs/defaults/main.yml @@ -1,4 +1,12 @@ --- +r_openshift_storage_nfs_firewall_enabled: "{{ os_firewall_enabled | default(True) }}" +r_openshift_storage_nfs_use_firewalld: "{{ os_firewall_use_firewalld | default(False) }}" + +r_openshift_storage_nfs_os_firewall_deny: [] +r_openshift_storage_nfs_os_firewall_allow: +- service: nfs + port: "2049/tcp" + openshift: hosted: registry: diff --git a/roles/openshift_storage_nfs/meta/main.yml b/roles/openshift_storage_nfs/meta/main.yml index 62e38bd8c..98f7c317e 100644 --- a/roles/openshift_storage_nfs/meta/main.yml +++ b/roles/openshift_storage_nfs/meta/main.yml @@ -10,9 +10,5 @@ galaxy_info: versions: - 7 dependencies: -- role: os_firewall - os_firewall_allow: - - service: nfs - port: "2049/tcp" +- role: lib_os_firewall - role: openshift_hosted_facts -- role: openshift_repos diff --git a/roles/openshift_storage_nfs/tasks/firewall.yml b/roles/openshift_storage_nfs/tasks/firewall.yml new file mode 100644 index 000000000..c1c318ff4 --- /dev/null +++ b/roles/openshift_storage_nfs/tasks/firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_openshift_storage_nfs_firewall_enabled | bool and not r_openshift_storage_nfs_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_storage_nfs_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_openshift_storage_nfs_os_firewall_deny }}" + +- when: r_openshift_storage_nfs_firewall_enabled | bool and r_openshift_storage_nfs_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_openshift_storage_nfs_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_openshift_storage_nfs_os_firewall_deny }}" diff --git a/roles/openshift_storage_nfs/tasks/main.yml b/roles/openshift_storage_nfs/tasks/main.yml index 019ada2fb..51f8f4e0e 100644 --- a/roles/openshift_storage_nfs/tasks/main.yml +++ b/roles/openshift_storage_nfs/tasks/main.yml @@ -1,4 +1,8 @@ --- +- name: setup firewall + include: firewall.yml + static: yes + - name: Install nfs-utils package: name=nfs-utils state=present diff --git a/roles/openshift_version/meta/main.yml b/roles/openshift_version/meta/main.yml index ca896addd..38b398343 100644 --- a/roles/openshift_version/meta/main.yml +++ b/roles/openshift_version/meta/main.yml @@ -12,7 +12,6 @@ galaxy_info: categories: - cloud dependencies: -- role: openshift_repos - role: openshift_docker_facts - role: docker when: openshift.common.is_containerized | default(False) | bool and not skip_docker_role | default(False) | bool diff --git a/roles/openshift_version/tasks/main.yml b/roles/openshift_version/tasks/main.yml index a6b8a40c8..c0ea00f34 100644 --- a/roles/openshift_version/tasks/main.yml +++ b/roles/openshift_version/tasks/main.yml @@ -60,13 +60,16 @@ # It also allows for optional trailing data which: # - must start with a dash # - may contain numbers + # - may containe dots (https://github.com/openshift/openshift-ansible/issues/5192) + # - name: (Enterprise) Verify openshift_image_tag is valid when: openshift.common.deployment_type == 'openshift-enterprise' assert: that: - - "{{ openshift_image_tag|match('(^v\\d+\\.\\d+[\\.\\d+]*(-\\d+)?$)') }}" + - "{{ openshift_image_tag|match('(^v\\d+\\.\\d+(\\.\\d+)*(-\\d+(\\.\\d+)*)?$)') }}" msg: |- - openshift_image_tag must be in the format v#.#[.#[.#]]. Examples: v1.2, v3.4.1, v3.5.1.3, v1.2-1, v1.2.3-4 + openshift_image_tag must be in the format v#.#[.#[.#]]. Examples: v1.2, v3.4.1, v3.5.1.3, + v3.5.1.3.4, v1.2-1, v1.2.3-4, v1.2.3-4.5, v1.2.3-4.5.6 You specified openshift_image_tag={{ openshift_image_tag }} # Make sure we copy this to a fact if given a var: diff --git a/roles/openshift_version/tasks/set_version_containerized.yml b/roles/openshift_version/tasks/set_version_containerized.yml index 4d9f72f01..a2a579e9d 100644 --- a/roles/openshift_version/tasks/set_version_containerized.yml +++ b/roles/openshift_version/tasks/set_version_containerized.yml @@ -1,6 +1,6 @@ --- - set_fact: - l_use_crio: "{{ openshift_docker_use_crio | default(false) }}" + l_use_crio: "{{ openshift_use_crio | default(false) }}" - name: Set containerized version to configure if openshift_image_tag specified set_fact: diff --git a/roles/os_firewall/README.md b/roles/os_firewall/README.md index e7ef544f4..be0b8291a 100644 --- a/roles/os_firewall/README.md +++ b/roles/os_firewall/README.md @@ -1,8 +1,8 @@ OS Firewall =========== -OS Firewall manages firewalld and iptables firewall settings for a minimal use -case (Adding/Removing rules based on protocol and port number). +OS Firewall manages firewalld and iptables installation. +case. Note: firewalld is not supported on Atomic Host https://bugzilla.redhat.com/show_bug.cgi?id=1403331 @@ -18,8 +18,6 @@ Role Variables | Name | Default | | |---------------------------|---------|----------------------------------------| | os_firewall_use_firewalld | False | If false, use iptables | -| os_firewall_allow | [] | List of service,port mappings to allow | -| os_firewall_deny | [] | List of service, port mappings to deny | Dependencies ------------ @@ -29,34 +27,27 @@ None. Example Playbook ---------------- -Use iptables and open tcp ports 80 and 443: +Use iptables: ``` --- - hosts: servers - vars: - os_firewall_use_firewalld: false - os_firewall_allow: - - service: httpd - port: 80/tcp - - service: https - port: 443/tcp - roles: - - os_firewall + task: + - include_role: + name: os_firewall + vars: + os_firewall_use_firewalld: false ``` -Use firewalld and open tcp port 443 and close previously open tcp port 80: +Use firewalld: ``` --- - hosts: servers vars: - os_firewall_allow: - - service: https - port: 443/tcp - os_firewall_deny: - - service: httpd - port: 80/tcp - roles: - - os_firewall + tasks: + - include_role: + name: os_firewall + vars: + os_firewall_use_firewalld: true ``` License diff --git a/roles/os_firewall/defaults/main.yml b/roles/os_firewall/defaults/main.yml index 01859e5fc..2cae94411 100644 --- a/roles/os_firewall/defaults/main.yml +++ b/roles/os_firewall/defaults/main.yml @@ -2,6 +2,4 @@ os_firewall_enabled: True # firewalld is not supported on Atomic Host # https://bugzilla.redhat.com/show_bug.cgi?id=1403331 -os_firewall_use_firewalld: "{{ False }}" -os_firewall_allow: [] -os_firewall_deny: [] +os_firewall_use_firewalld: False diff --git a/roles/os_firewall/meta/main.yml b/roles/os_firewall/meta/main.yml deleted file mode 100644 index dca5fc5ff..000000000 --- a/roles/os_firewall/meta/main.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -galaxy_info: - author: Jason DeTiberus - description: os_firewall - company: Red Hat, Inc. - license: Apache License, Version 2.0 - min_ansible_version: 2.2 - platforms: - - name: EL - versions: - - 7 - categories: - - system -allow_duplicates: yes -dependencies: - - role: openshift_facts diff --git a/roles/os_firewall/tasks/firewall/firewalld.yml b/roles/os_firewall/tasks/firewalld.yml index 509655b0c..54430f402 100644 --- a/roles/os_firewall/tasks/firewall/firewalld.yml +++ b/roles/os_firewall/tasks/firewalld.yml @@ -1,4 +1,9 @@ --- +- name: Fail - Firewalld is not supported on Atomic Host + fail: + msg: "Firewalld is not supported on Atomic Host" + when: r_os_firewall_is_atomic | bool + - name: Install firewalld packages package: name: firewalld @@ -31,7 +36,8 @@ register: result - name: need to pause here, otherwise the firewalld service starting can sometimes cause ssh to fail - pause: seconds=10 + pause: + seconds: 10 when: result | changed - name: Restart polkitd @@ -49,19 +55,3 @@ until: pkaction.rc == 0 retries: 6 delay: 10 - -- name: Add firewalld allow rules - firewalld: - port: "{{ item.port }}" - permanent: true - immediate: true - state: enabled - with_items: "{{ os_firewall_allow }}" - -- name: Remove firewalld allow rules - firewalld: - port: "{{ item.port }}" - permanent: true - immediate: true - state: disabled - with_items: "{{ os_firewall_deny }}" diff --git a/roles/os_firewall/tasks/firewall/iptables.yml b/roles/os_firewall/tasks/iptables.yml index 55f2fc471..0af5abf38 100644 --- a/roles/os_firewall/tasks/firewall/iptables.yml +++ b/roles/os_firewall/tasks/iptables.yml @@ -15,11 +15,13 @@ when: task_result | changed - name: Install iptables packages - package: name={{ item }} state=present + package: + name: "{{ item }}" + state: present with_items: - iptables - iptables-services - when: not openshift.common.is_atomic | bool + when: not r_os_firewall_is_atomic | bool - name: Start and enable iptables service systemd: @@ -29,23 +31,11 @@ masked: no daemon_reload: yes register: result + delegate_to: "{{item}}" + run_once: true + with_items: "{{ ansible_play_hosts }}" - name: need to pause here, otherwise the iptables service starting can sometimes cause ssh to fail - pause: seconds=10 + pause: + seconds: 10 when: result | changed - -- name: Add iptables allow rules - os_firewall_manage_iptables: - name: "{{ item.service }}" - action: add - protocol: "{{ item.port.split('/')[1] }}" - port: "{{ item.port.split('/')[0] }}" - with_items: "{{ os_firewall_allow }}" - -- name: Remove iptables rules - os_firewall_manage_iptables: - name: "{{ item.service }}" - action: remove - protocol: "{{ item.port.split('/')[1] }}" - port: "{{ item.port.split('/')[0] }}" - with_items: "{{ os_firewall_deny }}" diff --git a/roles/os_firewall/tasks/main.yml b/roles/os_firewall/tasks/main.yml index 20efe5b0d..c477d386c 100644 --- a/roles/os_firewall/tasks/main.yml +++ b/roles/os_firewall/tasks/main.yml @@ -1,12 +1,19 @@ --- -- name: Assert - Do not use firewalld on Atomic Host - assert: - that: not os_firewall_use_firewalld | bool - msg: "Firewalld is not supported on Atomic Host" - when: openshift.common.is_atomic | bool +- name: Detecting Atomic Host Operating System + stat: + path: /run/ostree-booted + register: r_os_firewall_ostree_booted -- include: firewall/firewalld.yml - when: os_firewall_enabled | bool and os_firewall_use_firewalld | bool +- name: Set fact r_os_firewall_is_atomic + set_fact: + r_os_firewall_is_atomic: "{{ r_os_firewall_ostree_booted.stat.exists }}" -- include: firewall/iptables.yml - when: os_firewall_enabled | bool and not os_firewall_use_firewalld | bool +- include: firewalld.yml + when: + - os_firewall_enabled | bool + - os_firewall_use_firewalld | bool + +- include: iptables.yml + when: + - os_firewall_enabled | bool + - not os_firewall_use_firewalld | bool diff --git a/roles/rhel_subscribe/tasks/enterprise.yml b/roles/rhel_subscribe/tasks/enterprise.yml index ea0c42150..39d59db70 100644 --- a/roles/rhel_subscribe/tasks/enterprise.yml +++ b/roles/rhel_subscribe/tasks/enterprise.yml @@ -7,7 +7,7 @@ when: deployment_type == 'enterprise' - set_fact: - default_ose_version: '3.5' + default_ose_version: '3.6' when: deployment_type in ['atomic-enterprise', 'openshift-enterprise'] - set_fact: @@ -16,7 +16,7 @@ - fail: msg: "{{ ose_version }} is not a valid version for {{ deployment_type }} deployment type" when: ( deployment_type == 'enterprise' and ose_version not in ['3.0'] ) or - ( deployment_type in ['atomic-enterprise', 'openshift-enterprise'] and ose_version not in ['3.1', '3.2', '3.3', '3.4', '3.5'] ) + ( deployment_type in ['atomic-enterprise', 'openshift-enterprise'] and ose_version not in ['3.1', '3.2', '3.3', '3.4', '3.5', '3.6'] ) - name: Enable RHEL repositories command: subscription-manager repos \ |