Files
kubernetes.core/plugins/modules/k8s_service.py
2020-06-09 19:00:21 +10:00

289 lines
8.9 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2018, KubeVirt Team <@kubevirt>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r'''
module: k8s_service
short_description: Manage Services on Kubernetes
author: KubeVirt Team (@kubevirt)
description:
- Use Openshift Python SDK to manage Services on Kubernetes
extends_documentation_fragment:
- community.kubernetes.k8s_auth_options
options:
resource_definition:
description:
- A partial YAML definition of the Service object being created/updated. Here you can define Kubernetes
Service Resource parameters not covered by this module's parameters.
- "NOTE: I(resource_definition) has lower priority than module parameters. If you try to define e.g.
I(metadata.namespace) here, that value will be ignored and I(metadata) used instead."
aliases:
- definition
- inline
type: dict
src:
description:
- "Provide a path to a file containing a valid YAML definition of an object dated. Mutually
exclusive with I(resource_definition). NOTE: I(kind), I(api_version), I(resource_name), and I(namespace)
will be overwritten by corresponding values found in the configuration read in from the I(src) file."
- Reads from the local file system. To read from the Ansible controller's file system, use the file lookup
plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to
I(resource_definition). See Examples below.
type: path
state:
description:
- Determines if an object should be created, patched, or deleted. When set to C(present), an object will be
created, if it does not already exist. If set to C(absent), an existing object will be deleted. If set to
C(present), an existing object will be patched, if its attributes differ from those specified using
module options and I(resource_definition).
default: present
choices:
- present
- absent
type: str
force:
description:
- If set to C(True), and I(state) is C(present), an existing object will be replaced.
default: false
type: bool
merge_type:
description:
- Whether to override the default patch merge approach with a specific type. By default, the strategic
merge will typically be used.
- For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may
want to use C(merge) if you see "strategic merge patch format is not supported"
- See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment)
- Requires openshift >= 0.6.2
- If more than one merge_type is given, the merge_types will be tried in order
- If openshift >= 0.6.2, this defaults to C(['strategic-merge', 'merge']), which is ideal for using the same parameters
on resource kinds that combine Custom Resources and built-in resources. For openshift < 0.6.2, the default
is simply C(strategic-merge).
choices:
- json
- merge
- strategic-merge
type: list
elements: str
name:
description:
- Use to specify a Service object name.
required: true
type: str
namespace:
description:
- Use to specify a Service object namespace.
required: true
type: str
type:
description:
- Specifies the type of Service to create.
- See U(https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types)
choices:
- NodePort
- ClusterIP
- LoadBalancer
- ExternalName
type: str
ports:
description:
- A list of ports to expose.
- U(https://kubernetes.io/docs/concepts/services-networking/service/#multi-port-services)
type: list
elements: dict
selector:
description:
- Label selectors identify objects this Service should apply to.
- U(https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
type: dict
apply:
description:
- C(apply) compares the desired resource definition with the previously supplied resource definition,
ignoring properties that are automatically generated
- C(apply) works better with Services than 'force=yes'
- mutually exclusive with C(merge_type)
type: bool
requirements:
- python >= 2.7
- openshift >= 0.6.2
'''
EXAMPLES = r'''
- name: Expose https port with ClusterIP
community.kubernetes.k8s_service:
state: present
name: test-https
namespace: default
ports:
- port: 443
protocol: TCP
selector:
key: special
- name: Expose https port with ClusterIP using spec
community.kubernetes.k8s_service:
state: present
name: test-https
namespace: default
inline:
spec:
ports:
- port: 443
protocol: TCP
selector:
key: special
'''
RETURN = r'''
result:
description:
- The created, patched, or otherwise present Service object. Will be empty in the case of a deletion.
returned: success
type: complex
contains:
api_version:
description: The versioned schema of this representation of an object.
returned: success
type: str
kind:
description: Always 'Service'.
returned: success
type: str
metadata:
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
returned: success
type: complex
spec:
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
returned: success
type: complex
status:
description: Current status details for the object.
returned: success
type: complex
'''
import copy
import traceback
from collections import defaultdict
from ansible_collections.community.kubernetes.plugins.module_utils.common import AUTH_ARG_SPEC
from ansible_collections.community.kubernetes.plugins.module_utils.raw import KubernetesRawModule
SERVICE_ARG_SPEC = {
'apply': {
'type': 'bool',
'default': False,
},
'state': {
'default': 'present',
'choices': ['present', 'absent'],
},
'force': {
'type': 'bool',
'default': False,
},
'resource_definition': {
'type': 'dict',
'aliases': ['definition', 'inline']
},
'name': {'required': True},
'namespace': {'required': True},
'merge_type': {'type': 'list', 'elements': 'str', 'choices': ['json', 'merge', 'strategic-merge']},
'selector': {'type': 'dict'},
'src': {
'type': 'path',
},
'type': {
'type': 'str',
'choices': [
'NodePort', 'ClusterIP', 'LoadBalancer', 'ExternalName'
],
},
'ports': {'type': 'list', 'elements': 'dict'},
}
class KubernetesService(KubernetesRawModule):
def __init__(self, *args, **kwargs):
super(KubernetesService, self).__init__(*args, k8s_kind='Service', **kwargs)
@staticmethod
def merge_dicts(x, y):
for k in set(x.keys()).union(y.keys()):
if k in x and k in y:
if isinstance(x[k], dict) and isinstance(y[k], dict):
yield (k, dict(KubernetesService.merge_dicts(x[k], y[k])))
else:
yield (k, y[k])
elif k in x:
yield (k, x[k])
else:
yield (k, y[k])
@property
def argspec(self):
""" argspec property builder """
argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
argument_spec.update(SERVICE_ARG_SPEC)
return argument_spec
def execute_module(self):
""" Module execution """
self.client = self.get_api_client()
api_version = 'v1'
selector = self.params.get('selector')
service_type = self.params.get('type')
ports = self.params.get('ports')
definition = defaultdict(defaultdict)
definition['kind'] = 'Service'
definition['apiVersion'] = api_version
def_spec = definition['spec']
def_spec['type'] = service_type
def_spec['ports'] = ports
def_spec['selector'] = selector
def_meta = definition['metadata']
def_meta['name'] = self.params.get('name')
def_meta['namespace'] = self.params.get('namespace')
# 'resource_definition:' has lower priority than module parameters
definition = dict(self.merge_dicts(self.resource_definitions[0], definition))
resource = self.find_resource('Service', api_version, fail=True)
definition = self.set_defaults(resource, definition)
result = self.perform_action(resource, definition)
self.exit_json(**result)
def main():
module = KubernetesService()
try:
module.execute_module()
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
if __name__ == '__main__':
main()