mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-24 19:31:26 -07:00
* openstack: standardize tls params * tower: tower_verify_ssl->validate_certs * docker: use standard tls config params - cacert_path -> ca_cert - cert_path -> client_cert - key_path -> client_key - tls_verify -> validate_certs * k8s: standardize tls connection params - verify_ssl -> validate_certs - ssl_ca_cert -> ca_cert - cert_file -> client_cert - key_file -> client_key * ingate: verify_ssl -> validate_certs * manageiq: standardize tls params - verify_ssl -> validate_certs - ca_bundle_path -> ca_cert * mysql: standardize tls params - ssl_ca -> ca_cert - ssl_cert -> client_cert - ssl_key -> client_key * nios: ssl_verify -> validate_certs * postgresql: ssl_rootcert -> ca_cert * rabbitmq: standardize tls params - cacert -> ca_cert - cert -> client_cert - key -> client_key * rackspace: verify_ssl -> validate_certs * vca: verify_certs -> validate_certs * kubevirt_cdi_upload: upload_host_verify_ssl -> upload_host_validate_certs * lxd: standardize tls params - key_file -> client_key - cert_file -> client_cert * get_certificate: ca_certs -> ca_cert * get_certificate.py: clarify one or more certs in a file Co-Authored-By: jamescassell <code@james.cassell.me> * zabbix: tls_issuer -> ca_cert * bigip_device_auth_ldap: standardize tls params - ssl_check_peer -> validate_certs - ssl_client_cert -> client_cert - ssl_client_key -> client_key - ssl_ca_cert -> ca_cert * vdirect: vdirect_validate_certs -> validate_certs * mqtt: standardize tls params - ca_certs -> ca_cert - certfile -> client_cert - keyfile -> client_key * pulp_repo: standardize tls params remove `importer_ssl` prefix * rhn_register: sslcacert -> ca_cert * yum_repository: standardize tls params The fix for yum_repository is not straightforward since this module is only a thin wrapper for the underlying commands and config. In this case, we add the new values as aliases, keeping the old as primary, only due to the internal structure of the module. Aliases added: - sslcacert -> ca_cert - sslclientcert -> client_cert - sslclientkey -> client_key - sslverify -> validate_certs * gitlab_hook: enable_ssl_verification -> hook_validate_certs * Adjust arguments for docker_swarm inventory plugin. * foreman callback: standardize tls params - ssl_cert -> client_cert - ssl_key -> client_key * grafana_annotations: validate_grafana_certs -> validate_certs * nrdp callback: validate_nrdp_certs -> validate_certs * kubectl connection: standardize tls params - kubectl_cert_file -> client_cert - kubectl_key_file -> client_key - kubectl_ssl_ca_cert -> ca_cert - kubectl_verify_ssl -> validate_certs * oc connection: standardize tls params - oc_cert_file -> client_cert - oc_key_file -> client_key - oc_ssl_ca_cert -> ca_cert - oc_verify_ssl -> validate_certs * psrp connection: cert_trust_path -> ca_cert TODO: cert_validation -> validate_certs (multi-valued vs bool) * k8s inventory: standardize tls params - cert_file -> client_cert - key_file -> client_key - ca_cert -> ca_cert - verify_ssl -> validate_certs * openshift inventory: standardize tls params - cert_file -> client_cert - key_file -> client_key - ca_cert -> ca_cert - verify_ssl -> validate_certs * tower inventory: verify_ssl -> validate_certs * hashi_vault lookup: cacert -> ca_cert * k8s lookup: standardize tls params - cert_file -> client_cert - key_file -> client_key - ca_cert -> ca_cert - verify_ssl -> validate_certs * laps_passord lookup: cacert_file -> ca_cert * changelog for TLS parameter standardization
270 lines
8.6 KiB
Python
270 lines
8.6 KiB
Python
# Copyright 2018 Red Hat | Ansible
|
|
#
|
|
# 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/>.
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
import copy
|
|
import json
|
|
import os
|
|
import traceback
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
|
from ansible.module_utils.common.dict_transformations import recursive_diff
|
|
from ansible.module_utils.six import iteritems, string_types
|
|
from ansible.module_utils._text import to_native
|
|
|
|
K8S_IMP_ERR = None
|
|
try:
|
|
import kubernetes
|
|
import openshift
|
|
from openshift.dynamic import DynamicClient
|
|
from openshift.dynamic.exceptions import ResourceNotFoundError, ResourceNotUniqueError
|
|
HAS_K8S_MODULE_HELPER = True
|
|
k8s_import_exception = None
|
|
except ImportError as e:
|
|
HAS_K8S_MODULE_HELPER = False
|
|
k8s_import_exception = e
|
|
K8S_IMP_ERR = traceback.format_exc()
|
|
|
|
YAML_IMP_ERR = None
|
|
try:
|
|
import yaml
|
|
HAS_YAML = True
|
|
except ImportError:
|
|
YAML_IMP_ERR = traceback.format_exc()
|
|
HAS_YAML = False
|
|
|
|
try:
|
|
import urllib3
|
|
urllib3.disable_warnings()
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
def list_dict_str(value):
|
|
if isinstance(value, list):
|
|
return value
|
|
elif isinstance(value, dict):
|
|
return value
|
|
elif isinstance(value, string_types):
|
|
return value
|
|
raise TypeError
|
|
|
|
|
|
ARG_ATTRIBUTES_BLACKLIST = ('property_path',)
|
|
|
|
COMMON_ARG_SPEC = {
|
|
'state': {
|
|
'default': 'present',
|
|
'choices': ['present', 'absent'],
|
|
},
|
|
'force': {
|
|
'type': 'bool',
|
|
'default': False,
|
|
},
|
|
'resource_definition': {
|
|
'type': list_dict_str,
|
|
'aliases': ['definition', 'inline']
|
|
},
|
|
'src': {
|
|
'type': 'path',
|
|
},
|
|
'kind': {},
|
|
'name': {},
|
|
'namespace': {},
|
|
'api_version': {
|
|
'default': 'v1',
|
|
'aliases': ['api', 'version'],
|
|
},
|
|
}
|
|
|
|
AUTH_ARG_SPEC = {
|
|
'kubeconfig': {
|
|
'type': 'path',
|
|
},
|
|
'context': {},
|
|
'host': {},
|
|
'api_key': {
|
|
'no_log': True,
|
|
},
|
|
'username': {},
|
|
'password': {
|
|
'no_log': True,
|
|
},
|
|
'validate_certs': {
|
|
'type': 'bool',
|
|
'aliases': ['verify_ssl'],
|
|
},
|
|
'ca_cert': {
|
|
'type': 'path',
|
|
'aliases': ['ssl_ca_cert'],
|
|
},
|
|
'client_cert': {
|
|
'type': 'path',
|
|
'aliases': ['cert_file'],
|
|
},
|
|
'client_key': {
|
|
'type': 'path',
|
|
'aliases': ['key_file'],
|
|
},
|
|
}
|
|
|
|
|
|
class K8sAnsibleMixin(object):
|
|
_argspec_cache = None
|
|
|
|
@property
|
|
def argspec(self):
|
|
"""
|
|
Introspect the model properties, and return an Ansible module arg_spec dict.
|
|
:return: dict
|
|
"""
|
|
if self._argspec_cache:
|
|
return self._argspec_cache
|
|
argument_spec = copy.deepcopy(COMMON_ARG_SPEC)
|
|
argument_spec.update(copy.deepcopy(AUTH_ARG_SPEC))
|
|
self._argspec_cache = argument_spec
|
|
return self._argspec_cache
|
|
|
|
def get_api_client(self, **auth_params):
|
|
auth_args = AUTH_ARG_SPEC.keys()
|
|
|
|
auth_params = auth_params or getattr(self, 'params', {})
|
|
auth = copy.deepcopy(auth_params)
|
|
|
|
# If authorization variables aren't defined, look for them in environment variables
|
|
for arg in auth_args:
|
|
if auth_params.get(arg) is None:
|
|
env_value = os.getenv('K8S_AUTH_{0}'.format(arg.upper()), None)
|
|
if env_value is not None:
|
|
if AUTH_ARG_SPEC[arg].get('type') == 'bool':
|
|
env_value = env_value.lower() not in ['0', 'false', 'no']
|
|
auth[arg] = env_value
|
|
|
|
def auth_set(*names):
|
|
return all([auth.get(name) for name in names])
|
|
|
|
if auth_set('username', 'password', 'host') or auth_set('api_key', 'host'):
|
|
# We have enough in the parameters to authenticate, no need to load incluster or kubeconfig
|
|
pass
|
|
elif auth_set('kubeconfig') or auth_set('context'):
|
|
kubernetes.config.load_kube_config(auth.get('kubeconfig'), auth.get('context'))
|
|
else:
|
|
# First try to do incluster config, then kubeconfig
|
|
try:
|
|
kubernetes.config.load_incluster_config()
|
|
except kubernetes.config.ConfigException:
|
|
kubernetes.config.load_kube_config(auth.get('kubeconfig'), auth.get('context'))
|
|
|
|
# Override any values in the default configuration with Ansible parameters
|
|
configuration = kubernetes.client.Configuration()
|
|
for key, value in iteritems(auth):
|
|
if key in auth_args and value is not None:
|
|
if key == 'api_key':
|
|
setattr(configuration, key, {'authorization': "Bearer {0}".format(value)})
|
|
else:
|
|
setattr(configuration, key, value)
|
|
|
|
kubernetes.client.Configuration.set_default(configuration)
|
|
return DynamicClient(kubernetes.client.ApiClient(configuration))
|
|
|
|
def find_resource(self, kind, api_version, fail=False):
|
|
for attribute in ['kind', 'name', 'singular_name']:
|
|
try:
|
|
return self.client.resources.get(**{'api_version': api_version, attribute: kind})
|
|
except (ResourceNotFoundError, ResourceNotUniqueError):
|
|
pass
|
|
try:
|
|
return self.client.resources.get(api_version=api_version, short_names=[kind])
|
|
except (ResourceNotFoundError, ResourceNotUniqueError):
|
|
if fail:
|
|
self.fail(msg='Failed to find exact match for {0}.{1} by [kind, name, singularName, shortNames]'.format(api_version, kind))
|
|
|
|
def kubernetes_facts(self, kind, api_version, name=None, namespace=None, label_selectors=None, field_selectors=None):
|
|
resource = self.find_resource(kind, api_version)
|
|
if not resource:
|
|
return dict(resources=[])
|
|
try:
|
|
result = resource.get(name=name,
|
|
namespace=namespace,
|
|
label_selector=','.join(label_selectors),
|
|
field_selector=','.join(field_selectors)).to_dict()
|
|
except openshift.dynamic.exceptions.NotFoundError:
|
|
return dict(resources=[])
|
|
|
|
if 'items' in result:
|
|
return dict(resources=result['items'])
|
|
else:
|
|
return dict(resources=[result])
|
|
|
|
def remove_aliases(self):
|
|
"""
|
|
The helper doesn't know what to do with aliased keys
|
|
"""
|
|
for k, v in iteritems(self.argspec):
|
|
if 'aliases' in v:
|
|
for alias in v['aliases']:
|
|
if alias in self.params:
|
|
self.params.pop(alias)
|
|
|
|
def load_resource_definitions(self, src):
|
|
""" Load the requested src path """
|
|
result = None
|
|
path = os.path.normpath(src)
|
|
if not os.path.exists(path):
|
|
self.fail(msg="Error accessing {0}. Does the file exist?".format(path))
|
|
try:
|
|
with open(path, 'r') as f:
|
|
result = list(yaml.safe_load_all(f))
|
|
except (IOError, yaml.YAMLError) as exc:
|
|
self.fail(msg="Error loading resource_definition: {0}".format(exc))
|
|
return result
|
|
|
|
@staticmethod
|
|
def diff_objects(existing, new):
|
|
result = dict()
|
|
diff = recursive_diff(existing, new)
|
|
if diff:
|
|
result['before'] = diff[0]
|
|
result['after'] = diff[1]
|
|
return not diff, result
|
|
|
|
|
|
class KubernetesAnsibleModule(AnsibleModule, K8sAnsibleMixin):
|
|
resource_definition = None
|
|
api_version = None
|
|
kind = None
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
kwargs['argument_spec'] = self.argspec
|
|
AnsibleModule.__init__(self, *args, **kwargs)
|
|
|
|
if not HAS_K8S_MODULE_HELPER:
|
|
self.fail_json(msg=missing_required_lib('openshift'), exception=K8S_IMP_ERR,
|
|
error=to_native(k8s_import_exception))
|
|
self.openshift_version = openshift.__version__
|
|
|
|
if not HAS_YAML:
|
|
self.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
|
|
|
|
def execute_module(self):
|
|
raise NotImplementedError()
|
|
|
|
def fail(self, msg=None):
|
|
self.fail_json(msg=msg)
|