#!/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 Kubernetes Python SDK to manage Services on Kubernetes extends_documentation_fragment: - kubernetes.core.k8s_auth_options - kubernetes.core.k8s_resource_options - kubernetes.core.k8s_state_options options: 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) - If more than one C(merge_type) is given, the merge_types will be tried in order - 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. 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) default: False type: bool requirements: - python >= 3.6 - kubernetes >= 12.0.0 """ EXAMPLES = r""" - name: Expose https port with ClusterIP kubernetes.core.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 kubernetes.core.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 from collections import defaultdict from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import ( AnsibleModule, ) from ansible_collections.kubernetes.core.plugins.module_utils.args_common import ( AUTH_ARG_SPEC, COMMON_ARG_SPEC, RESOURCE_ARG_SPEC, ) SERVICE_ARG_SPEC = { "apply": {"type": "bool", "default": False}, "name": {"required": True}, "namespace": {"required": True}, "merge_type": { "type": "list", "elements": "str", "choices": ["json", "merge", "strategic-merge"], }, "selector": {"type": "dict"}, "type": { "type": "str", "choices": ["NodePort", "ClusterIP", "LoadBalancer", "ExternalName"], }, "ports": {"type": "list", "elements": "dict"}, } 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(merge_dicts(x[k], y[k]))) else: yield (k, y[k]) elif k in x: yield (k, x[k]) else: yield (k, y[k]) def argspec(): """argspec property builder""" argument_spec = copy.deepcopy(AUTH_ARG_SPEC) argument_spec.update(COMMON_ARG_SPEC) argument_spec.update(RESOURCE_ARG_SPEC) argument_spec.update(SERVICE_ARG_SPEC) return argument_spec def execute_module(module, k8s_ansible_mixin): """Module execution""" k8s_ansible_mixin.set_resource_definitions(module) api_version = "v1" selector = module.params.get("selector") service_type = module.params.get("type") ports = module.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"] = module.params.get("name") def_meta["namespace"] = module.params.get("namespace") # 'resource_definition:' has lower priority than module parameters definition = dict( merge_dicts(k8s_ansible_mixin.resource_definitions[0], definition) ) resource = k8s_ansible_mixin.find_resource("Service", api_version, fail=True) definition = k8s_ansible_mixin.set_defaults(resource, definition) result = k8s_ansible_mixin.perform_action(resource, definition) module.exit_json(**result) def main(): module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True) from ansible_collections.kubernetes.core.plugins.module_utils.common import ( K8sAnsibleMixin, get_api_client, ) k8s_ansible_mixin = K8sAnsibleMixin(module) k8s_ansible_mixin.client = get_api_client(module=module) execute_module(module, k8s_ansible_mixin) if __name__ == "__main__": main()