Issue #1: Restructure role into collection using content_collector.

This commit is contained in:
Jeff Geerling
2020-01-30 10:50:44 -06:00
parent 7cfd49b1f2
commit 4b6fdb1615
270 changed files with 3676 additions and 54531 deletions

View File

View File

@@ -0,0 +1,18 @@
#!/usr/bin/python
# Copyright: (c) 2015, Google Inc. All Rights Reserved.
# 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': ['removed'],
'supported_by': 'community'}
from ansible.module_utils.common.removed import removed_module
if __name__ == '__main__':
removed_module(removed_in='2.9')

279
plugins/modules/k8s.py Normal file
View File

@@ -0,0 +1,279 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2018, Chris Houseknecht <@chouseknecht>
# 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
short_description: Manage Kubernetes (K8s) objects
version_added: "2.6"
author:
- "Chris Houseknecht (@chouseknecht)"
- "Fabian von Feilitzsch (@fabianvf)"
description:
- Use the OpenShift Python client to perform CRUD operations on K8s objects.
- Pass the object definition from a source file or inline. See examples for reading
files and using Jinja templates or vault-encrypted files.
- Access to the full range of K8s APIs.
- Use the M(k8s_info) module to obtain a list of items about an object of type C(kind)
- Authenticate using either a config file, certificates, password or token.
- Supports check mode.
extends_documentation_fragment:
- k8s_state_options
- k8s_name_options
- k8s_resource_options
- k8s_auth_options
notes:
- If your OpenShift Python library is not 0.9.0 or newer and you are trying to
remove an item from an associative array/dictionary, for example a label or
an annotation, you will need to explicitly set the value of the item to be
removed to `null`. Simply deleting the entry in the dictionary will not
remove it from openshift or kubernetes.
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)
- 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).
- mutually exclusive with C(apply)
choices:
- json
- merge
- strategic-merge
type: list
version_added: "2.7"
wait:
description:
- Whether to wait for certain resource kinds to end up in the desired state. By default the module exits once Kubernetes has
received the request
- Implemented for C(state=present) for C(Deployment), C(DaemonSet) and C(Pod), and for C(state=absent) for all resource kinds.
- For resource kinds without an implementation, C(wait) returns immediately unless C(wait_condition) is set.
default: no
type: bool
version_added: "2.8"
wait_sleep:
description:
- Number of seconds to sleep between checks.
default: 5
version_added: "2.9"
wait_timeout:
description:
- How long in seconds to wait for the resource to end up in the desired state. Ignored if C(wait) is not set.
default: 120
version_added: "2.8"
wait_condition:
description:
- Specifies a custom condition on the status to wait for. Ignored if C(wait) is not set or is set to False.
suboptions:
type:
description:
- The type of condition to wait for. For example, the C(Pod) resource will set the C(Ready) condition (among others)
- Required if you are specifying a C(wait_condition). If left empty, the C(wait_condition) field will be ignored.
- The possible types for a condition are specific to each resource type in Kubernetes. See the API documentation of the status field
for a given resource to see possible choices.
status:
description:
- The value of the status field in your desired condition.
- For example, if a C(Deployment) is paused, the C(Progressing) C(type) will have the C(Unknown) status.
choices:
- True
- False
- Unknown
reason:
description:
- The value of the reason field in your desired condition
- For example, if a C(Deployment) is paused, The C(Progressing) c(type) will have the C(DeploymentPaused) reason.
- The possible reasons in a condition are specific to each resource type in Kubernetes. See the API documentation of the status field
for a given resource to see possible choices.
version_added: "2.8"
validate:
description:
- how (if at all) to validate the resource definition against the kubernetes schema.
Requires the kubernetes-validate python module
suboptions:
fail_on_error:
description: whether to fail on validation errors.
required: yes
type: bool
version:
description: version of Kubernetes to validate against. defaults to Kubernetes server version
strict:
description: whether to fail when passing unexpected properties
default: no
type: bool
version_added: "2.8"
append_hash:
description:
- Whether to append a hash to a resource name for immutability purposes
- Applies only to ConfigMap and Secret resources
- The parameter will be silently ignored for other resource kinds
- The full definition of an object is needed to generate the hash - this means that deleting an object created with append_hash
will only work if the same object is passed with state=absent (alternatively, just use state=absent with the name including
the generated hash and append_hash=no)
type: bool
version_added: "2.8"
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
version_added: "2.9"
requirements:
- "python >= 2.7"
- "openshift >= 0.6"
- "PyYAML >= 3.11"
'''
EXAMPLES = '''
- name: Create a k8s namespace
k8s:
name: testing
api_version: v1
kind: Namespace
state: present
- name: Create a Service object from an inline definition
k8s:
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: web
namespace: testing
labels:
app: galaxy
service: web
spec:
selector:
app: galaxy
service: web
ports:
- protocol: TCP
targetPort: 8000
name: port-8000-tcp
port: 8000
- name: Create a Service object by reading the definition from a file
k8s:
state: present
src: /testing/service.yml
- name: Remove an existing Service object
k8s:
state: absent
api_version: v1
kind: Service
namespace: testing
name: web
# Passing the object definition from a file
- name: Create a Deployment by reading the definition from a local file
k8s:
state: present
src: /testing/deployment.yml
- name: >-
Read definition file from the Ansible controller file system.
If the definition file has been encrypted with Ansible Vault it will automatically be decrypted.
k8s:
state: present
definition: "{{ lookup('file', '/testing/deployment.yml') }}"
- name: Read definition file from the Ansible controller file system after Jinja templating
k8s:
state: present
definition: "{{ lookup('template', '/testing/deployment.yml') }}"
- name: fail on validation errors
k8s:
state: present
definition: "{{ lookup('template', '/testing/deployment.yml') }}"
validate:
fail_on_error: yes
- name: warn on validation errors, check for unexpected properties
k8s:
state: present
definition: "{{ lookup('template', '/testing/deployment.yml') }}"
validate:
fail_on_error: no
strict: yes
'''
RETURN = '''
result:
description:
- The created, patched, or otherwise present 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: Represents the REST resource this object represents.
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
items:
description: Returned only when multiple yaml documents are passed to src or resource_definition
returned: when resource_definition or src contains list of objects
type: list
duration:
description: elapsed time of task in seconds
returned: when C(wait) is true
type: int
sample: 48
'''
from ansible.module_utils.k8s.raw import KubernetesRawModule
def main():
KubernetesRawModule().execute_module()
if __name__ == '__main__':
main()

336
plugins/modules/k8s_auth.py Normal file
View File

@@ -0,0 +1,336 @@
#!/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_auth
short_description: Authenticate to Kubernetes clusters which require an explicit login step
version_added: "2.8"
author: KubeVirt Team (@kubevirt)
description:
- "This module handles authenticating to Kubernetes clusters requiring I(explicit) authentication procedures,
meaning ones where a client logs in (obtains an authentication token), performs API operations using said
token and then logs out (revokes the token). An example of a Kubernetes distribution requiring this module
is OpenShift."
- "On the other hand a popular configuration for username+password authentication is one utilizing HTTP Basic
Auth, which does not involve any additional login/logout steps (instead login credentials can be attached
to each and every API call performed) and as such is handled directly by the C(k8s) module (and other
resourcespecific modules) by utilizing the C(host), C(username) and C(password) parameters. Please
consult your preferred module's documentation for more details."
options:
state:
description:
- If set to I(present) connect to the API server using the URL specified in C(host) and attempt to log in.
- If set to I(absent) attempt to log out by revoking the authentication token specified in C(api_key).
default: present
choices:
- present
- absent
host:
description:
- Provide a URL for accessing the API server.
required: true
username:
description:
- Provide a username for authenticating with the API server.
password:
description:
- Provide a password for authenticating with the API server.
ca_cert:
description:
- "Path to a CA certificate file used to verify connection to the API server. The full certificate chain
must be provided to avoid certificate validation errors."
aliases: [ ssl_ca_cert ]
validate_certs:
description:
- "Whether or not to verify the API server's SSL certificates."
type: bool
default: true
aliases: [ verify_ssl ]
api_key:
description:
- When C(state) is set to I(absent), this specifies the token to revoke.
requirements:
- python >= 2.7
- urllib3
- requests
- requests-oauthlib
'''
EXAMPLES = '''
- hosts: localhost
module_defaults:
group/k8s:
host: https://k8s.example.com/
ca_cert: ca.pem
tasks:
- block:
# It's good practice to store login credentials in a secure vault and not
# directly in playbooks.
- include_vars: k8s_passwords.yml
- name: Log in (obtain access token)
k8s_auth:
username: admin
password: "{{ k8s_admin_password }}"
register: k8s_auth_results
# Previous task provides the token/api_key, while all other parameters
# are taken from module_defaults
- name: Get a list of all pods from any namespace
k8s_info:
api_key: "{{ k8s_auth_results.k8s_auth.api_key }}"
kind: Pod
register: pod_list
always:
- name: If login succeeded, try to log out (revoke access token)
when: k8s_auth_results.k8s_auth.api_key is defined
k8s_auth:
state: absent
api_key: "{{ k8s_auth_results.k8s_auth.api_key }}"
'''
# Returned value names need to match k8s modules parameter names, to make it
# easy to pass returned values of k8s_auth to other k8s modules.
# Discussion: https://github.com/ansible/ansible/pull/50807#discussion_r248827899
RETURN = '''
k8s_auth:
description: Kubernetes authentication facts.
returned: success
type: complex
contains:
api_key:
description: Authentication token.
returned: success
type: str
host:
description: URL for accessing the API server.
returned: success
type: str
ca_cert:
description: Path to a CA certificate file used to verify connection to the API server.
returned: success
type: str
validate_certs:
description: "Whether or not to verify the API server's SSL certificates."
returned: success
type: bool
username:
description: Username for authenticating with the API server.
returned: success
type: str
'''
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six.moves.urllib_parse import urlparse, parse_qs, urlencode
# 3rd party imports
try:
import requests
HAS_REQUESTS = True
except ImportError:
HAS_REQUESTS = False
try:
from requests_oauthlib import OAuth2Session
HAS_REQUESTS_OAUTH = True
except ImportError:
HAS_REQUESTS_OAUTH = False
try:
from urllib3.util import make_headers
HAS_URLLIB3 = True
except ImportError:
HAS_URLLIB3 = False
K8S_AUTH_ARG_SPEC = {
'state': {
'default': 'present',
'choices': ['present', 'absent'],
},
'host': {'required': True},
'username': {},
'password': {'no_log': True},
'ca_cert': {'type': 'path', 'aliases': ['ssl_ca_cert']},
'validate_certs': {
'type': 'bool',
'default': True,
'aliases': ['verify_ssl']
},
'api_key': {'no_log': True},
}
class KubernetesAuthModule(AnsibleModule):
def __init__(self):
AnsibleModule.__init__(
self,
argument_spec=K8S_AUTH_ARG_SPEC,
required_if=[
('state', 'present', ['username', 'password']),
('state', 'absent', ['api_key']),
]
)
if not HAS_REQUESTS:
self.fail("This module requires the python 'requests' package. Try `pip install requests`.")
if not HAS_REQUESTS_OAUTH:
self.fail("This module requires the python 'requests-oauthlib' package. Try `pip install requests-oauthlib`.")
if not HAS_URLLIB3:
self.fail("This module requires the python 'urllib3' package. Try `pip install urllib3`.")
def execute_module(self):
state = self.params.get('state')
verify_ssl = self.params.get('validate_certs')
ssl_ca_cert = self.params.get('ca_cert')
self.auth_username = self.params.get('username')
self.auth_password = self.params.get('password')
self.auth_api_key = self.params.get('api_key')
self.con_host = self.params.get('host')
# python-requests takes either a bool or a path to a ca file as the 'verify' param
if verify_ssl and ssl_ca_cert:
self.con_verify_ca = ssl_ca_cert # path
else:
self.con_verify_ca = verify_ssl # bool
# Get needed info to access authorization APIs
self.openshift_discover()
if state == 'present':
new_api_key = self.openshift_login()
result = dict(
host=self.con_host,
validate_certs=verify_ssl,
ca_cert=ssl_ca_cert,
api_key=new_api_key,
username=self.auth_username,
)
else:
self.openshift_logout()
result = dict()
self.exit_json(changed=False, k8s_auth=result)
def openshift_discover(self):
url = '{0}/.well-known/oauth-authorization-server'.format(self.con_host)
ret = requests.get(url, verify=self.con_verify_ca)
if ret.status_code != 200:
self.fail_request("Couldn't find OpenShift's OAuth API", method='GET', url=url,
reason=ret.reason, status_code=ret.status_code)
try:
oauth_info = ret.json()
self.openshift_auth_endpoint = oauth_info['authorization_endpoint']
self.openshift_token_endpoint = oauth_info['token_endpoint']
except Exception as e:
self.fail_json(msg="Something went wrong discovering OpenShift OAuth details.",
exception=traceback.format_exc())
def openshift_login(self):
os_oauth = OAuth2Session(client_id='openshift-challenging-client')
authorization_url, state = os_oauth.authorization_url(self.openshift_auth_endpoint,
state="1", code_challenge_method='S256')
auth_headers = make_headers(basic_auth='{0}:{1}'.format(self.auth_username, self.auth_password))
# Request authorization code using basic auth credentials
ret = os_oauth.get(
authorization_url,
headers={'X-Csrf-Token': state, 'authorization': auth_headers.get('authorization')},
verify=self.con_verify_ca,
allow_redirects=False
)
if ret.status_code != 302:
self.fail_request("Authorization failed.", method='GET', url=authorization_url,
reason=ret.reason, status_code=ret.status_code)
# In here we have `code` and `state`, I think `code` is the important one
qwargs = {}
for k, v in parse_qs(urlparse(ret.headers['Location']).query).items():
qwargs[k] = v[0]
qwargs['grant_type'] = 'authorization_code'
# Using authorization code given to us in the Location header of the previous request, request a token
ret = os_oauth.post(
self.openshift_token_endpoint,
headers={
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
# This is just base64 encoded 'openshift-challenging-client:'
'Authorization': 'Basic b3BlbnNoaWZ0LWNoYWxsZW5naW5nLWNsaWVudDo='
},
data=urlencode(qwargs),
verify=self.con_verify_ca
)
if ret.status_code != 200:
self.fail_request("Failed to obtain an authorization token.", method='POST',
url=self.openshift_token_endpoint,
reason=ret.reason, status_code=ret.status_code)
return ret.json()['access_token']
def openshift_logout(self):
url = '{0}/apis/oauth.openshift.io/v1/oauthaccesstokens/{1}'.format(self.con_host, self.auth_api_key)
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer {0}'.format(self.auth_api_key)
}
json = {
"apiVersion": "oauth.openshift.io/v1",
"kind": "DeleteOptions"
}
ret = requests.delete(url, headers=headers, json=json, verify=self.con_verify_ca)
# Ignore errors, the token will time out eventually anyway
def fail(self, msg=None):
self.fail_json(msg=msg)
def fail_request(self, msg, **kwargs):
req_info = {}
for k, v in kwargs.items():
req_info['req_' + k] = v
self.fail_json(msg=msg, **req_info)
def main():
module = KubernetesAuthModule()
try:
module.execute_module()
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
if __name__ == '__main__':
main()

