diff options
Diffstat (limited to 'roles')
11 files changed, 648 insertions, 74 deletions
diff --git a/roles/openshift_facts/tasks/main.yml b/roles/openshift_facts/tasks/main.yml index 11bd68207..9a1982076 100644 --- a/roles/openshift_facts/tasks/main.yml +++ b/roles/openshift_facts/tasks/main.yml @@ -1,64 +1,52 @@ --- -- block: - - name: Detecting Operating System - stat: - path: /run/ostree-booted - register: ostree_booted +- name: Detecting Operating System + stat: + path: /run/ostree-booted + register: ostree_booted - # Locally setup containerized facts for now - - set_fact: - l_is_atomic: "{{ ostree_booted.stat.exists }}" - - set_fact: - l_is_containerized: "{{ (l_is_atomic | bool) or (containerized | default(false) | bool) }}" - l_is_openvswitch_system_container: "{{ (use_openvswitch_system_container | default(use_system_containers) | bool) }}" - l_is_node_system_container: "{{ (use_node_system_container | default(use_system_containers) | bool) }}" - l_is_master_system_container: "{{ (use_master_system_container | default(use_system_containers) | bool) }}" - l_is_etcd_system_container: "{{ (use_etcd_system_container | default(use_system_containers) | bool) }}" +# Locally setup containerized facts for now +- set_fact: + l_is_atomic: "{{ ostree_booted.stat.exists }}" +- set_fact: + l_is_containerized: "{{ (l_is_atomic | bool) or (containerized | default(false) | bool) }}" + l_is_openvswitch_system_container: "{{ (use_openvswitch_system_container | default(use_system_containers) | bool) }}" + l_is_node_system_container: "{{ (use_node_system_container | default(use_system_containers) | bool) }}" + l_is_master_system_container: "{{ (use_master_system_container | default(use_system_containers) | bool) }}" + l_is_etcd_system_container: "{{ (use_etcd_system_container | default(use_system_containers) | bool) }}" - - name: Ensure various deps are installed - package: name={{ item }} state=present - with_items: "{{ required_packages }}" - when: not l_is_atomic | bool +- name: Ensure various deps are installed + package: name={{ item }} state=present + with_items: "{{ required_packages }}" + when: not l_is_atomic | bool - - name: Gather Cluster facts and set is_containerized if needed - openshift_facts: - role: common - local_facts: - debug_level: "{{ openshift_debug_level | default(2) }}" - # TODO: Deprecate deployment_type in favor of openshift_deployment_type - deployment_type: "{{ openshift_deployment_type | default(deployment_type) }}" - deployment_subtype: "{{ openshift_deployment_subtype | default(None) }}" - cluster_id: "{{ openshift_cluster_id | default('default') }}" - hostname: "{{ openshift_hostname | default(None) }}" - ip: "{{ openshift_ip | default(None) }}" - is_containerized: "{{ l_is_containerized | default(None) }}" - is_openvswitch_system_container: "{{ l_is_openvswitch_system_container | default(false) }}" - is_node_system_container: "{{ l_is_node_system_container | default(false) }}" - is_master_system_container: "{{ l_is_master_system_container | default(false) }}" - is_etcd_system_container: "{{ l_is_etcd_system_container | default(false) }}" - system_images_registry: "{{ system_images_registry | default('') }}" - public_hostname: "{{ openshift_public_hostname | default(None) }}" - public_ip: "{{ openshift_public_ip | default(None) }}" - portal_net: "{{ openshift_portal_net | default(openshift_master_portal_net) | default(None) }}" - http_proxy: "{{ openshift_http_proxy | default(None) }}" - https_proxy: "{{ openshift_https_proxy | default(None) }}" - no_proxy: "{{ openshift_no_proxy | default(None) }}" - generate_no_proxy_hosts: "{{ openshift_generate_no_proxy_hosts | default(True) }}" - no_proxy_internal_hostnames: "{{ openshift_no_proxy_internal_hostnames | default(None) }}" - sdn_network_plugin_name: "{{ os_sdn_network_plugin_name | default(None) }}" - use_openshift_sdn: "{{ openshift_use_openshift_sdn | default(None) }}" +- name: Gather Cluster facts and set is_containerized if needed + openshift_facts: + role: common + local_facts: + debug_level: "{{ openshift_debug_level | default(2) }}" + # TODO: Deprecate deployment_type in favor of openshift_deployment_type + deployment_type: "{{ openshift_deployment_type | default(deployment_type) }}" + deployment_subtype: "{{ openshift_deployment_subtype | default(None) }}" + cluster_id: "{{ openshift_cluster_id | default('default') }}" + hostname: "{{ openshift_hostname | default(None) }}" + ip: "{{ openshift_ip | default(None) }}" + is_containerized: "{{ l_is_containerized | default(None) }}" + is_openvswitch_system_container: "{{ l_is_openvswitch_system_container | default(false) }}" + is_node_system_container: "{{ l_is_node_system_container | default(false) }}" + is_master_system_container: "{{ l_is_master_system_container | default(false) }}" + is_etcd_system_container: "{{ l_is_etcd_system_container | default(false) }}" + system_images_registry: "{{ system_images_registry | default('') }}" + public_hostname: "{{ openshift_public_hostname | default(None) }}" + public_ip: "{{ openshift_public_ip | default(None) }}" + portal_net: "{{ openshift_portal_net | default(openshift_master_portal_net) | default(None) }}" + http_proxy: "{{ openshift_http_proxy | default(None) }}" + https_proxy: "{{ openshift_https_proxy | default(None) }}" + no_proxy: "{{ openshift_no_proxy | default(None) }}" + generate_no_proxy_hosts: "{{ openshift_generate_no_proxy_hosts | default(True) }}" + no_proxy_internal_hostnames: "{{ openshift_no_proxy_internal_hostnames | default(None) }}" + sdn_network_plugin_name: "{{ os_sdn_network_plugin_name | default(None) }}" + use_openshift_sdn: "{{ openshift_use_openshift_sdn | default(None) }}" - - name: Set repoquery command - set_fact: - repoquery_cmd: "{{ 'dnf repoquery --latest-limit 1 -d 0' if ansible_pkg_mgr == 'dnf' else 'repoquery --plugins' }}" - - # This `when` allows us to skip this expensive block of tasks on - # subsequent calls to the `openshift_facts` role. You will notice - # speed-ups in proportion to the size of your cluster as this will - # skip all tasks on the next calls to the `openshift_facts` role. - when: - - openshift_facts_init is not defined - -- name: Record that openshift_facts has initialized +- name: Set repoquery command set_fact: - openshift_facts_init: true + repoquery_cmd: "{{ 'dnf repoquery --latest-limit 1 -d 0' if ansible_pkg_mgr == 'dnf' else 'repoquery --plugins' }}" diff --git a/roles/openshift_logging/defaults/main.yml b/roles/openshift_logging/defaults/main.yml index d9eebe688..87fc7068f 100644 --- a/roles/openshift_logging/defaults/main.yml +++ b/roles/openshift_logging/defaults/main.yml @@ -3,7 +3,7 @@ openshift_logging_image_prefix: "{{ openshift_hosted_logging_deployer_prefix | d openshift_logging_image_version: "{{ openshift_hosted_logging_deployer_version | default('latest') }}" openshift_logging_use_ops: False openshift_logging_master_url: "https://kubernetes.default.svc.{{ openshift.common.dns_domain }}" -openshift_logging_master_public_url: "{{ openshift_hosted_logging_master_public_url | default('https://{{openshift.common.public_hostname}}:8443') }}" +openshift_logging_master_public_url: "{{ openshift_hosted_logging_master_public_url | default('https://' + openshift.common.public_hostname + ':8443') }}" openshift_logging_namespace: logging openshift_logging_install_logging: True @@ -19,7 +19,7 @@ openshift_logging_curator_memory_limit: null openshift_logging_curator_ops_cpu_limit: 100m openshift_logging_curator_ops_memory_limit: null -openshift_logging_kibana_hostname: "{{ openshift_hosted_logging_hostname | default('kibana.{{openshift.common.dns_domain}}') }}" +openshift_logging_kibana_hostname: "{{ openshift_hosted_logging_hostname | default('kibana.' + openshift.common.dns_domain) }}" openshift_logging_kibana_cpu_limit: null openshift_logging_kibana_memory_limit: null openshift_logging_kibana_proxy_debug: false @@ -39,7 +39,7 @@ openshift_logging_kibana_key: "" #for the public facing kibana certs openshift_logging_kibana_ca: "" -openshift_logging_kibana_ops_hostname: "{{ openshift_hosted_logging_ops_hostname | default('kibana-ops.{{openshift.common.dns_domain}}') }}" +openshift_logging_kibana_ops_hostname: "{{ openshift_hosted_logging_ops_hostname | default('kibana-ops.' + openshift.common.dns_domain) }}" openshift_logging_kibana_ops_cpu_limit: null openshift_logging_kibana_ops_memory_limit: null openshift_logging_kibana_ops_proxy_debug: false @@ -62,7 +62,7 @@ openshift_logging_es_client_cert: /etc/fluent/keys/cert openshift_logging_es_client_key: /etc/fluent/keys/key openshift_logging_es_cluster_size: "{{ openshift_hosted_logging_elasticsearch_cluster_size | default(1) }}" openshift_logging_es_cpu_limit: null -openshift_logging_es_memory_limit: 1024Mi +openshift_logging_es_memory_limit: 8Gi openshift_logging_es_pv_selector: null openshift_logging_es_pvc_dynamic: "{{ openshift_hosted_logging_elasticsearch_pvc_dynamic | default(False) }}" openshift_logging_es_pvc_size: "{{ openshift_hosted_logging_elasticsearch_pvc_size | default('') }}" @@ -80,7 +80,7 @@ openshift_logging_es_ops_client_cert: /etc/fluent/keys/cert openshift_logging_es_ops_client_key: /etc/fluent/keys/key openshift_logging_es_ops_cluster_size: "{{ openshift_hosted_logging_elasticsearch_ops_cluster_size | default(1) }}" openshift_logging_es_ops_cpu_limit: null -openshift_logging_es_ops_memory_limit: 1024Mi +openshift_logging_es_ops_memory_limit: 8Gi openshift_logging_es_ops_pv_selector: None openshift_logging_es_ops_pvc_dynamic: "{{ openshift_hosted_logging_elasticsearch_ops_pvc_dynamic | default(False) }}" openshift_logging_es_ops_pvc_size: "{{ openshift_hosted_logging_elasticsearch_ops_pvc_size | default('') }}" diff --git a/roles/openshift_logging/tasks/generate_secrets.yaml b/roles/openshift_logging/tasks/generate_secrets.yaml index 1829acaee..81fac8b5e 100644 --- a/roles/openshift_logging/tasks/generate_secrets.yaml +++ b/roles/openshift_logging/tasks/generate_secrets.yaml @@ -17,7 +17,7 @@ - name: Generating secrets for logging components template: src=secret.j2 dest={{mktemp.stdout}}/templates/{{secret_name}}-secret.yaml vars: - secret_name: logging-{{component}} + secret_name: "logging-{{component}}" secret_key_file: "{{component}}_key" secret_cert_file: "{{component}}_cert" secrets: diff --git a/roles/openshift_logging/tasks/start_cluster.yaml b/roles/openshift_logging/tasks/start_cluster.yaml index 3e97487dc..edbb62c3e 100644 --- a/roles/openshift_logging/tasks/start_cluster.yaml +++ b/roles/openshift_logging/tasks/start_cluster.yaml @@ -16,7 +16,7 @@ name: "{{ fluentd_host }}" kind: node state: add - label: "{{ openshift_logging_fluentd_nodeselector | oo_dict_to_list_of_dict }}" + labels: "{{ openshift_logging_fluentd_nodeselector | oo_dict_to_list_of_dict }}" with_items: "{{ openshift_logging_fluentd_hosts }}" loop_control: loop_var: fluentd_host diff --git a/roles/openshift_logging/tasks/stop_cluster.yaml b/roles/openshift_logging/tasks/stop_cluster.yaml index bae6aebbb..4b3722e29 100644 --- a/roles/openshift_logging/tasks/stop_cluster.yaml +++ b/roles/openshift_logging/tasks/stop_cluster.yaml @@ -16,7 +16,7 @@ name: "{{ fluentd_host }}" kind: node state: absent - label: "{{ openshift_logging_fluentd_nodeselector | oo_dict_to_list_of_dict }}" + labels: "{{ openshift_logging_fluentd_nodeselector | oo_dict_to_list_of_dict }}" with_items: "{{ openshift_logging_fluentd_hosts }}" loop_control: loop_var: fluentd_host diff --git a/roles/openshift_master_facts/filter_plugins/oo_filters.py b/roles/openshift_master_facts/filter_plugins/oo_filters.py new file mode 120000 index 000000000..6f9bc47c1 --- /dev/null +++ b/roles/openshift_master_facts/filter_plugins/oo_filters.py @@ -0,0 +1 @@ +../../../filter_plugins/oo_filters.py
\ No newline at end of file diff --git a/roles/openshift_master_facts/filter_plugins/openshift_master.py b/roles/openshift_master_facts/filter_plugins/openshift_master.py new file mode 100644 index 000000000..6d009077a --- /dev/null +++ b/roles/openshift_master_facts/filter_plugins/openshift_master.py @@ -0,0 +1,578 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# vim: expandtab:tabstop=4:shiftwidth=4 +''' +Custom filters for use in openshift-master +''' +import copy +import sys + +from distutils.version import LooseVersion # pylint: disable=no-name-in-module,import-error + +from ansible import errors +from ansible.parsing.yaml.dumper import AnsibleDumper +from ansible.plugins.filter.core import to_bool as ansible_bool +from six import string_types + +import yaml + + +class IdentityProviderBase(object): + """ IdentityProviderBase + + Attributes: + name (str): Identity provider Name + login (bool): Is this identity provider a login provider? + challenge (bool): Is this identity provider a challenge provider? + provider (dict): Provider specific config + _idp (dict): internal copy of the IDP dict passed in + _required (list): List of lists of strings for required attributes + _optional (list): List of lists of strings for optional attributes + _allow_additional (bool): Does this provider support attributes + not in _required and _optional + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + # disabling this check since the number of instance attributes are + # necessary for this class + # pylint: disable=too-many-instance-attributes + def __init__(self, api_version, idp): + if api_version not in ['v1']: + raise errors.AnsibleFilterError("|failed api version {0} unknown".format(api_version)) + + self._idp = copy.deepcopy(idp) + + if 'name' not in self._idp: + raise errors.AnsibleFilterError("|failed identity provider missing a name") + + if 'kind' not in self._idp: + raise errors.AnsibleFilterError("|failed identity provider missing a kind") + + self.name = self._idp.pop('name') + self.login = ansible_bool(self._idp.pop('login', False)) + self.challenge = ansible_bool(self._idp.pop('challenge', False)) + self.provider = dict(apiVersion=api_version, kind=self._idp.pop('kind')) + + mm_keys = ('mappingMethod', 'mapping_method') + mapping_method = None + for key in mm_keys: + if key in self._idp: + mapping_method = self._idp.pop(key) + if mapping_method is None: + mapping_method = self.get_default('mappingMethod') + self.mapping_method = mapping_method + + valid_mapping_methods = ['add', 'claim', 'generate', 'lookup'] + if self.mapping_method not in valid_mapping_methods: + raise errors.AnsibleFilterError("|failed unknown mapping method " + "for provider {0}".format(self.__class__.__name__)) + self._required = [] + self._optional = [] + self._allow_additional = True + + @staticmethod + def validate_idp_list(idp_list, openshift_version, deployment_type): + ''' validates a list of idps ''' + login_providers = [x.name for x in idp_list if x.login] + + multiple_logins_unsupported = False + if len(login_providers) > 1: + if deployment_type in ['enterprise', 'online', 'atomic-enterprise', 'openshift-enterprise']: + if LooseVersion(openshift_version) < LooseVersion('3.2'): + multiple_logins_unsupported = True + if deployment_type in ['origin']: + if LooseVersion(openshift_version) < LooseVersion('1.2'): + multiple_logins_unsupported = True + if multiple_logins_unsupported: + raise errors.AnsibleFilterError("|failed multiple providers are " + "not allowed for login. login " + "providers: {0}".format(', '.join(login_providers))) + + names = [x.name for x in idp_list] + if len(set(names)) != len(names): + raise errors.AnsibleFilterError("|failed more than one provider configured with the same name") + + for idp in idp_list: + idp.validate() + + def validate(self): + ''' validate an instance of this idp class ''' + pass + + @staticmethod + def get_default(key): + ''' get a default value for a given key ''' + if key == 'mappingMethod': + return 'claim' + else: + return None + + def set_provider_item(self, items, required=False): + ''' set a provider item based on the list of item names provided. ''' + for item in items: + provider_key = items[0] + if item in self._idp: + self.provider[provider_key] = self._idp.pop(item) + break + else: + default = self.get_default(provider_key) + if default is not None: + self.provider[provider_key] = default + elif required: + raise errors.AnsibleFilterError("|failed provider {0} missing " + "required key {1}".format(self.__class__.__name__, provider_key)) + + def set_provider_items(self): + ''' set the provider items for this idp ''' + for items in self._required: + self.set_provider_item(items, True) + for items in self._optional: + self.set_provider_item(items) + if self._allow_additional: + for key in self._idp.keys(): + self.set_provider_item([key]) + else: + if len(self._idp) > 0: + raise errors.AnsibleFilterError("|failed provider {0} " + "contains unknown keys " + "{1}".format(self.__class__.__name__, ', '.join(self._idp.keys()))) + + def to_dict(self): + ''' translate this idp to a dictionary ''' + return dict(name=self.name, challenge=self.challenge, + login=self.login, mappingMethod=self.mapping_method, + provider=self.provider) + + +class LDAPPasswordIdentityProvider(IdentityProviderBase): + """ LDAPPasswordIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + super(LDAPPasswordIdentityProvider, self).__init__(api_version, idp) + self._allow_additional = False + self._required += [['attributes'], ['url'], ['insecure']] + self._optional += [['ca'], + ['bindDN', 'bind_dn'], + ['bindPassword', 'bind_password']] + + self._idp['insecure'] = ansible_bool(self._idp.pop('insecure', False)) + + if 'attributes' in self._idp and 'preferred_username' in self._idp['attributes']: + pref_user = self._idp['attributes'].pop('preferred_username') + self._idp['attributes']['preferredUsername'] = pref_user + + def validate(self): + ''' validate this idp instance ''' + if not isinstance(self.provider['attributes'], dict): + raise errors.AnsibleFilterError("|failed attributes for provider " + "{0} must be a dictionary".format(self.__class__.__name__)) + + attrs = ['id', 'email', 'name', 'preferredUsername'] + for attr in attrs: + if attr in self.provider['attributes'] and not isinstance(self.provider['attributes'][attr], list): + raise errors.AnsibleFilterError("|failed {0} attribute for " + "provider {1} must be a list".format(attr, self.__class__.__name__)) + + unknown_attrs = set(self.provider['attributes'].keys()) - set(attrs) + if len(unknown_attrs) > 0: + raise errors.AnsibleFilterError("|failed provider {0} has unknown " + "attributes: {1}".format(self.__class__.__name__, ', '.join(unknown_attrs))) + + +class KeystonePasswordIdentityProvider(IdentityProviderBase): + """ KeystoneIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + super(KeystonePasswordIdentityProvider, self).__init__(api_version, idp) + self._allow_additional = False + self._required += [['url'], ['domainName', 'domain_name']] + self._optional += [['ca'], ['certFile', 'cert_file'], ['keyFile', 'key_file']] + + +class RequestHeaderIdentityProvider(IdentityProviderBase): + """ RequestHeaderIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + super(RequestHeaderIdentityProvider, self).__init__(api_version, idp) + self._allow_additional = False + self._required += [['headers']] + self._optional += [['challengeURL', 'challenge_url'], + ['loginURL', 'login_url'], + ['clientCA', 'client_ca'], + ['clientCommonNames', 'client_common_names'], + ['emailHeaders', 'email_headers'], + ['nameHeaders', 'name_headers'], + ['preferredUsernameHeaders', 'preferred_username_headers']] + + def validate(self): + ''' validate this idp instance ''' + if not isinstance(self.provider['headers'], list): + raise errors.AnsibleFilterError("|failed headers for provider {0} " + "must be a list".format(self.__class__.__name__)) + + +class AllowAllPasswordIdentityProvider(IdentityProviderBase): + """ AllowAllPasswordIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + super(AllowAllPasswordIdentityProvider, self).__init__(api_version, idp) + self._allow_additional = False + + +class DenyAllPasswordIdentityProvider(IdentityProviderBase): + """ DenyAllPasswordIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + super(DenyAllPasswordIdentityProvider, self).__init__(api_version, idp) + self._allow_additional = False + + +class HTPasswdPasswordIdentityProvider(IdentityProviderBase): + """ HTPasswdPasswordIdentity + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + super(HTPasswdPasswordIdentityProvider, self).__init__(api_version, idp) + self._allow_additional = False + self._required += [['file', 'filename', 'fileName', 'file_name']] + + @staticmethod + def get_default(key): + if key == 'file': + return '/etc/origin/htpasswd' + else: + return IdentityProviderBase.get_default(key) + + +class BasicAuthPasswordIdentityProvider(IdentityProviderBase): + """ BasicAuthPasswordIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + super(BasicAuthPasswordIdentityProvider, self).__init__(api_version, idp) + self._allow_additional = False + self._required += [['url']] + self._optional += [['ca'], ['certFile', 'cert_file'], ['keyFile', 'key_file']] + + +class IdentityProviderOauthBase(IdentityProviderBase): + """ IdentityProviderOauthBase + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + super(IdentityProviderOauthBase, self).__init__(api_version, idp) + self._allow_additional = False + self._required += [['clientID', 'client_id'], ['clientSecret', 'client_secret']] + + def validate(self): + ''' validate this idp instance ''' + if self.challenge: + raise errors.AnsibleFilterError("|failed provider {0} does not " + "allow challenge authentication".format(self.__class__.__name__)) + + +class OpenIDIdentityProvider(IdentityProviderOauthBase): + """ OpenIDIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + IdentityProviderOauthBase.__init__(self, api_version, idp) + self._required += [['claims'], ['urls']] + self._optional += [['ca'], + ['extraScopes'], + ['extraAuthorizeParameters']] + if 'claims' in self._idp and 'preferred_username' in self._idp['claims']: + pref_user = self._idp['claims'].pop('preferred_username') + self._idp['claims']['preferredUsername'] = pref_user + if 'urls' in self._idp and 'user_info' in self._idp['urls']: + user_info = self._idp['urls'].pop('user_info') + self._idp['urls']['userInfo'] = user_info + if 'extra_scopes' in self._idp: + self._idp['extraScopes'] = self._idp.pop('extra_scopes') + if 'extra_authorize_parameters' in self._idp: + self._idp['extraAuthorizeParameters'] = self._idp.pop('extra_authorize_parameters') + + if 'extraAuthorizeParameters' in self._idp: + if 'include_granted_scopes' in self._idp['extraAuthorizeParameters']: + val = ansible_bool(self._idp['extraAuthorizeParameters'].pop('include_granted_scopes')) + self._idp['extraAuthorizeParameters']['include_granted_scopes'] = val + + def validate(self): + ''' validate this idp instance ''' + IdentityProviderOauthBase.validate(self) + if not isinstance(self.provider['claims'], dict): + raise errors.AnsibleFilterError("|failed claims for provider {0} " + "must be a dictionary".format(self.__class__.__name__)) + + for var, var_type in (('extraScopes', list), ('extraAuthorizeParameters', dict)): + if var in self.provider and not isinstance(self.provider[var], var_type): + raise errors.AnsibleFilterError("|failed {1} for provider " + "{0} must be a {2}".format(self.__class__.__name__, + var, + var_type.__class__.__name__)) + + required_claims = ['id'] + optional_claims = ['email', 'name', 'preferredUsername'] + all_claims = required_claims + optional_claims + + for claim in required_claims: + if claim in required_claims and claim not in self.provider['claims']: + raise errors.AnsibleFilterError("|failed {0} claim missing " + "for provider {1}".format(claim, self.__class__.__name__)) + + for claim in all_claims: + if claim in self.provider['claims'] and not isinstance(self.provider['claims'][claim], list): + raise errors.AnsibleFilterError("|failed {0} claims for " + "provider {1} must be a list".format(claim, self.__class__.__name__)) + + unknown_claims = set(self.provider['claims'].keys()) - set(all_claims) + if len(unknown_claims) > 0: + raise errors.AnsibleFilterError("|failed provider {0} has unknown " + "claims: {1}".format(self.__class__.__name__, ', '.join(unknown_claims))) + + if not isinstance(self.provider['urls'], dict): + raise errors.AnsibleFilterError("|failed urls for provider {0} " + "must be a dictionary".format(self.__class__.__name__)) + + required_urls = ['authorize', 'token'] + optional_urls = ['userInfo'] + all_urls = required_urls + optional_urls + + for url in required_urls: + if url not in self.provider['urls']: + raise errors.AnsibleFilterError("|failed {0} url missing for " + "provider {1}".format(url, self.__class__.__name__)) + + unknown_urls = set(self.provider['urls'].keys()) - set(all_urls) + if len(unknown_urls) > 0: + raise errors.AnsibleFilterError("|failed provider {0} has unknown " + "urls: {1}".format(self.__class__.__name__, ', '.join(unknown_urls))) + + +class GoogleIdentityProvider(IdentityProviderOauthBase): + """ GoogleIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + IdentityProviderOauthBase.__init__(self, api_version, idp) + self._optional += [['hostedDomain', 'hosted_domain']] + + +class GitHubIdentityProvider(IdentityProviderOauthBase): + """ GitHubIdentityProvider + + Attributes: + + Args: + api_version(str): OpenShift config version + idp (dict): idp config dict + + Raises: + AnsibleFilterError: + """ + def __init__(self, api_version, idp): + IdentityProviderOauthBase.__init__(self, api_version, idp) + self._optional += [['organizations']] + + +class FilterModule(object): + ''' Custom ansible filters for use by the openshift_master role''' + + @staticmethod + def translate_idps(idps, api_version, openshift_version, deployment_type): + ''' Translates a list of dictionaries into a valid identityProviders config ''' + idp_list = [] + + if not isinstance(idps, list): + raise errors.AnsibleFilterError("|failed expects to filter on a list of identity providers") + for idp in idps: + if not isinstance(idp, dict): + raise errors.AnsibleFilterError("|failed identity providers must be a list of dictionaries") + + cur_module = sys.modules[__name__] + idp_class = getattr(cur_module, idp['kind'], None) + idp_inst = idp_class(api_version, idp) if idp_class is not None else IdentityProviderBase(api_version, idp) + idp_inst.set_provider_items() + idp_list.append(idp_inst) + + IdentityProviderBase.validate_idp_list(idp_list, openshift_version, deployment_type) + return yaml.dump([idp.to_dict() for idp in idp_list], + allow_unicode=True, + default_flow_style=False, + Dumper=AnsibleDumper) + + @staticmethod + def validate_pcs_cluster(data, masters=None): + ''' Validates output from "pcs status", ensuring that each master + provided is online. + Ex: data = ('...', + 'PCSD Status:', + 'master1.example.com: Online', + 'master2.example.com: Online', + 'master3.example.com: Online', + '...') + masters = ['master1.example.com', + 'master2.example.com', + 'master3.example.com'] + returns True + ''' + if not issubclass(type(data), string_types): + raise errors.AnsibleFilterError("|failed expects data is a string or unicode") + if not issubclass(type(masters), list): + raise errors.AnsibleFilterError("|failed expects masters is a list") + valid = True + for master in masters: + if "{0}: Online".format(master) not in data: + valid = False + return valid + + @staticmethod + def certificates_to_synchronize(hostvars, include_keys=True, include_ca=True): + ''' Return certificates to synchronize based on facts. ''' + if not issubclass(type(hostvars), dict): + raise errors.AnsibleFilterError("|failed expects hostvars is a dict") + certs = ['admin.crt', + 'admin.key', + 'admin.kubeconfig', + 'master.kubelet-client.crt', + 'master.kubelet-client.key'] + if bool(include_ca): + certs += ['ca.crt', 'ca.key'] + if bool(include_keys): + certs += ['serviceaccounts.private.key', + 'serviceaccounts.public.key'] + if bool(hostvars['openshift']['common']['version_gte_3_1_or_1_1']): + certs += ['master.proxy-client.crt', + 'master.proxy-client.key'] + if not bool(hostvars['openshift']['common']['version_gte_3_2_or_1_2']): + certs += ['openshift-master.crt', + 'openshift-master.key', + 'openshift-master.kubeconfig'] + if bool(hostvars['openshift']['common']['version_gte_3_3_or_1_3']): + certs += ['service-signer.crt', + 'service-signer.key'] + if not bool(hostvars['openshift']['common']['version_gte_3_5_or_1_5']): + certs += ['openshift-registry.crt', + 'openshift-registry.key', + 'openshift-registry.kubeconfig', + 'openshift-router.crt', + 'openshift-router.key', + 'openshift-router.kubeconfig'] + return certs + + @staticmethod + def oo_htpasswd_users_from_file(file_contents): + ''' return a dictionary of htpasswd users from htpasswd file contents ''' + htpasswd_entries = {} + if not isinstance(file_contents, string_types): + raise errors.AnsibleFilterError("failed, expects to filter on a string") + for line in file_contents.splitlines(): + user = None + passwd = None + if len(line) == 0: + continue + if ':' in line: + user, passwd = line.split(':', 1) + + if user is None or len(user) == 0 or passwd is None or len(passwd) == 0: + error_msg = "failed, expects each line to be a colon separated string representing the user and passwd" + raise errors.AnsibleFilterError(error_msg) + htpasswd_entries[user] = passwd + return htpasswd_entries + + def filters(self): + ''' returns a mapping of filters to methods ''' + return {"translate_idps": self.translate_idps, + "validate_pcs_cluster": self.validate_pcs_cluster, + "certificates_to_synchronize": self.certificates_to_synchronize, + "oo_htpasswd_users_from_file": self.oo_htpasswd_users_from_file} diff --git a/roles/openshift_master_facts/lookup_plugins/oo_option.py b/roles/openshift_master_facts/lookup_plugins/oo_option.py new file mode 120000 index 000000000..5ae43f8dd --- /dev/null +++ b/roles/openshift_master_facts/lookup_plugins/oo_option.py @@ -0,0 +1 @@ +../../../lookup_plugins/oo_option.py
\ No newline at end of file diff --git a/roles/openshift_master_facts/tasks/main.yml b/roles/openshift_master_facts/tasks/main.yml index 74885d713..6f8f09b22 100644 --- a/roles/openshift_master_facts/tasks/main.yml +++ b/roles/openshift_master_facts/tasks/main.yml @@ -6,7 +6,8 @@ openshift_master_default_subdomain: "{{ osm_default_subdomain | default(None) }}" when: openshift_master_default_subdomain is not defined -- fail: +- name: Verify required variables are set + fail: msg: openshift_master_default_subdomain must be set to deploy metrics when: openshift_hosted_metrics_deploy | default(false) | bool and openshift_master_default_subdomain | default("") == "" @@ -17,7 +18,8 @@ # path must stay consistent. As such if openshift_hosted_metrics_public_url is set in # inventory, we extract the hostname, and then reset openshift_hosted_metrics_public_url # to the format that we know is valid. (This may change in future) -- set_fact: +- name: Set g_metrics_hostname + set_fact: g_metrics_hostname: "{{ openshift_hosted_metrics_public_url | default('hawkular-metrics.' ~ (openshift_master_default_subdomain)) | oo_hostname_from_url }}" @@ -108,7 +110,8 @@ path: "{{ openshift_master_scheduler_conf }}" register: scheduler_config_stat -- set_fact: +- name: Set Default scheduler predicates and priorities + set_fact: openshift_master_scheduler_default_predicates: "{{ lookup('openshift_master_facts_default_predicates') }}" openshift_master_scheduler_default_priorities: "{{ lookup('openshift_master_facts_default_priorities') }}" @@ -118,14 +121,17 @@ src: "{{ openshift_master_scheduler_conf }}" register: current_scheduler_config - - set_fact: + - name: Set openshift_master_scheduler_current_config + set_fact: openshift_master_scheduler_current_config: "{{ current_scheduler_config.content | b64decode | from_json }}" - - fail: + - name: Test if scheduler config is readable + fail: msg: "Unknown scheduler config apiVersion {{ openshift_master_scheduler_config.apiVersion }}" when: "{{ openshift_master_scheduler_current_config.apiVersion | default(None) != 'v1' }}" - - set_fact: + - name: Set current scheduler predicates and priorities + set_fact: openshift_master_scheduler_current_predicates: "{{ openshift_master_scheduler_current_config.predicates }}" openshift_master_scheduler_current_priorities: "{{ openshift_master_scheduler_current_config.priorities }}" when: "{{ scheduler_config_stat.stat.exists }}" diff --git a/roles/openshift_metrics/tasks/generate_certificates.yaml b/roles/openshift_metrics/tasks/generate_certificates.yaml index f7cba0093..7af3f9467 100644 --- a/roles/openshift_metrics/tasks/generate_certificates.yaml +++ b/roles/openshift_metrics/tasks/generate_certificates.yaml @@ -6,6 +6,6 @@ --key='{{ mktemp.stdout }}/ca.key' --cert='{{ mktemp.stdout }}/ca.crt' --serial='{{ mktemp.stdout }}/ca.serial.txt' - --name="metrics-signer@$(date +%s)" + --name="metrics-signer@{{lookup('pipe','date +%s')}}" - include: generate_hawkular_certificates.yaml diff --git a/roles/openshift_metrics/tasks/generate_hawkular_certificates.yaml b/roles/openshift_metrics/tasks/generate_hawkular_certificates.yaml index 854697abb..9e7140bfa 100644 --- a/roles/openshift_metrics/tasks/generate_hawkular_certificates.yaml +++ b/roles/openshift_metrics/tasks/generate_hawkular_certificates.yaml @@ -3,7 +3,7 @@ include: setup_certificate.yaml vars: component: hawkular-metrics - hostnames: "hawkular-metrics,{{ openshift_metrics_hawkular_hostname }}" + hostnames: "hawkular-metrics,hawkular-metrics.{{ openshift_metrics_project }}.svc.cluster.local,{{ openshift_metrics_hawkular_hostname }}" changed_when: no - name: generate hawkular-cassandra certificates |