summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/okd/plugins/modules
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/community/okd/plugins/modules')
-rw-r--r--ansible_collections/community/okd/plugins/modules/k8s.py308
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_adm_groups_sync.py224
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_adm_migrate_template_instances.py371
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_adm_prune_auth.py132
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_adm_prune_builds.py124
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_adm_prune_deployments.py100
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_adm_prune_images.py315
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_auth.py392
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_build.py260
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_import_image.py194
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_process.py236
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_registry_info.py114
-rw-r--r--ansible_collections/community/okd/plugins/modules/openshift_route.py542
13 files changed, 3312 insertions, 0 deletions
diff --git a/ansible_collections/community/okd/plugins/modules/k8s.py b/ansible_collections/community/okd/plugins/modules/k8s.py
new file mode 100644
index 000000000..c3b8d1b66
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/k8s.py
@@ -0,0 +1,308 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+
+module: k8s
+
+short_description: Manage OpenShift objects
+
+author:
+ - "Chris Houseknecht (@chouseknecht)"
+ - "Fabian von Feilitzsch (@fabianvf)"
+
+description:
+ - Use the Kubernetes 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(kubernetes.core.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.
+ - Optimized for OKD/OpenShift Kubernetes flavors.
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_name_options
+ - kubernetes.core.k8s_resource_options
+ - kubernetes.core.k8s_auth_options
+ - kubernetes.core.k8s_wait_options
+ - kubernetes.core.k8s_delete_options
+
+options:
+ 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
+ I(resource_definition) or I(src).
+ - C(patched) state is an existing resource that has a given patch applied. If the resource doesn't exist, silently skip it (do not raise an error).
+ type: str
+ default: present
+ choices: [ absent, present, patched ]
+ force:
+ description:
+ - If set to C(yes), and I(state) is C(present), an existing object will be replaced.
+ type: bool
+ default: no
+ 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 merge_type is given, the merge_types will be tried in order
+ - 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.
+ - mutually exclusive with C(apply)
+ - I(merge_type=json) is deprecated and will be removed in version 3.0.0. Please use M(kubernetes.core.k8s_json_patch) instead.
+ choices:
+ - json
+ - merge
+ - strategic-merge
+ type: list
+ elements: str
+ 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.
+ type: bool
+ version:
+ description: version of Kubernetes to validate against. defaults to Kubernetes server version
+ type: str
+ strict:
+ description: whether to fail when passing unexpected properties
+ default: True
+ type: bool
+ type: dict
+ 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
+ default: false
+ 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
+ default: false
+ template:
+ description:
+ - Provide a valid YAML template definition file for an object when creating or updating.
+ - Value can be provided as string or dictionary.
+ - Mutually exclusive with C(src) and C(resource_definition).
+ - Template files needs to be present on the Ansible Controller's file system.
+ - Additional parameters can be specified using dictionary.
+ - 'Valid additional parameters - '
+ - 'C(newline_sequence) (str): Specify the newline sequence to use for templating files.
+ valid choices are "\n", "\r", "\r\n". Default value "\n".'
+ - 'C(block_start_string) (str): The string marking the beginning of a block.
+ Default value "{%".'
+ - 'C(block_end_string) (str): The string marking the end of a block.
+ Default value "%}".'
+ - 'C(variable_start_string) (str): The string marking the beginning of a print statement.
+ Default value "{{".'
+ - 'C(variable_end_string) (str): The string marking the end of a print statement.
+ Default value "}}".'
+ - 'C(trim_blocks) (bool): Determine when newlines should be removed from blocks. When set to C(yes) the first newline
+ after a block is removed (block, not variable tag!). Default value is true.'
+ - 'C(lstrip_blocks) (bool): Determine when leading spaces and tabs should be stripped.
+ When set to C(yes) leading spaces and tabs are stripped from the start of a line to a block.
+ This functionality requires Jinja 2.7 or newer. Default value is false.'
+ type: raw
+ version_added: '2.0.0'
+ continue_on_error:
+ description:
+ - Whether to continue on creation/deletion errors when multiple resources are defined.
+ - This has no effect on the validation step which is controlled by the C(validate.fail_on_error) parameter.
+ type: bool
+ default: False
+ version_added: 2.0.0
+
+requirements:
+ - "python >= 3.6"
+ - "kubernetes >= 12.0.0"
+ - "PyYAML >= 3.11"
+'''
+
+EXAMPLES = r'''
+- name: Create a k8s namespace
+ community.okd.k8s:
+ name: testing
+ api_version: v1
+ kind: Namespace
+ state: present
+
+- name: Create a Service object from an inline definition
+ community.okd.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: Remove an existing Service object
+ community.okd.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
+ community.okd.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.
+ community.okd.k8s:
+ state: present
+ definition: "{{ lookup('file', '/testing/deployment.yml') | from_yaml }}"
+
+- name: Read definition file from the Ansible controller file system after Jinja templating
+ community.okd.k8s:
+ state: present
+ definition: "{{ lookup('template', '/testing/deployment.yml') | from_yaml }}"
+
+- name: fail on validation errors
+ community.okd.k8s:
+ state: present
+ definition: "{{ lookup('template', '/testing/deployment.yml') | from_yaml }}"
+ validate:
+ fail_on_error: yes
+
+- name: warn on validation errors, check for unexpected properties
+ community.okd.k8s:
+ state: present
+ definition: "{{ lookup('template', '/testing/deployment.yml') | from_yaml }}"
+ validate:
+ fail_on_error: no
+ strict: yes
+'''
+
+RETURN = r'''
+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
+ error:
+ description: error while trying to create/delete the object.
+ returned: error
+ type: complex
+'''
+# ENDREMOVE (downstream)
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
+ NAME_ARG_SPEC, RESOURCE_ARG_SPEC, AUTH_ARG_SPEC, WAIT_ARG_SPEC, DELETE_OPTS_ARG_SPEC
+)
+
+
+def validate_spec():
+ return dict(
+ fail_on_error=dict(type='bool'),
+ version=dict(),
+ strict=dict(type='bool', default=True)
+ )
+
+
+def argspec():
+ argument_spec = {}
+ argument_spec.update(NAME_ARG_SPEC)
+ argument_spec.update(RESOURCE_ARG_SPEC)
+ argument_spec.update(AUTH_ARG_SPEC)
+ argument_spec.update(WAIT_ARG_SPEC)
+ argument_spec['merge_type'] = dict(type='list', elements='str', choices=['json', 'merge', 'strategic-merge'])
+ argument_spec['validate'] = dict(type='dict', default=None, options=validate_spec())
+ argument_spec['append_hash'] = dict(type='bool', default=False)
+ argument_spec['apply'] = dict(type='bool', default=False)
+ argument_spec['template'] = dict(type='raw', default=None)
+ argument_spec['delete_options'] = dict(type='dict', default=None, options=DELETE_OPTS_ARG_SPEC)
+ argument_spec['continue_on_error'] = dict(type='bool', default=False)
+ argument_spec['state'] = dict(default='present', choices=['present', 'absent', 'patched'])
+ argument_spec['force'] = dict(type='bool', default=False)
+ return argument_spec
+
+
+def main():
+ mutually_exclusive = [
+ ('resource_definition', 'src'),
+ ('merge_type', 'apply'),
+ ('template', 'resource_definition'),
+ ('template', 'src'),
+ ]
+
+ from ansible_collections.community.okd.plugins.module_utils.k8s import OKDRawModule
+ module = OKDRawModule(argument_spec=argspec(), supports_check_mode=True, mutually_exclusive=mutually_exclusive)
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_adm_groups_sync.py b/ansible_collections/community/okd/plugins/modules/openshift_adm_groups_sync.py
new file mode 100644
index 000000000..66b0fbb15
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_adm_groups_sync.py
@@ -0,0 +1,224 @@
+#!/usr/bin/python
+
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r"""
+
+module: openshift_adm_groups_sync
+
+short_description: Sync OpenShift Groups with records from an external provider.
+
+version_added: "2.1.0"
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - In order to sync/prune OpenShift Group records with those from an external provider, determine which Groups you wish to sync
+ and where their records live.
+ - Analogous to `oc adm prune groups` and `oc adm group sync`.
+ - LDAP sync configuration file syntax can be found here
+ U(https://docs.openshift.com/container-platform/4.9/authentication/ldap-syncing.html).
+ - The bindPassword attribute of the LDAP sync configuration is expected to be a string,
+ please use ansible-vault encryption to secure this information.
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+
+options:
+ state:
+ description:
+ - Determines if the group should be sync when set to C(present) or pruned when set to C(absent).
+ type: str
+ default: present
+ choices: [ absent, present ]
+ type:
+ description:
+ - which groups allow and deny list entries refer to.
+ type: str
+ default: ldap
+ choices: [ ldap, openshift ]
+ sync_config:
+ description:
+ - Provide a valid YAML definition of an LDAP sync configuration.
+ type: dict
+ aliases:
+ - config
+ - src
+ required: True
+ deny_groups:
+ description:
+ - Denied groups, could be openshift group name or LDAP group dn value.
+ - When parameter C(type) is set to I(ldap) this should contains only LDAP group definition
+ like I(cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat).
+ - The elements specified in this list will override the ones specified in C(allow_groups).
+ type: list
+ elements: str
+ default: []
+ allow_groups:
+ description:
+ - Allowed groups, could be openshift group name or LDAP group dn value.
+ - When parameter C(type) is set to I(ldap) this should contains only LDAP group definition
+ like I(cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat).
+ type: list
+ elements: str
+ default: []
+
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+ - python-ldap
+"""
+
+EXAMPLES = r"""
+# Prune all orphaned groups
+- name: Prune all orphan groups
+ openshift_adm_groups_sync:
+ state: absent
+ src: "{{ lookup('file', '/path/to/ldap-sync-config.yaml') | from_yaml }}"
+
+# Prune all orphaned groups from a list of specific groups specified in allow_groups
+- name: Prune all orphan groups from a list of specific groups specified in allow_groups
+ openshift_adm_groups_sync:
+ state: absent
+ src: "{{ lookup('file', '/path/to/ldap-sync-config.yaml') | from_yaml }}"
+ allow_groups:
+ - cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat
+ - cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat
+
+# Sync all groups from an LDAP server
+- name: Sync all groups from an LDAP server
+ openshift_adm_groups_sync:
+ src:
+ kind: LDAPSyncConfig
+ apiVersion: v1
+ url: ldap://localhost:1390
+ insecure: true
+ bindDN: cn=admin,dc=example,dc=org
+ bindPassword: adminpassword
+ rfc2307:
+ groupsQuery:
+ baseDN: "cn=admins,ou=groups,dc=example,dc=org"
+ scope: sub
+ derefAliases: never
+ filter: (objectClass=*)
+ pageSize: 0
+ groupUIDAttribute: dn
+ groupNameAttributes: [ cn ]
+ groupMembershipAttributes: [ member ]
+ usersQuery:
+ baseDN: "ou=users,dc=example,dc=org"
+ scope: sub
+ derefAliases: never
+ pageSize: 0
+ userUIDAttribute: dn
+ userNameAttributes: [ mail ]
+ tolerateMemberNotFoundErrors: true
+ tolerateMemberOutOfScopeErrors: true
+
+# Sync all groups except the ones from the deny_groups from an LDAP server
+- name: Sync all groups from an LDAP server using deny_groups
+ openshift_adm_groups_sync:
+ src: "{{ lookup('file', '/path/to/ldap-sync-config.yaml') | from_yaml }}"
+ deny_groups:
+ - cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat
+ - cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat
+
+# Sync all OpenShift Groups that have been synced previously with an LDAP server
+- name: Sync all OpenShift Groups that have been synced previously with an LDAP server
+ openshift_adm_groups_sync:
+ src: "{{ lookup('file', '/path/to/ldap-sync-config.yaml') | from_yaml }}"
+ type: openshift
+"""
+
+
+RETURN = r"""
+builds:
+ description:
+ - The groups that were created, updated or deleted
+ returned: success
+ type: list
+ elements: dict
+ sample: [
+ {
+ "apiVersion": "user.openshift.io/v1",
+ "kind": "Group",
+ "metadata": {
+ "annotations": {
+ "openshift.io/ldap.sync-time": "2021-12-17T12:20:28.125282",
+ "openshift.io/ldap.uid": "cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat",
+ "openshift.io/ldap.url": "localhost:1390"
+ },
+ "creationTimestamp": "2021-12-17T11:09:49Z",
+ "labels": {
+ "openshift.io/ldap.host": "localhost"
+ },
+ "managedFields": [{
+ "apiVersion": "user.openshift.io/v1",
+ "fieldsType": "FieldsV1",
+ "fieldsV1": {
+ "f:metadata": {
+ "f:annotations": {
+ ".": {},
+ "f:openshift.io/ldap.sync-time": {},
+ "f:openshift.io/ldap.uid": {},
+ "f:openshift.io/ldap.url": {}
+ },
+ "f:labels": {
+ ".": {},
+ "f:openshift.io/ldap.host": {}
+ }
+ },
+ "f:users": {}
+ },
+ "manager": "OpenAPI-Generator",
+ "operation": "Update",
+ "time": "2021-12-17T11:09:49Z"
+ }],
+ "name": "developers",
+ "resourceVersion": "2014696",
+ "uid": "8dc211cb-1544-41e1-96b1-efffeed2d7d7"
+ },
+ "users": ["jordanbulls@ansible.org"]
+ }
+ ]
+"""
+# ENDREMOVE (downstream)
+
+import copy
+import traceback
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import AUTH_ARG_SPEC
+
+
+def argument_spec():
+ args = copy.deepcopy(AUTH_ARG_SPEC)
+ args.update(
+ dict(
+ state=dict(type='str', choices=['absent', 'present'], default='present'),
+ type=dict(type='str', choices=['ldap', 'openshift'], default='ldap'),
+ sync_config=dict(type='dict', aliases=['config', 'src'], required=True),
+ deny_groups=dict(type='list', elements='str', default=[]),
+ allow_groups=dict(type='list', elements='str', default=[]),
+ )
+ )
+ return args
+
+
+def main():
+ from ansible_collections.community.okd.plugins.module_utils.openshift_groups import (
+ OpenshiftGroupsSync
+ )
+
+ module = OpenshiftGroupsSync(argument_spec=argument_spec(), supports_check_mode=True)
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_adm_migrate_template_instances.py b/ansible_collections/community/okd/plugins/modules/openshift_adm_migrate_template_instances.py
new file mode 100644
index 000000000..05d5563cd
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_adm_migrate_template_instances.py
@@ -0,0 +1,371 @@
+#!/usr/bin/python
+
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r"""
+module: openshift_adm_migrate_template_instances
+short_description: Update TemplateInstances to point to the latest group-version-kinds
+version_added: "2.2.0"
+author: Alina Buzachis (@alinabuzachis)
+description:
+ - Update TemplateInstances to point to the latest group-version-kinds.
+ - Analogous to C(oc adm migrate template-instances).
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+ - kubernetes.core.k8s_wait_options
+options:
+ namespace:
+ description:
+ - The namespace that the template can be found in.
+ - If no namespace if specified, migrate objects in all namespaces.
+ type: str
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+"""
+
+EXAMPLES = r"""
+ - name: Migrate TemplateInstances in namespace=test
+ community.okd.openshift_adm_migrate_template_instances:
+ namespace: test
+ register: _result
+
+ - name: Migrate TemplateInstances in all namespaces
+ community.okd.openshift_adm_migrate_template_instances:
+ register: _result
+"""
+
+RETURN = r"""
+result:
+ description:
+ - List with all TemplateInstances that have been migrated.
+ type: list
+ returned: success
+ elements: dict
+ sample: [
+ {
+ "apiVersion": "template.openshift.io/v1",
+ "kind": "TemplateInstance",
+ "metadata": {
+ "creationTimestamp": "2021-11-10T11:12:09Z",
+ "finalizers": [
+ "template.openshift.io/finalizer"
+ ],
+ "managedFields": [
+ {
+ "apiVersion": "template.openshift.io/v1",
+ "fieldsType": "FieldsV1",
+ "fieldsV1": {
+ "f:spec": {
+ "f:template": {
+ "f:metadata": {
+ "f:name": {}
+ },
+ "f:objects": {},
+ "f:parameters": {}
+ }
+ }
+ },
+ "manager": "kubectl-create",
+ "operation": "Update",
+ "time": "2021-11-10T11:12:09Z"
+ },
+ {
+ "apiVersion": "template.openshift.io/v1",
+ "fieldsType": "FieldsV1",
+ "fieldsV1": {
+ "f:metadata": {
+ "f:finalizers": {
+ ".": {},
+ "v:\"template.openshift.io/finalizer\"": {}
+ }
+ },
+ "f:status": {
+ "f:conditions": {}
+ }
+ },
+ "manager": "openshift-controller-manager",
+ "operation": "Update",
+ "time": "2021-11-10T11:12:09Z"
+ },
+ {
+ "apiVersion": "template.openshift.io/v1",
+ "fieldsType": "FieldsV1",
+ "fieldsV1": {
+ "f:status": {
+ "f:objects": {}
+ }
+ },
+ "manager": "OpenAPI-Generator",
+ "operation": "Update",
+ "time": "2021-11-10T11:12:33Z"
+ }
+ ],
+ "name": "demo",
+ "namespace": "test",
+ "resourceVersion": "545370",
+ "uid": "09b795d7-7f07-4d94-bf0f-2150ee66f88d"
+ },
+ "spec": {
+ "requester": {
+ "groups": [
+ "system:masters",
+ "system:authenticated"
+ ],
+ "username": "system:admin"
+ },
+ "template": {
+ "metadata": {
+ "creationTimestamp": null,
+ "name": "template"
+ },
+ "objects": [
+ {
+ "apiVersion": "v1",
+ "kind": "Secret",
+ "metadata": {
+ "labels": {
+ "foo": "bar"
+ },
+ "name": "secret"
+ }
+ },
+ {
+ "apiVersion": "apps/v1",
+ "kind": "Deployment",
+ "metadata": {
+ "name": "deployment"
+ },
+ "spec": {
+ "replicas": 0,
+ "selector": {
+ "matchLabels": {
+ "key": "value"
+ }
+ },
+ "template": {
+ "metadata": {
+ "labels": {
+ "key": "value"
+ }
+ },
+ "spec": {
+ "containers": [
+ {
+ "image": "k8s.gcr.io/e2e-test-images/agnhost:2.32",
+ "name": "hello-openshift"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "apiVersion": "v1",
+ "kind": "Route",
+ "metadata": {
+ "name": "route"
+ },
+ "spec": {
+ "to": {
+ "name": "foo"
+ }
+ }
+ }
+ ],
+ "parameters": [
+ {
+ "name": "NAME",
+ "value": "${NAME}"
+ }
+ ]
+ }
+ },
+ "status": {
+ "conditions": [
+ {
+ "lastTransitionTime": "2021-11-10T11:12:09Z",
+ "message": "",
+ "reason": "Created",
+ "status": "True",
+ "type": "Ready"
+ }
+ ],
+ "objects": [
+ {
+ "ref": {
+ "apiVersion": "v1",
+ "kind": "Secret",
+ "name": "secret",
+ "namespace": "test",
+ "uid": "33fad364-6d47-4f9c-9e51-92cba5602a57"
+ }
+ },
+ {
+ "ref": {
+ "apiVersion": "apps/v1",
+ "kind": "Deployment",
+ "name": "deployment",
+ "namespace": "test",
+ "uid": "3b527f88-42a1-4811-9e2f-baad4e4d8807"
+ }
+ },
+ {
+ "ref": {
+ "apiVersion": "route.openshift.io/v1.Route",
+ "kind": "Route",
+ "name": "route",
+ "namespace": "test",
+ "uid": "5b5411de-8769-4e27-ba52-6781630e4008"
+ }
+ }
+ ]
+ }
+ },
+ ...
+ ]
+"""
+# ENDREMOVE (downstream)
+
+from ansible.module_utils._text import to_native
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_common import AnsibleOpenshiftModule
+
+try:
+ from kubernetes.dynamic.exceptions import DynamicApiError
+except ImportError:
+ pass
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
+ AUTH_ARG_SPEC,
+ WAIT_ARG_SPEC,
+)
+
+transforms = {
+ "Build": "build.openshift.io/v1",
+ "BuildConfig": "build.openshift.io/v1",
+ "DeploymentConfig": "apps.openshift.io/v1",
+ "Route": "route.openshift.io/v1",
+}
+
+
+class OpenShiftMigrateTemplateInstances(AnsibleOpenshiftModule):
+ def __init__(self, **kwargs):
+ super(OpenShiftMigrateTemplateInstances, self).__init__(**kwargs)
+
+ def patch_template_instance(self, resource, templateinstance):
+ result = None
+
+ try:
+ result = resource.status.patch(templateinstance)
+ except Exception as exc:
+ self.fail_json(
+ msg="Failed to migrate TemplateInstance {0} due to: {1}".format(
+ templateinstance["metadata"]["name"], to_native(exc)
+ )
+ )
+
+ return result.to_dict()
+
+ @staticmethod
+ def perform_migrations(templateinstances):
+ ti_list = []
+ ti_to_be_migrated = []
+
+ ti_list = (
+ templateinstances.get("kind") == "TemplateInstanceList"
+ and templateinstances.get("items")
+ or [templateinstances]
+ )
+
+ for ti_elem in ti_list:
+ objects = ti_elem["status"].get("objects")
+ if objects:
+ for i, obj in enumerate(objects):
+ object_type = obj["ref"]["kind"]
+ if (
+ object_type in transforms.keys()
+ and obj["ref"].get("apiVersion") != transforms[object_type]
+ ):
+ ti_elem["status"]["objects"][i]["ref"][
+ "apiVersion"
+ ] = transforms[object_type]
+ ti_to_be_migrated.append(ti_elem)
+
+ return ti_to_be_migrated
+
+ def execute_module(self):
+ templateinstances = None
+ namespace = self.params.get("namespace")
+ results = {"changed": False, "result": []}
+
+ resource = self.find_resource(
+ "templateinstances", "template.openshift.io/v1", fail=True
+ )
+
+ if namespace:
+ # Get TemplateInstances from a provided namespace
+ try:
+ templateinstances = resource.get(namespace=namespace).to_dict()
+ except DynamicApiError as exc:
+ self.fail_json(
+ msg="Failed to retrieve TemplateInstances in namespace '{0}': {1}".format(
+ namespace, exc.body
+ ),
+ error=exc.status,
+ status=exc.status,
+ reason=exc.reason,
+ )
+ except Exception as exc:
+ self.fail_json(
+ msg="Failed to retrieve TemplateInstances in namespace '{0}': {1}".format(
+ namespace, to_native(exc)
+ ),
+ error="",
+ status="",
+ reason="",
+ )
+ else:
+ # Get TemplateInstances from all namespaces
+ templateinstances = resource.get().to_dict()
+
+ ti_to_be_migrated = self.perform_migrations(templateinstances)
+
+ if ti_to_be_migrated:
+ if self.check_mode:
+ self.exit_json(
+ **{"changed": True, "result": ti_to_be_migrated}
+ )
+ else:
+ for ti_elem in ti_to_be_migrated:
+ results["result"].append(
+ self.patch_template_instance(resource, ti_elem)
+ )
+ results["changed"] = True
+
+ self.exit_json(**results)
+
+
+def argspec():
+ argument_spec = {}
+ argument_spec.update(AUTH_ARG_SPEC)
+ argument_spec.update(WAIT_ARG_SPEC)
+ argument_spec["namespace"] = dict(type="str")
+
+ return argument_spec
+
+
+def main():
+ argument_spec = argspec()
+ module = OpenShiftMigrateTemplateInstances(argument_spec=argument_spec, supports_check_mode=True)
+ module.run_module()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_auth.py b/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_auth.py
new file mode 100644
index 000000000..a9833fa50
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_auth.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Red Hat
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+
+module: openshift_adm_prune_auth
+
+short_description: Removes references to the specified roles, clusterroles, users, and groups
+
+version_added: "2.2.0"
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - This module allow administrators to remove references to the specified roles, clusterroles, users, and groups.
+ - Analogous to C(oc adm prune auth).
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+
+options:
+ resource:
+ description:
+ - The specified resource to remove.
+ choices:
+ - roles
+ - clusterroles
+ - users
+ - groups
+ type: str
+ required: True
+ name:
+ description:
+ - Use to specify an object name to remove.
+ - Mutually exclusive with option I(label_selectors).
+ - If neither I(name) nor I(label_selectors) are specified, prune all resources in the namespace.
+ type: str
+ namespace:
+ description:
+ - Use to specify an object namespace.
+ - Ignored when I(resource) is set to C(clusterroles).
+ type: str
+ label_selectors:
+ description:
+ - Selector (label query) to filter on.
+ - Mutually exclusive with option I(name).
+ type: list
+ elements: str
+
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+'''
+
+EXAMPLES = r'''
+- name: Prune all roles from default namespace
+ openshift_adm_prune_auth:
+ resource: roles
+ namespace: testing
+
+- name: Prune clusterroles using label selectors
+ openshift_adm_prune_auth:
+ resource: roles
+ namespace: testing
+ label_selectors:
+ - phase=production
+'''
+
+
+RETURN = r'''
+cluster_role_binding:
+ type: list
+ description: list of cluster role binding deleted.
+ returned: always
+role_binding:
+ type: list
+ description: list of role binding deleted.
+ returned: I(resource=users) or I(resource=groups) or I(resource=clusterroles)
+security_context_constraints:
+ type: list
+ description: list of Security Context Constraints deleted.
+ returned: I(resource=users) or I(resource=groups)
+authorization:
+ type: list
+ description: list of OAuthClientAuthorization deleted.
+ returned: I(resource=users)
+group:
+ type: list
+ description: list of Security Context Constraints deleted.
+ returned: I(resource=users)
+'''
+# ENDREMOVE (downstream)
+
+import copy
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import AUTH_ARG_SPEC
+
+
+def argument_spec():
+ args = copy.deepcopy(AUTH_ARG_SPEC)
+ args.update(
+ dict(
+ resource=dict(type='str', required=True, choices=['roles', 'clusterroles', 'users', 'groups']),
+ namespace=dict(type='str'),
+ name=dict(type='str'),
+ label_selectors=dict(type='list', elements='str'),
+ )
+ )
+ return args
+
+
+def main():
+
+ from ansible_collections.community.okd.plugins.module_utils.openshift_adm_prune_auth import (
+ OpenShiftAdmPruneAuth)
+
+ module = OpenShiftAdmPruneAuth(argument_spec=argument_spec(),
+ mutually_exclusive=[("name", "label_selectors")],
+ supports_check_mode=True)
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_builds.py b/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_builds.py
new file mode 100644
index 000000000..b0b831e6f
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_builds.py
@@ -0,0 +1,124 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Red Hat
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+
+module: openshift_adm_prune_builds
+
+short_description: Prune old completed and failed builds
+
+version_added: "2.3.0"
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - This module allow administrators to delete old completed and failed builds.
+ - Analogous to C(oc adm prune builds).
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+
+options:
+ namespace:
+ description:
+ - Use to specify namespace for builds to be deleted.
+ type: str
+ keep_younger_than:
+ description:
+ - Specify the minimum age (in minutes) of a Build for it to be considered a candidate for pruning.
+ type: int
+ orphans:
+ description:
+ - If C(true), prune all builds whose associated BuildConfig no longer exists and whose status is
+ complete, failed, error, or cancelled.
+ type: bool
+ default: False
+
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+'''
+
+EXAMPLES = r'''
+# Run deleting older completed and failed builds and also including
+# all builds whose associated BuildConfig no longer exists
+- name: Run delete orphan Builds
+ community.okd.openshift_adm_prune_builds:
+ orphans: True
+
+# Run deleting older completed and failed builds keep younger than 2hours
+- name: Run delete builds, keep younger than 2h
+ community.okd.openshift_adm_prune_builds:
+ keep_younger_than: 120
+
+# Run deleting builds from specific namespace
+- name: Run delete builds from namespace
+ community.okd.openshift_adm_prune_builds:
+ namespace: testing_namespace
+'''
+
+RETURN = r'''
+builds:
+ description:
+ - The builds that were deleted
+ 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
+'''
+# ENDREMOVE (downstream)
+
+import copy
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import AUTH_ARG_SPEC
+
+
+def argument_spec():
+ args = copy.deepcopy(AUTH_ARG_SPEC)
+ args.update(
+ dict(
+ namespace=dict(type='str'),
+ keep_younger_than=dict(type='int'),
+ orphans=dict(type='bool', default=False),
+ )
+ )
+ return args
+
+
+def main():
+
+ from ansible_collections.community.okd.plugins.module_utils.openshift_builds import OpenShiftPruneBuilds
+
+ module = OpenShiftPruneBuilds(argument_spec=argument_spec(), supports_check_mode=True)
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_deployments.py b/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_deployments.py
new file mode 100644
index 000000000..bdef18460
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_deployments.py
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Red Hat
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+
+module: openshift_adm_prune_deployments
+
+short_description: Remove old completed and failed deployment configs
+
+version_added: "2.2.0"
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - This module allow administrators to remove old completed and failed deployment configs.
+ - Analogous to C(oc adm prune deployments).
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+
+options:
+ namespace:
+ description:
+ - Use to specify namespace for deployments to be deleted.
+ type: str
+ keep_younger_than:
+ description:
+ - Specify the minimum age (in minutes) of a deployment for it to be considered a candidate for pruning.
+ type: int
+ orphans:
+ description:
+ - If C(true), prune all deployments where the associated DeploymentConfig no longer exists,
+ the status is complete or failed, and the replica size is C(0).
+ type: bool
+ default: False
+
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+'''
+
+EXAMPLES = r'''
+- name: Prune Deployments from testing namespace
+ community.okd.openshift_adm_prune_deployments:
+ namespace: testing
+
+- name: Prune orphans deployments, keep younger than 2hours
+ community.okd.openshift_adm_prune_deployments:
+ orphans: True
+ keep_younger_than: 120
+'''
+
+
+RETURN = r'''
+replication_controllers:
+ type: list
+ description: list of replication controllers candidate for pruning.
+ returned: always
+'''
+# ENDREMOVE (downstream)
+
+import copy
+
+try:
+ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import AUTH_ARG_SPEC
+except ImportError as e:
+ pass
+
+
+def argument_spec():
+ args = copy.deepcopy(AUTH_ARG_SPEC)
+ args.update(
+ dict(
+ namespace=dict(type='str',),
+ keep_younger_than=dict(type='int',),
+ orphans=dict(type='bool', default=False),
+ )
+ )
+ return args
+
+
+def main():
+
+ from ansible_collections.community.okd.plugins.module_utils.openshift_adm_prune_deployments import (
+ OpenShiftAdmPruneDeployment)
+
+ module = OpenShiftAdmPruneDeployment(argument_spec=argument_spec(), supports_check_mode=True)
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_images.py b/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_images.py
new file mode 100644
index 000000000..d470fa871
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_adm_prune_images.py
@@ -0,0 +1,315 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Red Hat
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+
+module: openshift_adm_prune_images
+
+short_description: Remove unreferenced images
+
+version_added: "2.2.0"
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - This module allow administrators to remove references images.
+ - Note that if the C(namespace) is specified, only references images on Image stream for the corresponding
+ namespace will be candidate for prune if only they are not used or references in another Image stream from
+ another namespace.
+ - Analogous to C(oc adm prune images).
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+
+options:
+ namespace:
+ description:
+ - Use to specify namespace for objects.
+ type: str
+ all_images:
+ description:
+ - Include images that were imported from external registries as candidates for pruning.
+ - If pruned, all the mirrored objects associated with them will also be removed from the integrated registry.
+ type: bool
+ default: True
+ keep_younger_than:
+ description:
+ - Specify the minimum age (in minutes) of an image and its referrers for it to be considered a candidate for pruning.
+ type: int
+ prune_over_size_limit:
+ description:
+ - Specify if images which are exceeding LimitRanges specified in the same namespace,
+ should be considered for pruning.
+ type: bool
+ default: False
+ registry_url:
+ description:
+ - The address to use when contacting the registry, instead of using the default value.
+ - This is useful if you can't resolve or reach the default registry but you do have an
+ alternative route that works.
+ - Particular transport protocol can be enforced using '<scheme>://' prefix.
+ type: str
+ registry_ca_cert:
+ description:
+ - Path to a CA certificate used to contact registry. The full certificate chain must be provided to
+ avoid certificate validation errors.
+ type: path
+ registry_validate_certs:
+ description:
+ - Whether or not to verify the API server's SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL
+ environment variable.
+ type: bool
+ prune_registry:
+ description:
+ - If set to I(False), the prune operation will clean up image API objects, but
+ none of the associated content in the registry is removed.
+ type: bool
+ default: True
+ ignore_invalid_refs:
+ description:
+ - If set to I(True), the pruning process will ignore all errors while parsing image references.
+ - This means that the pruning process will ignore the intended connection between the object and the referenced image.
+ - As a result an image may be incorrectly deleted as unused.
+ type: bool
+ default: False
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+ - docker-image-py
+'''
+
+EXAMPLES = r'''
+# Prune if only images and their referrers were more than an hour old
+- name: Prune image with referrer been more than an hour old
+ community.okd.openshift_adm_prune_images:
+ keep_younger_than: 60
+
+# Remove images exceeding currently set limit ranges
+- name: Remove images exceeding currently set limit ranges
+ community.okd.openshift_adm_prune_images:
+ prune_over_size_limit: true
+
+# Force the insecure http protocol with the particular registry host name
+- name: Prune images using custom registry
+ community.okd.openshift_adm_prune_images:
+ registry_url: http://registry.example.org
+ registry_validate_certs: false
+'''
+
+
+RETURN = r'''
+updated_image_streams:
+ description:
+ - The images streams updated.
+ returned: success
+ type: list
+ elements: dict
+ sample: [
+ {
+ "apiVersion": "image.openshift.io/v1",
+ "kind": "ImageStream",
+ "metadata": {
+ "annotations": {
+ "openshift.io/image.dockerRepositoryCheck": "2021-12-07T07:55:30Z"
+ },
+ "creationTimestamp": "2021-12-07T07:55:30Z",
+ "generation": 1,
+ "name": "python",
+ "namespace": "images",
+ "resourceVersion": "1139215",
+ "uid": "443bad2c-9fd4-4c8f-8a24-3eca4426b07f"
+ },
+ "spec": {
+ "lookupPolicy": {
+ "local": false
+ },
+ "tags": [
+ {
+ "annotations": null,
+ "from": {
+ "kind": "DockerImage",
+ "name": "python:3.8.12"
+ },
+ "generation": 1,
+ "importPolicy": {
+ "insecure": true
+ },
+ "name": "3.8.12",
+ "referencePolicy": {
+ "type": "Source"
+ }
+ }
+ ]
+ },
+ "status": {
+ "dockerImageRepository": "image-registry.openshift-image-registry.svc:5000/images/python",
+ "publicDockerImageRepository": "default-route-openshift-image-registry.apps-crc.testing/images/python",
+ "tags": []
+ }
+ },
+ ...
+ ]
+deleted_images:
+ description:
+ - The images deleted.
+ returned: success
+ type: list
+ elements: dict
+ sample: [
+ {
+ "apiVersion": "image.openshift.io/v1",
+ "dockerImageLayers": [
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:5e0b432e8ba9d9029a000e627840b98ffc1ed0c5172075b7d3e869be0df0fe9b",
+ "size": 54932878
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:a84cfd68b5cea612a8343c346bfa5bd6c486769010d12f7ec86b23c74887feb2",
+ "size": 5153424
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:e8b8f2315954535f1e27cd13d777e73da4a787b0aebf4241d225beff3c91cbb1",
+ "size": 10871995
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:0598fa43a7e793a76c198e8d45d8810394e1cfc943b2673d7fcf5a6fdc4f45b3",
+ "size": 54567844
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:83098237b6d3febc7584c1f16076a32ac01def85b0d220ab46b6ebb2d6e7d4d4",
+ "size": 196499409
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:b92c73d4de9a6a8f6b96806a04857ab33cf6674f6411138603471d744f44ef55",
+ "size": 6290769
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:ef9b6ee59783b84a6ec0c8b109c409411ab7c88fa8c53fb3760b5fde4eb0aa07",
+ "size": 16812698
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:c1f6285e64066d36477a81a48d3c4f1dc3c03dddec9e72d97da13ba51bca0d68",
+ "size": 234
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "name": "sha256:a0ee7333301245b50eb700f96d9e13220cdc31871ec9d8e7f0ff7f03a17c6fb3",
+ "size": 2349241
+ }
+ ],
+ "dockerImageManifestMediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "dockerImageMetadata": {
+ "Architecture": "amd64",
+ "Config": {
+ "Cmd": [
+ "python3"
+ ],
+ "Env": [
+ "PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "LANG=C.UTF-8",
+ "GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568",
+ "PYTHON_VERSION=3.8.12",
+ "PYTHON_PIP_VERSION=21.2.4",
+ "PYTHON_SETUPTOOLS_VERSION=57.5.0",
+ "PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/3cb8888cc2869620f57d5d2da64da38f516078c7/public/get-pip.py",
+ "PYTHON_GET_PIP_SHA256=c518250e91a70d7b20cceb15272209a4ded2a0c263ae5776f129e0d9b5674309"
+ ],
+ "Image": "sha256:cc3a2931749afa7dede97e32edbbe3e627b275c07bf600ac05bc0dc22ef203de"
+ },
+ "Container": "b43fcf5052feb037f6d204247d51ac8581d45e50f41c6be2410d94b5c3a3453d",
+ "ContainerConfig": {
+ "Cmd": [
+ "/bin/sh",
+ "-c",
+ "#(nop) ",
+ "CMD [\"python3\"]"
+ ],
+ "Env": [
+ "PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "LANG=C.UTF-8",
+ "GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568",
+ "PYTHON_VERSION=3.8.12",
+ "PYTHON_PIP_VERSION=21.2.4",
+ "PYTHON_SETUPTOOLS_VERSION=57.5.0",
+ "PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/3cb8888cc2869620f57d5d2da64da38f516078c7/public/get-pip.py",
+ "PYTHON_GET_PIP_SHA256=c518250e91a70d7b20cceb15272209a4ded2a0c263ae5776f129e0d9b5674309"
+ ],
+ "Hostname": "b43fcf5052fe",
+ "Image": "sha256:cc3a2931749afa7dede97e32edbbe3e627b275c07bf600ac05bc0dc22ef203de"
+ },
+ "Created": "2021-12-03T01:53:41Z",
+ "DockerVersion": "20.10.7",
+ "Id": "sha256:f746089c9d02d7126bbe829f788e093853a11a7f0421049267a650d52bbcac37",
+ "Size": 347487141,
+ "apiVersion": "image.openshift.io/1.0",
+ "kind": "DockerImage"
+ },
+ "dockerImageMetadataVersion": "1.0",
+ "dockerImageReference": "python@sha256:a874dcabc74ca202b92b826521ff79dede61caca00ceab0b65024e895baceb58",
+ "kind": "Image",
+ "metadata": {
+ "annotations": {
+ "image.openshift.io/dockerLayersOrder": "ascending"
+ },
+ "creationTimestamp": "2021-12-07T07:55:30Z",
+ "name": "sha256:a874dcabc74ca202b92b826521ff79dede61caca00ceab0b65024e895baceb58",
+ "resourceVersion": "1139214",
+ "uid": "33be6ab4-af79-4f44-a0fd-4925bd473c1f"
+ }
+ },
+ ...
+ ]
+'''
+# ENDREMOVE (downstream)
+
+import copy
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import AUTH_ARG_SPEC
+
+
+def argument_spec():
+ args = copy.deepcopy(AUTH_ARG_SPEC)
+ args.update(
+ dict(
+ namespace=dict(type='str'),
+ all_images=dict(type='bool', default=True),
+ keep_younger_than=dict(type='int'),
+ prune_over_size_limit=dict(type='bool', default=False),
+ registry_url=dict(type='str'),
+ registry_validate_certs=dict(type='bool'),
+ registry_ca_cert=dict(type='path'),
+ prune_registry=dict(type='bool', default=True),
+ ignore_invalid_refs=dict(type='bool', default=False),
+ )
+ )
+ return args
+
+
+def main():
+
+ from ansible_collections.community.okd.plugins.module_utils.openshift_adm_prune_images import (
+ OpenShiftAdmPruneImages
+ )
+
+ module = OpenShiftAdmPruneImages(argument_spec=argument_spec(), supports_check_mode=True)
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_auth.py b/ansible_collections/community/okd/plugins/modules/openshift_auth.py
new file mode 100644
index 000000000..422018cc5
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_auth.py
@@ -0,0 +1,392 @@
+#!/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: openshift_auth
+
+short_description: Authenticate to OpenShift clusters which require an explicit login step
+
+version_added: "0.2.0"
+
+author:
+ - KubeVirt Team (@kubevirt)
+ - Fabian von Feilitzsch (@fabianvf)
+
+description:
+ - This module handles authenticating to OpenShift 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).
+ - 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
+ resource–specific 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
+ type: str
+ host:
+ description:
+ - Provide a URL for accessing the API server.
+ required: true
+ type: str
+ username:
+ description:
+ - Provide a username for authenticating with the API server.
+ type: str
+ password:
+ description:
+ - Provide a password for authenticating with the API server.
+ type: str
+ 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 ]
+ type: path
+ 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.
+ type: str
+
+requirements:
+ - python >= 3.6
+ - urllib3
+ - requests
+ - requests-oauthlib
+'''
+
+EXAMPLES = r'''
+- hosts: localhost
+ module_defaults:
+ group/community.okd.okd:
+ 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: openshift_passwords.yml
+
+ - name: Log in (obtain access token)
+ community.okd.openshift_auth:
+ username: admin
+ password: "{{ openshift_admin_password }}"
+ register: openshift_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
+ kubernetes.core.k8s_info:
+ api_key: "{{ openshift_auth_results.openshift_auth.api_key }}"
+ kind: Pod
+ register: pod_list
+
+ always:
+ - name: If login succeeded, try to log out (revoke access token)
+ when: openshift_auth_results.openshift_auth.api_key is defined
+ community.okd.openshift_auth:
+ state: absent
+ api_key: "{{ openshift_auth_results.openshift_auth.api_key }}"
+'''
+
+# Returned value names need to match k8s modules parameter names, to make it
+# easy to pass returned values of openshift_auth to other k8s modules.
+# Discussion: https://github.com/ansible/ansible/pull/50807#discussion_r248827899
+RETURN = r'''
+openshift_auth:
+ description: OpenShift 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
+k8s_auth:
+ description: Same as returned openshift_auth. Kept only for backwards compatibility
+ 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
+from urllib.parse import urljoin
+
+from base64 import urlsafe_b64encode
+import hashlib
+
+# 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},
+}
+
+
+def get_oauthaccesstoken_objectname_from_token(token_name):
+
+ """
+ openshift convert the access token to an OAuthAccessToken resource name using the algorithm
+ https://github.com/openshift/console/blob/9f352ba49f82ad693a72d0d35709961428b43b93/pkg/server/server.go#L609-L613
+ """
+
+ sha256Prefix = "sha256~"
+ content = token_name.strip(sha256Prefix)
+
+ b64encoded = urlsafe_b64encode(hashlib.sha256(content.encode()).digest()).rstrip(b'=')
+ return sha256Prefix + b64encoded.decode("utf-8")
+
+
+class OpenShiftAuthModule(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()
+
+ changed = False
+ result = dict()
+ 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:
+ changed = self.openshift_logout()
+
+ # return k8s_auth as well for backwards compatibility
+ self.exit_json(changed=changed, openshift_auth=result, k8s_auth=result)
+
+ def openshift_discover(self):
+ url = urljoin(self.con_host, '.well-known/oauth-authorization-server')
+ 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:
+ 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):
+
+ name = get_oauthaccesstoken_objectname_from_token(self.auth_api_key)
+ headers = {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'Authorization': "Bearer {0}".format(self.auth_api_key)
+ }
+
+ url = "{0}/apis/oauth.openshift.io/v1/useroauthaccesstokens/{1}".format(self.con_host, name)
+ json = {
+ "apiVersion": "oauth.openshift.io/v1",
+ "kind": "DeleteOptions",
+ "gracePeriodSeconds": 0
+ }
+
+ ret = requests.delete(url, json=json, verify=self.con_verify_ca, headers=headers)
+ if ret.status_code != 200:
+ self.fail_json(
+ msg="Couldn't delete user oauth access token '{0}' due to: {1}".format(name, ret.json().get("message")),
+ status_code=ret.status_code
+ )
+
+ return True
+
+ 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 = OpenShiftAuthModule()
+ try:
+ module.execute_module()
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_build.py b/ansible_collections/community/okd/plugins/modules/openshift_build.py
new file mode 100644
index 000000000..1259a102c
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_build.py
@@ -0,0 +1,260 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Red Hat
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+
+module: openshift_build
+
+short_description: Start a new build or Cancel running, pending, or new builds.
+
+version_added: "2.3.0"
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - This module starts a new build from the provided build config or build name.
+ - This module also cancel a new, pending or running build by requesting a graceful shutdown of the build.
+ There may be a delay between requesting the build and the time the build is terminated.
+ - This can also restart a new build when the current is cancelled.
+ - Analogous to C(oc cancel-build) and C(oc start-build).
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+
+options:
+ state:
+ description:
+ - Determines if a Build should be started ,cancelled or restarted.
+ - When set to C(restarted) a new build will be created after the current build is cancelled.
+ choices:
+ - started
+ - cancelled
+ - restarted
+ default: started
+ type: str
+ build_name:
+ description:
+ - Specify the name of a build which should be re-run.
+ - Mutually exclusive with parameter I(build_config_name).
+ type: str
+ build_config_name:
+ description:
+ - Specify the name of a build config from which a new build will be run.
+ - Mutually exclusive with parameter I(build_name).
+ type: str
+ namespace:
+ description:
+ - Specify the namespace for the build or the build config.
+ type: str
+ required: True
+ build_args:
+ description:
+ - Specify a list of key-value pair to pass to Docker during the build.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - docker build argument name.
+ type: str
+ required: true
+ value:
+ description:
+ - docker build argument value.
+ type: str
+ required: true
+ commit:
+ description:
+ - Specify the source code commit identifier the build should use;
+ requires a build based on a Git repository.
+ type: str
+ env_vars:
+ description:
+ - Specify a list of key-value pair for an environment variable to set for the build container.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Environment variable name.
+ type: str
+ required: true
+ value:
+ description:
+ - Environment variable value.
+ type: str
+ required: true
+ incremental:
+ description:
+ - Overrides the incremental setting in a source-strategy build, ignored if not specified.
+ type: bool
+ no_cache:
+ description:
+ - Overrides the noCache setting in a docker-strategy build, ignored if not specified.
+ type: bool
+ wait:
+ description:
+ - When C(state=started), specify whether to wait for a build to complete
+ and exit with a non-zero return code if the build fails.
+ - When I(state=cancelled), specify whether to wait for a build phase to be Cancelled.
+ default: False
+ type: bool
+ wait_sleep:
+ description:
+ - Number of seconds to sleep between checks.
+ - Ignored if C(wait=false).
+ default: 5
+ type: int
+ wait_timeout:
+ description:
+ - How long in seconds to wait for a build to complete.
+ - Ignored if C(wait=false).
+ default: 120
+ type: int
+ build_phases:
+ description:
+ - List of state for build to cancel.
+ - Ignored when C(state=started).
+ type: list
+ elements: str
+ choices:
+ - New
+ - Pending
+ - Running
+ default: []
+
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+'''
+
+EXAMPLES = r'''
+# Starts build from build config default/hello-world
+- name: Starts build from build config
+ community.okd.openshift_build:
+ namespace: default
+ build_config_name: hello-world
+
+# Starts build from a previous build "default/hello-world-1"
+- name: Starts build from a previous build
+ community.okd.openshift_build:
+ namespace: default
+ build_name: hello-world-1
+
+# Cancel the build with the given name
+- name: Cancel build from default namespace
+ community.okd.openshift_build:
+ namespace: "default"
+ build_name: ruby-build-1
+ state: cancelled
+
+# Cancel the named build and create a new one with the same parameters
+- name: Cancel build from default namespace and create a new one
+ community.okd.openshift_build:
+ namespace: "default"
+ build_name: ruby-build-1
+ state: restarted
+
+# Cancel all builds created from 'ruby-build' build configuration that are in 'new' state
+- name: Cancel build from default namespace and create a new one
+ community.okd.openshift_build:
+ namespace: "default"
+ build_config_name: ruby-build
+ build_phases:
+ - New
+ state: cancelled
+'''
+
+RETURN = r'''
+builds:
+ description:
+ - The builds that were started/cancelled.
+ 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 build.
+ returned: success
+ type: dict
+ status:
+ description: Current status details for the object.
+ returned: success
+ type: dict
+'''
+# ENDREMOVE (downstream)
+
+import copy
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import AUTH_ARG_SPEC
+
+
+def argument_spec():
+ args = copy.deepcopy(AUTH_ARG_SPEC)
+
+ args_options = dict(
+ name=dict(type='str', required=True),
+ value=dict(type='str', required=True)
+ )
+
+ args.update(
+ dict(
+ state=dict(type='str', choices=['started', 'cancelled', 'restarted'], default="started"),
+ build_args=dict(type='list', elements='dict', options=args_options),
+ commit=dict(type='str'),
+ env_vars=dict(type='list', elements='dict', options=args_options),
+ build_name=dict(type='str'),
+ build_config_name=dict(type='str'),
+ namespace=dict(type='str', required=True),
+ incremental=dict(type='bool'),
+ no_cache=dict(type='bool'),
+ wait=dict(type='bool', default=False),
+ wait_sleep=dict(type='int', default=5),
+ wait_timeout=dict(type='int', default=120),
+ build_phases=dict(type='list', elements='str', default=[], choices=["New", "Pending", "Running"]),
+ )
+ )
+ return args
+
+
+def main():
+ mutually_exclusive = [
+ ('build_name', 'build_config_name'),
+ ]
+ from ansible_collections.community.okd.plugins.module_utils.openshift_builds import (
+ OpenShiftBuilds
+ )
+ module = OpenShiftBuilds(
+ argument_spec=argument_spec(),
+ mutually_exclusive=mutually_exclusive,
+ required_one_of=[
+ [
+ 'build_name',
+ 'build_config_name',
+ ]
+ ],
+ )
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_import_image.py b/ansible_collections/community/okd/plugins/modules/openshift_import_image.py
new file mode 100644
index 000000000..df0588cf4
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_import_image.py
@@ -0,0 +1,194 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Red Hat
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+
+module: openshift_import_image
+
+short_description: Import the latest image information from a tag in a container image registry.
+
+version_added: "2.2.0"
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - Image streams allow you to control which images are rolled out to your builds and applications.
+ - This module fetches the latest version of an image from a remote repository and updates the image stream tag
+ if it does not match the previous value.
+ - Running the module multiple times will not create duplicate entries.
+ - When importing an image, only the image metadata is copied, not the image contents.
+ - Analogous to C(oc import-image).
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+
+options:
+ namespace:
+ description:
+ - Use to specify namespace for image stream to create/update.
+ type: str
+ required: True
+ name:
+ description:
+ - Image stream to import tag into.
+ - This can be provided as a list of images streams or a single value.
+ type: raw
+ required: True
+ all:
+ description:
+ - If set to I(true), import all tags from the provided source on creation or if C(source) is specified.
+ type: bool
+ default: False
+ validate_registry_certs:
+ description:
+ - If set to I(true), allow importing from registries that have invalid HTTPS certificates.
+ or are hosted via HTTP. This parameter will take precedence over the insecure annotation.
+ type: bool
+ reference_policy:
+ description:
+ - Allow to request pullthrough for external image when set to I(local).
+ default: source
+ choices:
+ - source
+ - local
+ type: str
+ scheduled:
+ description:
+ - Set each imported Docker image to be periodically imported from a remote repository.
+ type: bool
+ default: False
+ source:
+ description:
+ - A Docker image repository to import images from.
+ - Should be provided as 'registry.io/repo/image'
+ type: str
+
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+ - docker-image-py
+'''
+
+EXAMPLES = r'''
+# Import tag latest into a new image stream.
+- name: Import tag latest into new image stream
+ community.okd.openshift_import_image:
+ namespace: testing
+ name: mystream
+ source: registry.io/repo/image:latest
+
+# Update imported data for tag latest in an already existing image stream.
+- name: Update imported data for tag latest
+ community.okd.openshift_import_image:
+ namespace: testing
+ name: mystream
+
+# Update imported data for tag 'stable' in an already existing image stream.
+- name: Update imported data for tag latest
+ community.okd.openshift_import_image:
+ namespace: testing
+ name: mystream:stable
+
+# Update imported data for all tags in an existing image stream.
+- name: Update imported data for all tags
+ community.okd.openshift_import_image:
+ namespace: testing
+ name: mystream
+ all: true
+
+# Import all tags into a new image stream.
+- name: Import all tags into a new image stream.
+ community.okd.openshift_import_image:
+ namespace: testing
+ name: mystream
+ source: registry.io/repo/image:latest
+ all: true
+
+# Import all tags into a new image stream for a list of image streams
+- name: Import all tags into a new image stream.
+ community.okd.openshift_import_image:
+ namespace: testing
+ name:
+ - mystream1
+ - mystream2
+ - mystream3
+ source: registry.io/repo/image:latest
+ all: true
+'''
+
+
+RETURN = r'''
+result:
+ description:
+ - List with all ImageStreamImport that have been created.
+ type: list
+ returned: success
+ elements: dict
+ 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
+'''
+# ENDREMOVE (downstream)
+
+import copy
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import AUTH_ARG_SPEC
+
+
+def argument_spec():
+ args = copy.deepcopy(AUTH_ARG_SPEC)
+ args.update(
+ dict(
+ namespace=dict(type='str', required=True),
+ name=dict(type='raw', required=True),
+ all=dict(type='bool', default=False),
+ validate_registry_certs=dict(type='bool'),
+ reference_policy=dict(type='str', choices=["source", "local"], default="source"),
+ scheduled=dict(type='bool', default=False),
+ source=dict(type='str'),
+ )
+ )
+ return args
+
+
+def main():
+
+ from ansible_collections.community.okd.plugins.module_utils.openshift_import_image import (
+ OpenShiftImportImage
+ )
+
+ module = OpenShiftImportImage(
+ argument_spec=argument_spec(),
+ supports_check_mode=True
+ )
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_process.py b/ansible_collections/community/okd/plugins/modules/openshift_process.py
new file mode 100644
index 000000000..fb00ffbba
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_process.py
@@ -0,0 +1,236 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+# Copyright (c) 2020-2021, Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+module: openshift_process
+
+short_description: Process an OpenShift template.openshift.io/v1 Template
+
+version_added: "0.3.0"
+
+author: "Fabian von Feilitzsch (@fabianvf)"
+
+description:
+ - Processes a specified OpenShift template with the provided template.
+ - Templates can be provided inline, from a file, or specified by name and namespace in the cluster.
+ - Analogous to `oc process`.
+ - For CRUD operations on Template resources themselves, see the community.okd.k8s module.
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+ - kubernetes.core.k8s_wait_options
+ - kubernetes.core.k8s_resource_options
+
+requirements:
+ - "python >= 3.6"
+ - "kubernetes >= 12.0.0"
+ - "PyYAML >= 3.11"
+
+options:
+ name:
+ description:
+ - The name of the Template to process.
+ - The Template must be present in the cluster.
+ - When provided, I(namespace) is required.
+ - Mutually exclusive with I(resource_definition) or I(src)
+ type: str
+ namespace:
+ description:
+ - The namespace that the template can be found in.
+ type: str
+ namespace_target:
+ description:
+ - The namespace that resources should be created, updated, or deleted in.
+ - Only used when I(state) is present or absent.
+ parameters:
+ description:
+ - 'A set of key: value pairs that will be used to set/override values in the Template.'
+ - Corresponds to the `--param` argument to oc process.
+ type: dict
+ parameter_file:
+ description:
+ - A path to a file containing template parameter values to override/set values in the Template.
+ - Corresponds to the `--param-file` argument to oc process.
+ type: str
+ state:
+ description:
+ - Determines what to do with the rendered Template.
+ - The state I(rendered) will render the Template based on the provided parameters, and return the rendered
+ objects in the I(resources) field. These can then be referenced in future tasks.
+ - The state I(present) will cause the resources in the rendered Template to be created if they do not
+ already exist, and patched if they do.
+ - The state I(absent) will delete the resources in the rendered Template.
+ type: str
+ default: rendered
+ choices: [ absent, present, rendered ]
+'''
+
+EXAMPLES = r'''
+- name: Process a template in the cluster
+ community.okd.openshift_process:
+ name: nginx-example
+ namespace: openshift # only needed if using a template already on the server
+ parameters:
+ NAMESPACE: openshift
+ NAME: test123
+ state: rendered
+ register: result
+
+- name: Create the rendered resources using apply
+ community.okd.k8s:
+ namespace: default
+ definition: '{{ item }}'
+ wait: yes
+ apply: yes
+ loop: '{{ result.resources }}'
+
+- name: Process a template with parameters from an env file and create the resources
+ community.okd.openshift_process:
+ name: nginx-example
+ namespace: openshift
+ namespace_target: default
+ parameter_file: 'files/nginx.env'
+ state: present
+ wait: yes
+
+- name: Process a local template and create the resources
+ community.okd.openshift_process:
+ src: files/example-template.yaml
+ parameter_file: files/example.env
+ namespace_target: default
+ state: present
+
+- name: Process a local template, delete the resources, and wait for them to terminate
+ community.okd.openshift_process:
+ src: files/example-template.yaml
+ parameter_file: files/example.env
+ namespace_target: default
+ state: absent
+ wait: yes
+'''
+
+RETURN = r'''
+result:
+ description:
+ - The created, patched, or otherwise present object. Will be empty in the case of a deletion.
+ returned: on success when state is present or absent
+ type: complex
+ contains:
+ apiVersion:
+ 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
+ contains:
+ name:
+ description: The name of the resource
+ type: str
+ namespace:
+ description: The namespace of the resource
+ type: str
+ 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: complex
+ contains:
+ conditions:
+ type: complex
+ description: Array of status conditions for the object. Not guaranteed to be present
+ 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
+resources:
+ type: complex
+ description:
+ - The rendered resources defined in the Template
+ returned: on success when state is rendered
+ contains:
+ apiVersion:
+ 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
+ contains:
+ name:
+ description: The name of the resource
+ type: str
+ namespace:
+ description: The namespace of the resource
+ type: str
+ 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
+ contains:
+ conditions:
+ type: complex
+ description: Array of status conditions for the object. Not guaranteed to be present
+'''
+# ENDREMOVE (downstream)
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
+ AUTH_ARG_SPEC, RESOURCE_ARG_SPEC, WAIT_ARG_SPEC
+)
+
+
+def argspec():
+ argument_spec = {}
+ argument_spec.update(AUTH_ARG_SPEC)
+ argument_spec.update(WAIT_ARG_SPEC)
+ argument_spec.update(RESOURCE_ARG_SPEC)
+ argument_spec['state'] = dict(type='str', default='rendered', choices=['present', 'absent', 'rendered'])
+ argument_spec['namespace'] = dict(type='str')
+ argument_spec['namespace_target'] = dict(type='str')
+ argument_spec['parameters'] = dict(type='dict')
+ argument_spec['name'] = dict(type='str')
+ argument_spec['parameter_file'] = dict(type='str')
+
+ return argument_spec
+
+
+def main():
+
+ from ansible_collections.community.okd.plugins.module_utils.openshift_process import (
+ OpenShiftProcess)
+
+ module = OpenShiftProcess(argument_spec=argspec(), supports_check_mode=True)
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_registry_info.py b/ansible_collections/community/okd/plugins/modules/openshift_registry_info.py
new file mode 100644
index 000000000..a455ac50b
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_registry_info.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Red Hat
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+
+module: openshift_registry_info
+
+short_description: Display information about the integrated registry.
+
+version_added: "2.2.0"
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - This module exposes information about the integrated registry.
+ - Use C(check) to verify your local client can access the registry.
+ - If the adminstrator has not configured a public hostname for the registry then
+ this command may fail when run outside of the server.
+ - Analogous to C(oc registry info).
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+
+options:
+ check:
+ description:
+ - Attempt to contact the integrated registry using local client.
+ type: bool
+ default: False
+
+requirements:
+ - python >= 3.6
+ - kubernetes >= 12.0.0
+ - docker-image-py
+'''
+
+EXAMPLES = r'''
+# Get registry information
+- name: Read integrated registry information
+ community.okd.openshift_registry_info:
+
+# Read registry integrated information and attempt to contact using local client.
+- name: Attempt to contact integrated registry using local client
+ community.okd.openshift_registry_info:
+ check: yes
+'''
+
+
+RETURN = r'''
+internal_hostname:
+ description:
+ - The internal registry hostname.
+ type: str
+ returned: success
+public_hostname:
+ description:
+ - The public registry hostname.
+ type: str
+ returned: success
+check:
+ description:
+ - Whether the local client can contact or not the registry.
+ type: dict
+ returned: success
+ contains:
+ reached:
+ description: Whether the registry has been reached or not.
+ returned: success
+ type: str
+ msg:
+ description: message describing the ping operation.
+ returned: always
+ type: str
+'''
+# ENDREMOVE (downstream)
+
+import copy
+
+from ansible_collections.kubernetes.core.plugins.module_utils.args_common import AUTH_ARG_SPEC
+
+
+def argument_spec():
+ args = copy.deepcopy(AUTH_ARG_SPEC)
+ args.update(
+ dict(
+ check=dict(type='bool', default=False)
+ )
+ )
+ return args
+
+
+def main():
+
+ from ansible_collections.community.okd.plugins.module_utils.openshift_registry import (
+ OpenShiftRegistry
+ )
+
+ module = OpenShiftRegistry(
+ argument_spec=argument_spec(),
+ supports_check_mode=True
+ )
+ module.run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/plugins/modules/openshift_route.py b/ansible_collections/community/okd/plugins/modules/openshift_route.py
new file mode 100644
index 000000000..e452fc534
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/modules/openshift_route.py
@@ -0,0 +1,542 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020, Red Hat
+# 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
+
+# STARTREMOVE (downstream)
+DOCUMENTATION = r'''
+module: openshift_route
+
+short_description: Expose a Service as an OpenShift Route.
+
+version_added: "0.3.0"
+
+author: "Fabian von Feilitzsch (@fabianvf)"
+
+description:
+ - Looks up a Service and creates a new Route based on it.
+ - Analogous to `oc expose` and `oc create route` for creating Routes, but does not support creating Services.
+ - For creating Services from other resources, see kubernetes.core.k8s.
+
+extends_documentation_fragment:
+ - kubernetes.core.k8s_auth_options
+ - kubernetes.core.k8s_wait_options
+ - kubernetes.core.k8s_state_options
+
+requirements:
+ - "python >= 3.6"
+ - "kubernetes >= 12.0.0"
+ - "PyYAML >= 3.11"
+
+options:
+ service:
+ description:
+ - The name of the service to expose.
+ - Required when I(state) is not absent.
+ type: str
+ aliases: ['svc']
+ namespace:
+ description:
+ - The namespace of the resource being targeted.
+ - The Route will be created in this namespace as well.
+ required: yes
+ type: str
+ labels:
+ description:
+ - Specify the labels to apply to the created Route.
+ - 'A set of key: value pairs.'
+ type: dict
+ annotations:
+ description:
+ - Specify the Route Annotations.
+ - 'A set of key: value pairs.'
+ type: dict
+ version_added: "2.1.0"
+ name:
+ description:
+ - The desired name of the Route to be created.
+ - Defaults to the value of I(service)
+ type: str
+ hostname:
+ description:
+ - The hostname for the Route.
+ type: str
+ path:
+ description:
+ - The path for the Route
+ type: str
+ wildcard_policy:
+ description:
+ - The wildcard policy for the hostname.
+ - Currently only Subdomain is supported.
+ - If not provided, the default of None will be used.
+ choices:
+ - Subdomain
+ type: str
+ port:
+ description:
+ - Name or number of the port the Route will route traffic to.
+ type: str
+ tls:
+ description:
+ - TLS configuration for the newly created route.
+ - Only used when I(termination) is set.
+ type: dict
+ suboptions:
+ ca_certificate:
+ description:
+ - Path to a CA certificate file on the target host.
+ - Not supported when I(termination) is set to passthrough.
+ type: str
+ certificate:
+ description:
+ - Path to a certificate file on the target host.
+ - Not supported when I(termination) is set to passthrough.
+ type: str
+ destination_ca_certificate:
+ description:
+ - Path to a CA certificate file used for securing the connection.
+ - Only used when I(termination) is set to reencrypt.
+ - Defaults to the Service CA.
+ type: str
+ key:
+ description:
+ - Path to a key file on the target host.
+ - Not supported when I(termination) is set to passthrough.
+ type: str
+ insecure_policy:
+ description:
+ - Sets the InsecureEdgeTerminationPolicy for the Route.
+ - Not supported when I(termination) is set to reencrypt.
+ - When I(termination) is set to passthrough, only redirect is supported.
+ - If not provided, insecure traffic will be disallowed.
+ type: str
+ choices:
+ - allow
+ - redirect
+ - disallow
+ default: disallow
+ termination:
+ description:
+ - The termination type of the Route.
+ - If left empty no termination type will be set, and the route will be insecure.
+ - When set to insecure I(tls) will be ignored.
+ choices:
+ - edge
+ - passthrough
+ - reencrypt
+ - insecure
+ default: insecure
+ type: str
+'''
+
+EXAMPLES = r'''
+- name: Create hello-world deployment
+ community.okd.k8s:
+ definition:
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: hello-kubernetes
+ namespace: default
+ spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: hello-kubernetes
+ template:
+ metadata:
+ labels:
+ app: hello-kubernetes
+ spec:
+ containers:
+ - name: hello-kubernetes
+ image: paulbouwer/hello-kubernetes:1.8
+ ports:
+ - containerPort: 8080
+
+- name: Create Service for the hello-world deployment
+ community.okd.k8s:
+ definition:
+ apiVersion: v1
+ kind: Service
+ metadata:
+ name: hello-kubernetes
+ namespace: default
+ spec:
+ ports:
+ - port: 80
+ targetPort: 8080
+ selector:
+ app: hello-kubernetes
+
+- name: Expose the insecure hello-world service externally
+ community.okd.openshift_route:
+ service: hello-kubernetes
+ namespace: default
+ insecure_policy: allow
+ annotations:
+ haproxy.router.openshift.io/balance: roundrobin
+ register: route
+'''
+
+RETURN = r'''
+result:
+ description:
+ - The Route object that was created or updated. Will be empty in the case of deletion.
+ returned: success
+ type: complex
+ contains:
+ apiVersion:
+ 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
+ contains:
+ name:
+ description: The name of the created Route
+ type: str
+ namespace:
+ description: The namespace of the create Route
+ type: str
+ spec:
+ description: Specification for the Route
+ returned: success
+ type: complex
+ contains:
+ host:
+ description: Host is an alias/DNS that points to the service.
+ type: str
+ path:
+ description: Path that the router watches for, to route traffic for to the service.
+ type: str
+ port:
+ description: Defines a port mapping from a router to an endpoint in the service endpoints.
+ type: complex
+ contains:
+ targetPort:
+ description: The target port on pods selected by the service this route points to.
+ type: str
+ tls:
+ description: Defines config used to secure a route and provide termination.
+ type: complex
+ contains:
+ caCertificate:
+ description: Provides the cert authority certificate contents.
+ type: str
+ certificate:
+ description: Provides certificate contents.
+ type: str
+ destinationCACertificate:
+ description: Provides the contents of the ca certificate of the final destination.
+ type: str
+ insecureEdgeTerminationPolicy:
+ description: Indicates the desired behavior for insecure connections to a route.
+ type: str
+ key:
+ description: Provides key file contents.
+ type: str
+ termination:
+ description: Indicates termination type.
+ type: str
+ to:
+ description: Specifies the target that resolve into endpoints.
+ type: complex
+ contains:
+ kind:
+ description: The kind of target that the route is referring to. Currently, only 'Service' is allowed.
+ type: str
+ name:
+ description: Name of the service/target that is being referred to. e.g. name of the service.
+ type: str
+ weight:
+ description: Specifies the target's relative weight against other target reference objects.
+ type: int
+ wildcardPolicy:
+ description: Wildcard policy if any for the route.
+ type: str
+ status:
+ description: Current status details for the Route
+ returned: success
+ type: complex
+ contains:
+ ingress:
+ description: List of places where the route may be exposed.
+ type: complex
+ contains:
+ conditions:
+ description: Array of status conditions for the Route ingress.
+ type: complex
+ contains:
+ type:
+ description: The type of the condition. Currently only 'Ready'.
+ type: str
+ status:
+ description: The status of the condition. Can be True, False, Unknown.
+ type: str
+ host:
+ description: The host string under which the route is exposed.
+ type: str
+ routerCanonicalHostname:
+ description: The external host name for the router that can be used as a CNAME for the host requested for this route. May not be set.
+ type: str
+ routerName:
+ description: A name chosen by the router to identify itself.
+ type: str
+ wildcardPolicy:
+ description: The wildcard policy that was allowed where this route is exposed.
+ type: str
+duration:
+ description: elapsed time of task in seconds
+ returned: when C(wait) is true
+ type: int
+ sample: 48
+'''
+# ENDREMOVE (downstream)
+
+import copy
+
+from ansible.module_utils._text import to_native
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_common import AnsibleOpenshiftModule
+
+try:
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.runner import perform_action
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import Waiter
+ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
+ AUTH_ARG_SPEC, WAIT_ARG_SPEC, COMMON_ARG_SPEC
+ )
+except ImportError as e:
+ pass
+ AUTH_ARG_SPEC = WAIT_ARG_SPEC = COMMON_ARG_SPEC = {}
+
+try:
+ from kubernetes.dynamic.exceptions import DynamicApiError, NotFoundError
+except ImportError:
+ pass
+
+
+class OpenShiftRoute(AnsibleOpenshiftModule):
+
+ def __init__(self):
+ super(OpenShiftRoute, self).__init__(
+ argument_spec=self.argspec,
+ supports_check_mode=True,
+ )
+
+ self.append_hash = False
+ self.apply = False
+ self.warnings = []
+ self.params['merge_type'] = None
+
+ @property
+ def argspec(self):
+ spec = copy.deepcopy(AUTH_ARG_SPEC)
+ spec.update(copy.deepcopy(WAIT_ARG_SPEC))
+ spec.update(copy.deepcopy(COMMON_ARG_SPEC))
+
+ spec['service'] = dict(type='str', aliases=['svc'])
+ spec['namespace'] = dict(required=True, type='str')
+ spec['labels'] = dict(type='dict')
+ spec['name'] = dict(type='str')
+ spec['hostname'] = dict(type='str')
+ spec['path'] = dict(type='str')
+ spec['wildcard_policy'] = dict(choices=['Subdomain'], type='str')
+ spec['port'] = dict(type='str')
+ spec['tls'] = dict(type='dict', options=dict(
+ ca_certificate=dict(type='str'),
+ certificate=dict(type='str'),
+ destination_ca_certificate=dict(type='str'),
+ key=dict(type='str', no_log=False),
+ insecure_policy=dict(type='str', choices=['allow', 'redirect', 'disallow'], default='disallow'),
+ ))
+ spec['termination'] = dict(choices=['edge', 'passthrough', 'reencrypt', 'insecure'], default='insecure')
+ spec['annotations'] = dict(type='dict')
+
+ return spec
+
+ def execute_module(self):
+
+ service_name = self.params.get('service')
+ namespace = self.params['namespace']
+ termination_type = self.params.get('termination')
+ if termination_type == 'insecure':
+ termination_type = None
+ state = self.params.get('state')
+
+ if state != 'absent' and not service_name:
+ self.fail_json("If 'state' is not 'absent' then 'service' must be provided")
+
+ # We need to do something a little wonky to wait if the user doesn't supply a custom condition
+ custom_wait = self.params.get('wait') and not self.params.get('wait_condition') and state != 'absent'
+ if custom_wait:
+ # Don't use default wait logic in perform_action
+ self.params['wait'] = False
+
+ route_name = self.params.get('name') or service_name
+ labels = self.params.get('labels')
+ hostname = self.params.get('hostname')
+ path = self.params.get('path')
+ wildcard_policy = self.params.get('wildcard_policy')
+ port = self.params.get('port')
+ annotations = self.params.get('annotations')
+
+ if termination_type and self.params.get('tls'):
+ tls_ca_cert = self.params['tls'].get('ca_certificate')
+ tls_cert = self.params['tls'].get('certificate')
+ tls_dest_ca_cert = self.params['tls'].get('destination_ca_certificate')
+ tls_key = self.params['tls'].get('key')
+ tls_insecure_policy = self.params['tls'].get('insecure_policy')
+ if tls_insecure_policy == 'disallow':
+ tls_insecure_policy = None
+ else:
+ tls_ca_cert = tls_cert = tls_dest_ca_cert = tls_key = tls_insecure_policy = None
+
+ route = {
+ 'apiVersion': 'route.openshift.io/v1',
+ 'kind': 'Route',
+ 'metadata': {
+ 'name': route_name,
+ 'namespace': namespace,
+ 'labels': labels,
+ },
+ 'spec': {}
+ }
+
+ if annotations:
+ route['metadata']['annotations'] = annotations
+
+ if state != 'absent':
+ route['spec'] = self.build_route_spec(
+ service_name, namespace,
+ port=port,
+ wildcard_policy=wildcard_policy,
+ hostname=hostname,
+ path=path,
+ termination_type=termination_type,
+ tls_insecure_policy=tls_insecure_policy,
+ tls_ca_cert=tls_ca_cert,
+ tls_cert=tls_cert,
+ tls_key=tls_key,
+ tls_dest_ca_cert=tls_dest_ca_cert,
+ )
+
+ result = perform_action(self.svc, route, self.params)
+ timeout = self.params.get('wait_timeout')
+ sleep = self.params.get('wait_sleep')
+ if custom_wait:
+ v1_routes = self.find_resource('Route', 'route.openshift.io/v1', fail=True)
+ waiter = Waiter(self.client, v1_routes, wait_predicate)
+ success, result['result'], result['duration'] = waiter.wait(timeout=timeout, sleep=sleep, name=route_name, namespace=namespace)
+
+ self.exit_json(**result)
+
+ def build_route_spec(self, service_name, namespace, port=None, wildcard_policy=None, hostname=None, path=None, termination_type=None,
+ tls_insecure_policy=None, tls_ca_cert=None, tls_cert=None, tls_key=None, tls_dest_ca_cert=None):
+ v1_services = self.find_resource('Service', 'v1', fail=True)
+ try:
+ target_service = v1_services.get(name=service_name, namespace=namespace)
+ except NotFoundError:
+ if not port:
+ self.fail_json(msg="You need to provide the 'port' argument when exposing a non-existent service")
+ target_service = None
+ except DynamicApiError as exc:
+ self.fail_json(msg='Failed to retrieve service to be exposed: {0}'.format(exc.body),
+ error=exc.status, status=exc.status, reason=exc.reason)
+ except Exception as exc:
+ self.fail_json(msg='Failed to retrieve service to be exposed: {0}'.format(to_native(exc)),
+ error='', status='', reason='')
+
+ route_spec = {
+ 'tls': {},
+ 'to': {
+ 'kind': 'Service',
+ 'name': service_name,
+ },
+ 'port': {
+ 'targetPort': self.set_port(target_service, port),
+ },
+ 'wildcardPolicy': wildcard_policy
+ }
+
+ # Want to conditionally add these so we don't overwrite what is automically added when nothing is provided
+ if termination_type:
+ route_spec['tls'] = dict(termination=termination_type.capitalize())
+ if tls_insecure_policy:
+ if termination_type == 'edge':
+ route_spec['tls']['insecureEdgeTerminationPolicy'] = tls_insecure_policy.capitalize()
+ elif termination_type == 'passthrough':
+ if tls_insecure_policy != 'redirect':
+ self.fail_json("'redirect' is the only supported insecureEdgeTerminationPolicy for passthrough routes")
+ route_spec['tls']['insecureEdgeTerminationPolicy'] = tls_insecure_policy.capitalize()
+ elif termination_type == 'reencrypt':
+ self.fail_json("'tls.insecure_policy' is not supported with reencrypt routes")
+ else:
+ route_spec['tls']['insecureEdgeTerminationPolicy'] = None
+ if tls_ca_cert:
+ if termination_type == 'passthrough':
+ self.fail_json("'tls.ca_certificate' is not supported with passthrough routes")
+ route_spec['tls']['caCertificate'] = tls_ca_cert
+ if tls_cert:
+ if termination_type == 'passthrough':
+ self.fail_json("'tls.certificate' is not supported with passthrough routes")
+ route_spec['tls']['certificate'] = tls_cert
+ if tls_key:
+ if termination_type == 'passthrough':
+ self.fail_json("'tls.key' is not supported with passthrough routes")
+ route_spec['tls']['key'] = tls_key
+ if tls_dest_ca_cert:
+ if termination_type != 'reencrypt':
+ self.fail_json("'destination_certificate' is only valid for reencrypt routes")
+ route_spec['tls']['destinationCACertificate'] = tls_dest_ca_cert
+ else:
+ route_spec['tls'] = None
+ if hostname:
+ route_spec['host'] = hostname
+ if path:
+ route_spec['path'] = path
+
+ return route_spec
+
+ def set_port(self, service, port_arg):
+ if port_arg:
+ return port_arg
+ for p in service.spec.ports:
+ if p.protocol == 'TCP':
+ if p.name is not None:
+ return p.name
+ return p.targetPort
+ return None
+
+
+def wait_predicate(route):
+ if not (route.status and route.status.ingress):
+ return False
+ for ingress in route.status.ingress:
+ match = [x for x in ingress.conditions if x.type == 'Admitted']
+ if not match:
+ return False
+ match = match[0]
+ if match.status != "True":
+ return False
+ return True
+
+
+def main():
+ OpenShiftRoute().run_module()
+
+
+if __name__ == '__main__':
+ main()