179
plugins/modules/k8s_info.py Normal file
View File

@@ -0,0 +1,179 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2018, Will Thames <@willthames>
# 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_info
short_description: Describe Kubernetes (K8s) objects
version_added: "2.7"
author:
- "Will Thames (@willthames)"
description:
- Use the OpenShift Python client to perform read operations on K8s objects.
- Access to the full range of K8s APIs.
- Authenticate using either a config file, certificates, password or token.
- Supports check mode.
- This module was called C(k8s_facts) before Ansible 2.9. The usage did not change.
options:
api_version:
description:
- Use to specify the API version. in conjunction with I(kind), I(name), and I(namespace) to identify a
specific object.
default: v1
aliases:
- api
- version
kind:
description:
- Use to specify an object model. Use in conjunction with I(api_version), I(name), and I(namespace) to identify a
specific object.
required: yes
name:
description:
- Use to specify an object name. Use in conjunction with I(api_version), I(kind) and I(namespace) to identify a
specific object.
namespace:
description:
- Use to specify an object namespace. Use in conjunction with I(api_version), I(kind), and I(name)
to identify a specific object.
label_selectors:
description: List of label selectors to use to filter results
field_selectors:
description: List of field selectors to use to filter results
extends_documentation_fragment:
- k8s_auth_options
requirements:
- "python >= 2.7"
- "openshift >= 0.6"
- "PyYAML >= 3.11"
'''
EXAMPLES = '''
- name: Get an existing Service object
k8s_info:
api_version: v1
kind: Service
name: web
namespace: testing
register: web_service
- name: Get a list of all service objects
k8s_info:
api_version: v1
kind: Service
namespace: testing
register: service_list
- name: Get a list of all pods from any namespace
k8s_info:
kind: Pod
register: pod_list
- name: Search for all Pods labelled app=web
k8s_info:
kind: Pod
label_selectors:
- app = web
- tier in (dev, test)
- name: Search for all running pods
k8s_info:
kind: Pod
field_selectors:
- status.phase=Running
'''
RETURN = '''
resources:
description:
- The object(s) that exists
returned: success
type: complex
contains:
api_version:
description: The versioned schema of this representation of an object.
returned: success
type: str
kind:
description: Represents the REST resource this object represents.
returned: success
type: str
metadata:
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
returned: success
type: dict
spec:
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
returned: success
type: dict
status:
description: Current status details for the object.
returned: success
type: dict
'''
from ansible.module_utils.k8s.common import KubernetesAnsibleModule, AUTH_ARG_SPEC
import copy
class KubernetesInfoModule(KubernetesAnsibleModule):
def __init__(self, *args, **kwargs):
KubernetesAnsibleModule.__init__(self, *args,
supports_check_mode=True,
**kwargs)
if self._name == 'k8s_facts':
self.deprecate("The 'k8s_facts' module has been renamed to 'k8s_info'", version='2.13')
def execute_module(self):
self.client = self.get_api_client()
self.exit_json(changed=False,
**self.kubernetes_facts(self.params['kind'],
self.params['api_version'],
self.params['name'],
self.params['namespace'],
self.params['label_selectors'],
self.params['field_selectors']))
@property
def argspec(self):
args = copy.deepcopy(AUTH_ARG_SPEC)
args.update(
dict(
kind=dict(required=True),
api_version=dict(default='v1', aliases=['api', 'version']),
name=dict(),
namespace=dict(),
label_selectors=dict(type='list', default=[]),
field_selectors=dict(type='list', default=[]),
)
)
return args
def main():
KubernetesInfoModule().execute_module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,129 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2018, Chris Houseknecht <@chouseknecht>
# 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_scale
short_description: Set a new size for a Deployment, ReplicaSet, Replication Controller, or Job.
version_added: "2.5"
author:
- "Chris Houseknecht (@chouseknecht)"
- "Fabian von Feilitzsch (@fabianvf)"
description:
- Similar to the kubectl scale command. Use to set the number of replicas for a Deployment, ReplicaSet,
or Replication Controller, or the parallelism attribute of a Job. Supports check mode.
extends_documentation_fragment:
- k8s_name_options
- k8s_auth_options
- k8s_resource_options
- k8s_scale_options
requirements:
- "python >= 2.7"
- "openshift >= 0.6"
- "PyYAML >= 3.11"
'''
EXAMPLES = '''
- name: Scale deployment up, and extend timeout
k8s_scale:
api_version: v1
kind: Deployment
name: elastic
namespace: myproject
replicas: 3
wait_timeout: 60
- name: Scale deployment down when current replicas match
k8s_scale:
api_version: v1
kind: Deployment
name: elastic
namespace: myproject
current_replicas: 3
replicas: 2
- name: Increase job parallelism
k8s_scale:
api_version: batch/v1
kind: job
name: pi-with-timeout
namespace: testing
replicas: 2
# Match object using local file or inline definition
- name: Scale deployment based on a file from the local filesystem
k8s_scale:
src: /myproject/elastic_deployment.yml
replicas: 3
wait: no
- name: Scale deployment based on a template output
k8s_scale:
resource_definition: "{{ lookup('template', '/myproject/elastic_deployment.yml') | from_yaml }}"
replicas: 3
wait: no
- name: Scale deployment based on a file from the Ansible controller filesystem
k8s_scale:
resource_definition: "{{ lookup('file', '/myproject/elastic_deployment.yml') | from_yaml }}"
replicas: 3
wait: no
'''
RETURN = '''
result:
description:
- If a change was made, will return the patched object, otherwise returns the existing object.
returned: success
type: complex
contains:
api_version:
description: The versioned schema of this representation of an object.
returned: success
type: str
kind:
description: Represents the REST resource this object represents.
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
'''
from ansible.module_utils.k8s.scale import KubernetesAnsibleScaleModule
def main():
KubernetesAnsibleScaleModule().execute_module()
if __name__ == '__main__':
main()

View File

@@ -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()