k8s_service: add new kubernetes module for handling Services (#48872)

* k8s: add k8s_kind arg to KubernetesRawModule

Single–kind k8s modules (e.g. k8s_service) do not have a module
parameter called 'kind' and need to pass a static 'kind' on
KubernetesRawModule class creation. Hence this change.

* k8s: make 'validate' and 'wait' mod params optional

Not all k8s modules utilizing KubernetesRawModule will use these.

* k8s_service: new k8s module for handling Services
pull/49831/head
Mariusz Mazur 6 years ago committed by ansibot
parent 0e4a7b0889
commit d8a690952e

@ -73,7 +73,7 @@ class KubernetesRawModule(KubernetesAnsibleModule):
argument_spec['append_hash'] = dict(type='bool', default=False)
return argument_spec
def __init__(self, *args, **kwargs):
def __init__(self, k8s_kind=None, *args, **kwargs):
self.client = None
mutually_exclusive = [
@ -84,12 +84,13 @@ class KubernetesRawModule(KubernetesAnsibleModule):
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
**kwargs)
self.kind = self.params.get('kind')
self.kind = k8s_kind or self.params.get('kind')
self.api_version = self.params.get('api_version')
self.name = self.params.get('name')
self.namespace = self.params.get('namespace')
resource_definition = self.params.get('resource_definition')
if self.params['validate']:
validate = self.params.get('validate')
if validate:
if LooseVersion(self.openshift_version) < LooseVersion("0.8.0"):
self.fail_json(msg="openshift >= 0.8.0 is required for validate")
self.append_hash = self.params.get('append_hash')
@ -182,8 +183,8 @@ class KubernetesRawModule(KubernetesAnsibleModule):
name = definition['metadata'].get('name')
namespace = definition['metadata'].get('namespace')
existing = None
wait = self.params['wait']
wait_timeout = self.params['wait_timeout']
wait = self.params.get('wait')
wait_timeout = self.params.get('wait_timeout')
self.remove_aliases()

@ -0,0 +1,267 @@
#!/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
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: k8s_service
short_description: Manage Services on Kubernetes
version_added: "2.8"
author: KubeVirt Team (@kubevirt)
description:
- Use Openshift Python SDK to manage Services on Kubernetes
extends_documentation_fragment:
- 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
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
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
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
ports:
description:
- A list of ports to expose.
- U(https://kubernetes.io/docs/concepts/services-networking/service/#multi-port-services)
type: list
selector:
description:
- Label selectors identify objects this Service should apply to.
- U(https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
type: dict
requirements:
- python >= 2.7
- openshift >= 0.6.2
'''
EXAMPLES = '''
- name: Expose https port with ClusterIP
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
k8s_service:
state: present
name: test-https
namespace: default
inline:
spec:
ports:
- port: 443
protocol: TCP
selector:
key: special
'''
RETURN = '''
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.module_utils.k8s.common import AUTH_ARG_SPEC, COMMON_ARG_SPEC
from ansible.module_utils.k8s.raw import KubernetesRawModule
SERVICE_ARG_SPEC = {
'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', 'choices': ['json', 'merge', 'strategic-merge']},
'selector': {'type': 'dict'},
'type': {
'type': 'str',
'choices': [
'NodePort', 'ClusterIP', 'LoadBalancer', 'ExternalName'
],
},
'ports': {'type': 'list'},
}
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()
Loading…
Cancel
Save