summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/okd
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/community/okd')
-rw-r--r--ansible_collections/community/okd/.gitignore21
-rw-r--r--ansible_collections/community/okd/.yamllint33
-rw-r--r--ansible_collections/community/okd/CHANGELOG.rst197
-rw-r--r--ansible_collections/community/okd/CONTRIBUTING.md65
-rw-r--r--ansible_collections/community/okd/FILES.json1076
-rw-r--r--ansible_collections/community/okd/LICENSE674
-rw-r--r--ansible_collections/community/okd/MANIFEST.json41
-rw-r--r--ansible_collections/community/okd/Makefile55
-rw-r--r--ansible_collections/community/okd/OWNERS6
-rw-r--r--ansible_collections/community/okd/OWNERS_ALIASES19
-rw-r--r--ansible_collections/community/okd/README.md187
-rw-r--r--ansible_collections/community/okd/changelogs/.plugin-cache.yaml92
-rw-r--r--ansible_collections/community/okd/changelogs/changelog.yaml201
-rw-r--r--ansible_collections/community/okd/changelogs/config.yaml30
-rw-r--r--ansible_collections/community/okd/ci/Dockerfile43
-rwxr-xr-xansible_collections/community/okd/ci/doc_fragment_modules.py97
-rwxr-xr-xansible_collections/community/okd/ci/downstream.sh298
-rwxr-xr-xansible_collections/community/okd/ci/downstream_fragments.py32
-rwxr-xr-xansible_collections/community/okd/ci/incluster_integration.sh92
-rw-r--r--ansible_collections/community/okd/codecov.yml8
-rw-r--r--ansible_collections/community/okd/docs/ansible_turbo_mode.rst147
-rw-r--r--ansible_collections/community/okd/docs/community.okd.k8s_module.rst1160
-rw-r--r--ansible_collections/community/okd/docs/community.okd.oc_connection.rst315
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_adm_groups_sync_module.rst525
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_adm_migrate_template_instances_module.rst532
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_auth_module.rst514
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_builds_module.rst571
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_deployments_module.rst428
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_images_module.rst569
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_auth_module.rst424
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_build_module.rst844
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_import_image_module.rst626
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_inventory.rst356
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_process_module.rst1004
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_registry_info_module.rst464
-rw-r--r--ansible_collections/community/okd/docs/community.okd.openshift_route_module.rst1440
-rw-r--r--ansible_collections/community/okd/meta/runtime.yml24
-rw-r--r--ansible_collections/community/okd/molecule/default/README.md19
-rw-r--r--ansible_collections/community/okd/molecule/default/converge.yml99
-rw-r--r--ansible_collections/community/okd/molecule/default/destroy.yml6
-rw-r--r--ansible_collections/community/okd/molecule/default/files/crd-resource.yml21
-rw-r--r--ansible_collections/community/okd/molecule/default/files/example.env9
-rw-r--r--ansible_collections/community/okd/molecule/default/files/kuard-extra-property.yml22
-rw-r--r--ansible_collections/community/okd/molecule/default/files/kuard-invalid-type.yml21
-rw-r--r--ansible_collections/community/okd/molecule/default/files/nginx.env12
-rw-r--r--ansible_collections/community/okd/molecule/default/files/pod-template.yaml23
-rw-r--r--ansible_collections/community/okd/molecule/default/files/setup-crd.yml53
-rw-r--r--ansible_collections/community/okd/molecule/default/files/simple-template.yaml34
-rw-r--r--ansible_collections/community/okd/molecule/default/molecule.yml49
-rw-r--r--ansible_collections/community/okd/molecule/default/prepare.yml61
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/defaults/main.yml4
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry.py186
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry_info.py109
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/meta/main.yml4
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/activeDirectory.yml235
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/augmentedActiveDirectory.yml174
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/main.yml62
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/python-ldap-not-installed.yml42
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/rfc2307.yml468
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/ad/definition.j239
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/ad/sync-config.j212
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/augmented-ad/definition.j259
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/augmented-ad/sync-config.j220
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/rfc2307/definition.j2102
-rw-r--r--ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/rfc2307/sync-config.j2105
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml318
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_auth_roles.yml340
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_deployments.yml269
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_auth.yml111
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_builds.yml245
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_import_images.yml179
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_process.yml183
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_prune_images.yml217
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/openshift_route.yml275
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/validate_installed.yml122
-rw-r--r--ansible_collections/community/okd/molecule/default/tasks/validate_not_installed.yml25
-rw-r--r--ansible_collections/community/okd/molecule/default/vars/main.yml94
-rw-r--r--ansible_collections/community/okd/molecule/default/verify.yml89
-rw-r--r--ansible_collections/community/okd/plugins/connection/oc.py173
-rw-r--r--ansible_collections/community/okd/plugins/inventory/openshift.py215
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/k8s.py220
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_auth.py309
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_deployments.py122
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_images.py477
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_builds.py409
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_common.py93
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_docker_image.py103
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_groups.py442
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_images_common.py226
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_import_image.py390
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_ldap.py777
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_process.py199
-rw-r--r--ansible_collections/community/okd/plugins/module_utils/openshift_registry.py144
-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
-rw-r--r--ansible_collections/community/okd/requirements.txt2
-rw-r--r--ansible_collections/community/okd/requirements.yml3
-rw-r--r--ansible_collections/community/okd/setup.cfg3
-rw-r--r--ansible_collections/community/okd/test-requirements.txt4
-rw-r--r--ansible_collections/community/okd/tests/config.yml2
-rw-r--r--ansible_collections/community/okd/tests/sanity/ignore-2.10.txt4
-rw-r--r--ansible_collections/community/okd/tests/sanity/ignore-2.11.txt169
-rw-r--r--ansible_collections/community/okd/tests/sanity/ignore-2.12.txt3
-rw-r--r--ansible_collections/community/okd/tests/sanity/ignore-2.13.txt3
-rw-r--r--ansible_collections/community/okd/tests/sanity/ignore-2.14.txt3
-rw-r--r--ansible_collections/community/okd/tests/sanity/ignore-2.15.txt3
-rw-r--r--ansible_collections/community/okd/tests/sanity/ignore-2.9.txt3
-rw-r--r--ansible_collections/community/okd/tests/unit/plugins/module_utils/test_ldap_dn.py32
-rw-r--r--ansible_collections/community/okd/tests/unit/plugins/module_utils/test_ldap_sync_config.py63
-rw-r--r--ansible_collections/community/okd/tests/unit/plugins/module_utils/test_openshift_docker_image.py152
-rw-r--r--ansible_collections/community/okd/tests/unit/plugins/modules/test_openshift_adm_migrate_template_instances.py236
-rw-r--r--ansible_collections/community/okd/tests/unit/requirements.txt1
123 files changed, 26024 insertions, 0 deletions
diff --git a/ansible_collections/community/okd/.gitignore b/ansible_collections/community/okd/.gitignore
new file mode 100644
index 000000000..5af5e0de0
--- /dev/null
+++ b/ansible_collections/community/okd/.gitignore
@@ -0,0 +1,21 @@
+*.retry
+.idea
+*.log
+.cache/
+__pycache__/
+
+# temp collection path
+# Can't ignore this directory for now, as the files have to be viewable by git in order to be sanity-checked
+# ansible_collections/
+# Galaxy artifacts.
+*.tar.gz
+
+# Changelog cache files.
+changelogs/.plugin-cache.yaml
+
+# Temporary test files.
+tests/output
+tests/integration/cloud-config-*
+
+# Ignore the ansible_collections that the Makefile generates.
+ansible_collections
diff --git a/ansible_collections/community/okd/.yamllint b/ansible_collections/community/okd/.yamllint
new file mode 100644
index 000000000..882767605
--- /dev/null
+++ b/ansible_collections/community/okd/.yamllint
@@ -0,0 +1,33 @@
+---
+# Based on ansible-lint config
+extends: default
+
+rules:
+ braces:
+ max-spaces-inside: 1
+ level: error
+ brackets:
+ max-spaces-inside: 1
+ level: error
+ colons:
+ max-spaces-after: -1
+ level: error
+ commas:
+ max-spaces-after: -1
+ level: error
+ comments: disable
+ comments-indentation: disable
+ document-start: disable
+ empty-lines:
+ max: 3
+ level: error
+ hyphens:
+ level: error
+ indentation: disable
+ key-duplicates: enable
+ line-length: disable
+ new-line-at-end-of-file: disable
+ new-lines:
+ type: unix
+ trailing-spaces: disable
+ truthy: disable
diff --git a/ansible_collections/community/okd/CHANGELOG.rst b/ansible_collections/community/okd/CHANGELOG.rst
new file mode 100644
index 000000000..b86a7c409
--- /dev/null
+++ b/ansible_collections/community/okd/CHANGELOG.rst
@@ -0,0 +1,197 @@
+============================
+OKD Collection Release Notes
+============================
+
+.. contents:: Topics
+
+
+v2.3.0
+======
+
+Bugfixes
+--------
+
+- openshift_adm_groups_sync - initialize OpenshiftGroupSync attributes early to avoid Attribute error (https://github.com/openshift/community.okd/issues/155).
+- openshift_auth - Review the way the discard process is working, add openshift algorithm to convert token to resource object name (https://github.com/openshift/community.okd/issues/176).
+
+New Modules
+-----------
+
+- openshift_adm_prune_builds - Prune old completed and failed builds
+- openshift_build - Start a new build or Cancel running, pending, or new builds.
+
+v2.2.0
+======
+
+Minor Changes
+-------------
+
+- add action groups to runtime.yml (https://github.com/openshift/community.okd/issues/41).
+
+Bugfixes
+--------
+
+- fix ocp auth failing against cluster api url with trailing slash (https://github.com/openshift/community.okd/issues/139)
+
+New Modules
+-----------
+
+- openshift_adm_migrate_template_instances - Update TemplateInstances to point to the latest group-version-kinds
+- openshift_adm_prune_auth - Removes references to the specified roles, clusterroles, users, and groups
+- openshift_adm_prune_deployments - Remove old completed and failed deployment configs
+- openshift_adm_prune_images - Remove unreferenced images
+- openshift_import_image - Import the latest image information from a tag in a container image registry.
+- openshift_registry_info - Display information about the integrated registry.
+
+v2.1.0
+======
+
+Minor Changes
+-------------
+
+- add support for turbo mode (https://github.com/openshift/community.okd/pull/102).
+- openshift_route - Add support for Route annotations (https://github.com/ansible-collections/community.okd/pull/99).
+
+Bugfixes
+--------
+
+- fix broken links in Automation Hub for redhat.openshift (https://github.com/openshift/community.okd/issues/100).
+
+v2.0.1
+======
+
+Minor Changes
+-------------
+
+- increase kubernetes.core dependency version (https://github.com/openshift/community.okd/pull/97).
+
+v2.0.0
+======
+
+Major Changes
+-------------
+
+- update to use kubernetes.core 2.0 (https://github.com/openshift/community.okd/pull/93).
+
+Minor Changes
+-------------
+
+- Added documentation for the ``community.okd`` collection.
+- openshift - inventory plugin supports FQCN ``redhat.openshift``.
+
+Breaking Changes / Porting Guide
+--------------------------------
+
+- drop python 2 support (https://github.com/openshift/community.okd/pull/93).
+
+Bugfixes
+--------
+
+- fixes test suite to use correct versions of python and dependencies (https://github.com/ansible-collections/community.okd/pull/89).
+- openshift_process - fix module execution when template does not include a message (https://github.com/ansible-collections/community.okd/pull/87).
+
+v1.1.2
+======
+
+Bugfixes
+--------
+
+- include requirements.txt in downstream build process (https://github.com/ansible-collections/community.okd/pull/81).
+
+v1.1.1
+======
+
+Bugfixes
+--------
+
+- add missing requirements.txt file needed for execution environments (https://github.com/ansible-collections/community.okd/pull/78).
+- openshift_route - default to ``no_log=False`` for the ``key`` parameter in TLS configuration to fix sanity failures (https://github.com/ansible-collections/community.okd/pull/77).
+- restrict molecule version to <3.3.0 to address breaking change (https://github.com/ansible-collections/community.okd/pull/77).
+- update CI to work with ansible 2.11 (https://github.com/ansible-collections/community.okd/pull/80).
+
+v1.1.0
+======
+
+Minor Changes
+-------------
+
+- increase the kubernetes.core dependency version number (https://github.com/ansible-collections/community.okd/pull/71).
+
+v1.0.2
+======
+
+Minor Changes
+-------------
+
+- restrict the version of kubernetes.core dependency (https://github.com/ansible-collections/community.okd/pull/66).
+
+v1.0.1
+======
+
+Bugfixes
+--------
+
+- Generate downstream redhat.openshift documentation (https://github.com/ansible-collections/community.okd/pull/59).
+
+v1.0.0
+======
+
+Minor Changes
+-------------
+
+- Released version 1 to Automation Hub as redhat.openshift (https://github.com/ansible-collections/community.okd/issues/51).
+
+v0.3.0
+======
+
+Major Changes
+-------------
+
+- Add openshift_process module for template rendering and optional application of rendered resources (https://github.com/ansible-collections/community.okd/pull/44).
+- Add openshift_route module for creating routes from services (https://github.com/ansible-collections/community.okd/pull/40).
+
+New Modules
+-----------
+
+- openshift_process - Process an OpenShift template.openshift.io/v1 Template
+- openshift_route - Expose a Service as an OpenShift Route.
+
+v0.2.0
+======
+
+Major Changes
+-------------
+
+- openshift_auth - new module (migrated from k8s_auth in community.kubernetes) (https://github.com/ansible-collections/community.okd/pull/33).
+
+Minor Changes
+-------------
+
+- Add a contribution guide (https://github.com/ansible-collections/community.okd/pull/37).
+- Use the API Group APIVersion for the `Route` object (https://github.com/ansible-collections/community.okd/pull/27).
+
+New Modules
+-----------
+
+- openshift_auth - Authenticate to OpenShift clusters which require an explicit login step
+
+v0.1.0
+======
+
+Major Changes
+-------------
+
+- Add custom k8s module, integrate better Molecule tests (https://github.com/ansible-collections/community.okd/pull/7).
+- Add downstream build scripts to build redhat.openshift (https://github.com/ansible-collections/community.okd/pull/20).
+- Add openshift connection plugin, update inventory plugin to use it (https://github.com/ansible-collections/community.okd/pull/18).
+- Initial content migration from community.kubernetes (https://github.com/ansible-collections/community.okd/pull/3).
+
+Minor Changes
+-------------
+
+- Add incluster Makefile target for CI (https://github.com/ansible-collections/community.okd/pull/13).
+- Add tests for inventory plugin (https://github.com/ansible-collections/community.okd/pull/16).
+- CI Documentation for working with Prow (https://github.com/ansible-collections/community.okd/pull/15).
+- Docker container can run as an arbitrary user (https://github.com/ansible-collections/community.okd/pull/12).
+- Dockerfile now is properly set up to run tests in a rootless container (https://github.com/ansible-collections/community.okd/pull/11).
+- Integrate stale bot for issue queue maintenance (https://github.com/ansible-collections/community.okd/pull/14).
diff --git a/ansible_collections/community/okd/CONTRIBUTING.md b/ansible_collections/community/okd/CONTRIBUTING.md
new file mode 100644
index 000000000..33269486f
--- /dev/null
+++ b/ansible_collections/community/okd/CONTRIBUTING.md
@@ -0,0 +1,65 @@
+# Contributing
+
+## Getting Started
+
+General information about setting up your Python environment, testing modules,
+Ansible coding styles, and more can be found in the [Ansible Community Guide](
+https://docs.ansible.com/ansible/latest/community/index.html).
+
+
+## Kubernetes Collections
+
+### community.okd
+
+This collection contains modules and plugins contributed and maintained by the Ansible Kubernetes
+community.
+
+New modules and plugins developed by the community should be proposed to `community.okd`.
+
+## Submitting Issues
+All software has bugs, and the `community.okd` collection is no exception. When you find a bug,
+you can help tremendously by [telling us about it](https://github.com/ansible-collections/community.okd/issues/new/choose).
+
+If you should discover that the bug you're trying to file already exists in an issue,
+you can help by verifying the behavior of the reported bug with a comment in that
+issue, or by reporting any additional information.
+
+## Pull Requests
+
+All modules MUST have integration tests for new features.
+Bug fixes for modules that currently have integration tests SHOULD have tests added.
+New modules should be submitted to the [community.okd](https://github.com/ansible-collections/community.okd) collection and MUST have integration tests.
+
+Expected test criteria:
+* Resource creation under check mode
+* Resource creation
+* Resource creation again (idempotency) under check mode
+* Resource creation again (idempotency)
+* Resource modification under check mode
+* Resource modification
+* Resource modification again (idempotency) under check mode
+* Resource modification again (idempotency)
+* Resource deletion under check mode
+* Resource deletion
+* Resource deletion (of a non-existent resource) under check mode
+* Resource deletion (of a non-existent resource)
+
+Where modules have multiple parameters we recommend running through the 4-step modification cycle for each parameter the module accepts, as well as a modification cycle where as most, if not all, parameters are modified at the same time.
+
+For general information on running the integration tests see the
+[Integration Tests page of the Module Development Guide](https://docs.ansible.com/ansible/devel/dev_guide/testing_integration.html#testing-integration),
+especially the section on configuration for cloud tests. For questions about writing tests the Ansible Kubernetes community can be found on Libera.Chat IRC as detailed below.
+
+
+### Code of Conduct
+The `community.okd` collection follows the Ansible project's
+[Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html).
+Please read and familiarize yourself with this document.
+
+### IRC
+Our IRC channels may require you to register your nickname. If you receive an error when you connect, see
+[Libera.Chat's Nickname Registration guide](https://libera.chat/guides/registration) for instructions.
+
+The `#ansible-kubernetes` channel on [irc.libera.chat](https://libera.chat/) is the main and official place to discuss use and development of the `community.okd` collection.
+
+For more information about Ansible's Kubernetes and OpenShift integration, browse the resources in the [Kubernetes Working Group](https://github.com/ansible/community/wiki/Kubernetes) Community wiki page.
diff --git a/ansible_collections/community/okd/FILES.json b/ansible_collections/community/okd/FILES.json
new file mode 100644
index 000000000..f4086aab1
--- /dev/null
+++ b/ansible_collections/community/okd/FILES.json
@@ -0,0 +1,1076 @@
+{
+ "files": [
+ {
+ "name": ".",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".yamllint",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5845e18e9f23155f423207df9abac970aed687c638620bc2c9ee06706191054b",
+ "format": 1
+ },
+ {
+ "name": "tests",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/config.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9a009a349eaaf78c93ff56072d2ef171937bdb884e4976592ab5aaa9c68e1044",
+ "format": 1
+ },
+ {
+ "name": "tests/unit",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/requirements.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8004a972c3d5c274d8c808e8d8afe03b9aca8af8eebf3df4298f114d8008b754",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_openshift_docker_image.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2645a4d02f3adc9e4bbb8b69ecb9b327836c142d1115f616c2e0eb05f0414299",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_ldap_dn.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e692237cc900dce4c6bd3a1512c4027e6bb2c0a23e5d473a55c9137751c3559",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_ldap_sync_config.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d1647944d9370d05aedc63b115f427d037a3354f670b45e94bb3f4f4f95b162f",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_openshift_adm_migrate_template_instances.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4c426c87d28313524cbd378f917a2d70242f333571f942d44bc9bf8c7e591586",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.14.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "43bc048d24e8dd8fca8052b6135daaf59207e462f72cb9848f65de9c6e09552c",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.10.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ba2285dad70183ce56e9d43ed97d4b7a1b12e7cdd3f4e5856fb3cf380148a5ee",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.13.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "43bc048d24e8dd8fca8052b6135daaf59207e462f72cb9848f65de9c6e09552c",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.9.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf94385a1d8a2b51f40f88879b59922c8a8b07b8f1e0ac511a2454e257361995",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.12.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "43bc048d24e8dd8fca8052b6135daaf59207e462f72cb9848f65de9c6e09552c",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.11.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dc0294c142585d8904331fd57755efc876e592ce8242e42e1159bcfddd4adbba",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.15.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "43bc048d24e8dd8fca8052b6135daaf59207e462f72cb9848f65de9c6e09552c",
+ "format": 1
+ },
+ {
+ "name": "README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a49ac5f1fa3cdb032a5274e9d0d8a8fadcc5dd3ee1a0d3f872ca969a22be3056",
+ "format": 1
+ },
+ {
+ "name": "changelogs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "changelogs/changelog.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "36a4108575c00ad25e2774bcae35c32e4022d8d1b563ed72e536ec796b22b46f",
+ "format": 1
+ },
+ {
+ "name": "changelogs/config.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "46bf50de40f3abb6bc0497f3b188dba66c295a2c764337abf4dda636644cb0d9",
+ "format": 1
+ },
+ {
+ "name": "changelogs/.plugin-cache.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e9fb32d9dfa89439d04e50697ea1e8a5c77a2411dd6a061d2eddb5e328753abf",
+ "format": 1
+ },
+ {
+ "name": "CONTRIBUTING.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4156016c23606d288b98c4fa0eafc6afe1cf28e695fb5a267bcc3d337a7bfef0",
+ "format": 1
+ },
+ {
+ "name": "requirements.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d20fa49248fb968b70381346d2b2f6195e19dc1ddded06856bc4aefa6a25d431",
+ "format": 1
+ },
+ {
+ "name": "meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "meta/runtime.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "369f3c5a13d3d8400d0f662ec26d211c4e135f1b8b661c66346d899ad371b534",
+ "format": 1
+ },
+ {
+ "name": ".gitignore",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8344abb7aafbdb654f0c88532af7ab557dad7a6a7cba60f4834f33c2a1f8524f",
+ "format": 1
+ },
+ {
+ "name": "test-requirements.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "34db46c861b289148eb3ebff5ff7786aa091eec38c0f3d1606bf2e3d9f030b94",
+ "format": 1
+ },
+ {
+ "name": "codecov.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "caa848a2e02be5014890c5cbc7727e9a00d40394637c90886eb813d60f82c9c3",
+ "format": 1
+ },
+ {
+ "name": "setup.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "125fefc5c9470b8e72cffc06937c30e2bc073f4ca6c1a593f131a6e1fd76edf2",
+ "format": 1
+ },
+ {
+ "name": "OWNERS",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "771ebefb6c2b0154bf043cc537cc55b8421ddd4058248b00a62cdc9b190131da",
+ "format": 1
+ },
+ {
+ "name": "LICENSE",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8b1ba204bb69a0ade2bfcf65ef294a920f6bb361b317dba43c7ef29d96332b9b",
+ "format": 1
+ },
+ {
+ "name": "OWNERS_ALIASES",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fee153ef8daa4ed6170434daf598b3efdcaf80db39e5b4ef9f2ab8cd4f8f4c30",
+ "format": 1
+ },
+ {
+ "name": "plugins",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_common.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cbbbbe8693629763781768801033674d80fdcc5ae2b5d19eb35922bc50eee8ed",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_registry.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a2a40dbcae3074de050c5d9be2d1e4352cf0698ad8385fd81c03ce73805a6e93",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_adm_prune_deployments.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8fa9b77a5d2326893c1ecd9841d4b9e30e3e7bd788d7455efcbad45d010683f5",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_images_common.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d61fbb7ced206c7cbde4a610e643aa4a824e7856648ba598c5f630978bbafdaa",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_process.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a7f5b99e0f07aba90fd2744f6311c82a59698424e5904ceda7b2e0eaa3e080af",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_builds.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f7b18ce9225cb1fb06a970a591662ab9cd1c3015f18bae86570e4f2345b0449e",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_ldap.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "894c0aa5a40d6c227b73cb83c3e4217a6ed081cda9c3a89a0606b66a65b52287",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_import_image.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "47819d3ab99b61593b5db881b6d93855caf45ae3df71b77ad7b6528a1b51bbbd",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/k8s.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "386788c253525ffe4350e4b6fbbc1380e03f703bfe6fa3ca4506615e93b9173d",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_adm_prune_images.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cd027fb0716b5c92ac6176f6a46236c0379e9680cd9d743c5fd40617461502f8",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_adm_prune_auth.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "76a26d3ab75396a28fc546c19b1a8e215aa8a5fd59fc4491539e2ef6d5537d8c",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_docker_image.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d23dfbad39579c35a2a39bbf0e18e2e0e8bc3a0122c37d0fc87c2ea125f5af2c",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/openshift_groups.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f583086177b63a87fca75c3d41d1bce4bc4cecaf68c60a9008ca1ecb31d1c38a",
+ "format": 1
+ },
+ {
+ "name": "plugins/connection",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/connection/oc.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cb192532153b1625ec97437f5ac7177cfa78d8cba5c5eaa4633fab63ba54ca09",
+ "format": 1
+ },
+ {
+ "name": "plugins/inventory",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/inventory/openshift.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bb798a479803548cc8eeddb6fdb565d1b0f7b3222af445c42447247203953849",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_adm_groups_sync.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "80197c296c50931b3b0ed2a4841dd46ab98b6d0de3c923ddc8d60abd49cdecee",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_registry_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "abf945aba0bb5d769783c7e3ee1b19c2e4cd4f9e932a51e88dcb298a664cc677",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_auth.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a293bf87c5f13c610fd2e6032a76e2c813bfdae4953c7b33e2c6cbf3bf8249e1",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_route.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "78e6b48f0dcc76cecc8c0272768cfdeab62364b3658984fdddf888492f61dd03",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_build.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a0aff179625071a085d0d9014cfa8999dceb96ba2394db5f33991eca1a9c85c5",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_adm_prune_deployments.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9dc9dcfce2107deecd3da2144f0dee455a442afcd04428c042bd48d598978bd2",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_process.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "271370fe6c7eba007bd282d4bea738100278b4294df88d55b69f951309867a4e",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_import_image.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dc18021e7be860b0accbe89fc51e2de1ca03938bb159907ba45558527f808c2f",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/k8s.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ffca7a5e4ed3d2122106a601b511589507b99ab65a138e4c912dcd76fcb2d430",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_adm_prune_images.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c1f6d6e041d08ccd548a812c6bee4a972b260ebb86dbe35783140001c9bea681",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_adm_prune_auth.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "63c667799f92fdf025b577e2a6a3c84220389e35d357d95177f7acdb773d3da2",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_adm_prune_builds.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d40236b25580fa9fb242d42b43304865f51fceef82d6e1eb0ca158cf18a6253e",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/openshift_adm_migrate_template_instances.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0f6f0fd8d617700bdb22953555bb6fe2dd7dbe9c297264cf191cfca651dbb0b4",
+ "format": 1
+ },
+ {
+ "name": "Makefile",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "afa91ff62ed42d4b0fb73fa47899a01cde478809866d99c63787130074d5a039",
+ "format": 1
+ },
+ {
+ "name": "molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/prepare.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d9765b6cd95ab121281ca53b24f5a0954b211801ca070aa9402f746e593195df",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "75d6935060001109bea00ffcf6d289b29e7aa6afeaba2e1e1b85d767062f4da8",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_import_images.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0697c9bcd3f8b67cc778ddd5a4eaedf170f925501dae4e5836434b897a30d5dd",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/validate_installed.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a6abad4a4d33a77ca736a4835db0f5d11dc7742100ff4d9d36a9942e1f4b55e9",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_adm_prune_deployments.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "16b97fda417204c8df08a294c4f46011ad6bc617964d3b2a8c860134793ea175",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_builds.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dd3532196ebb99d4a467fb2cc4b958a1d5e551969c00224b07694c76b959dd45",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_adm_prune_auth_roles.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d83689cc8e59c041386545fb0afdde5ec3157bea33db35303529595ddffb5fe9",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/validate_not_installed.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9d493e30e52e26c73dd2d197e74b4d27776d01f27fb5cfd6ef982db157484cbb",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_prune_images.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "706dec431bd424212a1b43e7c7aa86bc9a01c3fd392d170f6486b1732ebcc317",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_auth.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0d8b02f57bd1ff3b9a65d46c40914a2ff765f6bd96901ebfa7ce1751154d1802",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_route.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "20d0f4944902de207884fe0a0a987070b9b3716a436d76264400cb67f53c38c3",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bc631199609c00aa4f669ccde1c351921ad43eec335c7abe6185f97c2b903f11",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/tasks/openshift_process.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "999932ada001df7be263ab2158592c6325d2827ee372ad38e9f0197b807cbf52",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/destroy.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "26670c28263a4f354e646c222e2c59ccfcb3cd807fe07d531e9874fc3748a44c",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/tasks/python-ldap-not-installed.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "05a6759b51bcd97f154db6d344367c55eb424533163fccce1493c49dd5913973",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d7b676aa4dd79463ad48beebb3b29dd7d73f65bad7df406ea8787214a9fa7004",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/tasks/augmentedActiveDirectory.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ad25bcfb4431257ab481f12cfcbae23620d8826fa25d38e93ed9a81ef646aa68",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/tasks/activeDirectory.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e2758c9d39c94892ca9976f8e5d7b360bfccb134ea7b95f83ba51c46b1d9195",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/tasks/rfc2307.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "87966533bdb6f1f62208f8c6e85ba38acdb3dfd44c6312eb5dca5026b4c9ea0e",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/library",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8e0d7d997ba74938fef19fead2cf1d2151644ad999e2285b0d769c89804abf5e",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0b98d02bb07c0ae7ba4da80ef95c02d2476a0106bd8f198779d296c81323bbfe",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8d57e1f764ed7af7cf34d0b039ef97eb206fd846d31025ac08ddefa855a83204",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/ad",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/ad/definition.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f59a7ee4f37de0040f293b615b45092f5bc9fb36f832d81ea6f0a7155bb2c1bd",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/ad/sync-config.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fef496b8ba026e34cdef374f373a15948db2bf6147bff5eb4ebdba4d1b3a4381",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/augmented-ad",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/augmented-ad/definition.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a2db659cc8d7467e4d9e78029027d6fff6cc1fd38e54c6ca954a9a64633470d0",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/augmented-ad/sync-config.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "76c20cd498ced5c2417a71611b35bde1b22b537fd106b5d8fecf1b896d7d5074",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/rfc2307",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/rfc2307/definition.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e2d7b8287dd1b1ad0067eb8a38d282ff43f95e3a91c3778b86205a14988b434",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/templates/rfc2307/sync-config.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "64cdf5017cb257f3b26d7eb858c8295313684b1147f0baee3f657fc3dc36c1f9",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/roles/openshift_adm_groups/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1bd380f6c0cdf0c2b25e6bf27e4304566f6b9efbbeaf15da3847a9744d685a72",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0e6bfb7aa0d20362717eebbcf8c8bee26211fc1d83bf23bdd5f8fb7046155c47",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bdd1f1cc563c232b34226f0316eb78d229ca562028e37e281ad76261131da044",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files/nginx.env",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a68c2914576119a809943116ce0050ca221c5b8f3a6722fa6abbf15dababde5f",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files/setup-crd.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "509878fff22a19715f1c491930eefd23430c0f571716b463c3ab9a754d0fb250",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files/pod-template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5fcb22787bccf2a5e1b6b90617f1aabadb71e9c9a906763e68b8573ef468f09a",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files/simple-template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "acbc61a1278772f1fd80a1bdb6546b6022842f7fd1c1b9aaf8bd65b606d93a2b",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files/kuard-invalid-type.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c0e8aa083eecb5c4d92108af83f9a2c931a2e5bbb766af4792adc4fb9fd9d32d",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files/example.env",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3701367ea0a79158d476e168c1b1e64639f703da926e63045cba5cfdbb6c3576",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files/kuard-extra-property.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "27a06de7ca760698dc1d3c46ef12e40e214ae319a791a3965806b30adca06de0",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/files/crd-resource.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4921362ac3c4afac5f42ebb90b37bcb75e1fe20929bb0e45d0df4c190d28f577",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/verify.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0aa66e5d35f985e47345d47e378816189d77cb131520e115f05c8eea2ab3bfc6",
+ "format": 1
+ },
+ {
+ "name": "molecule/default/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "molecule/default/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "75f11fae6d7f1e6574152509a4d7ad306966fc94eda2e610dfdb8a529a531228",
+ "format": 1
+ },
+ {
+ "name": "ci",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "ci/downstream.sh",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "75816fd56cb1a00c37234e84b4b2109f49cfad60901429a96e36f528202d3c2e",
+ "format": 1
+ },
+ {
+ "name": "ci/downstream_fragments.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9815e2aa84aef5d396fd07ddb371fa87362ade6fbe72d829e4d5983abe8af453",
+ "format": 1
+ },
+ {
+ "name": "ci/doc_fragment_modules.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e5f5e24396a2cc86faedf6d0b7a3835db06bbe6fc7b534018ed85ebecfa39e09",
+ "format": 1
+ },
+ {
+ "name": "ci/incluster_integration.sh",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "012ffb20a006094a4e8a00ead7947a196c477c4eb071a1dad5ffe42f509935e1",
+ "format": 1
+ },
+ {
+ "name": "ci/Dockerfile",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "372a61931849250f7805a630fd5fc8b854f457d2752e1a70912beaa4773855ec",
+ "format": 1
+ },
+ {
+ "name": "docs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_route_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "979c41e0c3ec23b57267104feaa400b798ddbb3f3157e77a22dad0b05d067d19",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_adm_prune_images_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4b5a9d72104e8cfee2e7973f094aaf8af666a2bdec615cee482411261f5dfa57",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.k8s_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5d8bf2916753820405f62f00174687e62756dd2b610faea666f1ba077a02d573",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_inventory.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "551e42026b2affd901c51f2be85fb69d9911e037edf424667eacc1b21aa00583",
+ "format": 1
+ },
+ {
+ "name": "docs/ansible_turbo_mode.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a9bcd000c20de1d1ad35f5516f4fdffce07c8e28a5ebee572629a3b9cb867152",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_process_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3910d82ee4a8a31d84b6cbac6b7e6d6331d1ef9fe2b4331ce82e3065090baf66",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_adm_groups_sync_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f8c405bb2c291cbdb3a0f4398bb9b205864dbb941cb0b0024e7cfa74d6ae49bd",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_auth_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9c361ea64a0005fb0682c8fbd863cb7ef7cd0eb45f52383cc1123690e91fed11",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.oc_connection.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c0ffee1276ab6cfa6677b42fed2fcd8b46622ed5cb4c8ee0f9c9cac4db870f75",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_adm_prune_auth_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fbad4ddccea64f4ed415cf86d54b7d6fc3e6a7be46c19d11fb20202b527275dd",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_adm_prune_builds_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d4762b6900b787b6efc27fbeede84044839e083fa5bd06165eb5c1b17f203a31",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_import_image_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f031bc1985bda8784a3cb684767db28a469216ba7e47940bf7c49844acc87b08",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_build_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "50c1219f5524d60a4b46bc58c6df30d8af7b6e6ecc20fd4d15cf3e0fc352c05e",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_registry_info_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c66f8f9adb98eedcfda0e31034f5d14e881e9a41d3793fb36a3f311635166e69",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_adm_prune_deployments_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "02693279a5b1c52eaf4fd70d8f01d87750f9b0554cd1d550892192b7070f558a",
+ "format": 1
+ },
+ {
+ "name": "docs/community.okd.openshift_adm_migrate_template_instances_module.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "99634e05dde50a01bd4cba0c696e3622864b744b6ea2f6103094c3809238ffd9",
+ "format": 1
+ },
+ {
+ "name": "CHANGELOG.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b22ef082f56193da0b6bd2984c3eff9beb4e5980017a7fcb4bcdad7898ff5112",
+ "format": 1
+ },
+ {
+ "name": "requirements.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a020e70b5ab59096b404e97d4f4a04a3d0b63627dbbcede89b3e131050fcccb4",
+ "format": 1
+ }
+ ],
+ "format": 1
+} \ No newline at end of file
diff --git a/ansible_collections/community/okd/LICENSE b/ansible_collections/community/okd/LICENSE
new file mode 100644
index 000000000..e72bfddab
--- /dev/null
+++ b/ansible_collections/community/okd/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>. \ No newline at end of file
diff --git a/ansible_collections/community/okd/MANIFEST.json b/ansible_collections/community/okd/MANIFEST.json
new file mode 100644
index 000000000..dd2937e35
--- /dev/null
+++ b/ansible_collections/community/okd/MANIFEST.json
@@ -0,0 +1,41 @@
+{
+ "collection_info": {
+ "namespace": "community",
+ "name": "okd",
+ "version": "2.3.0",
+ "authors": [
+ "geerlingguy (https://www.jeffgeerling.com/)",
+ "fabianvf (https://github.com/fabianvf)",
+ "willthames (https://github.com/willthames)",
+ "Akasurde (https://github.com/akasurde)"
+ ],
+ "readme": "README.md",
+ "tags": [
+ "kubernetes",
+ "k8s",
+ "cloud",
+ "infrastructure",
+ "openshift",
+ "okd",
+ "cluster"
+ ],
+ "description": "OKD Collection for Ansible.",
+ "license": [],
+ "license_file": "LICENSE",
+ "dependencies": {
+ "kubernetes.core": ">=2.4.0"
+ },
+ "repository": "https://github.com/openshift/community.okd",
+ "documentation": "",
+ "homepage": "",
+ "issues": "https://github.com/openshift/community.okd/issues"
+ },
+ "file_manifest_file": {
+ "name": "FILES.json",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6e3f825a7ea74f2f591751d9ee4aac800f99ca344e86a5d00477306b14419ae8",
+ "format": 1
+ },
+ "format": 1
+} \ No newline at end of file
diff --git a/ansible_collections/community/okd/Makefile b/ansible_collections/community/okd/Makefile
new file mode 100644
index 000000000..7990f8447
--- /dev/null
+++ b/ansible_collections/community/okd/Makefile
@@ -0,0 +1,55 @@
+.PHONY: molecule
+
+# Also needs to be updated in galaxy.yml
+VERSION = 2.3.0
+
+SANITY_TEST_ARGS ?= --docker --color
+UNITS_TEST_ARGS ?= --docker --color
+PYTHON_VERSION ?= `python3 -c 'import platform; print("{0}.{1}".format(platform.python_version_tuple()[0], platform.python_version_tuple()[1]))'`
+
+clean:
+ rm -f community-okd-$(VERSION).tar.gz
+ rm -f redhat-openshift-$(VERSION).tar.gz
+ rm -rf ansible_collections
+
+build: clean
+ ansible-galaxy collection build
+
+install: build
+ ansible-galaxy collection install -p ansible_collections community-okd-$(VERSION).tar.gz
+
+sanity: install
+ cd ansible_collections/community/okd && ansible-test sanity -v --python $(PYTHON_VERSION) $(SANITY_TEST_ARGS)
+
+units: install
+ cd ansible_collections/community/okd && ansible-test units -v --python $(PYTHON_VERSION) $(UNITS_TEST_ARGS)
+
+molecule: install
+ molecule test
+
+test-integration: upstream-test-integration downstream-test-integration
+
+test-sanity: upstream-test-sanity downstream-test-sanity
+
+test-units: upstream-test-units downstream-test-units
+
+test-integration-incluster:
+ ./ci/incluster_integration.sh
+
+upstream-test-sanity: sanity
+
+upstream-test-units: units
+
+upstream-test-integration: molecule
+
+downstream-test-sanity:
+ ./ci/downstream.sh -s
+
+downstream-test-units:
+ ./ci/downstream.sh -u
+
+downstream-test-integration:
+ ./ci/downstream.sh -i
+
+downstream-build:
+ ./ci/downstream.sh -b
diff --git a/ansible_collections/community/okd/OWNERS b/ansible_collections/community/okd/OWNERS
new file mode 100644
index 000000000..48437f309
--- /dev/null
+++ b/ansible_collections/community/okd/OWNERS
@@ -0,0 +1,6 @@
+# This file just uses aliases defined in OWNERS_ALIASES.
+
+approvers:
+ - community.okd-approvers
+reviewers:
+ - community.okd-reviewers
diff --git a/ansible_collections/community/okd/OWNERS_ALIASES b/ansible_collections/community/okd/OWNERS_ALIASES
new file mode 100644
index 000000000..f65b7d7aa
--- /dev/null
+++ b/ansible_collections/community/okd/OWNERS_ALIASES
@@ -0,0 +1,19 @@
+aliases:
+ community.okd-approvers:
+ - gravesm
+ - akasurde
+ - fabianvf
+ - tima
+ - goneri
+ - jillr
+ - alinabuzachis
+ - abikouo
+ community.okd-reviewers:
+ - gravesm
+ - akasurde
+ - fabianvf
+ - tima
+ - goneri
+ - jillr
+ - alinabuzachis
+ - abikouo
diff --git a/ansible_collections/community/okd/README.md b/ansible_collections/community/okd/README.md
new file mode 100644
index 000000000..f3e1bba67
--- /dev/null
+++ b/ansible_collections/community/okd/README.md
@@ -0,0 +1,187 @@
+# OKD Collection for Ansible
+
+<!--- STARTREMOVE --->
+[![CI](https://github.com/ansible-collections/community.okd/workflows/CI/badge.svg?event=push)](https://github.com/ansible-collections/community.okd/actions) [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.okd)](https://codecov.io/gh/ansible-collections/community.okd)
+
+This repo hosts the `community.okd` Ansible Collection.
+
+The collection includes a variety of Ansible content to help automate the management of applications in OKD clusters, as well as the provisioning and maintenance of clusters themselves.
+
+<!--start requires_ansible-->
+## Ansible version compatibility
+
+This collection has been tested against following Ansible versions: **>=2.9.17**.
+
+For collections that support Ansible 2.9, please ensure you update your `network_os` to use the
+fully qualified collection name (for example, `cisco.ios.ios`).
+Plugins and modules within a collection may be tested with only specific Ansible versions.
+A collection may contain metadata that identifies these versions.
+PEP440 is the schema used to describe the versions of Ansible.
+<!--end requires_ansible-->
+
+## Python Support
+
+* Collection supports 3.6+
+
+## Kubernetes Version Support
+
+This collection supports Kubernetes versions >=1.19.
+
+## Included content
+
+Click on the name of a plugin or module to view that content's documentation:
+
+<!--start collection content-->
+### Connection plugins
+Name | Description
+--- | ---
+[community.okd.oc](https://github.com/openshift/community.okd/blob/main/docs/community.okd.oc_connection.rst)|Execute tasks in pods running on OpenShift.
+
+### Inventory plugins
+Name | Description
+--- | ---
+[community.okd.openshift](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_inventory.rst)|OpenShift inventory source
+
+### Modules
+Name | Description
+--- | ---
+[community.okd.k8s](https://github.com/openshift/community.okd/blob/main/docs/community.okd.k8s_module.rst)|Manage OpenShift objects
+[community.okd.openshift_adm_groups_sync](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_adm_groups_sync_module.rst)|Sync OpenShift Groups with records from an external provider.
+[community.okd.openshift_adm_migrate_template_instances](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_adm_migrate_template_instances_module.rst)|Update TemplateInstances to point to the latest group-version-kinds
+[community.okd.openshift_adm_prune_auth](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_adm_prune_auth_module.rst)|Removes references to the specified roles, clusterroles, users, and groups
+[community.okd.openshift_adm_prune_builds](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_adm_prune_builds_module.rst)|Prune old completed and failed builds
+[community.okd.openshift_adm_prune_deployments](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_adm_prune_deployments_module.rst)|Remove old completed and failed deployment configs
+[community.okd.openshift_adm_prune_images](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_adm_prune_images_module.rst)|Remove unreferenced images
+[community.okd.openshift_auth](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_auth_module.rst)|Authenticate to OpenShift clusters which require an explicit login step
+[community.okd.openshift_build](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_build_module.rst)|Start a new build or Cancel running, pending, or new builds.
+[community.okd.openshift_import_image](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_import_image_module.rst)|Import the latest image information from a tag in a container image registry.
+[community.okd.openshift_process](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_process_module.rst)|Process an OpenShift template.openshift.io/v1 Template
+[community.okd.openshift_registry_info](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_registry_info_module.rst)|Display information about the integrated registry.
+[community.okd.openshift_route](https://github.com/openshift/community.okd/blob/main/docs/community.okd.openshift_route_module.rst)|Expose a Service as an OpenShift Route.
+
+<!--end collection content-->
+
+<!--- ENDREMOVE --->
+
+## Installation and Usage
+
+### Installing the Collection from Ansible Galaxy
+
+Before using the OKD collection, you need to install it with the Ansible Galaxy CLI:
+
+ ansible-galaxy collection install community.okd
+
+You can also include it in a `requirements.yml` file and install it via `ansible-galaxy collection install -r requirements.yml`, using the format:
+
+```yaml
+---
+collections:
+ - name: community.okd
+ version: 2.3.0
+```
+
+### Installing the Kubernetes Python Library
+
+Content in this collection requires the [Kubernetes Python client](https://pypi.org/project/kubernetes/) to interact with Kubernetes' APIs. You can install it with:
+
+ pip3 install kubernetes
+
+### Using modules from the OKD Collection in your playbooks
+
+It's preferable to use content in this collection using their Fully Qualified Collection Namespace (FQCN), for example `community.okd.openshift`:
+
+```yaml
+---
+plugin: community.okd.openshift
+connections:
+ - namespaces:
+ - testing
+```
+
+For documentation on how to use individual plugins included in this collection, please see the links in the 'Included content' section earlier in this README.
+
+## Ansible Turbo mode Tech Preview
+
+
+ The ``community.okd`` collection supports Ansible Turbo mode as a tech preview via the ``cloud.common`` collection. By default, this feature is disabled. To enable Turbo mode, set the environment variable `ENABLE_TURBO_MODE=1` on the managed node. For example:
+
+ ```yaml
+ ---
+ - hosts: remote
+ environment:
+ ENABLE_TURBO_MODE: 1
+ tasks:
+ ...
+ ```
+
+ Please read more about Ansible Turbo mode - [here](https://github.com/ansible-collections/community.okd/blob/main/docs/ansible_turbo_mode.rst).
+
+<!--- STARTREMOVE --->
+## Testing and Development
+
+If you want to develop new content for this collection or improve what's already here, the easiest way to work on the collection is to clone it into one of the configured [`COLLECTIONS_PATHS`](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#collections-paths), and work on it there.
+
+See [Contributing to community.okd](CONTRIBUTING.md).
+
+The `tests` directory contains configuration for running sanity tests using [`ansible-test`](https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html).
+
+You can run the `ansible-test` sanity tests with the command:
+
+ make test-sanity
+
+The `molecule` directory contains configuration for running integration tests using [`molecule`](https://molecule.readthedocs.io/).
+
+You can run the `molecule` integration tests with the command:
+
+ make test-integration
+
+These commands will create a directory called `ansible_collections` which should not be committed or added to the `.gitignore` (Tracking issue: https://github.com/ansible/ansible/issues/68499)
+
+
+### Prow
+
+This repository uses the OpenShift [Prow](https://github.com/kubernetes/test-infra/blob/master/prow/README.md) instance for testing against live OpenShift clusters.
+The configuration for the CI jobs that this repository runs can be found in the [`openshift/release repository`](https://github.com/openshift/release/blob/master/ci-operator/config/openshift/community.okd/openshift-community.okd-main.yaml).
+
+The [Prow CI integration test job](https://github.com/openshift/release/blob/master/ci-operator/config/openshift/community.okd/openshift-community.okd-main.yaml#L40-L43)
+runs the command:
+
+ make test-integration-incluster
+
+which will create a job that runs the normal `make integration` target. In order to mimic the Prow CI job, you must
+first build the test image using the Dockerfile in [`ci/Dockerfile`](ci/Dockerfile). Then, push the image
+somewhere that it will be accessible to the cluster, and run
+
+ IMAGE_FORMAT=<your image> make test-integration-incluser
+
+where the `IMAGE_FORMAT` environment variable is the full reference to your container (ie, `IMAGE_FORMAT=quay.io/example/molecule-test-runner`)
+
+## Publishing New Versions
+
+Releases are automatically built and pushed to Ansible Galaxy for any new tag. Before tagging a release, make sure to do the following:
+
+ 1. Update the version in the following places:
+ a. The `version` in `galaxy.yml`
+ b. This README's `requirements.yml` example
+ c. The `DOWNSTREAM_VERSION` in `ci/downstream.sh`
+ d. The `VERSION` in `Makefile`
+ e. The version in `requirements.yml`
+ 1. Update the CHANGELOG:
+ 1. Make sure you have [`antsibull-changelog`](https://pypi.org/project/antsibull-changelog/) installed.
+ 1. Make sure there are fragments for all known changes in `changelogs/fragments`.
+ 1. Run `antsibull-changelog release`.
+ 1. Commit the changes and create a PR with the changes. Wait for tests to pass, then merge it once they have.
+ 1. Tag the version in Git and push to GitHub.
+
+After the version is published, verify it exists on the [OKD Collection Galaxy page](https://galaxy.ansible.com/community/okd).
+<!--- ENDREMOVE --->
+
+## More Information
+
+For more information about Ansible's Kubernetes and OpenShift integrations, join the `#ansible-kubernetes` channel on [libera.chat](https://libera.chat/) IRC, and browse the resources in the [Kubernetes Working Group](https://github.com/ansible/community/wiki/Kubernetes) Community wiki page.
+
+## License
+
+GNU General Public License v3.0 or later
+
+See LICENCE to see the full text.
diff --git a/ansible_collections/community/okd/changelogs/.plugin-cache.yaml b/ansible_collections/community/okd/changelogs/.plugin-cache.yaml
new file mode 100644
index 000000000..223920534
--- /dev/null
+++ b/ansible_collections/community/okd/changelogs/.plugin-cache.yaml
@@ -0,0 +1,92 @@
+objects: {}
+plugins:
+ become: {}
+ cache: {}
+ callback: {}
+ cliconf: {}
+ connection:
+ oc:
+ description: Execute tasks in pods running on OpenShift.
+ name: oc
+ version_added: null
+ httpapi: {}
+ inventory:
+ openshift:
+ description: OpenShift inventory source
+ name: openshift
+ version_added: null
+ lookup: {}
+ module:
+ k8s:
+ description: Manage OpenShift objects
+ name: k8s
+ namespace: ''
+ version_added: null
+ openshift_adm_groups_sync:
+ description: Sync OpenShift Groups with records from an external provider.
+ name: openshift_adm_groups_sync
+ namespace: ''
+ version_added: 2.1.0
+ openshift_adm_migrate_template_instances:
+ description: Update TemplateInstances to point to the latest group-version-kinds
+ name: openshift_adm_migrate_template_instances
+ namespace: ''
+ version_added: 2.2.0
+ openshift_adm_prune_auth:
+ description: Removes references to the specified roles, clusterroles, users,
+ and groups
+ name: openshift_adm_prune_auth
+ namespace: ''
+ version_added: 2.2.0
+ openshift_adm_prune_builds:
+ description: Prune old completed and failed builds
+ name: openshift_adm_prune_builds
+ namespace: ''
+ version_added: 2.3.0
+ openshift_adm_prune_deployments:
+ description: Remove old completed and failed deployment configs
+ name: openshift_adm_prune_deployments
+ namespace: ''
+ version_added: 2.2.0
+ openshift_adm_prune_images:
+ description: Remove unreferenced images
+ name: openshift_adm_prune_images
+ namespace: ''
+ version_added: 2.2.0
+ openshift_auth:
+ description: Authenticate to OpenShift clusters which require an explicit login
+ step
+ name: openshift_auth
+ namespace: ''
+ version_added: 0.2.0
+ openshift_build:
+ description: Start a new build or Cancel running, pending, or new builds.
+ name: openshift_build
+ namespace: ''
+ version_added: 2.3.0
+ openshift_import_image:
+ description: Import the latest image information from a tag in a container image
+ registry.
+ name: openshift_import_image
+ namespace: ''
+ version_added: 2.2.0
+ openshift_process:
+ description: Process an OpenShift template.openshift.io/v1 Template
+ name: openshift_process
+ namespace: ''
+ version_added: 0.3.0
+ openshift_registry_info:
+ description: Display information about the integrated registry.
+ name: openshift_registry_info
+ namespace: ''
+ version_added: 2.2.0
+ openshift_route:
+ description: Expose a Service as an OpenShift Route.
+ name: openshift_route
+ namespace: ''
+ version_added: 0.3.0
+ netconf: {}
+ shell: {}
+ strategy: {}
+ vars: {}
+version: 2.3.0
diff --git a/ansible_collections/community/okd/changelogs/changelog.yaml b/ansible_collections/community/okd/changelogs/changelog.yaml
new file mode 100644
index 000000000..12184d712
--- /dev/null
+++ b/ansible_collections/community/okd/changelogs/changelog.yaml
@@ -0,0 +1,201 @@
+ancestor: null
+releases:
+ 0.1.0:
+ changes:
+ major_changes:
+ - Add custom k8s module, integrate better Molecule tests (https://github.com/ansible-collections/community.okd/pull/7).
+ - Add downstream build scripts to build redhat.openshift (https://github.com/ansible-collections/community.okd/pull/20).
+ - Add openshift connection plugin, update inventory plugin to use it (https://github.com/ansible-collections/community.okd/pull/18).
+ - Initial content migration from community.kubernetes (https://github.com/ansible-collections/community.okd/pull/3).
+ minor_changes:
+ - Add incluster Makefile target for CI (https://github.com/ansible-collections/community.okd/pull/13).
+ - Add tests for inventory plugin (https://github.com/ansible-collections/community.okd/pull/16).
+ - CI Documentation for working with Prow (https://github.com/ansible-collections/community.okd/pull/15).
+ - Docker container can run as an arbitrary user (https://github.com/ansible-collections/community.okd/pull/12).
+ - Dockerfile now is properly set up to run tests in a rootless container (https://github.com/ansible-collections/community.okd/pull/11).
+ - Integrate stale bot for issue queue maintenance (https://github.com/ansible-collections/community.okd/pull/14).
+ fragments:
+ - 1-initial-content.yml
+ - 11-dockerfile-tests.yml
+ - 12-dockerfile-tests.yml
+ - 13-makefile-tests.yml
+ - 15-ci-documentation.yml
+ - 16-inventory-plugin-tests.yml
+ - 18-openshift-connection-plugin.yml
+ - 20-downstream-build-scripts.yml
+ - 7-molecule-tests.yml
+ - 8-stale-bot.yml
+ release_date: '2020-09-04'
+ 0.2.0:
+ changes:
+ major_changes:
+ - openshift_auth - new module (migrated from k8s_auth in community.kubernetes)
+ (https://github.com/ansible-collections/community.okd/pull/33).
+ minor_changes:
+ - Add a contribution guide (https://github.com/ansible-collections/community.okd/pull/37).
+ - Use the API Group APIVersion for the `Route` object (https://github.com/ansible-collections/community.okd/pull/27).
+ fragments:
+ - 27-route-api-group.yml
+ - 33-add-k8s_auth.yml
+ - 36-contribution-guide.yml
+ modules:
+ - description: Authenticate to OpenShift clusters which require an explicit login
+ step
+ name: openshift_auth
+ namespace: ''
+ release_date: '2020-09-24'
+ 0.3.0:
+ changes:
+ major_changes:
+ - Add openshift_process module for template rendering and optional application
+ of rendered resources (https://github.com/ansible-collections/community.okd/pull/44).
+ - Add openshift_route module for creating routes from services (https://github.com/ansible-collections/community.okd/pull/40).
+ fragments:
+ - 40-openshift_route.yml
+ - 44-openshift_process.yml
+ modules:
+ - description: Process an OpenShift template.openshift.io/v1 Template
+ name: openshift_process
+ namespace: ''
+ - description: Expose a Service as an OpenShift Route.
+ name: openshift_route
+ namespace: ''
+ release_date: '2020-10-12'
+ 1.0.0:
+ changes:
+ minor_changes:
+ - Released version 1 to Automation Hub as redhat.openshift (https://github.com/ansible-collections/community.okd/issues/51).
+ fragments:
+ - 51-redhat-openshift-ah-release.yml
+ release_date: '2020-11-12'
+ 1.0.1:
+ changes:
+ bugfixes:
+ - Generate downstream redhat.openshift documentation (https://github.com/ansible-collections/community.okd/pull/59).
+ fragments:
+ - 59-downstream-docs.yml
+ release_date: '2020-11-17'
+ 1.0.2:
+ changes:
+ minor_changes:
+ - restrict the version of kubernetes.core dependency (https://github.com/ansible-collections/community.okd/pull/66).
+ fragments:
+ - 66-restrict-kubernetes-core-version.yaml
+ release_date: '2021-02-19'
+ 1.1.0:
+ changes:
+ minor_changes:
+ - increase the kubernetes.core dependency version number (https://github.com/ansible-collections/community.okd/pull/71).
+ fragments:
+ - 71-bump-kubernetes-core-version.yaml
+ release_date: '2021-02-23'
+ 1.1.1:
+ changes:
+ bugfixes:
+ - add missing requirements.txt file needed for execution environments (https://github.com/ansible-collections/community.okd/pull/78).
+ - openshift_route - default to ``no_log=False`` for the ``key`` parameter in
+ TLS configuration to fix sanity failures (https://github.com/ansible-collections/community.okd/pull/77).
+ - restrict molecule version to <3.3.0 to address breaking change (https://github.com/ansible-collections/community.okd/pull/77).
+ - update CI to work with ansible 2.11 (https://github.com/ansible-collections/community.okd/pull/80).
+ fragments:
+ - 77-fix-ci-failure.yaml
+ - 78-add-requirements-file.yaml
+ - 80-update-ci.yaml
+ release_date: '2021-04-06'
+ 1.1.2:
+ changes:
+ bugfixes:
+ - include requirements.txt in downstream build process (https://github.com/ansible-collections/community.okd/pull/81).
+ fragments:
+ - 81-include-requirements.yaml
+ release_date: '2021-04-08'
+ 2.0.0:
+ changes:
+ breaking_changes:
+ - drop python 2 support (https://github.com/openshift/community.okd/pull/93).
+ bugfixes:
+ - fixes test suite to use correct versions of python and dependencies (https://github.com/ansible-collections/community.okd/pull/89).
+ - openshift_process - fix module execution when template does not include a
+ message (https://github.com/ansible-collections/community.okd/pull/87).
+ major_changes:
+ - update to use kubernetes.core 2.0 (https://github.com/openshift/community.okd/pull/93).
+ minor_changes:
+ - Added documentation for the ``community.okd`` collection.
+ - openshift - inventory plugin supports FQCN ``redhat.openshift``.
+ fragments:
+ - 87-openshift_process-fix-template-without-message.yaml
+ - 89-clean-up-ci.yaml
+ - 93-update-to-k8s-2.yaml
+ - add_docs.yml
+ - fqcn_inventory.yml
+ release_date: '2021-06-22'
+ 2.0.1:
+ changes:
+ minor_changes:
+ - increase kubernetes.core dependency version (https://github.com/openshift/community.okd/pull/97).
+ fragments:
+ - 97-bump-k8s-version.yaml
+ release_date: '2021-06-24'
+ 2.1.0:
+ changes:
+ bugfixes:
+ - fix broken links in Automation Hub for redhat.openshift (https://github.com/openshift/community.okd/issues/100).
+ minor_changes:
+ - add support for turbo mode (https://github.com/openshift/community.okd/pull/102).
+ - openshift_route - Add support for Route annotations (https://github.com/ansible-collections/community.okd/pull/99).
+ fragments:
+ - 0-copy_ignore_txt.yml
+ - 100-fix-broken-links.yml
+ - 102-support-turbo-mode.yaml
+ - 99-openshift_route-add-support-for-annotations.yml
+ release_date: '2021-10-20'
+ 2.2.0:
+ changes:
+ bugfixes:
+ - fix ocp auth failing against cluster api url with trailing slash (https://github.com/openshift/community.okd/issues/139)
+ minor_changes:
+ - add action groups to runtime.yml (https://github.com/openshift/community.okd/issues/41).
+ fragments:
+ - 152-add-action-groups.yml
+ - auth-against-api-with-trailing-slash.yaml
+ modules:
+ - description: Update TemplateInstances to point to the latest group-version-kinds
+ name: openshift_adm_migrate_template_instances
+ namespace: ''
+ - description: Removes references to the specified roles, clusterroles, users,
+ and groups
+ name: openshift_adm_prune_auth
+ namespace: ''
+ - description: Remove old completed and failed deployment configs
+ name: openshift_adm_prune_deployments
+ namespace: ''
+ - description: Remove unreferenced images
+ name: openshift_adm_prune_images
+ namespace: ''
+ - description: Import the latest image information from a tag in a container image
+ registry.
+ name: openshift_import_image
+ namespace: ''
+ - description: Display information about the integrated registry.
+ name: openshift_registry_info
+ namespace: ''
+ release_date: '2022-05-05'
+ 2.3.0:
+ changes:
+ bugfixes:
+ - openshift_adm_groups_sync - initialize OpenshiftGroupSync attributes early
+ to avoid Attribute error (https://github.com/openshift/community.okd/issues/155).
+ - openshift_auth - Review the way the discard process is working, add openshift
+ algorithm to convert token to resource object name (https://github.com/openshift/community.okd/issues/176).
+ fragments:
+ - 165-initialize-attributes-early.yml
+ - 178-openshift_auth-fix-revoke-token.yml
+ - 180-default-values-doc.yml
+ modules:
+ - description: Prune old completed and failed builds
+ name: openshift_adm_prune_builds
+ namespace: ''
+ - description: Start a new build or Cancel running, pending, or new builds.
+ name: openshift_build
+ namespace: ''
+ release_date: '2023-02-03'
diff --git a/ansible_collections/community/okd/changelogs/config.yaml b/ansible_collections/community/okd/changelogs/config.yaml
new file mode 100644
index 000000000..1a31c10ed
--- /dev/null
+++ b/ansible_collections/community/okd/changelogs/config.yaml
@@ -0,0 +1,30 @@
+---
+changelog_filename_template: ../CHANGELOG.rst
+changelog_filename_version_depth: 0
+changes_file: changelog.yaml
+changes_format: combined
+keep_fragments: false
+mention_ancestor: true
+new_plugins_after_name: removed_features
+notesdir: fragments
+prelude_section_name: release_summary
+prelude_section_title: Release Summary
+sections:
+- - major_changes
+ - Major Changes
+- - minor_changes
+ - Minor Changes
+- - breaking_changes
+ - Breaking Changes / Porting Guide
+- - deprecated_features
+ - Deprecated Features
+- - removed_features
+ - Removed Features (previously deprecated)
+- - security_fixes
+ - Security Fixes
+- - bugfixes
+ - Bugfixes
+- - known_issues
+ - Known Issues
+title: OKD Collection
+trivial_section_name: trivial
diff --git a/ansible_collections/community/okd/ci/Dockerfile b/ansible_collections/community/okd/ci/Dockerfile
new file mode 100644
index 000000000..1a509190d
--- /dev/null
+++ b/ansible_collections/community/okd/ci/Dockerfile
@@ -0,0 +1,43 @@
+FROM registry.access.redhat.com/ubi8/ubi
+
+ENV OPERATOR=/usr/local/bin/ansible-operator \
+ USER_UID=1001 \
+ USER_NAME=ansible-operator\
+ HOME=/opt/ansible \
+ ANSIBLE_LOCAL_TMP=/opt/ansible/tmp \
+ DOWNSTREAM_BUILD_PYTHON=python3.9
+
+RUN yum install -y \
+ glibc-langpack-en \
+ git \
+ make \
+ python39 \
+ python39-devel \
+ python39-pip \
+ python39-setuptools \
+ gcc \
+ openldap-devel \
+ && pip3 install --no-cache-dir --upgrade setuptools pip \
+ && pip3 install --no-cache-dir \
+ kubernetes \
+ ansible==2.9.* \
+ "molecule<3.3.0" \
+ && yum clean all \
+ && rm -rf $HOME/.cache \
+ && curl -L https://github.com/openshift/okd/releases/download/4.5.0-0.okd-2020-08-12-020541/openshift-client-linux-4.5.0-0.okd-2020-08-12-020541.tar.gz | tar -xz -C /usr/local/bin
+ # TODO: Is there a better way to install this client in ubi8?
+
+COPY . /opt/ansible
+
+WORKDIR /opt/ansible
+
+RUN echo "${USER_NAME}:x:${USER_UID}:0:${USER_NAME} user:${HOME}:/sbin/nologin" >> /etc/passwd \
+ && mkdir -p "${HOME}/.ansible/tmp" \
+ && chown -R "${USER_UID}:0" "${HOME}" \
+ && chmod -R ug+rwX "${HOME}" \
+ && mkdir /go \
+ && chown -R "${USER_UID}:0" /go \
+ && chmod -R ug+rwX /go
+
+
+USER ${USER_UID}
diff --git a/ansible_collections/community/okd/ci/doc_fragment_modules.py b/ansible_collections/community/okd/ci/doc_fragment_modules.py
new file mode 100755
index 000000000..b92abe5bf
--- /dev/null
+++ b/ansible_collections/community/okd/ci/doc_fragment_modules.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import ast
+from pathlib import PosixPath
+import yaml
+import argparse
+import os
+
+
+def read_docstring(filename):
+
+ """
+ Search for assignment of the DOCUMENTATION and EXAMPLES variables in the given file.
+ Parse DOCUMENTATION from YAML and return the YAML doc or None together with EXAMPLES, as plain text.
+ """
+
+ data = {
+ 'doc': None,
+ 'plainexamples': None,
+ 'returndocs': None,
+ 'metadata': None, # NOTE: not used anymore, kept for compat
+ 'seealso': None,
+ }
+
+ string_to_vars = {
+ 'DOCUMENTATION': 'doc',
+ 'EXAMPLES': 'plainexamples',
+ 'RETURN': 'returndocs',
+ 'ANSIBLE_METADATA': 'metadata', # NOTE: now unused, but kept for backwards compat
+ }
+
+ try:
+ with open(filename, 'rb') as b_module_data:
+ M = ast.parse(b_module_data.read())
+
+ for child in M.body:
+ if isinstance(child, ast.Assign):
+ for t in child.targets:
+ try:
+ theid = t.id
+ except AttributeError:
+ # skip errors can happen when trying to use the normal code
+ # sys.stderr.write("Failed to assign id for %s on %s, skipping\n" % (t, filename))
+ continue
+
+ if theid in string_to_vars:
+ varkey = string_to_vars[theid]
+ if isinstance(child.value, ast.Dict):
+ data[varkey] = ast.literal_eval(child.value)
+ else:
+ if theid != 'EXAMPLES':
+ # string should be yaml if already not a dict
+ data[varkey] = child.value.s
+
+ # sys.stderr.write('assigned: %s\n' % varkey)
+
+ except Exception:
+ # sys.stderr.write("unable to parse %s" % filename)
+ return
+
+ return yaml.safe_load(data["doc"]) if data["doc"] is not None else None
+
+
+def is_extending_collection(result, col_fqcn):
+ if result:
+ for x in result.get("extends_documentation_fragment", []):
+ if x.startswith(col_fqcn):
+ return True
+ return False
+
+
+def main():
+
+ parser = argparse.ArgumentParser(
+ description="list modules with inherited doc fragments from kubernetes.core that need rendering to deal with Galaxy/AH lack of functionality."
+ )
+ parser.add_argument(
+ "-c", "--collection-path", type=str, default=os.getcwd(), help="path to the collection"
+ )
+
+ args = parser.parse_args()
+
+ path = PosixPath(args.collection_path) / PosixPath("plugins/modules")
+ output = []
+ for d in path.iterdir():
+ if d.is_file():
+ result = read_docstring(str(d))
+ if is_extending_collection(result, "kubernetes.core."):
+ output.append(d.stem.replace(".py", ""))
+ print("\n".join(output))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/ci/downstream.sh b/ansible_collections/community/okd/ci/downstream.sh
new file mode 100755
index 000000000..001959c7e
--- /dev/null
+++ b/ansible_collections/community/okd/ci/downstream.sh
@@ -0,0 +1,298 @@
+#!/bin/bash -eu
+
+# Script to dual-home the upstream and downstream Collection in a single repo
+#
+# This script will build or test a downstream collection, removing any
+# upstream components that will not ship in the downstream release
+#
+# NOTES:
+# - All functions are prefixed with f_ so it's obvious where they come
+# from when in use throughout the script
+
+DOWNSTREAM_VERSION="2.3.0"
+KEEP_DOWNSTREAM_TMPDIR="${KEEP_DOWNSTREAM_TMPDIR:-''}"
+INSTALL_DOWNSTREAM_COLLECTION_PATH="${INSTALL_DOWNSTREAM_COLLECTION_PATH:-}"
+_build_dir=""
+
+f_log_info()
+{
+ printf "%s:LOG:INFO: %s\n" "${0}" "${1}"
+}
+
+f_show_help()
+{
+ printf "Usage: downstream.sh [OPTION]\n"
+ printf "\t-s\t\tCreate a temporary downstream release and perform sanity tests.\n"
+ printf "\t-u\t\tCreate a temporary downstream release and perform units tests.\n"
+ printf "\t-i\t\tCreate a temporary downstream release and perform integration tests.\n"
+ printf "\t-m\t\tCreate a temporary downstream release and perform molecule tests.\n"
+ printf "\t-b\t\tCreate a downstream release and stage for release.\n"
+ printf "\t-r\t\tCreate a downstream release and publish release.\n"
+}
+
+f_text_sub()
+{
+ # Switch FQCN and dependent components
+ OKD_sed_files="${_build_dir}/README.md ${_build_dir}/CHANGELOG.rst ${_build_dir}/changelogs/config.yaml ${_build_dir}/ci/downstream.sh ${_build_dir}/galaxy.yml"
+ # shellcheck disable=SC2068
+ for okd_file in ${OKD_sed_files[@]}; do sed -i.bak "s/OKD/OpenShift/g" "${okd_file}"; done
+
+ sed -i.bak "s/============================/==================================/" "${_build_dir}/CHANGELOG.rst"
+ sed -i.bak "s/Ansible Galaxy/Automation Hub/" "${_build_dir}/README.md"
+ sed -i.bak "s/community-okd/redhat-openshift/" "${_build_dir}/Makefile"
+ sed -i.bak "s/community\/okd/redhat\/openshift/" "${_build_dir}/Makefile"
+ sed -i.bak "s/^VERSION\:/VERSION: ${DOWNSTREAM_VERSION}/" "${_build_dir}/Makefile"
+ sed -i.bak "s/name\:.*$/name: openshift/" "${_build_dir}/galaxy.yml"
+ sed -i.bak "s/namespace\:.*$/namespace: redhat/" "${_build_dir}/galaxy.yml"
+ sed -i.bak "s/Kubernetes/OpenShift/g" "${_build_dir}/galaxy.yml"
+ sed -i.bak "s/^version\:.*$/version: ${DOWNSTREAM_VERSION}/" "${_build_dir}/galaxy.yml"
+ sed -i.bak "/STARTREMOVE/,/ENDREMOVE/d" "${_build_dir}/README.md"
+ sed -i.bak "s/[[:space:]]okd:$/ openshift:/" ${_build_dir}/meta/runtime.yml
+
+ find "${_build_dir}" -type f ! -name galaxy.yml -exec sed -i.bak "s/community\.okd/redhat\.openshift/g" {} \;
+ find "${_build_dir}" -type f -name "*.bak" -delete
+}
+
+f_prep()
+{
+ f_log_info "${FUNCNAME[0]}"
+ # Array of excluded files from downstream build (relative path)
+ _file_exclude=(
+ )
+
+ # Files to copy downstream (relative repo root dir path)
+ _file_manifest=(
+ CHANGELOG.rst
+ galaxy.yml
+ LICENSE
+ README.md
+ Makefile
+ setup.cfg
+ .yamllint
+ requirements.txt
+ requirements.yml
+ test-requirements.txt
+ )
+
+ # Directories to recursively copy downstream (relative repo root dir path)
+ _dir_manifest=(
+ changelogs
+ ci
+ meta
+ molecule
+ plugins
+ tests
+ )
+
+ # Temp build dir
+ _tmp_dir=$(mktemp -d)
+ _start_dir="${PWD}"
+ _build_dir="${_tmp_dir}/ansible_collections/redhat/openshift"
+ mkdir -p "${_build_dir}"
+}
+
+
+f_cleanup()
+{
+ f_log_info "${FUNCNAME[0]}"
+ if [[ -n "${_build_dir}" ]]; then
+ if [[ -n ${KEEP_DOWNSTREAM_TMPDIR} ]]; then
+ if [[ -d ${_build_dir} ]]; then
+ rm -fr "${_build_dir}"
+ fi
+ fi
+ else
+ exit 0
+ fi
+}
+
+# Exit and handle cleanup processes if needed
+f_exit()
+{
+ f_cleanup
+ exit "$0"
+}
+
+f_create_collection_dir_structure()
+{
+ f_log_info "${FUNCNAME[0]}"
+ # Create the Collection
+ for f_name in "${_file_manifest[@]}";
+ do
+ cp "./${f_name}" "${_build_dir}/${f_name}"
+ done
+ for d_name in "${_dir_manifest[@]}";
+ do
+ cp -r "./${d_name}" "${_build_dir}/${d_name}"
+ done
+ if [ -n "${_file_exclude:-}" ]; then
+ for exclude_file in "${_file_exclude[@]}";
+ do
+ if [[ -f "${_build_dir}/${exclude_file}" ]]; then
+ rm -f "${_build_dir}/${exclude_file}"
+ fi
+ done
+ fi
+}
+
+f_handle_doc_fragments_workaround()
+{
+ f_log_info "${FUNCNAME[0]}"
+ local install_collections_dir="${_build_dir}/collections/"
+ local temp_fragments_json="${_tmp_dir}/fragments.json"
+ local temp_start="${_tmp_dir}/startfile.txt"
+ local temp_end="${_tmp_dir}/endfile.txt"
+ local rendered_fragments="./rendereddocfragments.txt"
+
+ # FIXME: Check Python interpreter from environment variable to work with prow
+ PYTHON=${DOWNSTREAM_BUILD_PYTHON:-/usr/bin/python3.6}
+ f_log_info "Using Python interpreter: ${PYTHON}"
+
+ # Modules with inherited doc fragments from kubernetes.core that need
+ # rendering to deal with Galaxy/AH lack of functionality.
+ # shellcheck disable=SC2207
+ _doc_fragment_modules=($("${PYTHON}" "${_start_dir}/ci/doc_fragment_modules.py" -c "${_start_dir}"))
+
+ # Build the collection, export docs, render them, stitch it all back together
+ pushd "${_build_dir}" || return
+ ansible-galaxy collection build
+ ansible-galaxy collection install -p "${install_collections_dir}" ./*.tar.gz
+ rm ./*.tar.gz
+ for doc_fragment_mod in "${_doc_fragment_modules[@]}"
+ do
+ local module_py="plugins/modules/${doc_fragment_mod}.py"
+ f_log_info "Processing doc fragments for ${module_py}"
+ # We need following variable for ansible-doc only
+ # shellcheck disable=SC2097,SC2098
+ ANSIBLE_COLLECTIONS_PATH="${install_collections_dir}" \
+ ANSIBLE_COLLECTIONS_PATHS="${ANSIBLE_COLLECTIONS_PATH}:${install_collections_dir}" \
+ ansible-doc -j "redhat.openshift.${doc_fragment_mod}" > "${temp_fragments_json}"
+ "${PYTHON}" "${_start_dir}/ci/downstream_fragments.py" "redhat.openshift.${doc_fragment_mod}" "${temp_fragments_json}"
+ sed -n '/STARTREMOVE/q;p' "${module_py}" > "${temp_start}"
+ sed '1,/ENDREMOVE/d' "${module_py}" > "${temp_end}"
+ cat "${temp_start}" "${rendered_fragments}" "${temp_end}" > "${module_py}"
+ done
+ rm -f "${rendered_fragments}"
+ rm -fr "${install_collections_dir}"
+ popd
+
+}
+
+f_copy_collection_to_working_dir()
+{
+ f_log_info "${FUNCNAME[0]}"
+ # Copy the Collection build result into original working dir
+ f_log_info "copying built collection *.tar.gz into ./"
+ cp "${_build_dir}"/*.tar.gz ./
+ # Install downstream collection into provided path
+ if [[ -n ${INSTALL_DOWNSTREAM_COLLECTION_PATH} ]]; then
+ f_log_info "Install built collection *.tar.gz into ${INSTALL_DOWNSTREAM_COLLECTION_PATH}"
+ ansible-galaxy collection install -p "${INSTALL_DOWNSTREAM_COLLECTION_PATH}" "${_build_dir}"/*.tar.gz
+ fi
+ rm -f "${_build_dir}"/*.tar.gz
+}
+
+f_common_steps()
+{
+ f_log_info "${FUNCNAME[0]}"
+ f_prep
+ f_create_collection_dir_structure
+ f_text_sub
+ f_handle_doc_fragments_workaround
+}
+
+# Run the test sanity scanerio
+f_test_sanity_option()
+{
+ f_log_info "${FUNCNAME[0]}"
+ f_common_steps
+ pushd "${_build_dir}" || return
+ if command -v docker &> /dev/null
+ then
+ make sanity
+ else
+ SANITY_TEST_ARGS="--venv --color" make sanity
+ fi
+ f_log_info "SANITY TEST PWD: ${PWD}"
+ make sanity
+ popd || return
+ f_cleanup
+}
+
+# Run the test integration
+f_test_integration_option()
+{
+ f_log_info "${FUNCNAME[0]}"
+ f_common_steps
+ pushd "${_build_dir}" || return
+ f_log_info "INTEGRATION TEST WD: ${PWD}"
+ make molecule
+ popd || return
+ f_cleanup
+}
+
+# Run the test units
+f_test_units_option()
+{
+ f_log_info "${FUNCNAME[0]}"
+ f_common_steps
+ pushd "${_build_dir}" || return
+ if command -v docker &> /dev/null
+ then
+ make units
+ else
+ UNITS_TEST_ARGS="--venv --color" make units
+ fi
+ f_log_info "UNITS TEST PWD: ${PWD}"
+ make units
+ popd || return
+ f_cleanup
+}
+
+# Run the build scanerio
+f_build_option()
+{
+ f_log_info "${FUNCNAME[0]}"
+ f_common_steps
+ pushd "${_build_dir}" || return
+ f_log_info "BUILD WD: ${PWD}"
+ make build
+ popd || return
+ f_copy_collection_to_working_dir
+ f_cleanup
+}
+
+# If no options are passed, display usage and exit
+if [[ "${#}" -eq "0" ]]; then
+ f_show_help
+ f_exit 0
+fi
+
+# Handle options
+while getopts ":siurb" option
+do
+ case $option in
+ s)
+ f_test_sanity_option
+ ;;
+ i)
+ f_test_integration_option
+ ;;
+ u)
+ f_test_units_option
+ ;;
+ r)
+ f_release_option
+ ;;
+ b)
+ f_build_option
+ ;;
+ *)
+ printf "ERROR: Unimplemented option chosen.\n"
+ f_show_help
+ f_exit 1
+ ;; # Default.
+ esac
+done
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/ansible_collections/community/okd/ci/downstream_fragments.py b/ansible_collections/community/okd/ci/downstream_fragments.py
new file mode 100755
index 000000000..727be61e8
--- /dev/null
+++ b/ansible_collections/community/okd/ci/downstream_fragments.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+import sys
+import yaml
+
+with open("./rendereddocfragments.txt", 'w') as df_fd:
+ with open(sys.argv[2], 'r') as fd:
+ json_docs = json.load(fd)
+
+ json_docs[sys.argv[1]]['doc'].pop('collection', '')
+ json_docs[sys.argv[1]]['doc'].pop('filename', '')
+ json_docs[sys.argv[1]]['doc'].pop('has_action', '')
+
+ df_fd.write('DOCUMENTATION = """\n')
+ df_fd.write(yaml.dump(json_docs[sys.argv[1]]['doc'], default_flow_style=False))
+ df_fd.write('"""\n\n')
+
+ df_fd.write('EXAMPLES = """')
+ df_fd.write(json_docs[sys.argv[1]]['examples'])
+ df_fd.write('"""\n\n')
+
+ df_fd.write('RETURN = r"""')
+ data = json_docs[sys.argv[1]]['return']
+ if isinstance(data, dict):
+ df_fd.write(yaml.dump(data, default_flow_style=False))
+ else:
+ df_fd.write(data)
+ df_fd.write('"""\n\n')
diff --git a/ansible_collections/community/okd/ci/incluster_integration.sh b/ansible_collections/community/okd/ci/incluster_integration.sh
new file mode 100755
index 000000000..3d2a55da6
--- /dev/null
+++ b/ansible_collections/community/okd/ci/incluster_integration.sh
@@ -0,0 +1,92 @@
+#!/bin/bash -eu
+
+set -x
+
+NAMESPACE=${NAMESPACE:-default}
+
+# IMAGE_FORMAT is in the form $registry/$org/$image:$$component, ie
+# quay.io/openshift/release:$component
+# To test with your own image, build and push the test image
+# (using the Dockerfile in ci/Dockerfile)
+# and set the IMAGE_FORMAT environment variable so that it properly
+# resolves to your image. For example, quay.io/mynamespace/$component
+# would resolve to quay.io/mynamespace/molecule-test-runner
+# shellcheck disable=SC2034
+component='molecule-test-runner'
+if [[ -n "${MOLECULE_IMAGE}" ]]; then
+ IMAGE="${MOLECULE_IMAGE}"
+else
+ IMAGE="${IMAGE_FORMAT}"
+fi
+
+PULL_POLICY=${PULL_POLICY:-IfNotPresent}
+
+if ! oc get namespace "$NAMESPACE"
+then
+ oc create namespace "$NAMESPACE"
+fi
+
+oc project "$NAMESPACE"
+oc adm policy add-cluster-role-to-user cluster-admin -z default
+oc adm policy who-can create projectrequests
+
+echo "Deleting test job if it exists"
+oc delete job molecule-integration-test --wait --ignore-not-found
+
+echo "Creating molecule test job"
+cat << EOF | oc create -f -
+---
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: molecule-integration-test
+spec:
+ template:
+ spec:
+ containers:
+ - name: test-runner
+ image: ${IMAGE}
+ imagePullPolicy: ${PULL_POLICY}
+ command:
+ - make
+ - test-integration
+ restartPolicy: Never
+ backoffLimit: 2
+ completions: 1
+ parallelism: 1
+EOF
+
+function check_success {
+ oc wait --for=condition=complete job/molecule-integration-test --timeout 5s -n "$NAMESPACE" \
+ && oc logs job/molecule-integration-test \
+ && echo "Molecule integration tests ran successfully" \
+ && return 0
+ return 1
+}
+
+function check_failure {
+ oc wait --for=condition=failed job/molecule-integration-test --timeout 5s -n "$NAMESPACE" \
+ && oc logs job/molecule-integration-test \
+ && echo "Molecule integration tests failed, see logs for more information..." \
+ && return 0
+ return 1
+}
+
+runtime="30 minute"
+endtime=$(date -ud "$runtime" +%s)
+
+echo "Waiting for test job to complete"
+while [[ $(date -u +%s) -le $endtime ]]
+do
+ if check_success
+ then
+ exit 0
+ elif check_failure
+ then
+ exit 1
+ fi
+ sleep 10
+done
+
+oc logs job/molecule-integration-test
+exit 1
diff --git a/ansible_collections/community/okd/codecov.yml b/ansible_collections/community/okd/codecov.yml
new file mode 100644
index 000000000..71e957c6a
--- /dev/null
+++ b/ansible_collections/community/okd/codecov.yml
@@ -0,0 +1,8 @@
+---
+coverage:
+ precision: 2
+ round: down
+ range: "70...100"
+ status:
+ project:
+ default: false
diff --git a/ansible_collections/community/okd/docs/ansible_turbo_mode.rst b/ansible_collections/community/okd/docs/ansible_turbo_mode.rst
new file mode 100644
index 000000000..d63311f6b
--- /dev/null
+++ b/ansible_collections/community/okd/docs/ansible_turbo_mode.rst
@@ -0,0 +1,147 @@
+.. _ansible_turbo_mode:
+
+
+******************
+Ansible Turbo mode
+******************
+
+Following document provides overview of Ansible Turbo mode in ``community.okd`` collection.
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- A brief introduction about Ansible Turbo mode in ``community.okd`` collection.
+- Ansible Turbo mode is an optional performance optimization. It can be enabled by installing the cloud.common collection and setting the ``ENABLE_TURBO_MODE`` environment variable.
+
+Requirements
+------------
+
+The following requirement is needed on the host that executes this module.
+
+- The ``cloud.common`` collection (https://github.com/ansible-collections/cloud.common)
+
+You will also need to set the environment variable ``ENABLE_TURBO_MODE=1`` on the managed host. This can be done in the same ways you would usually do so, for example::
+
+ ---
+ - hosts: remote
+ environment:
+ ENABLE_TURBO_MODE: 1
+ tasks:
+ ...
+
+
+Installation
+------------
+
+You can install ``cloud.common`` collection using following command::
+
+ # ansible-galaxy collection install cloud.common
+
+
+Current situation without Ansible Turbo mode
+============================================
+
+The traditional execution flow of an Ansible module includes the following steps:
+
+- Upload of a ZIP archive with the module and its dependencies
+- Execution of the module
+- Ansible collects the results once the script is finished
+
+These steps happen for each task of a playbook, and on every host.
+
+Most of the time, the execution of a module is fast enough for
+the user. However, sometime the module requires significant amount of time,
+just to initialize itself. This is a common situation with the API based modules.
+
+A classic initialization involves the following steps:
+
+- Load a Python library to access the remote resource (via SDK)
+- Open a client
+ - Load a bunch of Python modules.
+ - Request a new TCP connection.
+ - Create a session.
+ - Authenticate the client.
+
+All these steps are time consuming and the same operations will be running again and again.
+
+For instance, here:
+
+- ``import openstack``: takes 0.569s
+- ``client = openstack.connect()``: takes 0.065s
+- ``client.authorize()``: takes 1.360s,
+
+These numbers are from test running against VexxHost public cloud.
+
+In this case, it's a 2s-ish overhead per task. If the playbook
+comes with 10 tasks, the execution time cannot go below 20s.
+
+How Ansible Turbo Module improve the situation
+==============================================
+
+``AnsibleTurboModule`` is actually a class that inherites from
+the standard ``AnsibleModule`` class that your modules probably
+already use.
+The big difference is that when a module starts, it also spawns
+a little Python daemon. If a daemon already exists, it will just
+reuse it.
+All the module logic is run inside this Python daemon. This means:
+
+- Python modules are actually loaded one time
+- Ansible module can reuse an existing authenticated session.
+
+The background service
+======================
+
+The daemon kills itself after 15s, and communication are done
+through an Unix socket.
+It runs in one single process and uses ``asyncio`` internally.
+Consequently you can use the ``async`` keyword in your Ansible module.
+This will be handy if you interact with a lot of remote systems
+at the same time.
+
+Security impact
+===============
+
+``ansible_module.turbo`` open an Unix socket to interact with the background service.
+We use this service to open the connection toward the different target systems.
+
+This is similar to what SSH does with the sockets.
+
+Keep in mind that:
+
+- All the modules can access the same cache. Soon an isolation will be done at the collection level (https://github.com/ansible-collections/cloud.common/pull/17)
+- A task can load a different version of a library and impact the next tasks.
+- If the same user runs two ``ansible-playbook`` at the same time, they will have access to the same cache.
+
+When a module stores a session in a cache, it's a good idea to use a hash of the authentication information to identify the session.
+
+Error management
+================
+
+``ansible_module.turbo`` uses exceptions to communicate a result back to the module.
+
+- ``EmbeddedModuleFailure`` is raised when ``json_fail()`` is called.
+- ``EmbeddedModuleSuccess`` is raised in case of success and returns the result to the origin module process.
+
+These exceptions are defined in ``ansible_collections.cloud.common.plugins.module_utils.turbo.exceptions``.
+You can raise ``EmbeddedModuleFailure`` exception yourself, for instance from a module in ``module_utils``.
+
+.. note:: Be careful with the ``except Exception:`` blocks.
+ Not only they are bad practice, but also may interface with this
+ mechanism.
+
+
+Troubleshooting
+===============
+
+You may want to manually start the server. This can be done with the following command:
+
+.. code-block:: shell
+
+ PYTHONPATH=$HOME/.ansible/collections python -m ansible_collections.cloud.common.plugins.module_utils.turbo.server --socket-path $HOME/.ansible/tmp/turbo_mode.kubernetes.core.socket
+
+You can use the ``--help`` argument to get a list of the optional parameters.
diff --git a/ansible_collections/community/okd/docs/community.okd.k8s_module.rst b/ansible_collections/community/okd/docs/community.okd.k8s_module.rst
new file mode 100644
index 000000000..8d0e0f9dc
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.k8s_module.rst
@@ -0,0 +1,1160 @@
+.. _community.okd.k8s_module:
+
+
+*****************
+community.okd.k8s
+*****************
+
+**Manage OpenShift objects**
+
+
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- 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 :ref:`kubernetes.core.k8s_info <kubernetes.core.k8s_info_module>` module to obtain a list of items about an object of type ``kind``.
+- Authenticate using either a config file, certificates, password or token.
+- Supports check mode.
+- Optimized for OKD/OpenShift Kubernetes flavors.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+- PyYAML >= 3.11
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="3">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_version</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">"v1"</div>
+ </td>
+ <td>
+ <div>Use to specify the API version.</div>
+ <div>Use to create, delete, or discover an object without providing a full resource definition.</div>
+ <div>Use in conjunction with <em>kind</em>, <em>name</em>, and <em>namespace</em> to identify a specific object.</div>
+ <div>If <em>resource definition</em> is provided, the <em>apiVersion</em> value from the <em>resource_definition</em> will override this option.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: api, version</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>append_hash</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether to append a hash to a resource name for immutability purposes</div>
+ <div>Applies only to ConfigMap and Secret resources</div>
+ <div>The parameter will be silently ignored for other resource kinds</div>
+ <div>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)</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>apply</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div><code>apply</code> compares the desired resource definition with the previously supplied resource definition, ignoring properties that are automatically generated</div>
+ <div><code>apply</code> works better with Services than &#x27;force=yes&#x27;</div>
+ <div>mutually exclusive with <code>merge_type</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>continue_on_error</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether to continue on creation/deletion errors when multiple resources are defined.</div>
+ <div>This has no effect on the validation step which is controlled by the <code>validate.fail_on_error</code> parameter.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>delete_options</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.2.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Configure behavior when deleting an object.</div>
+ <div>Only used when <em>state=absent</em>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>gracePeriodSeconds</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify how many seconds to wait before forcefully terminating.</div>
+ <div>Only implemented for Pod resources.</div>
+ <div>If not specified, the default grace period for the object type will be used.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>preconditions</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify condition that must be met for delete to proceed.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>resourceVersion</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the resource version of the target object.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>uid</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the UID of the target object.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>propagationPolicy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>Foreground</li>
+ <li>Background</li>
+ <li>Orphan</li>
+ </ul>
+ </td>
+ <td>
+ <div>Use to control how dependent objects are deleted.</div>
+ <div>If not specified, the default policy for the object type will be used. This may vary across object types.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>force</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>If set to <code>yes</code>, and <em>state</em> is <code>present</code>, an existing object will be replaced.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify an object model.</div>
+ <div>Use to create, delete, or discover an object without providing a full resource definition.</div>
+ <div>Use in conjunction with <em>api_version</em>, <em>name</em>, and <em>namespace</em> to identify a specific object.</div>
+ <div>If <em>resource definition</em> is provided, the <em>kind</em> value from the <em>resource_definition</em> will override this option.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>merge_type</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>json</li>
+ <li>merge</li>
+ <li>strategic-merge</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether to override the default patch merge approach with a specific type. By default, the strategic merge will typically be used.</div>
+ <div>For example, Custom Resource Definitions typically aren&#x27;t updatable by the usual strategic merge. You may want to use <code>merge</code> if you see &quot;strategic merge patch format is not supported&quot;</div>
+ <div>See <a href='https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment'>https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment</a></div>
+ <div>If more than one merge_type is given, the merge_types will be tried in order</div>
+ <div>Defaults to <code>[&#x27;strategic-merge&#x27;, &#x27;merge&#x27;]</code>, which is ideal for using the same parameters on resource kinds that combine Custom Resources and built-in resources.</div>
+ <div>mutually exclusive with <code>apply</code></div>
+ <div><em>merge_type=json</em> is deprecated and will be removed in version 3.0.0. Please use <span class='module'>kubernetes.core.k8s_json_patch</span> instead.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify an object name.</div>
+ <div>Use to create, delete, or discover an object without providing a full resource definition.</div>
+ <div>Use in conjunction with <em>api_version</em>, <em>kind</em> and <em>namespace</em> to identify a specific object.</div>
+ <div>If <em>resource definition</em> is provided, the <em>metadata.name</em> value from the <em>resource_definition</em> will override this option.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify an object namespace.</div>
+ <div>Useful when creating, deleting, or discovering an object without providing a full resource definition.</div>
+ <div>Use in conjunction with <em>api_version</em>, <em>kind</em>, and <em>name</em> to identify a specific object.</div>
+ <div>If <em>resource definition</em> is provided, the <em>metadata.namespace</em> value from the <em>resource_definition</em> will override this option.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>resource_definition</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a valid YAML definition (either as a string, list, or dict) for an object when creating or updating.</div>
+ <div>NOTE: <em>kind</em>, <em>api_version</em>, <em>name</em>, and <em>namespace</em> will be overwritten by corresponding values found in the provided <em>resource_definition</em>.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: definition, inline</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>src</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a path to a file containing a valid YAML definition of an object or objects to be created or updated. Mutually exclusive with <em>resource_definition</em>. NOTE: <em>kind</em>, <em>api_version</em>, <em>name</em>, and <em>namespace</em> will be overwritten by corresponding values found in the configuration read in from the <em>src</em> file.</div>
+ <div>Reads from the local file system. To read from the Ansible controller&#x27;s file system, including vaulted files, use the file lookup plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to <em>resource_definition</em>. See Examples below.</div>
+ <div>Mutually exclusive with <em>template</em> in case of <span class='module'>kubernetes.core.k8s</span> module.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>state</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>absent</li>
+ <li><div style="color: blue"><b>present</b>&nbsp;&larr;</div></li>
+ <li>patched</li>
+ </ul>
+ </td>
+ <td>
+ <div>Determines if an object should be created, patched, or deleted. When set to <code>present</code>, an object will be created, if it does not already exist. If set to <code>absent</code>, an existing object will be deleted. If set to <code>present</code>, an existing object will be patched, if its attributes differ from those specified using <em>resource_definition</em> or <em>src</em>.</div>
+ <div><code>patched</code> state is an existing resource that has a given patch applied. If the resource doesn&#x27;t exist, silently skip it (do not raise an error).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>template</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a valid YAML template definition file for an object when creating or updating.</div>
+ <div>Value can be provided as string or dictionary.</div>
+ <div>Mutually exclusive with <code>src</code> and <code>resource_definition</code>.</div>
+ <div>Template files needs to be present on the Ansible Controller&#x27;s file system.</div>
+ <div>Additional parameters can be specified using dictionary.</div>
+ <div>Valid additional parameters -</div>
+ <div><code>newline_sequence</code> (str): Specify the newline sequence to use for templating files. valid choices are &quot;\n&quot;, &quot;\r&quot;, &quot;\r\n&quot;. Default value &quot;\n&quot;.</div>
+ <div><code>block_start_string</code> (str): The string marking the beginning of a block. Default value &quot;{%&quot;.</div>
+ <div><code>block_end_string</code> (str): The string marking the end of a block. Default value &quot;%}&quot;.</div>
+ <div><code>variable_start_string</code> (str): The string marking the beginning of a print statement. Default value &quot;{{&quot;.</div>
+ <div><code>variable_end_string</code> (str): The string marking the end of a print statement. Default value &quot;}}&quot;.</div>
+ <div><code>trim_blocks</code> (bool): Determine when newlines should be removed from blocks. When set to <code>yes</code> the first newline after a block is removed (block, not variable tag!). Default value is true.</div>
+ <div><code>lstrip_blocks</code> (bool): Determine when leading spaces and tabs should be stripped. When set to <code>yes</code> 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.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>how (if at all) to validate the resource definition against the kubernetes schema. Requires the kubernetes-validate python module</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>fail_on_error</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>whether to fail on validation errors.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>strict</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li><div style="color: blue"><b>yes</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>whether to fail when passing unexpected properties</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>version</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>version of Kubernetes to validate against. defaults to Kubernetes server version</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether to wait for certain resource kinds to end up in the desired state.</div>
+ <div>By default the module exits once Kubernetes has received the request.</div>
+ <div>Implemented for <code>state=present</code> for <code>Deployment</code>, <code>DaemonSet</code> and <code>Pod</code>, and for <code>state=absent</code> for all resource kinds.</div>
+ <div>For resource kinds without an implementation, <code>wait</code> returns immediately unless <code>wait_condition</code> is set.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_condition</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specifies a custom condition on the status to wait for.</div>
+ <div>Ignored if <code>wait</code> is not set or is set to False.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>reason</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The value of the reason field in your desired condition</div>
+ <div>For example, if a <code>Deployment</code> is paused, The <code>Progressing</code> <code>type</code> will have the <code>DeploymentPaused</code> reason.</div>
+ <div>The possible reasons in a condition are specific to each resource type in Kubernetes.</div>
+ <div>See the API documentation of the status field for a given resource to see possible choices.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>True</b>&nbsp;&larr;</div></li>
+ <li>False</li>
+ <li>Unknown</li>
+ </ul>
+ </td>
+ <td>
+ <div>The value of the status field in your desired condition.</div>
+ <div>For example, if a <code>Deployment</code> is paused, the <code>Progressing</code> <code>type</code> will have the <code>Unknown</code> status.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>type</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The type of condition to wait for.</div>
+ <div>For example, the <code>Pod</code> resource will set the <code>Ready</code> condition (among others).</div>
+ <div>Required if you are specifying a <code>wait_condition</code>.</div>
+ <div>If left empty, the <code>wait_condition</code> field will be ignored.</div>
+ <div>The possible types for a condition are specific to each resource type in Kubernetes.</div>
+ <div>See the API documentation of the status field for a given resource to see possible choices.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_sleep</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">5</div>
+ </td>
+ <td>
+ <div>Number of seconds to sleep between checks.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_timeout</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">120</div>
+ </td>
+ <td>
+ <div>How long in seconds to wait for the resource to end up in the desired state.</div>
+ <div>Ignored if <code>wait</code> is not set.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ - 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>result</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The created, patched, or otherwise present object. Will be empty in the case of a deletion.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>api_version</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The versioned schema of this representation of an object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>duration</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>when <code>wait</code> is true</td>
+ <td>
+ <div>elapsed time of task in seconds</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">48</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>error</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>error</td>
+ <td>
+ <div>error while trying to create/delete the object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>items</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td>when resource_definition or src contains list of objects</td>
+ <td>
+ <div>Returned only when multiple yaml documents are passed to src or resource_definition</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Represents the REST resource this object represents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>metadata</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>spec</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Specific attributes of the object. Will vary based on the <em>api_version</em> and <em>kind</em>.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Current status details for the object.</div>
+ <br/>
+ </td>
+ </tr>
+
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Chris Houseknecht (@chouseknecht)
+- Fabian von Feilitzsch (@fabianvf)
diff --git a/ansible_collections/community/okd/docs/community.okd.oc_connection.rst b/ansible_collections/community/okd/docs/community.okd.oc_connection.rst
new file mode 100644
index 000000000..14f2477cc
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.oc_connection.rst
@@ -0,0 +1,315 @@
+.. _community.okd.oc_connection:
+
+
+****************
+community.okd.oc
+****************
+
+**Execute tasks in pods running on OpenShift.**
+
+
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- Use the oc exec command to run tasks in, or put/fetch files to, pods running on the OpenShift container platform.
+
+
+
+Requirements
+------------
+The below requirements are needed on the local Ansible controller node that executes this connection.
+
+- oc (go binary)
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th>Configuration</th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_SSL_CA_CERT</div>
+ <div>var: ansible_oc_ssl_ca_cert</div>
+ <div>var: ansible_oc_ca_cert</div>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: oc_ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_CERT_FILE</div>
+ <div>var: ansible_oc_cert_file</div>
+ <div>var: ansible_oc_client_cert</div>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: oc_cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_KEY_FILE</div>
+ <div>var: ansible_oc_key_file</div>
+ <div>var: ansible_oc_client_key</div>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: oc_key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>oc_container</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_CONTAINER</div>
+ <div>var: ansible_oc_container</div>
+ </td>
+ <td>
+ <div>Container name. Required when a pod contains more than one container.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>oc_context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_CONTEXT</div>
+ <div>var: ansible_oc_context</div>
+ </td>
+ <td>
+ <div>The name of a context found in the K8s config file.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>oc_extra_args</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_EXTRA_ARGS</div>
+ <div>var: ansible_oc_extra_args</div>
+ </td>
+ <td>
+ <div>Extra arguments to pass to the oc command line.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>oc_host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_HOST</div>
+ <div>env:K8S_AUTH_SERVER</div>
+ <div>var: ansible_oc_host</div>
+ <div>var: ansible_oc_server</div>
+ </td>
+ <td>
+ <div>URL for accessing the API.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>oc_kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_KUBECONFIG</div>
+ <div>var: ansible_oc_kubeconfig</div>
+ <div>var: ansible_oc_config</div>
+ </td>
+ <td>
+ <div>Path to a oc config file. Defaults to <em>~/.kube/config</em></div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>oc_namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_NAMESPACE</div>
+ <div>var: ansible_oc_namespace</div>
+ </td>
+ <td>
+ <div>The namespace of the pod</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>oc_pod</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_POD</div>
+ <div>var: ansible_oc_pod</div>
+ </td>
+ <td>
+ <div>Pod name. Required when the host name does not match pod name.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>oc_token</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_TOKEN</div>
+ <div>env:K8S_AUTH_API_KEY</div>
+ <div>var: ansible_oc_token</div>
+ <div>var: ansible_oc_api_key</div>
+ </td>
+ <td>
+ <div>API authentication bearer token.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">""</div>
+ </td>
+ <td>
+ <div>env:K8S_AUTH_VERIFY_SSL</div>
+ <div>var: ansible_oc_verify_ssl</div>
+ <div>var: ansible_oc_validate_certs</div>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificate. Defaults to <em>true</em>.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: oc_verify_ssl</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+
+
+
+
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- xuxinkun
+
+
+.. hint::
+ Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up.
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_adm_groups_sync_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_adm_groups_sync_module.rst
new file mode 100644
index 000000000..e16aa4d54
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_adm_groups_sync_module.rst
@@ -0,0 +1,525 @@
+.. _community.okd.openshift_adm_groups_sync_module:
+
+
+***************************************
+community.okd.openshift_adm_groups_sync
+***************************************
+
+**Sync OpenShift Groups with records from an external provider.**
+
+
+Version added: 2.1.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- 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 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.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+- python-ldap
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>allow_groups</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Allowed groups, could be openshift group name or LDAP group dn value.</div>
+ <div>When parameter <code>type</code> is set to <em>ldap</em> this should contains only LDAP group definition like <em>cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat</em>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>deny_groups</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Denied groups, could be openshift group name or LDAP group dn value.</div>
+ <div>When parameter <code>type</code> is set to <em>ldap</em> this should contains only LDAP group definition like <em>cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat</em>.</div>
+ <div>The elements specified in this list will override the ones specified in <code>allow_groups</code>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>state</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>absent</li>
+ <li><div style="color: blue"><b>present</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>Determines if the group should be sync when set to <code>present</code> or pruned when set to <code>absent</code>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>sync_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a valid YAML definition of an LDAP sync configuration.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: config, src</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>type</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>ldap</b>&nbsp;&larr;</div></li>
+ <li>openshift</li>
+ </ul>
+ </td>
+ <td>
+ <div>which groups allow and deny list entries refer to.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ # 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>builds</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The groups that were created, updated or deleted</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{&#x27;apiVersion&#x27;: &#x27;user.openshift.io/v1&#x27;, &#x27;kind&#x27;: &#x27;Group&#x27;, &#x27;metadata&#x27;: {&#x27;annotations&#x27;: {&#x27;openshift.io/ldap.sync-time&#x27;: &#x27;2021-12-17T12:20:28.125282&#x27;, &#x27;openshift.io/ldap.uid&#x27;: &#x27;cn=developers,ou=groups,ou=rfc2307,dc=ansible,dc=redhat&#x27;, &#x27;openshift.io/ldap.url&#x27;: &#x27;localhost:1390&#x27;}, &#x27;creationTimestamp&#x27;: &#x27;2021-12-17T11:09:49Z&#x27;, &#x27;labels&#x27;: {&#x27;openshift.io/ldap.host&#x27;: &#x27;localhost&#x27;}, &#x27;managedFields&#x27;: [{&#x27;apiVersion&#x27;: &#x27;user.openshift.io/v1&#x27;, &#x27;fieldsType&#x27;: &#x27;FieldsV1&#x27;, &#x27;fieldsV1&#x27;: {&#x27;f:metadata&#x27;: {&#x27;f:annotations&#x27;: {&#x27;.&#x27;: {}, &#x27;f:openshift.io/ldap.sync-time&#x27;: {}, &#x27;f:openshift.io/ldap.uid&#x27;: {}, &#x27;f:openshift.io/ldap.url&#x27;: {}}, &#x27;f:labels&#x27;: {&#x27;.&#x27;: {}, &#x27;f:openshift.io/ldap.host&#x27;: {}}}, &#x27;f:users&#x27;: {}}, &#x27;manager&#x27;: &#x27;OpenAPI-Generator&#x27;, &#x27;operation&#x27;: &#x27;Update&#x27;, &#x27;time&#x27;: &#x27;2021-12-17T11:09:49Z&#x27;}], &#x27;name&#x27;: &#x27;developers&#x27;, &#x27;resourceVersion&#x27;: &#x27;2014696&#x27;, &#x27;uid&#x27;: &#x27;8dc211cb-1544-41e1-96b1-efffeed2d7d7&#x27;}, &#x27;users&#x27;: [&#x27;jordanbulls@ansible.org&#x27;]}]</div>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Aubin Bikouo (@abikouo)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_adm_migrate_template_instances_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_adm_migrate_template_instances_module.rst
new file mode 100644
index 000000000..a7940ca85
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_adm_migrate_template_instances_module.rst
@@ -0,0 +1,532 @@
+.. _community.okd.openshift_adm_migrate_template_instances_module:
+
+
+******************************************************
+community.okd.openshift_adm_migrate_template_instances
+******************************************************
+
+**Update TemplateInstances to point to the latest group-version-kinds**
+
+
+Version added: 2.2.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- Update TemplateInstances to point to the latest group-version-kinds.
+- Analogous to ``oc adm migrate template-instances``.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The namespace that the template can be found in.</div>
+ <div>If no namespace if specified, migrate objects in all namespaces.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether to wait for certain resource kinds to end up in the desired state.</div>
+ <div>By default the module exits once Kubernetes has received the request.</div>
+ <div>Implemented for <code>state=present</code> for <code>Deployment</code>, <code>DaemonSet</code> and <code>Pod</code>, and for <code>state=absent</code> for all resource kinds.</div>
+ <div>For resource kinds without an implementation, <code>wait</code> returns immediately unless <code>wait_condition</code> is set.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_condition</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specifies a custom condition on the status to wait for.</div>
+ <div>Ignored if <code>wait</code> is not set or is set to False.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>reason</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The value of the reason field in your desired condition</div>
+ <div>For example, if a <code>Deployment</code> is paused, The <code>Progressing</code> <code>type</code> will have the <code>DeploymentPaused</code> reason.</div>
+ <div>The possible reasons in a condition are specific to each resource type in Kubernetes.</div>
+ <div>See the API documentation of the status field for a given resource to see possible choices.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>True</b>&nbsp;&larr;</div></li>
+ <li>False</li>
+ <li>Unknown</li>
+ </ul>
+ </td>
+ <td>
+ <div>The value of the status field in your desired condition.</div>
+ <div>For example, if a <code>Deployment</code> is paused, the <code>Progressing</code> <code>type</code> will have the <code>Unknown</code> status.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>type</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The type of condition to wait for.</div>
+ <div>For example, the <code>Pod</code> resource will set the <code>Ready</code> condition (among others).</div>
+ <div>Required if you are specifying a <code>wait_condition</code>.</div>
+ <div>If left empty, the <code>wait_condition</code> field will be ignored.</div>
+ <div>The possible types for a condition are specific to each resource type in Kubernetes.</div>
+ <div>See the API documentation of the status field for a given resource to see possible choices.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_sleep</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">5</div>
+ </td>
+ <td>
+ <div>Number of seconds to sleep between checks.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_timeout</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">120</div>
+ </td>
+ <td>
+ <div>How long in seconds to wait for the resource to end up in the desired state.</div>
+ <div>Ignored if <code>wait</code> is not set.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ - 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>result</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>List with all TemplateInstances that have been migrated.</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{&#x27;apiVersion&#x27;: &#x27;template.openshift.io/v1&#x27;, &#x27;kind&#x27;: &#x27;TemplateInstance&#x27;, &#x27;metadata&#x27;: {&#x27;creationTimestamp&#x27;: &#x27;2021-11-10T11:12:09Z&#x27;, &#x27;finalizers&#x27;: [&#x27;template.openshift.io/finalizer&#x27;], &#x27;managedFields&#x27;: [{&#x27;apiVersion&#x27;: &#x27;template.openshift.io/v1&#x27;, &#x27;fieldsType&#x27;: &#x27;FieldsV1&#x27;, &#x27;fieldsV1&#x27;: {&#x27;f:spec&#x27;: {&#x27;f:template&#x27;: {&#x27;f:metadata&#x27;: {&#x27;f:name&#x27;: {}}, &#x27;f:objects&#x27;: {}, &#x27;f:parameters&#x27;: {}}}}, &#x27;manager&#x27;: &#x27;kubectl-create&#x27;, &#x27;operation&#x27;: &#x27;Update&#x27;, &#x27;time&#x27;: &#x27;2021-11-10T11:12:09Z&#x27;}, {&#x27;apiVersion&#x27;: &#x27;template.openshift.io/v1&#x27;, &#x27;fieldsType&#x27;: &#x27;FieldsV1&#x27;, &#x27;fieldsV1&#x27;: {&#x27;f:metadata&#x27;: {&#x27;f:finalizers&#x27;: {&#x27;.&#x27;: {}, &#x27;v:&quot;template.openshift.io/finalizer&quot;&#x27;: {}}}, &#x27;f:status&#x27;: {&#x27;f:conditions&#x27;: {}}}, &#x27;manager&#x27;: &#x27;openshift-controller-manager&#x27;, &#x27;operation&#x27;: &#x27;Update&#x27;, &#x27;time&#x27;: &#x27;2021-11-10T11:12:09Z&#x27;}, {&#x27;apiVersion&#x27;: &#x27;template.openshift.io/v1&#x27;, &#x27;fieldsType&#x27;: &#x27;FieldsV1&#x27;, &#x27;fieldsV1&#x27;: {&#x27;f:status&#x27;: {&#x27;f:objects&#x27;: {}}}, &#x27;manager&#x27;: &#x27;OpenAPI-Generator&#x27;, &#x27;operation&#x27;: &#x27;Update&#x27;, &#x27;time&#x27;: &#x27;2021-11-10T11:12:33Z&#x27;}], &#x27;name&#x27;: &#x27;demo&#x27;, &#x27;namespace&#x27;: &#x27;test&#x27;, &#x27;resourceVersion&#x27;: &#x27;545370&#x27;, &#x27;uid&#x27;: &#x27;09b795d7-7f07-4d94-bf0f-2150ee66f88d&#x27;}, &#x27;spec&#x27;: {&#x27;requester&#x27;: {&#x27;groups&#x27;: [&#x27;system:masters&#x27;, &#x27;system:authenticated&#x27;], &#x27;username&#x27;: &#x27;system:admin&#x27;}, &#x27;template&#x27;: {&#x27;metadata&#x27;: {&#x27;creationTimestamp&#x27;: None, &#x27;name&#x27;: &#x27;template&#x27;}, &#x27;objects&#x27;: [{&#x27;apiVersion&#x27;: &#x27;v1&#x27;, &#x27;kind&#x27;: &#x27;Secret&#x27;, &#x27;metadata&#x27;: {&#x27;labels&#x27;: {&#x27;foo&#x27;: &#x27;bar&#x27;}, &#x27;name&#x27;: &#x27;secret&#x27;}}, {&#x27;apiVersion&#x27;: &#x27;apps/v1&#x27;, &#x27;kind&#x27;: &#x27;Deployment&#x27;, &#x27;metadata&#x27;: {&#x27;name&#x27;: &#x27;deployment&#x27;}, &#x27;spec&#x27;: {&#x27;replicas&#x27;: 0, &#x27;selector&#x27;: {&#x27;matchLabels&#x27;: {&#x27;key&#x27;: &#x27;value&#x27;}}, &#x27;template&#x27;: {&#x27;metadata&#x27;: {&#x27;labels&#x27;: {&#x27;key&#x27;: &#x27;value&#x27;}}, &#x27;spec&#x27;: {&#x27;containers&#x27;: [{&#x27;image&#x27;: &#x27;k8s.gcr.io/e2e-test-images/agnhost:2.32&#x27;, &#x27;name&#x27;: &#x27;hello-openshift&#x27;}]}}}}, {&#x27;apiVersion&#x27;: &#x27;v1&#x27;, &#x27;kind&#x27;: &#x27;Route&#x27;, &#x27;metadata&#x27;: {&#x27;name&#x27;: &#x27;route&#x27;}, &#x27;spec&#x27;: {&#x27;to&#x27;: {&#x27;name&#x27;: &#x27;foo&#x27;}}}], &#x27;parameters&#x27;: [{&#x27;name&#x27;: &#x27;NAME&#x27;, &#x27;value&#x27;: &#x27;${NAME}&#x27;}]}}, &#x27;status&#x27;: {&#x27;conditions&#x27;: [{&#x27;lastTransitionTime&#x27;: &#x27;2021-11-10T11:12:09Z&#x27;, &#x27;message&#x27;: &#x27;&#x27;, &#x27;reason&#x27;: &#x27;Created&#x27;, &#x27;status&#x27;: &#x27;True&#x27;, &#x27;type&#x27;: &#x27;Ready&#x27;}], &#x27;objects&#x27;: [{&#x27;ref&#x27;: {&#x27;apiVersion&#x27;: &#x27;v1&#x27;, &#x27;kind&#x27;: &#x27;Secret&#x27;, &#x27;name&#x27;: &#x27;secret&#x27;, &#x27;namespace&#x27;: &#x27;test&#x27;, &#x27;uid&#x27;: &#x27;33fad364-6d47-4f9c-9e51-92cba5602a57&#x27;}}, {&#x27;ref&#x27;: {&#x27;apiVersion&#x27;: &#x27;apps/v1&#x27;, &#x27;kind&#x27;: &#x27;Deployment&#x27;, &#x27;name&#x27;: &#x27;deployment&#x27;, &#x27;namespace&#x27;: &#x27;test&#x27;, &#x27;uid&#x27;: &#x27;3b527f88-42a1-4811-9e2f-baad4e4d8807&#x27;}}, {&#x27;ref&#x27;: {&#x27;apiVersion&#x27;: &#x27;route.openshift.io/v1.Route&#x27;, &#x27;kind&#x27;: &#x27;Route&#x27;, &#x27;name&#x27;: &#x27;route&#x27;, &#x27;namespace&#x27;: &#x27;test&#x27;, &#x27;uid&#x27;: &#x27;5b5411de-8769-4e27-ba52-6781630e4008&#x27;}}]}}, &#x27;...&#x27;]</div>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Alina Buzachis (@alinabuzachis)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_auth_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_auth_module.rst
new file mode 100644
index 000000000..b4b11c2be
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_auth_module.rst
@@ -0,0 +1,514 @@
+.. _community.okd.openshift_adm_prune_auth_module:
+
+
+**************************************
+community.okd.openshift_adm_prune_auth
+**************************************
+
+**Removes references to the specified roles, clusterroles, users, and groups**
+
+
+Version added: 2.2.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This module allow administrators to remove references to the specified roles, clusterroles, users, and groups.
+- Analogous to ``oc adm prune auth``.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>label_selectors</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Selector (label query) to filter on.</div>
+ <div>Mutually exclusive with option <em>name</em>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify an object name to remove.</div>
+ <div>Mutually exclusive with option <em>label_selectors</em>.</div>
+ <div>If neither <em>name</em> nor <em>label_selectors</em> are specified, prune all resources in the namespace.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify an object namespace.</div>
+ <div>Ignored when <em>resource</em> is set to <code>clusterroles</code>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>resource</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>roles</li>
+ <li>clusterroles</li>
+ <li>users</li>
+ <li>groups</li>
+ </ul>
+ </td>
+ <td>
+ <div>The specified resource to remove.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ - 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>authorization</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td><em>resource=users</em></td>
+ <td>
+ <div>list of OAuthClientAuthorization deleted.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>cluster_role_binding</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td>always</td>
+ <td>
+ <div>list of cluster role binding deleted.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>group</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td><em>resource=users</em></td>
+ <td>
+ <div>list of Security Context Constraints deleted.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>role_binding</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td><em>resource=users</em> or <em>resource=groups</em> or <em>resource=clusterroles</em></td>
+ <td>
+ <div>list of role binding deleted.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>security_context_constraints</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td><em>resource=users</em> or <em>resource=groups</em></td>
+ <td>
+ <div>list of Security Context Constraints deleted.</div>
+ <br/>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Aubin Bikouo (@abikouo)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_builds_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_builds_module.rst
new file mode 100644
index 000000000..6fb9443b7
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_builds_module.rst
@@ -0,0 +1,571 @@
+.. _community.okd.openshift_adm_prune_builds_module:
+
+
+****************************************
+community.okd.openshift_adm_prune_builds
+****************************************
+
+**Prune old completed and failed builds**
+
+
+Version added: 2.3.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This module allow administrators to delete old completed and failed builds.
+- Analogous to ``oc adm prune builds``.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>impersonate_groups</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Group(s) to impersonate for the operation.</div>
+ <div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>impersonate_user</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Username to impersonate for the operation.</div>
+ <div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>keep_younger_than</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the minimum age (in minutes) of a Build for it to be considered a candidate for pruning.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>Multiple Kubernetes config file can be provided using separator &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify namespace for builds to be deleted.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>no_proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
+ <div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
+ <div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>orphans</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>If <code>true</code>, prune all builds whose associated BuildConfig no longer exists and whose status is complete, failed, error, or cancelled.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ # 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>builds</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The builds that were deleted</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>api_version</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The versioned schema of this representation of an object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Represents the REST resource this object represents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>metadata</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>spec</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Specific attributes of the object. Will vary based on the <em>api_version</em> and <em>kind</em>.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Current status details for the object.</div>
+ <br/>
+ </td>
+ </tr>
+
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Aubin Bikouo (@abikouo)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_deployments_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_deployments_module.rst
new file mode 100644
index 000000000..16e0deda9
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_deployments_module.rst
@@ -0,0 +1,428 @@
+.. _community.okd.openshift_adm_prune_deployments_module:
+
+
+*********************************************
+community.okd.openshift_adm_prune_deployments
+*********************************************
+
+**Remove old completed and failed deployment configs**
+
+
+Version added: 2.2.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This module allow administrators to remove old completed and failed deployment configs.
+- Analogous to ``oc adm prune deployments``.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>keep_younger_than</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the minimum age (in minutes) of a deployment for it to be considered a candidate for pruning.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify namespace for deployments to be deleted.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>orphans</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>If <code>true</code>, prune all deployments where the associated DeploymentConfig no longer exists, the status is complete or failed, and the replica size is <code>0</code>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ - 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>replication_controllers</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td>always</td>
+ <td>
+ <div>list of replication controllers candidate for pruning.</div>
+ <br/>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Aubin Bikouo (@abikouo)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_images_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_images_module.rst
new file mode 100644
index 000000000..08fd357a3
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_adm_prune_images_module.rst
@@ -0,0 +1,569 @@
+.. _community.okd.openshift_adm_prune_images_module:
+
+
+****************************************
+community.okd.openshift_adm_prune_images
+****************************************
+
+**Remove unreferenced images**
+
+
+Version added: 2.2.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This module allow administrators to remove references images.
+- Note that if the ``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 ``oc adm prune images``.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+- docker-image-py
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>all_images</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li><div style="color: blue"><b>yes</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>Include images that were imported from external registries as candidates for pruning.</div>
+ <div>If pruned, all the mirrored objects associated with them will also be removed from the integrated registry.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ignore_invalid_refs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>If set to <em>True</em>, the pruning process will ignore all errors while parsing image references.</div>
+ <div>This means that the pruning process will ignore the intended connection between the object and the referenced image.</div>
+ <div>As a result an image may be incorrectly deleted as unused.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>keep_younger_than</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the minimum age (in minutes) of an image and its referrers for it to be considered a candidate for pruning.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify namespace for objects.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>prune_over_size_limit</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Specify if images which are exceeding LimitRanges specified in the same namespace, should be considered for pruning.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>prune_registry</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li><div style="color: blue"><b>yes</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>If set to <em>False</em>, the prune operation will clean up image API objects, but none of the associated content in the registry is removed.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>registry_ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to contact registry. The full certificate chain must be provided to avoid certificate validation errors.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>registry_url</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The address to use when contacting the registry, instead of using the default value.</div>
+ <div>This is useful if you can&#x27;t resolve or reach the default registry but you do have an alternative route that works.</div>
+ <div>Particular transport protocol can be enforced using &#x27;&lt;scheme&gt;://&#x27; prefix.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>registry_validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ # 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>deleted_images</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The images deleted.</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{&#x27;apiVersion&#x27;: &#x27;image.openshift.io/v1&#x27;, &#x27;dockerImageLayers&#x27;: [{&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:5e0b432e8ba9d9029a000e627840b98ffc1ed0c5172075b7d3e869be0df0fe9b&#x27;, &#x27;size&#x27;: 54932878}, {&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:a84cfd68b5cea612a8343c346bfa5bd6c486769010d12f7ec86b23c74887feb2&#x27;, &#x27;size&#x27;: 5153424}, {&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:e8b8f2315954535f1e27cd13d777e73da4a787b0aebf4241d225beff3c91cbb1&#x27;, &#x27;size&#x27;: 10871995}, {&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:0598fa43a7e793a76c198e8d45d8810394e1cfc943b2673d7fcf5a6fdc4f45b3&#x27;, &#x27;size&#x27;: 54567844}, {&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:83098237b6d3febc7584c1f16076a32ac01def85b0d220ab46b6ebb2d6e7d4d4&#x27;, &#x27;size&#x27;: 196499409}, {&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:b92c73d4de9a6a8f6b96806a04857ab33cf6674f6411138603471d744f44ef55&#x27;, &#x27;size&#x27;: 6290769}, {&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:ef9b6ee59783b84a6ec0c8b109c409411ab7c88fa8c53fb3760b5fde4eb0aa07&#x27;, &#x27;size&#x27;: 16812698}, {&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:c1f6285e64066d36477a81a48d3c4f1dc3c03dddec9e72d97da13ba51bca0d68&#x27;, &#x27;size&#x27;: 234}, {&#x27;mediaType&#x27;: &#x27;application/vnd.docker.image.rootfs.diff.tar.gzip&#x27;, &#x27;name&#x27;: &#x27;sha256:a0ee7333301245b50eb700f96d9e13220cdc31871ec9d8e7f0ff7f03a17c6fb3&#x27;, &#x27;size&#x27;: 2349241}], &#x27;dockerImageManifestMediaType&#x27;: &#x27;application/vnd.docker.distribution.manifest.v2+json&#x27;, &#x27;dockerImageMetadata&#x27;: {&#x27;Architecture&#x27;: &#x27;amd64&#x27;, &#x27;Config&#x27;: {&#x27;Cmd&#x27;: [&#x27;python3&#x27;], &#x27;Env&#x27;: [&#x27;PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&#x27;, &#x27;LANG=C.UTF-8&#x27;, &#x27;GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568&#x27;, &#x27;PYTHON_VERSION=3.8.12&#x27;, &#x27;PYTHON_PIP_VERSION=21.2.4&#x27;, &#x27;PYTHON_SETUPTOOLS_VERSION=57.5.0&#x27;, &#x27;PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/3cb8888cc2869620f57d5d2da64da38f516078c7/public/get-pip.py&#x27;, &#x27;PYTHON_GET_PIP_SHA256=c518250e91a70d7b20cceb15272209a4ded2a0c263ae5776f129e0d9b5674309&#x27;], &#x27;Image&#x27;: &#x27;sha256:cc3a2931749afa7dede97e32edbbe3e627b275c07bf600ac05bc0dc22ef203de&#x27;}, &#x27;Container&#x27;: &#x27;b43fcf5052feb037f6d204247d51ac8581d45e50f41c6be2410d94b5c3a3453d&#x27;, &#x27;ContainerConfig&#x27;: {&#x27;Cmd&#x27;: [&#x27;/bin/sh&#x27;, &#x27;-c&#x27;, &#x27;#(nop) &#x27;, &#x27;CMD [&quot;python3&quot;]&#x27;], &#x27;Env&#x27;: [&#x27;PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&#x27;, &#x27;LANG=C.UTF-8&#x27;, &#x27;GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568&#x27;, &#x27;PYTHON_VERSION=3.8.12&#x27;, &#x27;PYTHON_PIP_VERSION=21.2.4&#x27;, &#x27;PYTHON_SETUPTOOLS_VERSION=57.5.0&#x27;, &#x27;PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/3cb8888cc2869620f57d5d2da64da38f516078c7/public/get-pip.py&#x27;, &#x27;PYTHON_GET_PIP_SHA256=c518250e91a70d7b20cceb15272209a4ded2a0c263ae5776f129e0d9b5674309&#x27;], &#x27;Hostname&#x27;: &#x27;b43fcf5052fe&#x27;, &#x27;Image&#x27;: &#x27;sha256:cc3a2931749afa7dede97e32edbbe3e627b275c07bf600ac05bc0dc22ef203de&#x27;}, &#x27;Created&#x27;: &#x27;2021-12-03T01:53:41Z&#x27;, &#x27;DockerVersion&#x27;: &#x27;20.10.7&#x27;, &#x27;Id&#x27;: &#x27;sha256:f746089c9d02d7126bbe829f788e093853a11a7f0421049267a650d52bbcac37&#x27;, &#x27;Size&#x27;: 347487141, &#x27;apiVersion&#x27;: &#x27;image.openshift.io/1.0&#x27;, &#x27;kind&#x27;: &#x27;DockerImage&#x27;}, &#x27;dockerImageMetadataVersion&#x27;: &#x27;1.0&#x27;, &#x27;dockerImageReference&#x27;: &#x27;python@sha256:a874dcabc74ca202b92b826521ff79dede61caca00ceab0b65024e895baceb58&#x27;, &#x27;kind&#x27;: &#x27;Image&#x27;, &#x27;metadata&#x27;: {&#x27;annotations&#x27;: {&#x27;image.openshift.io/dockerLayersOrder&#x27;: &#x27;ascending&#x27;}, &#x27;creationTimestamp&#x27;: &#x27;2021-12-07T07:55:30Z&#x27;, &#x27;name&#x27;: &#x27;sha256:a874dcabc74ca202b92b826521ff79dede61caca00ceab0b65024e895baceb58&#x27;, &#x27;resourceVersion&#x27;: &#x27;1139214&#x27;, &#x27;uid&#x27;: &#x27;33be6ab4-af79-4f44-a0fd-4925bd473c1f&#x27;}}, &#x27;...&#x27;]</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>updated_image_streams</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The images streams updated.</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{&#x27;apiVersion&#x27;: &#x27;image.openshift.io/v1&#x27;, &#x27;kind&#x27;: &#x27;ImageStream&#x27;, &#x27;metadata&#x27;: {&#x27;annotations&#x27;: {&#x27;openshift.io/image.dockerRepositoryCheck&#x27;: &#x27;2021-12-07T07:55:30Z&#x27;}, &#x27;creationTimestamp&#x27;: &#x27;2021-12-07T07:55:30Z&#x27;, &#x27;generation&#x27;: 1, &#x27;name&#x27;: &#x27;python&#x27;, &#x27;namespace&#x27;: &#x27;images&#x27;, &#x27;resourceVersion&#x27;: &#x27;1139215&#x27;, &#x27;uid&#x27;: &#x27;443bad2c-9fd4-4c8f-8a24-3eca4426b07f&#x27;}, &#x27;spec&#x27;: {&#x27;lookupPolicy&#x27;: {&#x27;local&#x27;: False}, &#x27;tags&#x27;: [{&#x27;annotations&#x27;: None, &#x27;from&#x27;: {&#x27;kind&#x27;: &#x27;DockerImage&#x27;, &#x27;name&#x27;: &#x27;python:3.8.12&#x27;}, &#x27;generation&#x27;: 1, &#x27;importPolicy&#x27;: {&#x27;insecure&#x27;: True}, &#x27;name&#x27;: &#x27;3.8.12&#x27;, &#x27;referencePolicy&#x27;: {&#x27;type&#x27;: &#x27;Source&#x27;}}]}, &#x27;status&#x27;: {&#x27;dockerImageRepository&#x27;: &#x27;image-registry.openshift-image-registry.svc:5000/images/python&#x27;, &#x27;publicDockerImageRepository&#x27;: &#x27;default-route-openshift-image-registry.apps-crc.testing/images/python&#x27;, &#x27;tags&#x27;: []}}, &#x27;...&#x27;]</div>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Aubin Bikouo (@abikouo)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_auth_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_auth_module.rst
new file mode 100644
index 000000000..3619940ea
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_auth_module.rst
@@ -0,0 +1,424 @@
+.. _community.okd.openshift_auth_module:
+
+
+****************************
+community.okd.openshift_auth
+****************************
+
+**Authenticate to OpenShift clusters which require an explicit login step**
+
+
+Version added: 0.2.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This module handles authenticating to OpenShift clusters requiring *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 ``k8s`` module (and other resource–specific modules) by utilizing the ``host``, ``username`` and ``password`` parameters. Please consult your preferred module's documentation for more details.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- urllib3
+- requests
+- requests-oauthlib
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>When <code>state</code> is set to <em>absent</em>, this specifies the token to revoke.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>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.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API server.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API server.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>state</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>present</b>&nbsp;&larr;</div></li>
+ <li>absent</li>
+ </ul>
+ </td>
+ <td>
+ <div>If set to <em>present</em> connect to the API server using the URL specified in <code>host</code> and attempt to log in.</div>
+ <div>If set to <em>absent</em> attempt to log out by revoking the authentication token specified in <code>api_key</code>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API server.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li><div style="color: blue"><b>yes</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ - hosts: localhost
+ module_defaults:
+ group/k8s:
+ host: https://k8s.example.com/
+ ca_cert: ca.pem
+ tasks:
+ - block:
+ # It's good practice to store login credentials in a secure vault and not
+ # directly in playbooks.
+ - include_vars: 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 }}"
+
+
+
+Return Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>k8s_auth</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Same as returned openshift_auth. Kept only for backwards compatibility</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Authentication token.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Path to a CA certificate file used to verify connection to the API server.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>URL for accessing the API server.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Username for authenticating with the API server.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates.</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>openshift_auth</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>OpenShift authentication facts.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Authentication token.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Path to a CA certificate file used to verify connection to the API server.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>URL for accessing the API server.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Username for authenticating with the API server.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates.</div>
+ <br/>
+ </td>
+ </tr>
+
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- KubeVirt Team (@kubevirt)
+- Fabian von Feilitzsch (@fabianvf)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_build_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_build_module.rst
new file mode 100644
index 000000000..dc177bef0
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_build_module.rst
@@ -0,0 +1,844 @@
+.. _community.okd.openshift_build_module:
+
+
+*****************************
+community.okd.openshift_build
+*****************************
+
+**Start a new build or Cancel running, pending, or new builds.**
+
+
+Version added: 2.3.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- 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 ``oc cancel-build`` and ``oc start-build``.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>build_args</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify a list of key-value pair to pass to Docker during the build.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>docker build argument name.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>value</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>docker build argument value.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>build_config_name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the name of a build config from which a new build will be run.</div>
+ <div>Mutually exclusive with parameter <em>build_name</em>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>build_name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the name of a build which should be re-run.</div>
+ <div>Mutually exclusive with parameter <em>build_config_name</em>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>build_phases</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>New</li>
+ <li>Pending</li>
+ <li>Running</li>
+ </ul>
+ <b>Default:</b><br/><div style="color: blue">[]</div>
+ </td>
+ <td>
+ <div>List of state for build to cancel.</div>
+ <div>Ignored when <code>state=started</code>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>commit</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the source code commit identifier the build should use; requires a build based on a Git repository.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>env_vars</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify a list of key-value pair for an environment variable to set for the build container.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Environment variable name.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>value</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Environment variable value.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>impersonate_groups</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Group(s) to impersonate for the operation.</div>
+ <div>Can also be specified via K8S_AUTH_IMPERSONATE_GROUPS environment. Example: Group1,Group2</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>impersonate_user</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Username to impersonate for the operation.</div>
+ <div>Can also be specified via K8S_AUTH_IMPERSONATE_USER environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>incremental</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Overrides the incremental setting in a source-strategy build, ignored if not specified.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>Multiple Kubernetes config file can be provided using separator &#x27;;&#x27; for Windows platform or &#x27;:&#x27; for others platforms.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the namespace for the build or the build config.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>no_cache</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Overrides the noCache setting in a docker-strategy build, ignored if not specified.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>no_proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.3.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The comma separated list of hosts/domains/IP/CIDR that shouldn&#x27;t go through proxy. Can also be specified via K8S_AUTH_NO_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. NO_PROXY).</div>
+ <div>This feature requires kubernetes&gt;=19.15.0. When kubernetes library is less than 19.15.0, it fails even no_proxy set in correct.</div>
+ <div>example value is &quot;localhost,.local,.example.com,127.0.0.1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16&quot;</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>state</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>started</b>&nbsp;&larr;</div></li>
+ <li>cancelled</li>
+ <li>restarted</li>
+ </ul>
+ </td>
+ <td>
+ <div>Determines if a Build should be started ,cancelled or restarted.</div>
+ <div>When set to <code>restarted</code> a new build will be created after the current build is cancelled.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>When <code>state=started</code>, specify whether to wait for a build to complete and exit with a non-zero return code if the build fails.</div>
+ <div>When <em>state=cancelled</em>, specify whether to wait for a build phase to be Cancelled.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_sleep</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">5</div>
+ </td>
+ <td>
+ <div>Number of seconds to sleep between checks.</div>
+ <div>Ignored if <code>wait=false</code>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_timeout</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">120</div>
+ </td>
+ <td>
+ <div>How long in seconds to wait for a build to complete.</div>
+ <div>Ignored if <code>wait=false</code>.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ # 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>builds</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The builds that were started/cancelled.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>api_version</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The versioned schema of this representation of an object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Represents the REST resource this object represents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>metadata</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>spec</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Specific attributes of the build.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Current status details for the object.</div>
+ <br/>
+ </td>
+ </tr>
+
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Aubin Bikouo (@abikouo)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_import_image_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_import_image_module.rst
new file mode 100644
index 000000000..920c8405b
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_import_image_module.rst
@@ -0,0 +1,626 @@
+.. _community.okd.openshift_import_image_module:
+
+
+************************************
+community.okd.openshift_import_image
+************************************
+
+**Import the latest image information from a tag in a container image registry.**
+
+
+Version added: 2.2.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- 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 ``oc import-image``.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+- docker-image-py
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>all</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>If set to <em>true</em>, import all tags from the provided source on creation or if <code>source</code> is specified.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Image stream to import tag into.</div>
+ <div>This can be provided as a list of images streams or a single value.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Use to specify namespace for image stream to create/update.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>reference_policy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>source</b>&nbsp;&larr;</div></li>
+ <li>local</li>
+ </ul>
+ </td>
+ <td>
+ <div>Allow to request pullthrough for external image when set to <em>local</em>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>scheduled</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Set each imported Docker image to be periodically imported from a remote repository.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>source</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>A Docker image repository to import images from.</div>
+ <div>Should be provided as &#x27;registry.io/repo/image&#x27;</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_registry_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>If set to <em>true</em>, allow importing from registries that have invalid HTTPS certificates. or are hosted via HTTP. This parameter will take precedence over the insecure annotation.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ # 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>result</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>List with all ImageStreamImport that have been created.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>api_version</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The versioned schema of this representation of an object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Represents the REST resource this object represents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>metadata</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>spec</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Specific attributes of the object. Will vary based on the <em>api_version</em> and <em>kind</em>.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Current status details for the object.</div>
+ <br/>
+ </td>
+ </tr>
+
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Aubin Bikouo (@abikouo)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_inventory.rst b/ansible_collections/community/okd/docs/community.okd.openshift_inventory.rst
new file mode 100644
index 000000000..57527f454
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_inventory.rst
@@ -0,0 +1,356 @@
+.. _community.okd.openshift_inventory:
+
+
+***********************
+community.okd.openshift
+***********************
+
+**OpenShift inventory source**
+
+
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- Fetch containers, services and routes for one or more clusters
+- Groups by cluster name, namespace, namespace_services, namespace_pods, namespace_routes, and labels
+- Uses openshift.(yml|yaml) YAML configuration file to set parameter values.
+
+
+
+Requirements
+------------
+The below requirements are needed on the local Ansible controller node that executes this inventory.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+- PyYAML >= 3.11
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th>Configuration</th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>connections</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Optional list of cluster connection settings. If no connections are provided, the default <em>~/.kube/config</em> and active context will be used, and objects will be returned for all namespaces the active user is authorized to access.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Optional name to assign to the cluster. If not provided, a name is constructed from the server and port.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespaces</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>List of namespaces. If not specified, will fetch all containers for all namespaces user is authorized to access.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>plugin</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>openshift</li>
+ <li>community.okd.openshift</li>
+ </ul>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>token that ensures this is a source file for the &#x27;openshift&#x27; plugin.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ # File must be named openshift.yaml or openshift.yml
+
+ # Authenticate with token, and return all pods and services for all namespaces
+ plugin: community.okd.openshift
+ connections:
+ - host: https://192.168.64.4:8443
+ api_key: xxxxxxxxxxxxxxxx
+ verify_ssl: false
+
+ # Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
+ plugin: community.okd.openshift
+ connections:
+ - namespaces:
+ - testing
+
+ # Use a custom config file, and a specific context.
+ plugin: community.okd.openshift
+ connections:
+ - kubeconfig: /path/to/config
+ context: 'awx/192-168-64-4:8443/developer'
+
+
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Chris Houseknecht <@chouseknecht>
+
+
+.. hint::
+ Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up.
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_process_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_process_module.rst
new file mode 100644
index 000000000..7de7e8c3a
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_process_module.rst
@@ -0,0 +1,1004 @@
+.. _community.okd.openshift_process_module:
+
+
+*******************************
+community.okd.openshift_process
+*******************************
+
+**Process an OpenShift template.openshift.io/v1 Template**
+
+
+Version added: 0.3.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- 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.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+- PyYAML >= 3.11
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of the Template to process.</div>
+ <div>The Template must be present in the cluster.</div>
+ <div>When provided, <em>namespace</em> is required.</div>
+ <div>Mutually exclusive with <em>resource_definition</em> or <em>src</em></div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The namespace that the template can be found in.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace_target</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The namespace that resources should be created, updated, or deleted in.</div>
+ <div>Only used when <em>state</em> is present or absent.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>parameter_file</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>A path to a file containing template parameter values to override/set values in the Template.</div>
+ <div>Corresponds to the `--param-file` argument to oc process.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>parameters</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>A set of key: value pairs that will be used to set/override values in the Template.</div>
+ <div>Corresponds to the `--param` argument to oc process.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>resource_definition</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">-</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a valid YAML definition (either as a string, list, or dict) for an object when creating or updating.</div>
+ <div>NOTE: <em>kind</em>, <em>api_version</em>, <em>name</em>, and <em>namespace</em> will be overwritten by corresponding values found in the provided <em>resource_definition</em>.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: definition, inline</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>src</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a path to a file containing a valid YAML definition of an object or objects to be created or updated. Mutually exclusive with <em>resource_definition</em>. NOTE: <em>kind</em>, <em>api_version</em>, <em>name</em>, and <em>namespace</em> will be overwritten by corresponding values found in the configuration read in from the <em>src</em> file.</div>
+ <div>Reads from the local file system. To read from the Ansible controller&#x27;s file system, including vaulted files, use the file lookup plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to <em>resource_definition</em>. See Examples below.</div>
+ <div>Mutually exclusive with <em>template</em> in case of <span class='module'>kubernetes.core.k8s</span> module.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>state</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>absent</li>
+ <li>present</li>
+ <li><div style="color: blue"><b>rendered</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>Determines what to do with the rendered Template.</div>
+ <div>The state <em>rendered</em> will render the Template based on the provided parameters, and return the rendered objects in the <em>resources</em> field. These can then be referenced in future tasks.</div>
+ <div>The state <em>present</em> will cause the resources in the rendered Template to be created if they do not already exist, and patched if they do.</div>
+ <div>The state <em>absent</em> will delete the resources in the rendered Template.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether to wait for certain resource kinds to end up in the desired state.</div>
+ <div>By default the module exits once Kubernetes has received the request.</div>
+ <div>Implemented for <code>state=present</code> for <code>Deployment</code>, <code>DaemonSet</code> and <code>Pod</code>, and for <code>state=absent</code> for all resource kinds.</div>
+ <div>For resource kinds without an implementation, <code>wait</code> returns immediately unless <code>wait_condition</code> is set.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_condition</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specifies a custom condition on the status to wait for.</div>
+ <div>Ignored if <code>wait</code> is not set or is set to False.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>reason</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The value of the reason field in your desired condition</div>
+ <div>For example, if a <code>Deployment</code> is paused, The <code>Progressing</code> <code>type</code> will have the <code>DeploymentPaused</code> reason.</div>
+ <div>The possible reasons in a condition are specific to each resource type in Kubernetes.</div>
+ <div>See the API documentation of the status field for a given resource to see possible choices.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>True</b>&nbsp;&larr;</div></li>
+ <li>False</li>
+ <li>Unknown</li>
+ </ul>
+ </td>
+ <td>
+ <div>The value of the status field in your desired condition.</div>
+ <div>For example, if a <code>Deployment</code> is paused, the <code>Progressing</code> <code>type</code> will have the <code>Unknown</code> status.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>type</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The type of condition to wait for.</div>
+ <div>For example, the <code>Pod</code> resource will set the <code>Ready</code> condition (among others).</div>
+ <div>Required if you are specifying a <code>wait_condition</code>.</div>
+ <div>If left empty, the <code>wait_condition</code> field will be ignored.</div>
+ <div>The possible types for a condition are specific to each resource type in Kubernetes.</div>
+ <div>See the API documentation of the status field for a given resource to see possible choices.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_sleep</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">5</div>
+ </td>
+ <td>
+ <div>Number of seconds to sleep between checks.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_timeout</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">120</div>
+ </td>
+ <td>
+ <div>How long in seconds to wait for the resource to end up in the desired state.</div>
+ <div>Ignored if <code>wait</code> is not set.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ - 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="3">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>resources</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>on success when state is rendered</td>
+ <td>
+ <div>The rendered resources defined in the Template</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>apiVersion</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The versioned schema of this representation of an object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Represents the REST resource this object represents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>metadata</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The name of the resource</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The namespace of the resource</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>spec</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Specific attributes of the object. Will vary based on the <em>api_version</em> and <em>kind</em>.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Current status details for the object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>conditions</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Array of status conditions for the object. Not guaranteed to be present</div>
+ <br/>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>result</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>on success when state is present or absent</td>
+ <td>
+ <div>The created, patched, or otherwise present object. Will be empty in the case of a deletion.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>apiVersion</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The versioned schema of this representation of an object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>duration</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>when <code>wait</code> is true</td>
+ <td>
+ <div>elapsed time of task in seconds</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">48</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>items</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td>when resource_definition or src contains list of objects</td>
+ <td>
+ <div>Returned only when multiple yaml documents are passed to src or resource_definition</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Represents the REST resource this object represents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>metadata</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The name of the resource</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The namespace of the resource</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>spec</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Specific attributes of the object. Will vary based on the <em>api_version</em> and <em>kind</em>.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Current status details for the object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>conditions</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Array of status conditions for the object. Not guaranteed to be present</div>
+ <br/>
+ </td>
+ </tr>
+
+
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Fabian von Feilitzsch (@fabianvf)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_registry_info_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_registry_info_module.rst
new file mode 100644
index 000000000..f556d0f64
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_registry_info_module.rst
@@ -0,0 +1,464 @@
+.. _community.okd.openshift_registry_info_module:
+
+
+*************************************
+community.okd.openshift_registry_info
+*************************************
+
+**Display information about the integrated registry.**
+
+
+Version added: 2.2.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This module exposes information about the integrated registry.
+- Use ``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 ``oc registry info``.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+- docker-image-py
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>check</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Attempt to contact the integrated registry using local client.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ # 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>check</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Whether the local client can contact or not the registry.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>msg</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>always</td>
+ <td>
+ <div>message describing the ping operation.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>reached</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Whether the registry has been reached or not.</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>internal_hostname</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The internal registry hostname.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>public_hostname</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The public registry hostname.</div>
+ <br/>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Aubin Bikouo (@abikouo)
diff --git a/ansible_collections/community/okd/docs/community.okd.openshift_route_module.rst b/ansible_collections/community/okd/docs/community.okd.openshift_route_module.rst
new file mode 100644
index 000000000..fc62623c5
--- /dev/null
+++ b/ansible_collections/community/okd/docs/community.okd.openshift_route_module.rst
@@ -0,0 +1,1440 @@
+.. _community.okd.openshift_route_module:
+
+
+*****************************
+community.okd.openshift_route
+*****************************
+
+**Expose a Service as an OpenShift Route.**
+
+
+Version added: 0.3.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- 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.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- python >= 3.6
+- kubernetes >= 12.0.0
+- PyYAML >= 3.11
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="2">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>annotations</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.1.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the Route Annotations.</div>
+ <div>A set of key: value pairs.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>api_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to avoid certificate validation errors. Can also be specified via K8S_AUTH_SSL_CA_CERT environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: ssl_ca_cert</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_cert</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: cert_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>client_key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">path</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: key_file</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>context</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>force</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>If set to <code>yes</code>, and <em>state</em> is <code>present</code>, an existing object will be replaced.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>hostname</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The hostname for the Route.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>kubeconfig</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">raw</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to an existing Kubernetes config file. If not provided, and no other connection options are provided, the Kubernetes client will attempt to load the default configuration file from <em>~/.kube/config</em>. Can also be specified via K8S_AUTH_KUBECONFIG environment variable.</div>
+ <div>The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version &gt;= 17.17.0. Added in version 2.2.0.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>labels</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specify the labels to apply to the created Route.</div>
+ <div>A set of key: value pairs.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The desired name of the Route to be created.</div>
+ <div>Defaults to the value of <em>service</em></div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The namespace of the resource being targeted.</div>
+ <div>The Route will be created in this namespace as well.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment variable.</div>
+ <div>Please read the description of the <code>username</code> option for a discussion of when this option is applicable.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>path</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The path for the Route</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persist_config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to save the kube config refresh tokens. Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.</div>
+ <div>When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth), the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the new refresh token to the kube config file.</div>
+ <div>Default to false.</div>
+ <div>Please note that the current version of the k8s python client library does not support setting this flag to True yet.</div>
+ <div>The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>port</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Name or number of the port the Route will route traffic to.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.</div>
+ <div>Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_headers</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.0.0</div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The Header used for the HTTP proxy.</div>
+ <div>Documentation can be found here <a href='https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers'>https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html?highlight=proxy_headers#urllib3.util.make_headers</a>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>proxy_basic_auth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Colon-separated username:password for proxy basic authentication header.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH environment.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>user_agent</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>String representing the user-agent you want, such as foo/1.0.</div>
+ <div>Can also be specified via K8S_AUTH_PROXY_HEADERS_USER_AGENT environment.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>service</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The name of the service to expose.</div>
+ <div>Required when <em>state</em> is not absent.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: svc</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>state</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>absent</li>
+ <li><div style="color: blue"><b>present</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>Determines if an object should be created, patched, or deleted. When set to <code>present</code>, an object will be created, if it does not already exist. If set to <code>absent</code>, an existing object will be deleted. If set to <code>present</code>, an existing object will be patched, if its attributes differ from those specified using <em>resource_definition</em> or <em>src</em>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>termination</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>edge</li>
+ <li>passthrough</li>
+ <li>reencrypt</li>
+ <li><div style="color: blue"><b>insecure</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>The termination type of the Route.</div>
+ <div>If left empty no termination type will be set, and the route will be insecure.</div>
+ <div>When set to insecure <em>tls</em> will be ignored.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>tls</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>TLS configuration for the newly created route.</div>
+ <div>Only used when <em>termination</em> is set.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ca_certificate</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate file on the target host.</div>
+ <div>Not supported when <em>termination</em> is set to passthrough.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>certificate</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a certificate file on the target host.</div>
+ <div>Not supported when <em>termination</em> is set to passthrough.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>destination_ca_certificate</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a CA certificate file used for securing the connection.</div>
+ <div>Only used when <em>termination</em> is set to reencrypt.</div>
+ <div>Defaults to the Service CA.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>insecure_policy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>allow</li>
+ <li>redirect</li>
+ <li><div style="color: blue"><b>disallow</b>&nbsp;&larr;</div></li>
+ </ul>
+ </td>
+ <td>
+ <div>Sets the InsecureEdgeTerminationPolicy for the Route.</div>
+ <div>Not supported when <em>termination</em> is set to reencrypt.</div>
+ <div>When <em>termination</em> is set to passthrough, only redirect is supported.</div>
+ <div>If not provided, insecure traffic will be disallowed.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>key</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Path to a key file on the target host.</div>
+ <div>Not supported when <em>termination</em> is set to passthrough.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>username</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment variable.</div>
+ <div>Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you should look into the <span class='module'>community.okd.k8s_auth</span> module, as that might do what you need.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>validate_certs</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether or not to verify the API server&#x27;s SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL environment variable.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: verify_ssl</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Whether to wait for certain resource kinds to end up in the desired state.</div>
+ <div>By default the module exits once Kubernetes has received the request.</div>
+ <div>Implemented for <code>state=present</code> for <code>Deployment</code>, <code>DaemonSet</code> and <code>Pod</code>, and for <code>state=absent</code> for all resource kinds.</div>
+ <div>For resource kinds without an implementation, <code>wait</code> returns immediately unless <code>wait_condition</code> is set.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_condition</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Specifies a custom condition on the status to wait for.</div>
+ <div>Ignored if <code>wait</code> is not set or is set to False.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>reason</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The value of the reason field in your desired condition</div>
+ <div>For example, if a <code>Deployment</code> is paused, The <code>Progressing</code> <code>type</code> will have the <code>DeploymentPaused</code> reason.</div>
+ <div>The possible reasons in a condition are specific to each resource type in Kubernetes.</div>
+ <div>See the API documentation of the status field for a given resource to see possible choices.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>True</b>&nbsp;&larr;</div></li>
+ <li>False</li>
+ <li>Unknown</li>
+ </ul>
+ </td>
+ <td>
+ <div>The value of the status field in your desired condition.</div>
+ <div>For example, if a <code>Deployment</code> is paused, the <code>Progressing</code> <code>type</code> will have the <code>Unknown</code> status.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>type</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>The type of condition to wait for.</div>
+ <div>For example, the <code>Pod</code> resource will set the <code>Ready</code> condition (among others).</div>
+ <div>Required if you are specifying a <code>wait_condition</code>.</div>
+ <div>If left empty, the <code>wait_condition</code> field will be ignored.</div>
+ <div>The possible types for a condition are specific to each resource type in Kubernetes.</div>
+ <div>See the API documentation of the status field for a given resource to see possible choices.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_sleep</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">5</div>
+ </td>
+ <td>
+ <div>Number of seconds to sleep between checks.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wait_timeout</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">120</div>
+ </td>
+ <td>
+ <div>How long in seconds to wait for the resource to end up in the desired state.</div>
+ <div>Ignored if <code>wait</code> is not set.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>wildcard_policy</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>Subdomain</li>
+ </ul>
+ </td>
+ <td>
+ <div>The wildcard policy for the hostname.</div>
+ <div>Currently only Subdomain is supported.</div>
+ <div>If not provided, the default of None will be used.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+Notes
+-----
+
+.. note::
+ - To avoid SSL certificate validation errors when ``validate_certs`` is *True*, the full certificate chain for the API server must be provided via ``ca_cert`` or in the kubeconfig file.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ - 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 Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="5">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="5">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>duration</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>when <code>wait</code> is true</td>
+ <td>
+ <div>elapsed time of task in seconds</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">48</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="5">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>result</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The Route object that was created or updated. Will be empty in the case of deletion.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="4">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>apiVersion</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>The versioned schema of this representation of an object.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="4">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Represents the REST resource this object represents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="4">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>metadata</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Standard object metadata. Includes name, namespace, annotations, labels, etc.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The name of the created Route</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>namespace</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The namespace of the create Route</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="4">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>spec</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Specification for the Route</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Host is an alias/DNS that points to the service.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>path</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Path that the router watches for, to route traffic for to the service.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>port</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Defines a port mapping from a router to an endpoint in the service endpoints.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>targetPort</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The target port on pods selected by the service this route points to.</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>tls</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Defines config used to secure a route and provide termination.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>caCertificate</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Provides the cert authority certificate contents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>certificate</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Provides certificate contents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>destinationCACertificate</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Provides the contents of the ca certificate of the final destination.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>insecureEdgeTerminationPolicy</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Indicates the desired behavior for insecure connections to a route.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>key</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Provides key file contents.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>termination</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Indicates termination type.</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>to</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Specifies the target that resolve into endpoints.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>kind</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The kind of target that the route is referring to. Currently, only &#x27;Service&#x27; is allowed.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Name of the service/target that is being referred to. e.g. name of the service.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>weight</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Specifies the target&#x27;s relative weight against other target reference objects.</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>wildcardPolicy</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Wildcard policy if any for the route.</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="4">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td>success</td>
+ <td>
+ <div>Current status details for the Route</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>ingress</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>List of places where the route may be exposed.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>conditions</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">complex</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Array of status conditions for the Route ingress.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>status</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The status of the condition. Can be True, False, Unknown.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>type</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The type of the condition. Currently only &#x27;Ready&#x27;.</div>
+ <br/>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>host</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The host string under which the route is exposed.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>routerCanonicalHostname</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>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.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>routerName</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>A name chosen by the router to identify itself.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td class="elbow-placeholder">&nbsp;</td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>wildcardPolicy</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>The wildcard policy that was allowed where this route is exposed.</div>
+ <br/>
+ </td>
+ </tr>
+
+
+
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Fabian von Feilitzsch (@fabianvf)
diff --git a/ansible_collections/community/okd/meta/runtime.yml b/ansible_collections/community/okd/meta/runtime.yml
new file mode 100644
index 000000000..e1ab57a85
--- /dev/null
+++ b/ansible_collections/community/okd/meta/runtime.yml
@@ -0,0 +1,24 @@
+---
+requires_ansible: '>=2.9.17'
+action_groups:
+ okd:
+ - k8s
+ - openshift_adm_groups_sync
+ - openshift_adm_migrate_template_instances
+ - openshift_adm_prune_auth
+ - openshift_adm_prune_deployments
+ - openshift_adm_prune_images
+ - openshift_auth
+ - openshift_import_image
+ - openshift_process
+ - openshift_registry_info
+ - openshift_route
+plugin_routing:
+ modules:
+ k8s_auth:
+ redirect: community.okd.openshift_auth
+ action:
+ k8s:
+ redirect: kubernetes.core.k8s_info
+ openshift_adm_groups_sync:
+ redirect: kubernetes.core.k8s_info
diff --git a/ansible_collections/community/okd/molecule/default/README.md b/ansible_collections/community/okd/molecule/default/README.md
new file mode 100644
index 000000000..153dfa515
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/README.md
@@ -0,0 +1,19 @@
+Wait tests
+----------
+
+wait tests require at least one node, and don't work on the normal k8s
+openshift-origin container as provided by ansible-test --docker -v k8s
+
+minikube, Kubernetes from Docker or any other Kubernetes service will
+suffice.
+
+If kubectl is already using the right config file and context, you can
+just do
+
+```
+cd tests/integration/targets/okd
+./runme.sh -vv
+```
+
+otherwise set one or both of `K8S_AUTH_KUBECONFIG` and `K8S_AUTH_CONTEXT`
+and use the same command
diff --git a/ansible_collections/community/okd/molecule/default/converge.yml b/ansible_collections/community/okd/molecule/default/converge.yml
new file mode 100644
index 000000000..7fe9e8209
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/converge.yml
@@ -0,0 +1,99 @@
+---
+- name: Converge
+ hosts: localhost
+ connection: local
+ gather_facts: no
+ vars:
+ ansible_python_interpreter: '{{ virtualenv_interpreter }}'
+ vars_files:
+ - vars/main.yml
+ tasks:
+ # OpenShift Resources
+ - name: Create a project
+ community.okd.k8s:
+ name: testing
+ kind: Project
+ api_version: project.openshift.io/v1
+ apply: no
+ register: output
+
+ - name: show output
+ debug:
+ var: output
+
+ - name: Create deployment config
+ community.okd.k8s:
+ state: present
+ name: hello-world
+ namespace: testing
+ definition: '{{ okd_dc_template }}'
+ wait: yes
+ wait_condition:
+ type: Available
+ status: True
+ vars:
+ k8s_pod_name: hello-world
+ k8s_pod_image: python
+ k8s_pod_command:
+ - python
+ - '-m'
+ - http.server
+ k8s_pod_env:
+ - name: TEST
+ value: test
+ okd_dc_triggers:
+ - type: ConfigChange
+ register: output
+
+ - name: Show output
+ debug:
+ var: output
+
+ - vars:
+ image: docker.io/python
+ image_name: python
+ image_tag: latest
+ k8s_pod_image: python
+ k8s_pod_command:
+ - python
+ - '-m'
+ - http.server
+ namespace: idempotence-testing
+ block:
+ - name: Create a namespace
+ community.okd.k8s:
+ name: '{{ namespace }}'
+ kind: Namespace
+ api_version: v1
+
+ - name: Create imagestream
+ community.okd.k8s:
+ namespace: '{{ namespace }}'
+ definition: '{{ okd_imagestream_template }}'
+
+ - name: Create DeploymentConfig to reference ImageStream
+ community.okd.k8s:
+ name: '{{ k8s_pod_name }}'
+ namespace: '{{ namespace }}'
+ definition: '{{ okd_dc_template }}'
+ vars:
+ k8s_pod_name: is-idempotent-dc
+
+ - name: Create Deployment to reference ImageStream
+ community.okd.k8s:
+ name: '{{ k8s_pod_name }}'
+ namespace: '{{ namespace }}'
+ definition: '{{ k8s_deployment_template | combine(metadata) }}'
+ vars:
+ k8s_pod_annotations:
+ "alpha.image.policy.openshift.io/resolve-names": "*"
+ k8s_pod_name: is-idempotent-deployment
+ annotation:
+ - from:
+ kind: ImageStreamTag
+ name: "{{ image_name }}:{{ image_tag}}}"
+ fieldPath: 'spec.template.spec.containers[?(@.name=="{{ k8s_pod_name }}")].image}'
+ metadata:
+ metadata:
+ annotations:
+ image.openshift.io/triggers: '{{ annotation | to_json }}'
diff --git a/ansible_collections/community/okd/molecule/default/destroy.yml b/ansible_collections/community/okd/molecule/default/destroy.yml
new file mode 100644
index 000000000..0bd583e1c
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/destroy.yml
@@ -0,0 +1,6 @@
+---
+- name: Destroy
+ hosts: localhost
+ connection: local
+ gather_facts: no
+ tasks: []
diff --git a/ansible_collections/community/okd/molecule/default/files/crd-resource.yml b/ansible_collections/community/okd/molecule/default/files/crd-resource.yml
new file mode 100644
index 000000000..23d0663c3
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/files/crd-resource.yml
@@ -0,0 +1,21 @@
+---
+apiVersion: certmanager.k8s.io/v1alpha1
+kind: Certificate
+metadata:
+ name: acme-crt
+spec:
+ secretName: acme-crt-secret
+ dnsNames:
+ - foo.example.com
+ - bar.example.com
+ acme:
+ config:
+ - ingressClass: nginx
+ domains:
+ - foo.example.com
+ - bar.example.com
+ issuerRef:
+ name: letsencrypt-prod
+ # We can reference ClusterIssuers by changing the kind here.
+ # The default value is Issuer (i.e. a locally namespaced Issuer)
+ kind: Issuer
diff --git a/ansible_collections/community/okd/molecule/default/files/example.env b/ansible_collections/community/okd/molecule/default/files/example.env
new file mode 100644
index 000000000..f98f7d9ee
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/files/example.env
@@ -0,0 +1,9 @@
+#
+NAME=example
+# Multiline values shouldn't break things
+export CONTENT=This is a long message\
+ that may take one or more lines to parse\
+ but should still work without issue
+
+# This shouldn't throw an error
+UNUSED=
diff --git a/ansible_collections/community/okd/molecule/default/files/kuard-extra-property.yml b/ansible_collections/community/okd/molecule/default/files/kuard-extra-property.yml
new file mode 100644
index 000000000..1da160b33
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/files/kuard-extra-property.yml
@@ -0,0 +1,22 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app: kuard
+ name: kuard
+ namespace: default
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: kuard
+ unwanted: value
+ template:
+ metadata:
+ labels:
+ app: kuard
+ spec:
+ containers:
+ - image: gcr.io/kuar-demo/kuard-amd64:1
+ name: kuard
diff --git a/ansible_collections/community/okd/molecule/default/files/kuard-invalid-type.yml b/ansible_collections/community/okd/molecule/default/files/kuard-invalid-type.yml
new file mode 100644
index 000000000..6ff8018ef
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/files/kuard-invalid-type.yml
@@ -0,0 +1,21 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app: kuard
+ name: kuard
+ namespace: default
+spec:
+ replicas: hello
+ selector:
+ matchLabels:
+ app: kuard
+ template:
+ metadata:
+ labels:
+ app: kuard
+ spec:
+ containers:
+ - image: gcr.io/kuar-demo/kuard-amd64:1
+ name: kuard
diff --git a/ansible_collections/community/okd/molecule/default/files/nginx.env b/ansible_collections/community/okd/molecule/default/files/nginx.env
new file mode 100644
index 000000000..939ad0d7f
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/files/nginx.env
@@ -0,0 +1,12 @@
+# Want to make sure comments don't break it
+export NAME=test123
+NAMESPACE=openshift
+
+
+
+
+# Blank lines should be fine too
+
+# Equals in comments shouldn't break things=True
+MEMORY_LIMIT=1Gi
+
diff --git a/ansible_collections/community/okd/molecule/default/files/pod-template.yaml b/ansible_collections/community/okd/molecule/default/files/pod-template.yaml
new file mode 100644
index 000000000..ac388ad67
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/files/pod-template.yaml
@@ -0,0 +1,23 @@
+---
+kind: Template
+apiVersion: template.openshift.io/v1
+metadata:
+ name: pod-template
+objects:
+ - apiVersion: v1
+ kind: Pod
+ metadata:
+ name: "Pod-${{ NAME }}"
+ spec:
+ containers:
+ - args:
+ - /bin/sh
+ - -c
+ - while true; do echo $(date); sleep 15; done
+ image: python:3.7-alpine
+ imagePullPolicy: Always
+ name: python
+parameters:
+ - name: NAME
+ description: trailing name of the pod
+ required: true
diff --git a/ansible_collections/community/okd/molecule/default/files/setup-crd.yml b/ansible_collections/community/okd/molecule/default/files/setup-crd.yml
new file mode 100644
index 000000000..15debdbdd
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/files/setup-crd.yml
@@ -0,0 +1,53 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ name: certificates.certmanager.k8s.io
+ annotations:
+ "api-approved.kubernetes.io": "https://github.com/kubernetes/kubernetes/pull/78458"
+spec:
+ group: certmanager.k8s.io
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+ schema:
+ openAPIV3Schema:
+ type: object
+ properties:
+ certificate:
+ type: string
+ secretName:
+ type: string
+ dnsNames:
+ type: array
+ items:
+ type: string
+ acme:
+ type: object
+ properties:
+ config:
+ type: array
+ items:
+ type: object
+ properties:
+ ingressClass:
+ type: string
+ domains:
+ type: array
+ items:
+ type: string
+ issuerRef:
+ type: object
+ properties:
+ name:
+ type: string
+ kind:
+ type: string
+ scope: Namespaced
+ names:
+ kind: Certificate
+ plural: certificates
+ shortNames:
+ - cert
+ - certs
diff --git a/ansible_collections/community/okd/molecule/default/files/simple-template.yaml b/ansible_collections/community/okd/molecule/default/files/simple-template.yaml
new file mode 100644
index 000000000..29c85b9cd
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/files/simple-template.yaml
@@ -0,0 +1,34 @@
+---
+apiVersion: template.openshift.io/v1
+kind: Template
+labels:
+ template: simple-example-test
+message: |-
+ The following configmaps have been created in your project: ${NAME}.
+metadata:
+ annotations:
+ description: A super basic template for testing
+ openshift.io/display-name: Super basic template
+ openshift.io/provider-display-name: Red Hat, Inc.
+ tags: quickstart,examples
+ name: simple-example
+objects:
+- apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ annotations:
+ description: Big example
+ name: ${NAME}
+ data:
+ content: "${CONTENT}"
+parameters:
+- description: The name assigned to the ConfigMap
+ displayName: Name
+ name: NAME
+ required: true
+ value: example
+- description: The value for the content key of the configmap
+ displayName: Content
+ name: CONTENT
+ required: true
+ value: ''
diff --git a/ansible_collections/community/okd/molecule/default/molecule.yml b/ansible_collections/community/okd/molecule/default/molecule.yml
new file mode 100644
index 000000000..43407bd26
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/molecule.yml
@@ -0,0 +1,49 @@
+---
+dependency:
+ name: galaxy
+ options:
+ requirements-file: requirements.yml
+driver:
+ name: delegated
+platforms:
+ - name: cluster
+ groups:
+ - k8s
+provisioner:
+ name: ansible
+ log: true
+ options:
+ vvv: True
+ config_options:
+ inventory:
+ enable_plugins: community.okd.openshift
+ lint: |
+ set -e
+ ansible-lint
+ inventory:
+ hosts:
+ plugin: community.okd.openshift
+ host_vars:
+ localhost:
+ virtualenv: ${MOLECULE_EPHEMERAL_DIRECTORY}/virtualenv
+ virtualenv_command: '{{ ansible_playbook_python }} -m virtualenv'
+ virtualenv_interpreter: '{{ virtualenv }}/bin/python'
+ playbook_namespace: molecule-tests
+ env:
+ ANSIBLE_FORCE_COLOR: 'true'
+ ANSIBLE_COLLECTIONS_PATHS: ${OVERRIDE_COLLECTION_PATH:-$MOLECULE_PROJECT_DIRECTORY}
+verifier:
+ name: ansible
+ lint: |
+ set -e
+ ansible-lint
+scenario:
+ name: default
+ test_sequence:
+ - dependency
+ - lint
+ - syntax
+ - prepare
+ - converge
+ - idempotence
+ - verify
diff --git a/ansible_collections/community/okd/molecule/default/prepare.yml b/ansible_collections/community/okd/molecule/default/prepare.yml
new file mode 100644
index 000000000..f155ec1d4
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/prepare.yml
@@ -0,0 +1,61 @@
+---
+- name: Prepare
+ hosts: localhost
+ connection: local
+ gather_facts: no
+
+ tasks:
+ - pip:
+ name: virtualenv
+
+ - pip:
+ name:
+ - kubernetes>=12.0.0
+ - coverage
+ - python-ldap
+ virtualenv: "{{ virtualenv }}"
+ virtualenv_command: "{{ virtualenv_command }}"
+ virtualenv_site_packages: no
+
+ - name: 'Configure htpasswd secret (username: test, password: testing123)'
+ community.okd.k8s:
+ definition:
+ apiVersion: v1
+ kind: Secret
+ metadata:
+ name: htpass-secret
+ namespace: openshift-config
+ stringData:
+ htpasswd: "test:$2y$05$zgjczyp96jCIp//CGmnWiefhd7G3l54IdsZoV4IwA1UWtd04L0lE2"
+
+ - name: Configure htpasswd identity provider
+ community.okd.k8s:
+ definition:
+ apiVersion: config.openshift.io/v1
+ kind: OAuth
+ metadata:
+ name: cluster
+ spec:
+ identityProviders:
+ - name: htpasswd_provider
+ mappingMethod: claim
+ type: HTPasswd
+ htpasswd:
+ fileData:
+ name: htpass-secret
+
+ - name: Create ClusterRoleBinding for test user
+ community.okd.k8s:
+ definition:
+ apiVersion: rbac.authorization.k8s.io/v1
+ kind: ClusterRoleBinding
+ metadata:
+ name: test-cluster-reader
+ roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+ subjects:
+ - apiGroup: rbac.authorization.k8s.io
+ kind: User
+ name: test
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/defaults/main.yml b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/defaults/main.yml
new file mode 100644
index 000000000..b58e2d3ce
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/defaults/main.yml
@@ -0,0 +1,4 @@
+---
+ldap_admin_user: "admin"
+ldap_admin_password: "testing123!"
+ldap_root: "dc=ansible,dc=redhat" \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry.py b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry.py
new file mode 100644
index 000000000..6f5ca4b24
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry.py
@@ -0,0 +1,186 @@
+# 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_ldap_entry
+
+short_description: add/remove entry to LDAP Server.
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - This module perform basic operations on the LDAP Server (add/remove entries).
+ - Similar to `community.general.ldap_entry` this has been created to avoid dependency with this collection for the test.
+ - This module is not supported outside of testing this collection.
+
+options:
+ attributes:
+ description:
+ - If I(state=present), attributes necessary to create an entry. Existing
+ entries are never modified. To assert specific attribute values on an
+ existing entry, use M(community.general.ldap_attrs) module instead.
+ type: dict
+ objectClass:
+ description:
+ - If I(state=present), value or list of values to use when creating
+ the entry. It can either be a string or an actual list of
+ strings.
+ type: list
+ elements: str
+ state:
+ description:
+ - The target state of the entry.
+ choices: [present, absent]
+ default: present
+ type: str
+ bind_dn:
+ description:
+ - A DN to bind with. If this is omitted, we'll try a SASL bind with the EXTERNAL mechanism as default.
+ - If this is blank, we'll use an anonymous bind.
+ type: str
+ required: true
+ bind_pw:
+ description:
+ - The password to use with I(bind_dn).
+ type: str
+ dn:
+ required: true
+ description:
+ - The DN of the entry to add or remove.
+ type: str
+ server_uri:
+ description:
+ - A URI to the LDAP server.
+ - The default value lets the underlying LDAP client library look for a UNIX domain socket in its default location.
+ type: str
+ default: ldapi:///
+
+requirements:
+ - python-ldap
+'''
+
+EXAMPLES = r'''
+'''
+
+
+RETURN = r'''
+# Default return values
+'''
+
+import traceback
+
+from ansible.module_utils.basic import AnsibleModule, missing_required_lib
+from ansible.module_utils.common.text.converters import to_native, to_bytes
+
+LDAP_IMP_ERR = None
+try:
+ import ldap
+ import ldap.modlist
+
+ HAS_LDAP = True
+except ImportError:
+ LDAP_IMP_ERR = traceback.format_exc()
+ HAS_LDAP = False
+
+
+def argument_spec():
+ args = {}
+ args['attributes'] = dict(default={}, type='dict')
+ args['objectClass'] = dict(type='list', elements='str')
+ args['state'] = dict(default='present', choices=['present', 'absent'])
+ args['bind_dn'] = dict(required=True)
+ args['bind_pw'] = dict(default='', no_log=True)
+ args['dn'] = dict(required=True)
+ args['server_uri'] = dict(default='ldapi:///')
+ return args
+
+
+class LdapEntry(AnsibleModule):
+ def __init__(self):
+
+ AnsibleModule.__init__(
+ self,
+ argument_spec=argument_spec(),
+ required_if=[('state', 'present', ['objectClass'])],
+ )
+
+ if not HAS_LDAP:
+ self.fail_json(msg=missing_required_lib('python-ldap'), exception=LDAP_IMP_ERR)
+
+ self.__connection = None
+ # Add the objectClass into the list of attributes
+ self.params['attributes']['objectClass'] = (self.params['objectClass'])
+
+ # Load attributes
+ if self.params['state'] == 'present':
+ self.attrs = {}
+ for name, value in self.params['attributes'].items():
+ if isinstance(value, list):
+ self.attrs[name] = list(map(to_bytes, value))
+ else:
+ self.attrs[name] = [to_bytes(value)]
+
+ @property
+ def connection(self):
+ if not self.__connection:
+ ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
+ self.__connection = ldap.initialize(self.params['server_uri'])
+ try:
+ self.__connection.simple_bind_s(self.params['bind_dn'], self.params['bind_pw'])
+ except ldap.LDAPError as e:
+ self.fail_json(msg="Cannot bind to the server due to: %s" % e)
+ return self.__connection
+
+ def add(self):
+ """ If self.dn does not exist, returns a callable that will add it. """
+ changed = False
+ msg = "LDAP Entry '%s' already exist." % self.params["dn"]
+ if not self._is_entry_present():
+ modlist = ldap.modlist.addModlist(self.attrs)
+ self.connection.add_s(self.params['dn'], modlist)
+ changed = True
+ msg = "LDAP Entry '%s' successfully created." % self.params["dn"]
+ self.exit_json(changed=changed, msg=msg)
+
+ def delete(self):
+ """ If self.dn exists, returns a callable that will delete it. """
+ changed = False
+ msg = "LDAP Entry '%s' does not exist." % self.params["dn"]
+ if self._is_entry_present():
+ self.connection.delete_s(self.params['dn'])
+ changed = True
+ msg = "LDAP Entry '%s' successfully deleted." % self.params["dn"]
+ self.exit_json(changed=changed, msg=msg)
+
+ def _is_entry_present(self):
+ try:
+ self.connection.search_s(self.params['dn'], ldap.SCOPE_BASE)
+ except ldap.NO_SUCH_OBJECT:
+ is_present = False
+ else:
+ is_present = True
+
+ return is_present
+
+ def execute(self):
+ try:
+ if self.params['state'] == 'present':
+ self.add()
+ else:
+ self.delete()
+ except Exception as e:
+ self.fail_json(msg="Entry action failed.", details=to_native(e), exception=traceback.format_exc())
+
+
+def main():
+ module = LdapEntry()
+ module.execute()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry_info.py b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry_info.py
new file mode 100644
index 000000000..ba49f724d
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/library/openshift_ldap_entry_info.py
@@ -0,0 +1,109 @@
+# 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_ldap_entry_info
+
+short_description: Validate entry from LDAP server.
+
+author:
+ - Aubin Bikouo (@abikouo)
+
+description:
+ - This module connect to a ldap server and search for entry.
+ - This module is not supported outside of testing this collection.
+
+options:
+ bind_dn:
+ description:
+ - A DN to bind with. If this is omitted, we'll try a SASL bind with the EXTERNAL mechanism as default.
+ - If this is blank, we'll use an anonymous bind.
+ type: str
+ required: true
+ bind_pw:
+ description:
+ - The password to use with I(bind_dn).
+ type: str
+ required: True
+ dn:
+ description:
+ - The DN of the entry to test.
+ type: str
+ required: True
+ server_uri:
+ description:
+ - A URI to the LDAP server.
+ - The default value lets the underlying LDAP client library look for a UNIX domain socket in its default location.
+ type: str
+ default: ldapi:///
+ required: True
+
+requirements:
+ - python-ldap
+'''
+
+EXAMPLES = r'''
+'''
+
+
+RETURN = r'''
+# Default return values
+'''
+
+import traceback
+
+from ansible.module_utils.basic import AnsibleModule, missing_required_lib
+
+LDAP_IMP_ERR = None
+try:
+ import ldap
+ import ldap.modlist
+ HAS_LDAP = True
+except ImportError:
+ LDAP_IMP_ERR = traceback.format_exc()
+ HAS_LDAP = False
+
+
+def argument_spec():
+ args = {}
+ args['bind_dn'] = dict(required=True)
+ args['bind_pw'] = dict(required=True, no_log=True)
+ args['dn'] = dict(required=True)
+ args['server_uri'] = dict(required=True)
+ return args
+
+
+def execute():
+ module = AnsibleModule(
+ argument_spec=argument_spec(),
+ supports_check_mode=True
+ )
+
+ if not HAS_LDAP:
+ module.fail_json(msg=missing_required_lib("python-ldap"), exception=LDAP_IMP_ERR)
+
+ ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
+ connection = ldap.initialize(module.params['server_uri'])
+ try:
+ connection.simple_bind_s(module.params['bind_dn'], module.params['bind_pw'])
+ except ldap.LDAPError as e:
+ module.fail_json(msg="Cannot bind to the server due to: %s" % e)
+
+ try:
+ connection.search_s(module.params['dn'], ldap.SCOPE_BASE)
+ module.exit_json(changed=False, found=True)
+ except ldap.NO_SUCH_OBJECT:
+ module.exit_json(changed=False, found=False)
+
+
+def main():
+ execute()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/meta/main.yml b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/meta/main.yml
new file mode 100644
index 000000000..eef26f23d
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/meta/main.yml
@@ -0,0 +1,4 @@
+---
+collections:
+ - community.okd
+ - kubernetes.core \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/activeDirectory.yml b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/activeDirectory.yml
new file mode 100644
index 000000000..da99f324e
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/activeDirectory.yml
@@ -0,0 +1,235 @@
+- block:
+ - name: Get LDAP definition
+ set_fact:
+ ldap_entries: "{{ lookup('template', 'ad/definition.j2') | from_yaml }}"
+
+ - name: Delete openshift groups if existing
+ community.okd.k8s:
+ state: absent
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: "{{ item }}"
+ with_items:
+ - admins
+ - developers
+
+ - name: Delete existing LDAP Entries
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item.dn }}"
+ state: absent
+ with_items: "{{ ldap_entries.users + ldap_entries.units | reverse | list }}"
+
+ - name: Create LDAP Entries
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item.dn }}"
+ attributes: "{{ item.attr }}"
+ objectClass: "{{ item.class }}"
+ with_items: "{{ ldap_entries.units + ldap_entries.users }}"
+
+ - name: Load test configurations
+ set_fact:
+ sync_config: "{{ lookup('template', 'ad/sync-config.j2') | from_yaml }}"
+
+ - name: Synchronize Groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ check_mode: yes
+ register: result
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+ - admins_group
+ - devs_group
+ - '"jane.smith@ansible.org" in {{ admins_group.users }}'
+ - '"jim.adams@ansible.org" in {{ admins_group.users }}'
+ - '"jordanbulls@ansible.org" in {{ devs_group.users }}'
+ - admins_group.users | length == 2
+ - devs_group.users | length == 1
+ vars:
+ admins_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'admins') | first }}"
+ devs_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'developers') | first }}"
+
+
+ - name: Synchronize Groups (Remove check_mode)
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ register: result
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+
+ - name: Read admins group
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: admins
+ register: result
+
+ - name: Validate group was created
+ assert:
+ that:
+ - result.resources | length == 1
+ - '"jane.smith@ansible.org" in {{ result.resources.0.users }}'
+ - '"jim.adams@ansible.org" in {{ result.resources.0.users }}'
+
+ - name: Read developers group
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: developers
+ register: result
+
+ - name: Validate group was created
+ assert:
+ that:
+ - result.resources | length == 1
+ - '"jordanbulls@ansible.org" in {{ result.resources.0.users }}'
+
+ - name: Define user dn to delete
+ set_fact:
+ user_to_delete: "cn=Jane,ou=engineers,ou=activeD,{{ ldap_root }}"
+
+ - name: Delete 1 admin user
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ user_to_delete }}"
+ state: absent
+
+ - name: Synchronize Openshift groups using allow_groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ allow_groups:
+ - developers
+ type: openshift
+ register: openshift_sync
+
+ - name: Validate that only developers group was sync
+ assert:
+ that:
+ - openshift_sync is changed
+ - openshift_sync.groups | length == 1
+ - openshift_sync.groups.0.metadata.name == "developers"
+
+ - name: Read admins group
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: admins
+ register: result
+
+ - name: Validate admins group content has not changed
+ assert:
+ that:
+ - result.resources | length == 1
+ - '"jane.smith@ansible.org" in {{ result.resources.0.users }}'
+ - '"jim.adams@ansible.org" in {{ result.resources.0.users }}'
+
+ - name: Synchronize Openshift groups using deny_groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ deny_groups:
+ - developers
+ type: openshift
+ register: openshift_sync
+
+ - name: Validate that only admins group was sync
+ assert:
+ that:
+ - openshift_sync is changed
+ - openshift_sync.groups | length == 1
+ - openshift_sync.groups.0.metadata.name == "admins"
+
+ - name: Read admins group
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: admins
+ register: result
+
+ - name: Validate admins group contains only 1 user now
+ assert:
+ that:
+ - result.resources | length == 1
+ - result.resources.0.users == ["jim.adams@ansible.org"]
+
+ - name: Set users to delete (delete all developers users)
+ set_fact:
+ user_to_delete: "cn=Jordan,ou=engineers,ou=activeD,{{ ldap_root }}"
+
+ - name: Delete 1 admin user
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ user_to_delete }}"
+ state: absent
+
+ - name: Prune groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ state: absent
+ register: result
+
+ - name: Validate result is changed (only developers group be deleted)
+ assert:
+ that:
+ - result is changed
+ - result.groups | length == 1
+
+ - name: Get developers group info
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: developers
+ register: result
+
+ - name: assert group was deleted
+ assert:
+ that:
+ - result.resources | length == 0
+
+ - name: Get admins group info
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: admins
+ register: result
+
+ - name: assert group was not deleted
+ assert:
+ that:
+ - result.resources | length == 1
+
+ - name: Prune groups once again (idempotency)
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ state: absent
+ register: result
+
+ - name: Assert nothing was changed
+ assert:
+ that:
+ - result is not changed
+
+ always:
+ - name: Delete openshift groups if existing
+ community.okd.k8s:
+ state: absent
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: "{{ item }}"
+ with_items:
+ - admins
+ - developers
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/augmentedActiveDirectory.yml b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/augmentedActiveDirectory.yml
new file mode 100644
index 000000000..f70d3bd8e
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/augmentedActiveDirectory.yml
@@ -0,0 +1,174 @@
+- block:
+ - name: Get LDAP definition
+ set_fact:
+ ldap_entries: "{{ lookup('template', 'augmented-ad/definition.j2') | from_yaml }}"
+
+ - name: Delete openshift groups if existing
+ community.okd.k8s:
+ state: absent
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: "{{ item }}"
+ with_items:
+ - banking
+ - insurance
+
+ - name: Delete existing LDAP entries
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item.dn }}"
+ state: absent
+ with_items: "{{ ldap_entries.users + ldap_entries.groups + ldap_entries.units | reverse | list }}"
+
+ - name: Create LDAP Entries
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item.dn }}"
+ attributes: "{{ item.attr }}"
+ objectClass: "{{ item.class }}"
+ with_items: "{{ ldap_entries.units + ldap_entries.groups + ldap_entries.users }}"
+
+ - name: Load test configurations
+ set_fact:
+ sync_config: "{{ lookup('template', 'augmented-ad/sync-config.j2') | from_yaml }}"
+
+ - name: Synchronize Groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ check_mode: yes
+ register: result
+
+ - name: Validate that 'banking' and 'insurance' groups were created
+ assert:
+ that:
+ - result is changed
+ - banking_group
+ - insurance_group
+ - '"james-allan@ansible.org" in {{ banking_group.users }}'
+ - '"gordon-kane@ansible.org" in {{ banking_group.users }}'
+ - '"alice-courtney@ansible.org" in {{ insurance_group.users }}'
+ - banking_group.users | length == 2
+ - insurance_group.users | length == 1
+ vars:
+ banking_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'banking') | first }}"
+ insurance_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'insurance') | first }}"
+
+
+ - name: Synchronize Groups (Remove check_mode)
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ register: result
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+
+ - name: Define facts for group to create
+ set_fact:
+ ldap_groups:
+ - name: banking
+ users:
+ - "james-allan@ansible.org"
+ - "gordon-kane@ansible.org"
+ - name: insurance
+ users:
+ - "alice-courtney@ansible.org"
+
+
+ - name: Read 'banking' openshift group
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: banking
+ register: result
+
+ - name: Validate group info
+ assert:
+ that:
+ - result.resources | length == 1
+ - '"james-allan@ansible.org" in {{ result.resources.0.users }}'
+ - '"gordon-kane@ansible.org" in {{ result.resources.0.users }}'
+
+ - name: Read 'insurance' openshift group
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: insurance
+ register: result
+
+ - name: Validate group info
+ assert:
+ that:
+ - result.resources | length == 1
+ - 'result.resources.0.users == ["alice-courtney@ansible.org"]'
+
+ - name: Delete employee from 'insurance' group
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "cn=Alice,ou=employee,ou=augmentedAD,{{ ldap_root }}"
+ state: absent
+
+ - name: Prune groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ state: absent
+ register: result
+
+ - name: Validate result is changed (only insurance group be deleted)
+ assert:
+ that:
+ - result is changed
+ - result.groups | length == 1
+
+ - name: Get 'insurance' openshift group info
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: insurance
+ register: result
+
+ - name: assert group was deleted
+ assert:
+ that:
+ - result.resources | length == 0
+
+ - name: Get 'banking' openshift group info
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: banking
+ register: result
+
+ - name: assert group was not deleted
+ assert:
+ that:
+ - result.resources | length == 1
+
+ - name: Prune groups once again (idempotency)
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ sync_config }}"
+ state: absent
+ register: result
+
+ - name: Assert no change was made
+ assert:
+ that:
+ - result is not changed
+
+ always:
+ - name: Delete openshift groups if existing
+ community.okd.k8s:
+ state: absent
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: "{{ item }}"
+ with_items:
+ - banking
+ - insurance \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/main.yml b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/main.yml
new file mode 100644
index 000000000..88bfd67f8
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/main.yml
@@ -0,0 +1,62 @@
+---
+- name: Get cluster information
+ kubernetes.core.k8s_cluster_info:
+ register: info
+
+- name: Create LDAP Pod
+ community.okd.k8s:
+ namespace: "default"
+ wait: yes
+ definition:
+ kind: Pod
+ apiVersion: v1
+ metadata:
+ name: ldap-pod
+ labels:
+ app: ldap
+ spec:
+ containers:
+ - name: ldap
+ image: bitnami/openldap
+ env:
+ - name: LDAP_ADMIN_USERNAME
+ value: "{{ ldap_admin_user }}"
+ - name: LDAP_ADMIN_PASSWORD
+ value: "{{ ldap_admin_password }}"
+ - name: LDAP_USERS
+ value: "ansible"
+ - name: LDAP_PASSWORDS
+ value: "ansible123"
+ - name: LDAP_ROOT
+ value: "{{ ldap_root }}"
+ ports:
+ - containerPort: 1389
+ register: pod_info
+
+- name: Set Pod Internal IP
+ set_fact:
+ podIp: "{{ pod_info.result.status.podIP }}"
+
+- name: Set LDAP Common facts
+ set_fact:
+ ldap_server_uri: "ldap://{{ podIp }}:1389"
+ ldap_bind_dn: "cn={{ ldap_admin_user }},{{ ldap_root }}"
+ ldap_bind_pw: "{{ ldap_admin_password }}"
+
+- name: Display LDAP Server URI
+ debug:
+ var: ldap_server_uri
+
+- name: Test existing user from LDAP server
+ openshift_ldap_entry_info:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ dn: "ou=users,{{ ldap_root }}"
+ server_uri: "{{ ldap_server_uri }}"
+ # ignore_errors: true
+ # register: ping_ldap
+
+- include_tasks: "tasks/python-ldap-not-installed.yml"
+- include_tasks: "tasks/rfc2307.yml"
+- include_tasks: "tasks/activeDirectory.yml"
+- include_tasks: "tasks/augmentedActiveDirectory.yml"
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/python-ldap-not-installed.yml b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/python-ldap-not-installed.yml
new file mode 100644
index 000000000..a79af51c2
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/python-ldap-not-installed.yml
@@ -0,0 +1,42 @@
+- block:
+ - name: Create temp directory
+ tempfile:
+ state: directory
+ register: test_dir
+
+ - set_fact:
+ test_dir: "{{ test_dir.path }}"
+
+ - set_fact:
+ venv: "{{ test_dir }}/virtualenv"
+
+ - pip:
+ name:
+ - kubernetes
+ virtualenv: "{{ venv }}"
+ virtualenv_command: "{{ virtualenv_command }}"
+ virtualenv_site_packages: false
+
+ - name: Load test configurations
+ set_fact:
+ configs: "{{ lookup('template', 'rfc2307/sync-config.j2') | from_yaml }}"
+
+ - name: Synchronize Groups without python-ldap
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.simple }}"
+ register: result
+ ignore_errors: true
+ vars:
+ ansible_python_interpreter: "{{ venv }}/bin/python"
+
+ - name: Check that module failed gracefully
+ assert:
+ that:
+ - '"Failed to import the required Python library (python-ldap)" in result.msg'
+
+ always:
+ - name: Remove temp directory
+ file:
+ path: "{{ test_dir }}"
+ state: absent
+ ignore_errors: true
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/rfc2307.yml b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/rfc2307.yml
new file mode 100644
index 000000000..7660bf625
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/tasks/rfc2307.yml
@@ -0,0 +1,468 @@
+- block:
+ - name: Get LDAP definition
+ set_fact:
+ ldap_resources: "{{ lookup('template', 'rfc2307/definition.j2') | from_yaml }}"
+
+ - name: Delete openshift groups if existing
+ community.okd.k8s:
+ state: absent
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: "{{ item }}"
+ with_items:
+ - admins
+ - engineers
+ - developers
+
+ - name: Delete existing LDAP entries
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item.dn }}"
+ state: absent
+ with_items: "{{ ldap_resources.users + ldap_resources.groups + ldap_resources.units | reverse | list }}"
+
+ - name: Create LDAP units
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item.dn }}"
+ attributes: "{{ item.attr }}"
+ objectClass: "{{ item.class }}"
+ with_items: "{{ ldap_resources.units }}"
+
+ - name: Create LDAP Groups
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item.dn }}"
+ attributes: "{{ item.attr }}"
+ objectClass: "{{ item.class }}"
+ with_items: "{{ ldap_resources.groups }}"
+
+ - name: Create LDAP users
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item.dn }}"
+ attributes: "{{ item.attr }}"
+ objectClass: "{{ item.class }}"
+ with_items: "{{ ldap_resources.users }}"
+
+ - name: Load test configurations
+ set_fact:
+ configs: "{{ lookup('template', 'rfc2307/sync-config.j2') | from_yaml }}"
+
+ - name: Synchronize Groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.simple }}"
+ check_mode: yes
+ register: result
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+ - admins_group
+ - devs_group
+ - '"jane.smith@ansible.org" in {{ admins_group.users }}'
+ - '"jim.adams@ansible.org" in {{ devs_group.users }}'
+ - '"jordanbulls@ansible.org" in {{ devs_group.users }}'
+ - admins_group.users | length == 1
+ - devs_group.users | length == 2
+ vars:
+ admins_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'admins') | first }}"
+ devs_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'developers') | first }}"
+
+ - name: Synchronize Groups - User defined mapping
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.user_defined }}"
+ check_mode: yes
+ register: result
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+ - admins_group
+ - devs_group
+ - '"jane.smith@ansible.org" in {{ admins_group.users }}'
+ - '"jim.adams@ansible.org" in {{ devs_group.users }}'
+ - '"jordanbulls@ansible.org" in {{ devs_group.users }}'
+ - admins_group.users | length == 1
+ - devs_group.users | length == 2
+ vars:
+ admins_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'ansible-admins') | first }}"
+ devs_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'ansible-devs') | first }}"
+
+ - name: Synchronize Groups - Using dn for every query
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.dn_everywhere }}"
+ check_mode: yes
+ register: result
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+ - admins_group
+ - devs_group
+ - '"cn=Jane,ou=people,ou=rfc2307,{{ ldap_root }}" in {{ admins_group.users }}'
+ - '"cn=Jim,ou=people,ou=rfc2307,{{ ldap_root }}" in {{ devs_group.users }}'
+ - '"cn=Jordan,ou=people,ou=rfc2307,{{ ldap_root }}" in {{ devs_group.users }}'
+ - admins_group.users | length == 1
+ - devs_group.users | length == 2
+ vars:
+ admins_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'cn=admins,ou=groups,ou=rfc2307,' + ldap_root ) | first }}"
+ devs_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'cn=developers,ou=groups,ou=rfc2307,' + ldap_root ) | first }}"
+
+ - name: Synchronize Groups - Partially user defined mapping
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.partially_user_defined }}"
+ check_mode: yes
+ register: result
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+ - admins_group
+ - devs_group
+ - '"jane.smith@ansible.org" in {{ admins_group.users }}'
+ - '"jim.adams@ansible.org" in {{ devs_group.users }}'
+ - '"jordanbulls@ansible.org" in {{ devs_group.users }}'
+ - admins_group.users | length == 1
+ - devs_group.users | length == 2
+ vars:
+ admins_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'ansible-admins') | first }}"
+ devs_group: "{{ result.groups | selectattr('metadata.name', 'equalto', 'developers') | first }}"
+
+ - name: Delete Group 'engineers' if created before
+ community.okd.k8s:
+ state: absent
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: 'engineers'
+ wait: yes
+ ignore_errors: yes
+
+ - name: Synchronize Groups - Partially user defined mapping
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.out_scope }}"
+ check_mode: yes
+ register: result
+ ignore_errors: yes
+
+ - name: Assert group sync failed due to non-existent member
+ assert:
+ that:
+ - result is failed
+ - result.msg.startswith("Entry not found for base='cn=Matthew,ou=people,ou=outrfc2307,{{ ldap_root }}'")
+
+ - name: Define sync configuration with tolerateMemberNotFoundErrors
+ set_fact:
+ config_out_of_scope_tolerate_not_found: "{{ configs.out_scope | combine({'rfc2307': merge_rfc2307 })}}"
+ vars:
+ merge_rfc2307: "{{ configs.out_scope.rfc2307 | combine({'tolerateMemberNotFoundErrors': 'true'}) }}"
+
+ - name: Synchronize Groups - Partially user defined mapping (tolerateMemberNotFoundErrors=true)
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ config_out_of_scope_tolerate_not_found }}"
+ check_mode: yes
+ register: result
+
+ - name: Assert group sync did not fail (tolerateMemberNotFoundErrors=true)
+ assert:
+ that:
+ - result is changed
+ - result.groups | length == 1
+ - result.groups.0.metadata.name == 'engineers'
+ - result.groups.0.users == ['Abraham']
+
+ - name: Create Group 'engineers'
+ community.okd.k8s:
+ state: present
+ wait: yes
+ definition:
+ kind: Group
+ apiVersion: "user.openshift.io/v1"
+ metadata:
+ name: engineers
+ users: []
+
+ - name: Try to sync LDAP group with Openshift existing group not created using sync should failed
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ config_out_of_scope_tolerate_not_found }}"
+ check_mode: yes
+ register: result
+ ignore_errors: yes
+
+ - name: Validate group sync failed
+ assert:
+ that:
+ - result is failed
+ - '"openshift.io/ldap.host label did not match sync host" in result.msg'
+
+ - name: Define allow_groups and deny_groups groups
+ set_fact:
+ allow_groups:
+ - "cn=developers,ou=groups,ou=rfc2307,{{ ldap_root }}"
+ deny_groups:
+ - "cn=admins,ou=groups,ou=rfc2307,{{ ldap_root }}"
+
+ - name: Synchronize Groups using allow_groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.simple }}"
+ allow_groups: "{{ allow_groups }}"
+ register: result
+ check_mode: yes
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+ - result.groups | length == 1
+ - result.groups.0.metadata.name == "developers"
+
+ - name: Synchronize Groups using deny_groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.simple }}"
+ deny_groups: "{{ deny_groups }}"
+ register: result
+ check_mode: yes
+
+ - name: Validate Group going to be created
+ assert:
+ that:
+ - result is changed
+ - result.groups | length == 1
+ - result.groups.0.metadata.name == "developers"
+
+ - name: Synchronize groups, remove check_mode
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ configs.simple }}"
+ register: result
+
+ - name: Validate result is changed
+ assert:
+ that:
+ - result is changed
+
+ - name: Read Groups
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: admins
+ register: result
+
+ - name: Validate group was created
+ assert:
+ that:
+ - result.resources | length == 1
+ - '"jane.smith@ansible.org" in {{ result.resources.0.users }}'
+
+ - name: Read Groups
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: developers
+ register: result
+
+ - name: Validate group was created
+ assert:
+ that:
+ - result.resources | length == 1
+ - '"jim.adams@ansible.org" in {{ result.resources.0.users }}'
+ - '"jordanbulls@ansible.org" in {{ result.resources.0.users }}'
+
+ - name: Set users to delete (no admins users anymore and only 1 developer kept)
+ set_fact:
+ users_to_delete:
+ - "cn=Jane,ou=people,ou=rfc2307,{{ ldap_root }}"
+ - "cn=Jim,ou=people,ou=rfc2307,{{ ldap_root }}"
+
+ - name: Delete users from LDAP servers
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item }}"
+ state: absent
+ with_items: "{{ users_to_delete }}"
+
+ - name: Define sync configuration with tolerateMemberNotFoundErrors
+ set_fact:
+ config_simple_tolerate_not_found: "{{ configs.simple | combine({'rfc2307': merge_rfc2307 })}}"
+ vars:
+ merge_rfc2307: "{{ configs.simple.rfc2307 | combine({'tolerateMemberNotFoundErrors': 'true'}) }}"
+
+ - name: Synchronize groups once again after users deletion
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ config_simple_tolerate_not_found }}"
+ register: result
+
+ - name: Validate result is changed
+ assert:
+ that:
+ - result is changed
+
+ - name: Read Groups
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: admins
+ register: result
+
+ - name: Validate admins group does not contains users anymore
+ assert:
+ that:
+ - result.resources | length == 1
+ - result.resources.0.users == []
+
+ - name: Read Groups
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: developers
+ register: result
+
+ - name: Validate group was created
+ assert:
+ that:
+ - result.resources | length == 1
+ - '"jordanbulls@ansible.org" in {{ result.resources.0.users }}'
+
+ - name: Set group to delete
+ set_fact:
+ groups_to_delete:
+ - "cn=developers,ou=groups,ou=rfc2307,{{ ldap_root }}"
+
+ - name: Delete Group from LDAP servers
+ openshift_ldap_entry:
+ bind_dn: "{{ ldap_bind_dn }}"
+ bind_pw: "{{ ldap_bind_pw }}"
+ server_uri: "{{ ldap_server_uri }}"
+ dn: "{{ item }}"
+ state: absent
+ with_items: "{{ groups_to_delete }}"
+
+ - name: Prune groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ config_simple_tolerate_not_found }}"
+ state: absent
+ register: result
+ check_mode: yes
+
+ - name: Validate that only developers group is candidate for Prune
+ assert:
+ that:
+ - result is changed
+ - result.groups | length == 1
+ - result.groups.0.metadata.name == "developers"
+
+ - name: Read Group (validate that check_mode did not performed update in the cluster)
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: developers
+ register: result
+
+ - name: Assert group was found
+ assert:
+ that:
+ - result.resources | length == 1
+
+ - name: Prune using allow_groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ config_simple_tolerate_not_found }}"
+ allow_groups:
+ - developers
+ state: absent
+ register: result
+ check_mode: yes
+
+ - name: assert developers group was candidate for prune
+ assert:
+ that:
+ - result is changed
+ - result.groups | length == 1
+ - result.groups.0.metadata.name == "developers"
+
+ - name: Prune using deny_groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ config_simple_tolerate_not_found }}"
+ deny_groups:
+ - developers
+ state: absent
+ register: result
+ check_mode: yes
+
+ - name: assert nothing found candidate for prune
+ assert:
+ that:
+ - result is not changed
+ - result.groups | length == 0
+
+ - name: Prune groups
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ config_simple_tolerate_not_found }}"
+ state: absent
+ register: result
+
+ - name: Validate result is changed
+ assert:
+ that:
+ - result is changed
+ - result.groups | length == 1
+
+ - name: Get developers group info
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: developers
+ register: result
+
+ - name: assert group was deleted
+ assert:
+ that:
+ - result.resources | length == 0
+
+ - name: Get admins group info
+ kubernetes.core.k8s_info:
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: admins
+ register: result
+
+ - name: assert group was not deleted
+ assert:
+ that:
+ - result.resources | length == 1
+
+ - name: Prune groups once again (idempotency)
+ community.okd.openshift_adm_groups_sync:
+ config: "{{ config_simple_tolerate_not_found }}"
+ state: absent
+ register: result
+
+ - name: Assert nothing changed
+ assert:
+ that:
+ - result is not changed
+ - result.groups | length == 0
+
+ always:
+ - name: Delete openshift groups if existing
+ community.okd.k8s:
+ state: absent
+ kind: Group
+ version: "user.openshift.io/v1"
+ name: "{{ item }}"
+ with_items:
+ - admins
+ - engineers
+ - developers \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/ad/definition.j2 b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/ad/definition.j2
new file mode 100644
index 000000000..f1cc6d9f0
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/ad/definition.j2
@@ -0,0 +1,39 @@
+units:
+ - dn: "ou=activeD,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: activeD
+ - dn: "ou=engineers,ou=activeD,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: engineers
+users:
+ - dn: cn=Jane,ou=engineers,ou=activeD,{{ ldap_root }}
+ class:
+ - inetOrgPerson
+ attr:
+ cn: Jane
+ sn: Smith
+ displayName: Jane Smith
+ mail: jane.smith@ansible.org
+ employeeType: admins
+ - dn: cn=Jim,ou=engineers,ou=activeD,{{ ldap_root }}
+ class:
+ - inetOrgPerson
+ attr:
+ cn: Jim
+ sn: Adams
+ displayName: Jim Adams
+ mail: jim.adams@ansible.org
+ employeeType: admins
+ - dn: cn=Jordan,ou=engineers,ou=activeD,{{ ldap_root }}
+ class:
+ - inetOrgPerson
+ attr:
+ cn: Jordan
+ sn: Bulls
+ displayName: Jordan Bulls
+ mail: jordanbulls@ansible.org
+ employeeType: developers \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/ad/sync-config.j2 b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/ad/sync-config.j2
new file mode 100644
index 000000000..5599affa8
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/ad/sync-config.j2
@@ -0,0 +1,12 @@
+kind: LDAPSyncConfig
+apiVersion: v1
+url: "{{ ldap_server_uri }}"
+insecure: true
+activeDirectory:
+ usersQuery:
+ baseDN: "ou=engineers,ou=activeD,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ filter: (objectclass=inetOrgPerson)
+ userNameAttributes: [ mail ]
+ groupMembershipAttributes: [ employeeType ] \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/augmented-ad/definition.j2 b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/augmented-ad/definition.j2
new file mode 100644
index 000000000..039c30828
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/augmented-ad/definition.j2
@@ -0,0 +1,59 @@
+units:
+ - dn: "ou=augmentedAD,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: augmentedAD
+ - dn: "ou=employee,ou=augmentedAD,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: employee
+ - dn: "ou=category,ou=augmentedAD,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: category
+groups:
+ - dn: "cn=banking,ou=category,ou=augmentedAD,{{ ldap_root }}"
+ class:
+ - groupOfNames
+ attr:
+ cn: banking
+ description: Banking employees
+ member:
+ - cn=James,ou=employee,ou=augmentedAD,{{ ldap_root }}
+ - cn=Gordon,ou=employee,ou=augmentedAD,{{ ldap_root }}
+ - dn: "cn=insurance,ou=category,ou=augmentedAD,{{ ldap_root }}"
+ class:
+ - groupOfNames
+ attr:
+ cn: insurance
+ description: Insurance employees
+ member:
+ - cn=Alice,ou=employee,ou=augmentedAD,{{ ldap_root }}
+users:
+ - dn: cn=James,ou=employee,ou=augmentedAD,{{ ldap_root }}
+ class:
+ - inetOrgPerson
+ attr:
+ cn: James
+ sn: Allan
+ mail: james-allan@ansible.org
+ businessCategory: cn=banking,ou=category,ou=augmentedAD,{{ ldap_root }}
+ - dn: cn=Gordon,ou=employee,ou=augmentedAD,{{ ldap_root }}
+ class:
+ - inetOrgPerson
+ attr:
+ cn: Gordon
+ sn: Kane
+ mail: gordon-kane@ansible.org
+ businessCategory: cn=banking,ou=category,ou=augmentedAD,{{ ldap_root }}
+ - dn: cn=Alice,ou=employee,ou=augmentedAD,{{ ldap_root }}
+ class:
+ - inetOrgPerson
+ attr:
+ cn: Alice
+ sn: Courtney
+ mail: alice-courtney@ansible.org
+ businessCategory: cn=insurance,ou=category,ou=augmentedAD,{{ ldap_root }} \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/augmented-ad/sync-config.j2 b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/augmented-ad/sync-config.j2
new file mode 100644
index 000000000..197204465
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/augmented-ad/sync-config.j2
@@ -0,0 +1,20 @@
+kind: LDAPSyncConfig
+apiVersion: v1
+url: "{{ ldap_server_uri }}"
+insecure: true
+augmentedActiveDirectory:
+ groupsQuery:
+ baseDN: "ou=category,ou=augmentedAD,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ pageSize: 0
+ groupUIDAttribute: dn
+ groupNameAttributes: [ cn ]
+ usersQuery:
+ baseDN: "ou=employee,ou=augmentedAD,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ filter: (objectclass=inetOrgPerson)
+ pageSize: 0
+ userNameAttributes: [ mail ]
+ groupMembershipAttributes: [ businessCategory ] \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/rfc2307/definition.j2 b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/rfc2307/definition.j2
new file mode 100644
index 000000000..521eaee98
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/rfc2307/definition.j2
@@ -0,0 +1,102 @@
+units:
+ - dn: "ou=rfc2307,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: rfc2307
+ - dn: "ou=groups,ou=rfc2307,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: groups
+ - dn: "ou=people,ou=rfc2307,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: people
+ - dn: "ou=outrfc2307,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: outrfc2307
+ - dn: "ou=groups,ou=outrfc2307,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: groups
+ - dn: "ou=people,ou=outrfc2307,{{ ldap_root }}"
+ class:
+ - organizationalUnit
+ attr:
+ ou: people
+groups:
+ - dn: "cn=admins,ou=groups,ou=rfc2307,{{ ldap_root }}"
+ class:
+ - groupOfNames
+ attr:
+ cn: admins
+ description: System Administrators
+ member:
+ - cn=Jane,ou=people,ou=rfc2307,{{ ldap_root }}
+ - dn: "cn=developers,ou=groups,ou=rfc2307,{{ ldap_root }}"
+ class:
+ - groupOfNames
+ attr:
+ cn: developers
+ description: Developers
+ member:
+ - cn=Jim,ou=people,ou=rfc2307,{{ ldap_root }}
+ - cn=Jordan,ou=people,ou=rfc2307,{{ ldap_root }}
+ - dn: "cn=engineers,ou=groups,ou=outrfc2307,{{ ldap_root }}"
+ class:
+ - groupOfNames
+ attr:
+ cn: engineers
+ description: Engineers
+ member:
+ - cn=Jim,ou=people,ou=rfc2307,{{ ldap_root }}
+ - cn=Jordan,ou=people,ou=rfc2307,{{ ldap_root }}
+ - cn=Julia,ou=people,ou=outrfc2307,{{ ldap_root }}
+ - cn=Matthew,ou=people,ou=outrfc2307,{{ ldap_root }}
+users:
+ - dn: cn=Jane,ou=people,ou=rfc2307,{{ ldap_root }}
+ class:
+ - person
+ - organizationalPerson
+ - inetOrgPerson
+ attr:
+ cn: Jane
+ sn: Smith
+ displayName: Jane Smith
+ mail: jane.smith@ansible.org
+ admin: yes
+ - dn: cn=Jim,ou=people,ou=rfc2307,{{ ldap_root }}
+ class:
+ - person
+ - organizationalPerson
+ - inetOrgPerson
+ attr:
+ cn: Jim
+ sn: Adams
+ displayName: Jim Adams
+ mail: jim.adams@ansible.org
+ - dn: cn=Jordan,ou=people,ou=rfc2307,{{ ldap_root }}
+ class:
+ - person
+ - organizationalPerson
+ - inetOrgPerson
+ attr:
+ cn: Jordan
+ sn: Bulls
+ displayName: Jordan Bulls
+ mail: jordanbulls@ansible.org
+ - dn: cn=Julia,ou=people,ou=outrfc2307,{{ ldap_root }}
+ class:
+ - person
+ - organizationalPerson
+ - inetOrgPerson
+ attr:
+ cn: Julia
+ sn: Abraham
+ displayName: Julia Abraham
+ mail: juliaabraham@ansible.org \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/rfc2307/sync-config.j2 b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/rfc2307/sync-config.j2
new file mode 100644
index 000000000..70198ca4b
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/roles/openshift_adm_groups/templates/rfc2307/sync-config.j2
@@ -0,0 +1,105 @@
+simple:
+ kind: LDAPSyncConfig
+ apiVersion: v1
+ url: "{{ ldap_server_uri }}"
+ insecure: true
+ rfc2307:
+ groupsQuery:
+ baseDN: "ou=groups,ou=rfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ filter: (objectclass=groupOfNames)
+ groupUIDAttribute: dn
+ groupNameAttributes: [ cn ]
+ groupMembershipAttributes: [ member ]
+ usersQuery:
+ baseDN: "ou=people,ou=rfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ userUIDAttribute: dn
+ userNameAttributes: [ mail ]
+user_defined:
+ kind: LDAPSyncConfig
+ apiVersion: v1
+ url: "{{ ldap_server_uri }}"
+ insecure: true
+ groupUIDNameMapping:
+ "cn=admins,ou=groups,ou=rfc2307,{{ ldap_root }}": ansible-admins
+ "cn=developers,ou=groups,ou=rfc2307,{{ ldap_root }}": ansible-devs
+ rfc2307:
+ groupsQuery:
+ baseDN: "ou=groups,ou=rfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ filter: (objectclass=groupOfNames)
+ groupUIDAttribute: dn
+ groupNameAttributes: [ cn ]
+ groupMembershipAttributes: [ member ]
+ usersQuery:
+ baseDN: "ou=people,ou=rfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ userUIDAttribute: dn
+ userNameAttributes: [ mail ]
+partially_user_defined:
+ kind: LDAPSyncConfig
+ apiVersion: v1
+ url: "{{ ldap_server_uri }}"
+ insecure: true
+ groupUIDNameMapping:
+ "cn=admins,ou=groups,ou=rfc2307,{{ ldap_root }}": ansible-admins
+ rfc2307:
+ groupsQuery:
+ baseDN: "ou=groups,ou=rfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ filter: (objectclass=groupOfNames)
+ groupUIDAttribute: dn
+ groupNameAttributes: [ cn ]
+ groupMembershipAttributes: [ member ]
+ usersQuery:
+ baseDN: "ou=people,ou=rfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ userUIDAttribute: dn
+ userNameAttributes: [ mail ]
+dn_everywhere:
+ kind: LDAPSyncConfig
+ apiVersion: v1
+ url: "{{ ldap_server_uri }}"
+ insecure: true
+ rfc2307:
+ groupsQuery:
+ baseDN: "ou=groups,ou=rfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ filter: (objectclass=groupOfNames)
+ groupUIDAttribute: dn
+ groupNameAttributes: [ dn ]
+ groupMembershipAttributes: [ member ]
+ usersQuery:
+ baseDN: "ou=people,ou=rfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ userUIDAttribute: dn
+ userNameAttributes: [ dn ]
+out_scope:
+ kind: LDAPSyncConfig
+ apiVersion: v1
+ url: "{{ ldap_server_uri }}"
+ insecure: true
+ rfc2307:
+ groupsQuery:
+ baseDN: "ou=groups,ou=outrfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ filter: (objectclass=groupOfNames)
+ groupUIDAttribute: dn
+ groupNameAttributes: [ cn ]
+ groupMembershipAttributes: [ member ]
+ usersQuery:
+ baseDN: "ou=people,ou=outrfc2307,{{ ldap_root }}"
+ scope: sub
+ derefAliases: never
+ userUIDAttribute: dn
+ userNameAttributes: [ sn ] \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml
new file mode 100644
index 000000000..4de4894e2
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml
@@ -0,0 +1,318 @@
+- block:
+ - set_fact:
+ test_sa: "clusterrole-sa"
+ test_ns: "clusterrole-ns"
+
+ - name: Ensure namespace
+ kubernetes.core.k8s:
+ kind: Namespace
+ name: "{{ test_ns }}"
+
+ - name: Get cluster information
+ kubernetes.core.k8s_cluster_info:
+ register: cluster_info
+ no_log: true
+
+ - set_fact:
+ cluster_host: "{{ cluster_info['connection']['host'] }}"
+
+ - name: Create Service account
+ kubernetes.core.k8s:
+ definition:
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: "{{ test_sa }}"
+ namespace: "{{ test_ns }}"
+
+ - name: Read Service Account
+ kubernetes.core.k8s_info:
+ kind: ServiceAccount
+ namespace: "{{ test_ns }}"
+ name: "{{ test_sa }}"
+ register: result
+
+ - set_fact:
+ secret_token: "{{ result.resources[0]['secrets'][0]['name'] }}"
+
+ - name: Get secret details
+ kubernetes.core.k8s_info:
+ kind: Secret
+ namespace: '{{ test_ns }}'
+ name: '{{ secret_token }}'
+ register: _secret
+ retries: 10
+ delay: 10
+ until:
+ - ("'openshift.io/token-secret.value' in _secret.resources[0]['metadata']['annotations']") or ("'token' in _secret.resources[0]['data']")
+
+ - set_fact:
+ api_token: "{{ _secret.resources[0]['metadata']['annotations']['openshift.io/token-secret.value'] }}"
+ when: "'openshift.io/token-secret.value' in _secret.resources[0]['metadata']['annotations']"
+
+ - set_fact:
+ api_token: "{{ _secret.resources[0]['data']['token'] | b64decode }}"
+ when: "'token' in _secret.resources[0]['data']"
+
+ - name: list Node should failed (forbidden user)
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Node
+ register: error
+ ignore_errors: true
+
+ - assert:
+ that:
+ - '"nodes is forbidden: User" in error.msg'
+
+ - name: list Pod for all namespace should failed
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Pod
+ register: error
+ ignore_errors: true
+
+ - assert:
+ that:
+ - '"pods is forbidden: User" in error.msg'
+
+ - name: list Pod for test namespace should failed
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Pod
+ namespace: "{{ test_ns }}"
+ register: error
+ ignore_errors: true
+
+ - assert:
+ that:
+ - '"pods is forbidden: User" in error.msg'
+
+ - set_fact:
+ test_labels:
+ phase: dev
+ cluster_roles:
+ - name: pod-manager
+ resources:
+ - pods
+ verbs:
+ - list
+ api_version_binding: "authorization.openshift.io/v1"
+ - name: node-manager
+ resources:
+ - nodes
+ verbs:
+ - list
+ api_version_binding: "rbac.authorization.k8s.io/v1"
+
+ - name: Create cluster roles
+ kubernetes.core.k8s:
+ definition:
+ kind: ClusterRole
+ apiVersion: "rbac.authorization.k8s.io/v1"
+ metadata:
+ name: "{{ item.name }}"
+ labels: "{{ test_labels }}"
+ rules:
+ - apiGroups: [""]
+ resources: "{{ item.resources }}"
+ verbs: "{{ item.verbs }}"
+ with_items: '{{ cluster_roles }}'
+
+ - name: Create Role Binding (namespaced)
+ kubernetes.core.k8s:
+ definition:
+ kind: RoleBinding
+ apiVersion: "rbac.authorization.k8s.io/v1"
+ metadata:
+ name: "{{ cluster_roles[0].name }}-binding"
+ namespace: "{{ test_ns }}"
+ labels: "{{ test_labels }}"
+ subjects:
+ - kind: ServiceAccount
+ name: "{{ test_sa }}"
+ namespace: "{{ test_ns }}"
+ apiGroup: ""
+ roleRef:
+ kind: ClusterRole
+ name: "{{ cluster_roles[0].name }}"
+ apiGroup: ""
+
+ - name: list Pod for all namespace should failed
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Pod
+ register: error
+ ignore_errors: true
+
+ - assert:
+ that:
+ - '"pods is forbidden: User" in error.msg'
+
+ - name: list Pod for test namespace should succeed
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Pod
+ namespace: "{{ test_ns }}"
+ no_log: true
+
+ - name: Create Cluster role Binding
+ kubernetes.core.k8s:
+ definition:
+ kind: ClusterRoleBinding
+ apiVersion: "{{ item.api_version_binding }}"
+ metadata:
+ name: "{{ item.name }}-binding"
+ labels: "{{ test_labels }}"
+ subjects:
+ - kind: ServiceAccount
+ name: "{{ test_sa }}"
+ namespace: "{{ test_ns }}"
+ apiGroup: ""
+ roleRef:
+ kind: ClusterRole
+ name: "{{ item.name }}"
+ apiGroup: ""
+ with_items: "{{ cluster_roles }}"
+
+ - name: list Pod for all namespace should succeed
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Pod
+ no_log: true
+
+ - name: list Pod for test namespace should succeed
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Pod
+ namespace: "{{ test_ns }}"
+ no_log: true
+
+ - name: list Node using ServiceAccount
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Node
+ namespace: "{{ test_ns }}"
+ no_log: true
+
+ - name: Prune clusterroles (check mode)
+ community.okd.openshift_adm_prune_auth:
+ resource: clusterroles
+ label_selectors:
+ - phase=dev
+ register: check
+ check_mode: true
+
+ - name: validate clusterrole binding candidates for prune
+ assert:
+ that:
+ - '"{{ item.name }}-binding" in check.cluster_role_binding'
+ - '"{{ test_ns }}/{{ cluster_roles[0].name }}-binding" in check.role_binding'
+ with_items: "{{ cluster_roles }}"
+
+ - name: Prune Cluster Role for managing Pod
+ community.okd.openshift_adm_prune_auth:
+ resource: clusterroles
+ name: "{{ cluster_roles[0].name }}"
+
+ - name: list Pod for all namespace should failed
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Pod
+ register: error
+ no_log: true
+ ignore_errors: true
+
+ - assert:
+ that:
+ - '"pods is forbidden: User" in error.msg'
+
+ - name: list Pod for test namespace should failed
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Pod
+ namespace: "{{ test_ns }}"
+ register: error
+ no_log: true
+ ignore_errors: true
+
+ - assert:
+ that:
+ - '"pods is forbidden: User" in error.msg'
+
+ - name: list Node using ServiceAccount
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Node
+ namespace: "{{ test_ns }}"
+ no_log: true
+
+ - name: Prune clusterroles (remaining)
+ community.okd.openshift_adm_prune_auth:
+ resource: clusterroles
+ label_selectors:
+ - phase=dev
+
+ - name: list Node using ServiceAccount should fail
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ kind: Node
+ namespace: "{{ test_ns }}"
+ register: error
+ ignore_errors: true
+
+ - assert:
+ that:
+ - '"nodes is forbidden: User" in error.msg'
+
+ always:
+ - name: Ensure namespace is deleted
+ kubernetes.core.k8s:
+ state: absent
+ kind: Namespace
+ name: "{{ test_ns }}"
+ ignore_errors: true
+
+ - name: Delete ClusterRoleBinding
+ kubernetes.core.k8s:
+ kind: ClusterRoleBinding
+ api_version: "rbac.authorization.k8s.io/v1"
+ name: "{{ item.name }}-binding"
+ state: absent
+ ignore_errors: true
+ with_items: "{{ cluster_roles }}"
+ when: cluster_roles is defined
+
+ - name: Delete ClusterRole
+ kubernetes.core.k8s:
+ kind: ClusterRole
+ api_version: "rbac.authorization.k8s.io/v1"
+ name: "{{ item.name }}"
+ state: absent
+ ignore_errors: true
+ with_items: "{{ cluster_roles }}"
+ when: cluster_roles is defined
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_auth_roles.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_auth_roles.yml
new file mode 100644
index 000000000..1724a1938
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_auth_roles.yml
@@ -0,0 +1,340 @@
+- block:
+ - set_fact:
+ test_ns: "prune-roles"
+ sa_name: "roles-sa"
+ pod_name: "pod-prune"
+ role_definition:
+ - name: pod-list
+ labels:
+ action: list
+ verbs:
+ - list
+ role_binding:
+ api_version: rbac.authorization.k8s.io/v1
+ - name: pod-create
+ labels:
+ action: create
+ verbs:
+ - create
+ - get
+ role_binding:
+ api_version: authorization.openshift.io/v1
+ - name: pod-delete
+ labels:
+ action: delete
+ verbs:
+ - delete
+ role_binding:
+ api_version: rbac.authorization.k8s.io/v1
+
+ - name: Ensure namespace
+ kubernetes.core.k8s:
+ kind: Namespace
+ name: '{{ test_ns }}'
+
+ - name: Get cluster information
+ kubernetes.core.k8s_cluster_info:
+ register: cluster_info
+ no_log: true
+
+ - set_fact:
+ cluster_host: "{{ cluster_info['connection']['host'] }}"
+
+ - name: Create Service account
+ kubernetes.core.k8s:
+ definition:
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: '{{ sa_name }}'
+ namespace: '{{ test_ns }}'
+
+ - name: Read Service Account
+ kubernetes.core.k8s_info:
+ kind: ServiceAccount
+ namespace: '{{ test_ns }}'
+ name: '{{ sa_name }}'
+ register: sa_out
+
+ - set_fact:
+ secret_token: "{{ sa_out.resources[0]['secrets'][0]['name'] }}"
+
+ - name: Get secret details
+ kubernetes.core.k8s_info:
+ kind: Secret
+ namespace: '{{ test_ns }}'
+ name: '{{ secret_token }}'
+ register: r_secret
+ retries: 10
+ delay: 10
+ until:
+ - ("'openshift.io/token-secret.value' in r_secret.resources[0]['metadata']['annotations']") or ("'token' in r_secret.resources[0]['data']")
+
+ - set_fact:
+ api_token: "{{ r_secret.resources[0]['metadata']['annotations']['openshift.io/token-secret.value'] }}"
+ when: "'openshift.io/token-secret.value' in r_secret.resources[0]['metadata']['annotations']"
+
+ - set_fact:
+ api_token: "{{ r_secret.resources[0]['data']['token'] | b64decode }}"
+ when: "'token' in r_secret.resources[0]['data']"
+
+ - name: list resources using service account
+ kubernetes.core.k8s_info:
+ api_key: '{{ api_token }}'
+ host: '{{ cluster_host }}'
+ validate_certs: no
+ kind: Pod
+ namespace: '{{ test_ns }}'
+ register: error
+ ignore_errors: true
+
+ - assert:
+ that:
+ - '"pods is forbidden: User" in error.msg'
+
+ - name: Create a role to manage Pod from namespace "{{ test_ns }}"
+ kubernetes.core.k8s:
+ definition:
+ kind: Role
+ apiVersion: rbac.authorization.k8s.io/v1
+ metadata:
+ namespace: "{{ test_ns }}"
+ name: "{{ item.name }}"
+ labels: "{{ item.labels }}"
+ rules:
+ - apiGroups: [""]
+ resources: ["pods"]
+ verbs: "{{ item.verbs }}"
+ with_items: "{{ role_definition }}"
+
+ - name: Create Role Binding
+ kubernetes.core.k8s:
+ definition:
+ kind: RoleBinding
+ apiVersion: "{{ item.role_binding.api_version }}"
+ metadata:
+ name: "{{ item.name }}-bind"
+ namespace: "{{ test_ns }}"
+ subjects:
+ - kind: ServiceAccount
+ name: "{{ sa_name }}"
+ namespace: "{{ test_ns }}"
+ apiGroup: ""
+ roleRef:
+ kind: Role
+ name: "{{ item.name }}"
+ namespace: "{{ test_ns }}"
+ apiGroup: ""
+ with_items: "{{ role_definition }}"
+
+ - name: Create Pod should succeed
+ kubernetes.core.k8s:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ namespace: "{{ test_ns }}"
+ definition:
+ kind: Pod
+ metadata:
+ name: "{{ pod_name }}"
+ spec:
+ containers:
+ - name: python
+ image: python:3.7-alpine
+ command:
+ - /bin/sh
+ - -c
+ - while true; do echo $(date); sleep 15; done
+ imagePullPolicy: IfNotPresent
+ register: result
+
+ - name: assert pod creation succeed
+ assert:
+ that:
+ - result is successful
+
+ - name: List Pod
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ namespace: "{{ test_ns }}"
+ kind: Pod
+ register: result
+
+ - name: assert user is still authorize to list pods
+ assert:
+ that:
+ - result is successful
+
+ - name: Prune auth roles (check mode)
+ community.okd.openshift_adm_prune_auth:
+ resource: roles
+ namespace: "{{ test_ns }}"
+ register: check
+ check_mode: true
+
+ - name: validate that list role binding are candidates for prune
+ assert:
+ that: '"{{ test_ns }}/{{ item.name }}-bind" in check.role_binding'
+ with_items: "{{ role_definition }}"
+
+ - name: Prune resource using label_selectors option
+ community.okd.openshift_adm_prune_auth:
+ resource: roles
+ namespace: "{{ test_ns }}"
+ label_selectors:
+ - action=delete
+ register: prune
+
+ - name: assert that role binding 'delete' was pruned
+ assert:
+ that:
+ - prune is changed
+ - '"{{ test_ns }}/{{ role_definition[2].name }}-bind" in check.role_binding'
+
+ - name: assert that user could not delete pod anymore
+ kubernetes.core.k8s:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ state: absent
+ namespace: "{{ test_ns }}"
+ kind: Pod
+ name: "{{ pod_name }}"
+ register: result
+ ignore_errors: true
+
+ - name: assert pod deletion failed due to forbidden user
+ assert:
+ that:
+ - '"forbidden: User" in error.msg'
+
+ - name: List Pod
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ namespace: "{{ test_ns }}"
+ kind: Pod
+ register: result
+
+ - name: assert user is still able to list pods
+ assert:
+ that:
+ - result is successful
+
+ - name: Create Pod should succeed
+ kubernetes.core.k8s:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ namespace: "{{ test_ns }}"
+ definition:
+ kind: Pod
+ metadata:
+ name: "{{ pod_name }}-1"
+ spec:
+ containers:
+ - name: python
+ image: python:3.7-alpine
+ command:
+ - /bin/sh
+ - -c
+ - while true; do echo $(date); sleep 15; done
+ imagePullPolicy: IfNotPresent
+ register: result
+
+ - name: assert user is still authorize to create pod
+ assert:
+ that:
+ - result is successful
+
+ - name: Prune role using name
+ community.okd.openshift_adm_prune_auth:
+ resource: roles
+ namespace: "{{ test_ns }}"
+ name: "{{ role_definition[1].name }}"
+ register: prune
+
+ - name: assert that role binding 'create' was pruned
+ assert:
+ that:
+ - prune is changed
+ - '"{{ test_ns }}/{{ role_definition[1].name }}-bind" in check.role_binding'
+
+ - name: Create Pod (should failed)
+ kubernetes.core.k8s:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ namespace: "{{ test_ns }}"
+ definition:
+ kind: Pod
+ metadata:
+ name: "{{ pod_name }}-2"
+ spec:
+ containers:
+ - name: python
+ image: python:3.7-alpine
+ command:
+ - /bin/sh
+ - -c
+ - while true; do echo $(date); sleep 15; done
+ imagePullPolicy: IfNotPresent
+ register: result
+ ignore_errors: true
+
+ - name: assert user is not authorize to create pod anymore
+ assert:
+ that:
+ - '"forbidden: User" in error.msg'
+
+ - name: List Pod
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ namespace: "{{ test_ns }}"
+ kind: Pod
+ register: result
+
+ - name: assert user is still able to list pods
+ assert:
+ that:
+ - result is successful
+
+ - name: Prune all role for namespace (neither name nor label_selectors are specified)
+ community.okd.openshift_adm_prune_auth:
+ resource: roles
+ namespace: "{{ test_ns }}"
+ register: prune
+
+ - name: assert that role binding 'list' was pruned
+ assert:
+ that:
+ - prune is changed
+ - '"{{ test_ns }}/{{ role_definition[0].name }}-bind" in check.role_binding'
+
+ - name: List Pod
+ kubernetes.core.k8s_info:
+ api_key: "{{ api_token }}"
+ host: "{{ cluster_host }}"
+ validate_certs: no
+ namespace: "{{ test_ns }}"
+ kind: Pod
+ register: result
+ ignore_errors: true
+
+ - name: assert user is not authorize to list pod anymore
+ assert:
+ that:
+ - '"forbidden: User" in error.msg'
+
+ always:
+ - name: Ensure namespace is deleted
+ kubernetes.core.k8s:
+ state: absent
+ kind: Namespace
+ name: "{{ test_ns }}"
+ ignore_errors: true
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_deployments.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_deployments.yml
new file mode 100644
index 000000000..baa024188
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_adm_prune_deployments.yml
@@ -0,0 +1,269 @@
+- name: Prune deployments
+ block:
+ - set_fact:
+ dc_name: "hello"
+ deployment_ns: "prune-deployments"
+ deployment_ns_2: "prune-deployments-2"
+
+
+ - name: Ensure namespace
+ community.okd.k8s:
+ kind: Namespace
+ name: '{{ deployment_ns }}'
+
+ - name: Create deployment config
+ community.okd.k8s:
+ namespace: '{{ deployment_ns }}'
+ definition:
+ kind: DeploymentConfig
+ apiVersion: apps.openshift.io/v1
+ metadata:
+ name: '{{ dc_name }}'
+ spec:
+ replicas: 1
+ selector:
+ name: '{{ dc_name }}'
+ template:
+ metadata:
+ labels:
+ name: '{{ dc_name }}'
+ spec:
+ containers:
+ - name: hello-openshift
+ imagePullPolicy: IfNotPresent
+ image: python:3.7-alpine
+ command: [ "/bin/sh", "-c", "while true;do date;sleep 2s; done"]
+ wait: yes
+
+ - name: prune deployments (no candidate DeploymentConfig)
+ community.okd.openshift_adm_prune_deployments:
+ namespace: "{{ deployment_ns }}"
+ register: test_prune
+
+ - assert:
+ that:
+ - test_prune is not changed
+ - test_prune.replication_controllers | length == 0
+
+ - name: Update DeploymentConfig - set replicas to 0
+ community.okd.k8s:
+ namespace: "{{ deployment_ns }}"
+ definition:
+ kind: DeploymentConfig
+ apiVersion: "apps.openshift.io/v1"
+ metadata:
+ name: "{{ dc_name }}"
+ spec:
+ replicas: 0
+ selector:
+ name: "{{ dc_name }}"
+ template:
+ metadata:
+ labels:
+ name: "{{ dc_name }}"
+ spec:
+ containers:
+ - name: hello-openshift
+ imagePullPolicy: IfNotPresent
+ image: python:3.7-alpine
+ command: [ "/bin/sh", "-c", "while true;do date;sleep 2s; done"]
+ wait: yes
+
+ - name: Wait for ReplicationController candidate for pruning
+ kubernetes.core.k8s_info:
+ kind: ReplicationController
+ namespace: "{{ deployment_ns }}"
+ register: result
+ retries: 10
+ delay: 30
+ until:
+ - result.resources.0.metadata.annotations["openshift.io/deployment.phase"] in ("Failed", "Complete")
+
+ - name: Prune deployments - should delete 1 ReplicationController
+ community.okd.openshift_adm_prune_deployments:
+ namespace: "{{ deployment_ns }}"
+ check_mode: yes
+ register: test_prune
+
+ - name: Read ReplicationController
+ kubernetes.core.k8s_info:
+ kind: ReplicationController
+ namespace: "{{ deployment_ns }}"
+ register: replications
+
+ - name: Assert that Replication controller was not deleted
+ assert:
+ that:
+ - replications.resources | length == 1
+ - 'replications.resources.0.metadata.name is match("{{ dc_name }}-*")'
+
+ - name: Assure that candidate ReplicationController was found for pruning
+ assert:
+ that:
+ - test_prune is changed
+ - test_prune.replication_controllers | length == 1
+ - test_prune.replication_controllers.0.metadata.name == replications.resources.0.metadata.name
+ - test_prune.replication_controllers.0.metadata.namespace == replications.resources.0.metadata.namespace
+
+ - name: Prune deployments - keep younger than 45min (check_mode)
+ community.okd.openshift_adm_prune_deployments:
+ keep_younger_than: 45
+ namespace: "{{ deployment_ns }}"
+ check_mode: true
+ register: keep_younger
+
+ - name: assert no candidate was found
+ assert:
+ that:
+ - keep_younger is not changed
+ - keep_younger.replication_controllers == []
+
+ - name: Ensure second namespace is created
+ community.okd.k8s:
+ kind: Namespace
+ name: '{{ deployment_ns_2 }}'
+
+ - name: Create deployment config from 2nd namespace
+ community.okd.k8s:
+ namespace: '{{ deployment_ns_2 }}'
+ definition:
+ kind: DeploymentConfig
+ apiVersion: apps.openshift.io/v1
+ metadata:
+ name: '{{ dc_name }}2'
+ spec:
+ replicas: 1
+ selector:
+ name: '{{ dc_name }}2'
+ template:
+ metadata:
+ labels:
+ name: '{{ dc_name }}2'
+ spec:
+ containers:
+ - name: hello-openshift
+ imagePullPolicy: IfNotPresent
+ image: python:3.7-alpine
+ command: [ "/bin/sh", "-c", "while true;do date;sleep 2s; done"]
+ wait: yes
+
+ - name: Stop deployment config - replicas = 0
+ community.okd.k8s:
+ namespace: '{{ deployment_ns_2 }}'
+ definition:
+ kind: DeploymentConfig
+ apiVersion: apps.openshift.io/v1
+ metadata:
+ name: '{{ dc_name }}2'
+ spec:
+ replicas: 0
+ selector:
+ name: '{{ dc_name }}2'
+ template:
+ metadata:
+ labels:
+ name: '{{ dc_name }}2'
+ spec:
+ containers:
+ - name: hello-openshift
+ imagePullPolicy: IfNotPresent
+ image: python:3.7-alpine
+ command: [ "/bin/sh", "-c", "while true;do date;sleep 2s; done"]
+ wait: yes
+
+ - name: Wait for ReplicationController candidate for pruning
+ kubernetes.core.k8s_info:
+ kind: ReplicationController
+ namespace: "{{ deployment_ns_2 }}"
+ register: result
+ retries: 10
+ delay: 30
+ until:
+ - result.resources.0.metadata.annotations["openshift.io/deployment.phase"] in ("Failed", "Complete")
+
+ # Prune from one namespace should not have any effect on others namespaces
+ - name: Prune deployments from 2nd namespace
+ community.okd.openshift_adm_prune_deployments:
+ namespace: "{{ deployment_ns_2 }}"
+ check_mode: yes
+ register: test_prune
+
+ - name: Assure that candidate ReplicationController was found for pruning
+ assert:
+ that:
+ - test_prune is changed
+ - test_prune.replication_controllers | length == 1
+ - "test_prune.replication_controllers.0.metadata.namespace == deployment_ns_2"
+
+ # Prune without namespace option
+ - name: Prune from all namespace should update more deployments
+ community.okd.openshift_adm_prune_deployments:
+ check_mode: yes
+ register: no_namespace_prune
+
+ - name: Assure multiple ReplicationController were found for pruning
+ assert:
+ that:
+ - no_namespace_prune is changed
+ - no_namespace_prune.replication_controllers | length == 2
+
+ # Execute Prune from 2nd namespace
+ - name: Read ReplicationController before Prune operation
+ kubernetes.core.k8s_info:
+ kind: ReplicationController
+ namespace: "{{ deployment_ns_2 }}"
+ register: replications
+
+ - assert:
+ that:
+ - replications.resources | length == 1
+
+ - name: Prune DeploymentConfig from 2nd namespace
+ community.okd.openshift_adm_prune_deployments:
+ namespace: "{{ deployment_ns_2 }}"
+ register: _prune
+
+ - name: Assert DeploymentConfig was deleted
+ assert:
+ that:
+ - _prune is changed
+ - _prune.replication_controllers | length == 1
+ - _prune.replication_controllers.0.details.name == replications.resources.0.metadata.name
+
+ # Execute Prune without namespace option
+ - name: Read ReplicationController before Prune operation
+ kubernetes.core.k8s_info:
+ kind: ReplicationController
+ namespace: "{{ deployment_ns }}"
+ register: replications
+
+ - assert:
+ that:
+ - replications.resources | length == 1
+
+ - name: Prune from all namespace should update more deployments
+ community.okd.openshift_adm_prune_deployments:
+ register: _prune
+
+ - name: Assure multiple ReplicationController were found for pruning
+ assert:
+ that:
+ - _prune is changed
+ - _prune.replication_controllers | length > 0
+
+ always:
+ - name: Delete 1st namespace
+ community.okd.k8s:
+ state: absent
+ kind: Namespace
+ name: "{{ deployment_ns }}"
+ ignore_errors: yes
+ when: deployment_ns is defined
+
+ - name: Delete 2nd namespace
+ community.okd.k8s:
+ state: absent
+ kind: Namespace
+ name: "{{ deployment_ns_2 }}"
+ ignore_errors: yes
+ when: deployment_ns_2 is defined \ No newline at end of file
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_auth.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_auth.yml
new file mode 100644
index 000000000..aeeee4c72
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_auth.yml
@@ -0,0 +1,111 @@
+---
+- block:
+ - set_fact:
+ admin_user: test
+ admin_pass: testing123
+
+ - name: Retrieve cluster info
+ kubernetes.core.k8s_cluster_info:
+ register: k8s_cluster
+
+ - name: set openshift host value
+ set_fact:
+ openshift_host: "{{ k8s_cluster.connection.host }}"
+
+ - name: Log in (obtain access token)
+ community.okd.openshift_auth:
+ username: "{{ admin_user }}"
+ password: "{{ admin_pass }}"
+ host: '{{ openshift_host }}'
+ verify_ssl: false
+ register: openshift_auth_results
+
+ - set_fact:
+ auth_api_key: "{{ openshift_auth_results.openshift_auth.api_key }}"
+
+ - name: "Get the {{ admin_user }} User"
+ kubernetes.core.k8s_info:
+ api_key: "{{ auth_api_key }}"
+ host: '{{ openshift_host }}'
+ verify_ssl: false
+ kind: User
+ api_version: user.openshift.io/v1
+ name: "{{ admin_user }}"
+ register: user_result
+
+ - name: assert that the user was found
+ assert:
+ that: (user_result.resources | length) == 1
+
+ - name: list available tokens
+ kubernetes.core.k8s_info:
+ kind: UserOAuthAccessToken
+ version: oauth.openshift.io/v1
+ register: tokens
+
+ - debug: var=tokens
+
+ - set_fact:
+ token_names: "{{ tokens.resources | map(attribute='metadata.name') | list }}"
+
+ - block:
+ - debug: var=token_names
+
+ - name: Revoke access token
+ community.okd.openshift_auth:
+ state: absent
+ api_key: "{{ auth_api_key }}"
+ host: '{{ openshift_host }}'
+ verify_ssl: false
+ register: _revoke
+
+ - name: Ensure that token has been revoked
+ assert:
+ that:
+ - _revoke is changed
+
+ - name: "Get the {{ admin_user }} User (after token deletion)"
+ kubernetes.core.k8s_info:
+ api_key: "{{ auth_api_key }}"
+ host: '{{ openshift_host }}'
+ verify_ssl: false
+ kind: User
+ api_version: user.openshift.io/v1
+ name: "{{ admin_user }}"
+ ignore_errors: true
+ retries: 50
+ until: user_result is failed
+ delay: 20
+ register: user_result
+
+ - name: Ensure that task has failed due to revoked token
+ assert:
+ that:
+ - user_result is failed
+
+ - name: Revoke access token once again (should fail)
+ community.okd.openshift_auth:
+ state: absent
+ api_key: "{{ auth_api_key }}"
+ host: '{{ openshift_host }}'
+ verify_ssl: false
+ register: _revoke
+ ignore_errors: true
+
+ - name: Ensure that nothing changed
+ assert:
+ that:
+ - _revoke is failed
+ - _revoke.msg.startswith("Couldn't delete user oauth access token")
+
+ when: token_names | length > 0
+
+ always:
+ - name: If login succeeded, try to log out (revoke access token)
+ when: auth_api_key is defined
+ community.okd.openshift_auth:
+ state: absent
+ api_key: "{{ auth_api_key }}"
+ host: '{{ openshift_host }}'
+ verify_ssl: false
+ ignore_errors: true
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_builds.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_builds.yml
new file mode 100644
index 000000000..b564f8bcd
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_builds.yml
@@ -0,0 +1,245 @@
+- block:
+ - set_fact:
+ build_ns: "builds"
+ build_config: "start-build"
+ is_name: "ruby"
+ prune_build: "prune-build"
+
+ - name: Ensure namespace
+ kubernetes.core.k8s:
+ kind: Namespace
+ name: "{{ build_ns }}"
+
+ - name: Create ImageStream
+ community.okd.k8s:
+ namespace: "{{ build_ns }}"
+ definition:
+ apiVersion: image.openshift.io/v1
+ kind: ImageStream
+ metadata:
+ name: "{{ is_name }}"
+ spec:
+ lookupPolicy:
+ local: false
+ tags: []
+
+ - name: Create build configuration
+ community.okd.k8s:
+ namespace: "{{ build_ns }}"
+ definition:
+ kind: BuildConfig
+ apiVersion: build.openshift.io/v1
+ metadata:
+ name: "{{ build_config }}"
+ spec:
+ source:
+ dockerfile: |
+ FROM openshift/ruby-22-centos7
+ RUN sleep 60s
+ USER ansible
+ strategy:
+ type: Docker
+ output:
+ to:
+ kind: "ImageStreamTag"
+ name: "{{ is_name }}:latest"
+
+ - name: Start Build from Build configuration
+ community.okd.openshift_build:
+ namespace: "{{ build_ns }}"
+ build_config_name: "{{ build_config }}"
+ register: new_build
+
+ - name: Assert that a build has been created
+ assert:
+ that:
+ - new_build is changed
+ - new_build.builds.0.metadata.name == "{{ build_config }}-1"
+
+ - name: Start a new Build from previous Build
+ community.okd.openshift_build:
+ namespace: "{{ build_ns }}"
+ build_name: "{{ new_build.builds.0.metadata.name }}"
+ register: rerun_build
+
+ - name: Assert that another build has been created
+ assert:
+ that:
+ - rerun_build is changed
+ - rerun_build.builds.0.metadata.name == "{{ build_config }}-2"
+
+ - name: Cancel first build created
+ community.okd.openshift_build:
+ namespace: "{{ build_ns }}"
+ build_name: "{{ build_config }}-1"
+ state: cancelled
+ wait: yes
+ register: cancel
+
+ - name: Assert that the Build was cancelled
+ assert:
+ that:
+ - cancel is changed
+ - cancel.builds | length == 1
+ - cancel.builds.0.metadata.name == "{{ build_config }}-1"
+ - cancel.builds.0.metadata.namespace == "{{ build_ns }}"
+ - cancel.builds.0.status.cancelled
+
+ - name: Get Build info
+ kubernetes.core.k8s_info:
+ version: build.openshift.io/v1
+ kind: Build
+ namespace: "{{ build_ns }}"
+ name: "{{ cancel.builds.0.metadata.name }}"
+ register: build
+
+ - name: Assert that build phase is cancelled
+ assert:
+ that:
+ - build.resources | length == 1
+ - build.resources.0.status.cancelled
+ - build.resources.0.status.phase == 'Cancelled'
+
+ - name: Cancel and restart Build using build config name
+ community.okd.openshift_build:
+ namespace: "{{ build_ns }}"
+ build_config_name: "{{ build_config }}"
+ state: restarted
+ build_phases:
+ - Running
+ - New
+ register: restart
+
+ - name: assert that new build was created
+ assert:
+ that:
+ - restart is changed
+ - restart.builds | length == 1
+ - 'restart.builds.0.metadata.name == "{{ build_config }}-3"'
+
+ - name: Get Build 2 info
+ kubernetes.core.k8s_info:
+ version: build.openshift.io/v1
+ kind: Build
+ namespace: "{{ build_ns }}"
+ name: "{{ build_config }}-2"
+ register: build
+
+ - name: Assert that build phase is cancelled
+ assert:
+ that:
+ - build.resources | length == 1
+ - build.resources.0.status.cancelled
+ - build.resources.0.status.phase == 'Cancelled'
+
+ - name: Get Build info
+ kubernetes.core.k8s_info:
+ version: build.openshift.io/v1
+ kind: Build
+ namespace: "{{ build_ns }}"
+ name: "{{ build_config }}-3"
+ register: build
+
+ - name: Assert that Build is not cancelled
+ assert:
+ that:
+ - build.resources | length == 1
+ - '"cancelled" not in build.resources.0.status'
+ - "build.resources.0.status.phase in ('New', 'Pending', 'Running')"
+
+ - name: Prune Builds keep younger than 30min
+ community.okd.openshift_adm_prune_builds:
+ keep_younger_than: 30
+ namespace: "{{ build_ns }}"
+ register: prune
+ check_mode: yes
+
+ - name: Assert that no Builds were found
+ assert:
+ that:
+ - not prune.changed
+ - prune.builds | length == 0
+
+ - name: Prune Builds without namespace
+ community.okd.openshift_adm_prune_builds:
+ register: prune_without_ns
+ check_mode: yes
+
+ - name: Assert that completed build are candidate for prune
+ assert:
+ that:
+ - prune_without_ns is changed
+ - prune_without_ns.builds | length > 0
+ - '"{{ build_config }}-1" in build_names'
+ - '"{{ build_config }}-2" in build_names'
+ vars:
+ build_names: '{{ prune_without_ns.builds | map(attribute="metadata") | flatten | map(attribute="name") | list }}'
+
+ - name: Prune Builds using namespace
+ community.okd.openshift_adm_prune_builds:
+ namespace: "{{ build_ns }}"
+ register: prune_with_ns
+ check_mode: yes
+
+ - name: Assert that prune operation found the completed build
+ assert:
+ that:
+ - prune_with_ns is changed
+ - prune_with_ns.builds | length == 2
+
+ - name: Check Build before prune
+ kubernetes.core.k8s_info:
+ kind: Build
+ api_version: build.openshift.io/v1
+ name: "{{ build_config }}-1"
+ namespace: "{{ build_ns }}"
+ register: resource
+
+ - name: Validate that any previous build operation executed with check_mode did not deleted the build
+ assert:
+ that:
+ - resource.resources | length == 1
+
+ - name: Execute prune operation
+ community.okd.openshift_adm_prune_builds:
+ namespace: "{{ build_ns }}"
+ register: prune
+
+ - name: assert prune is changed
+ assert:
+ that:
+ - prune is changed
+
+ - name: Check Build
+ kubernetes.core.k8s_info:
+ kind: Build
+ api_version: build.openshift.io/v1
+ name: "{{ build_config }}-1"
+ namespace: "{{ build_ns }}"
+ register: resource
+
+ - name: Assert that the Build does not exist anymore
+ assert:
+ that:
+ - resource.resources | length == 0
+
+ - name: Check Build
+ kubernetes.core.k8s_info:
+ kind: Build
+ api_version: build.openshift.io/v1
+ name: "{{ build_config }}-2"
+ namespace: "{{ build_ns }}"
+ register: resource
+
+ - name: Assert that the Build does not exist anymore
+ assert:
+ that:
+ - resource.resources | length == 0
+
+ always:
+ - name: Ensure namespace is deleted
+ kubernetes.core.k8s:
+ state: absent
+ kind: Namespace
+ name: "{{ build_ns }}"
+ ignore_errors: true
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_import_images.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_import_images.yml
new file mode 100644
index 000000000..04392bb26
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_import_images.yml
@@ -0,0 +1,179 @@
+- name: Openshift import image testing
+ block:
+
+ - set_fact:
+ test_ns: "import-images"
+
+ - name: Ensure namespace
+ community.okd.k8s:
+ kind: Namespace
+ name: '{{ test_ns }}'
+
+ - name: Import image using tag (should import latest tag only)
+ community.okd.openshift_import_image:
+ namespace: "{{ test_ns }}"
+ name: "ansible/awx"
+ check_mode: yes
+ register: import_tag
+
+ - name: Assert only latest was imported
+ assert:
+ that:
+ - import_tag is changed
+ - import_tag.result | length == 1
+ - import_tag.result.0.spec.import
+ - import_tag.result.0.spec.images.0.from.kind == "DockerImage"
+ - import_tag.result.0.spec.images.0.from.name == "ansible/awx"
+
+ - name: check image stream
+ kubernetes.core.k8s_info:
+ kind: ImageStream
+ namespace: "{{ test_ns }}"
+ name: awx
+ register: resource
+
+ - name: assert that image stream is not created when using check_mode=yes
+ assert:
+ that:
+ - resource.resources == []
+
+ - name: Import image using tag (should import latest tag only)
+ community.okd.openshift_import_image:
+ namespace: "{{ test_ns }}"
+ name: "ansible/awx"
+ register: import_tag
+
+ - name: Assert only latest was imported
+ assert:
+ that:
+ - import_tag is changed
+
+ - name: check image stream
+ kubernetes.core.k8s_info:
+ kind: ImageStream
+ namespace: "{{ test_ns }}"
+ name: awx
+ register: resource
+
+ - name: assert that image stream contains only tag latest
+ assert:
+ that:
+ - resource.resources | length == 1
+ - resource.resources.0.status.tags.0.tag == 'latest'
+
+ - name: Import once again the latest tag
+ community.okd.openshift_import_image:
+ namespace: "{{ test_ns }}"
+ name: "ansible/awx"
+ register: import_tag
+
+ - name: assert change was performed
+ assert:
+ that:
+ - import_tag is changed
+
+ - name: check image stream
+ kubernetes.core.k8s_info:
+ kind: ImageStream
+ version: image.openshift.io/v1
+ namespace: "{{ test_ns }}"
+ name: awx
+ register: resource
+
+ - name: assert that image stream still contains unique tag
+ assert:
+ that:
+ - resource.resources | length == 1
+ - resource.resources.0.status.tags.0.tag == 'latest'
+
+ - name: Import another tags
+ community.okd.openshift_import_image:
+ namespace: "{{ test_ns }}"
+ name: "ansible/awx:17.1.0"
+ register: import_another_tag
+ ignore_errors: yes
+
+ - name: assert that another tag was imported
+ assert:
+ that:
+ - import_another_tag is failed
+ - '"the tag 17.1.0 does not exist on the image stream" in import_another_tag.msg'
+
+ - name: Create simple ImageStream (without docker external container)
+ community.okd.k8s:
+ namespace: "{{ test_ns }}"
+ name: "local-is"
+ definition:
+ apiVersion: image.openshift.io/v1
+ kind: ImageStream
+ spec:
+ lookupPolicy:
+ local: false
+ tags: []
+
+ - name: Import all tag for image stream not pointing on external container image should failed
+ community.okd.openshift_import_image:
+ namespace: "{{ test_ns }}"
+ name: "local-is"
+ all: true
+ register: error_tag
+ ignore_errors: true
+ check_mode: yes
+
+ - name: Assert module cannot import from non-existing tag from ImageStream
+ assert:
+ that:
+ - error_tag is failed
+ - 'error_tag.msg == "image stream {{ test_ns }}/local-is does not have tags pointing to external container images"'
+
+ - name: import all tags for container image ibmcom/pause and specific tag for redhat/ubi8-micro
+ community.okd.openshift_import_image:
+ namespace: "{{ test_ns }}"
+ name:
+ - "ibmcom/pause"
+ - "redhat/ubi8-micro:8.5-437"
+ all: true
+ register: multiple_import
+
+ - name: Assert that import succeed
+ assert:
+ that:
+ - multiple_import is changed
+ - multiple_import.result | length == 2
+
+ - name: Read ibmcom/pause ImageStream
+ kubernetes.core.k8s_info:
+ version: image.openshift.io/v1
+ kind: ImageStream
+ namespace: "{{ test_ns }}"
+ name: pause
+ register: pause
+
+ - name: assert that ibmcom/pause has multiple tags
+ assert:
+ that:
+ - pause.resources | length == 1
+ - pause.resources.0.status.tags | length > 1
+
+ - name: Read redhat/ubi8-micro ImageStream
+ kubernetes.core.k8s_info:
+ version: image.openshift.io/v1
+ kind: ImageStream
+ namespace: "{{ test_ns }}"
+ name: ubi8-micro
+ register: resource
+
+ - name: assert that redhat/ubi8-micro has only one tag
+ assert:
+ that:
+ - resource.resources | length == 1
+ - resource.resources.0.status.tags | length == 1
+ - 'resource.resources.0.status.tags.0.tag == "8.5-437"'
+
+ always:
+ - name: Delete testing namespace
+ community.okd.k8s:
+ state: absent
+ kind: Namespace
+ name: "{{ test_ns }}"
+ ignore_errors: yes
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_process.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_process.yml
new file mode 100644
index 000000000..4341bf21c
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_process.yml
@@ -0,0 +1,183 @@
+---
+
+- 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
+ register: result
+
+- name: Create the rendered resources
+ community.okd.k8s:
+ namespace: process-test
+ definition: '{{ item }}'
+ wait: yes
+ apply: yes
+ loop: '{{ result.resources }}'
+
+- name: Delete the rendered resources
+ community.okd.k8s:
+ namespace: process-test
+ definition: '{{ item }}'
+ wait: yes
+ state: absent
+ loop: '{{ result.resources }}'
+
+- name: Process a template and create the resources 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: present
+ namespace_target: process-test
+ register: result
+
+- name: Process a template and update the resources 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
+ MEMORY_LIMIT: 1Gi
+ state: present
+ namespace_target: process-test
+ register: result
+
+- name: Process a template and delete the resources 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: absent
+ namespace_target: process-test
+ register: result
+
+- 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: process-test
+ parameter_file: '{{ files_dir }}/nginx.env'
+ state: present
+ wait: yes
+
+- name: Process a template with parameters from an env file and delete the resources
+ community.okd.openshift_process:
+ name: nginx-example
+ namespace: openshift
+ namespace_target: process-test
+ parameter_file: '{{ files_dir }}/nginx.env'
+ state: absent
+ wait: yes
+
+
+- name: Process a template with duplicate values
+ community.okd.openshift_process:
+ name: nginx-example
+ namespace: openshift # only needed if using a template already on the server
+ parameters:
+ NAME: test123
+ parameter_file: '{{ files_dir }}/nginx.env'
+ ignore_errors: yes
+ register: result
+
+- name: Assert the expected failure occurred
+ assert:
+ that:
+ - result.msg is defined
+ - result.msg == "Duplicate value for 'NAME' detected in parameter file"
+
+- name: Process a local template
+ community.okd.openshift_process:
+ src: '{{ files_dir }}/simple-template.yaml'
+ parameter_file: '{{ files_dir }}/example.env'
+ register: rendered
+
+- name: Process a local template and create the resources
+ community.okd.openshift_process:
+ src: '{{ files_dir }}/simple-template.yaml'
+ parameter_file: '{{ files_dir }}/example.env'
+ namespace_target: process-test
+ state: present
+ register: result
+
+- assert:
+ that: result is changed
+
+- name: Create the processed resources
+ community.okd.k8s:
+ namespace: process-test
+ definition: '{{ item }}'
+ loop: '{{ rendered.resources }}'
+ register: result
+
+- assert:
+ that: result is not changed
+
+- name: Process a local template and create the resources
+ community.okd.openshift_process:
+ definition: "{{ lookup('template', files_dir + '/simple-template.yaml') | from_yaml }}"
+ parameter_file: '{{ files_dir }}/example.env'
+ namespace_target: process-test
+ state: present
+ register: result
+
+- assert:
+ that: result is not changed
+
+- name: Get the created configmap
+ kubernetes.core.k8s_info:
+ api_version: v1
+ kind: ConfigMap
+ name: example
+ namespace: process-test
+ register: templated_cm
+
+- assert:
+ that:
+ - (templated_cm.resources | length) == 1
+ - templated_cm.resources.0.data.content is defined
+ - templated_cm.resources.0.data.content == "This is a long message that may take one or more lines to parse but should still work without issue"
+
+- name: Create the Template resource
+ community.okd.k8s:
+ src: '{{ files_dir }}/simple-template.yaml'
+ namespace: process-test
+
+- name: Process the template and create the resources
+ community.okd.openshift_process:
+ name: simple-example
+ namespace: process-test # only needed if using a template already on the server
+ namespace_target: process-test
+ parameter_file: '{{ files_dir }}/example.env'
+ state: present
+ register: result
+
+- assert:
+ that: result is not changed
+
+# Processing template without message
+- name: create template with file {{ files_dir }}/pod-template.yaml
+ kubernetes.core.k8s:
+ namespace: process-test
+ src: "{{ files_dir }}/pod-template.yaml"
+ state: present
+
+- name: Process pod template
+ community.okd.openshift_process:
+ name: pod-template
+ namespace: process-test
+ state: rendered
+ parameters:
+ NAME: ansible
+ register: rendered_template
+
+- assert:
+ that: rendered_template.message == ""
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_prune_images.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_prune_images.yml
new file mode 100644
index 000000000..86630da69
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_prune_images.yml
@@ -0,0 +1,217 @@
+---
+- name: Read registry information
+ community.okd.openshift_registry_info:
+ check: yes
+ register: registry
+
+- name: Display registry information
+ debug: var=registry
+
+- block:
+ - set_fact:
+ prune_ns: "prune-images"
+ prune_registry: "{{ registry.public_hostname }}"
+ container:
+ name: "httpd"
+ from: "centos/python-38-centos7:20210629-304c7c8"
+ pod_name: "test-pod"
+
+ - name: Ensure namespace is created
+ community.okd.k8s:
+ kind: Namespace
+ name: "{{ prune_ns }}"
+
+ - name: Import image into internal registry
+ community.okd.openshift_import_image:
+ namespace: "{{ prune_ns }}"
+ name: "{{ container.name }}"
+ source: "{{ container.from }}"
+
+ - name: Create simple Pod
+ community.okd.k8s:
+ namespace: "{{ prune_ns }}"
+ wait: yes
+ definition:
+ apiVersion: v1
+ kind: Pod
+ metadata:
+ name: "{{ pod_name }}"
+ spec:
+ containers:
+ - name: test-container
+ image: "{{ prune_registry }}/{{ prune_ns }}/{{ container.name }}:latest"
+ command:
+ - /bin/sh
+ - -c
+ - while true;do date;sleep 5; done
+
+ - name: Create limit range for images size
+ community.okd.k8s:
+ namespace: "{{ prune_ns }}"
+ definition:
+ kind: "LimitRange"
+ metadata:
+ name: "image-resource-limits"
+ spec:
+ limits:
+ - type: openshift.io/Image
+ max:
+ storage: 1Gi
+
+ - name: Prune images from namespace
+ community.okd.openshift_adm_prune_images:
+ registry_url: "{{ prune_registry }}"
+ namespace: "{{ prune_ns }}"
+ check_mode: yes
+ register: prune
+
+ - name: Assert that nothing to prune as image is in used
+ assert:
+ that:
+ - prune is not changed
+ - prune is successful
+ - prune.deleted_images == []
+ - prune.updated_image_streams == []
+
+ - name: Delete Pod created before
+ community.okd.k8s:
+ state: absent
+ name: "{{ pod_name }}"
+ kind: Pod
+ namespace: "{{ prune_ns }}"
+ wait: yes
+
+ - name: Prune images from namespace
+ community.okd.openshift_adm_prune_images:
+ registry_url: "{{ prune_registry }}"
+ namespace: "{{ prune_ns }}"
+ check_mode: yes
+ register: prune
+
+ - name: Read ImageStream
+ kubernetes.core.k8s_info:
+ version: image.openshift.io/v1
+ kind: ImageStream
+ namespace: "{{ prune_ns }}"
+ name: "{{ container.name }}"
+ register: isinfo
+
+ - set_fact:
+ is_image_name: "{{ isinfo.resources.0.status.tags[0]['items'].0.image }}"
+
+ - name: Assert that corresponding Image and ImageStream were candidate for pruning
+ assert:
+ that:
+ - prune is changed
+ - prune.deleted_images | length == 1
+ - prune.deleted_images.0.metadata.name == is_image_name
+ - prune.updated_image_streams | length == 1
+ - prune.updated_image_streams.0.metadata.name == container.name
+ - prune.updated_image_streams.0.metadata.namespace == prune_ns
+ - prune.updated_image_streams.0.status.tags == []
+
+ - name: Prune images from namespace keeping images and referrer younger than 60minutes
+ community.okd.openshift_adm_prune_images:
+ registry_url: "{{ prune_registry }}"
+ namespace: "{{ prune_ns }}"
+ keep_younger_than: 60
+ check_mode: yes
+ register: younger
+
+ - assert:
+ that:
+ - younger is not changed
+ - younger is successful
+ - younger.deleted_images == []
+ - younger.updated_image_streams == []
+
+ - name: Prune images over size limit
+ community.okd.openshift_adm_prune_images:
+ registry_url: "{{ prune_registry }}"
+ namespace: "{{ prune_ns }}"
+ prune_over_size_limit: yes
+ check_mode: yes
+ register: prune_over_size
+
+ - assert:
+ that:
+ - prune_over_size is not changed
+ - prune_over_size is successful
+ - prune_over_size.deleted_images == []
+ - prune_over_size.updated_image_streams == []
+
+ - name: Update limit range for images size
+ community.okd.k8s:
+ namespace: "{{ prune_ns }}"
+ definition:
+ kind: "LimitRange"
+ metadata:
+ name: "image-resource-limits"
+ spec:
+ limits:
+ - type: openshift.io/Image
+ max:
+ storage: 1Ki
+
+ - name: Prune images over size limit (check_mode=yes)
+ community.okd.openshift_adm_prune_images:
+ registry_url: "{{ prune_registry }}"
+ namespace: "{{ prune_ns }}"
+ prune_over_size_limit: yes
+ check_mode: yes
+ register: prune
+
+ - name: Assert Images and ImageStream were candidate for prune
+ assert:
+ that:
+ - prune is changed
+ - prune.deleted_images | length == 1
+ - prune.deleted_images.0.metadata.name == is_image_name
+ - prune.updated_image_streams | length == 1
+ - prune.updated_image_streams.0.metadata.name == container.name
+ - prune.updated_image_streams.0.metadata.namespace == prune_ns
+ - prune.updated_image_streams.0.status.tags == []
+
+ - name: Prune images over size limit
+ community.okd.openshift_adm_prune_images:
+ registry_url: "{{ prune_registry }}"
+ namespace: "{{ prune_ns }}"
+ prune_over_size_limit: yes
+ register: prune
+
+ - name: Assert that Images and ImageStream were candidate for prune
+ assert:
+ that:
+ - prune is changed
+ - prune.deleted_images | length == 1
+ - prune.deleted_images.0.details.name == is_image_name
+ - prune.updated_image_streams | length == 1
+ - prune.updated_image_streams.0.metadata.name == container.name
+ - prune.updated_image_streams.0.metadata.namespace == prune_ns
+ - '"tags" not in prune.updated_image_streams.0.status'
+
+ - name: Validate that ImageStream was updated
+ kubernetes.core.k8s_info:
+ version: image.openshift.io/v1
+ kind: ImageStream
+ namespace: "{{ prune_ns }}"
+ name: "{{ container.name }}"
+ register: stream
+
+ - name: Assert that ImageStream was updated
+ assert:
+ that:
+ - stream.resources | length == 1
+ - '"tags" not in stream.resources.0.status'
+
+ always:
+ - name: Delete namespace
+ community.okd.k8s:
+ name: "{{ prune_ns }}"
+ kind: Namespace
+ state: absent
+ ignore_errors: true
+
+ when:
+ - registry.public_hostname
+ - registry.check.reached
diff --git a/ansible_collections/community/okd/molecule/default/tasks/openshift_route.yml b/ansible_collections/community/okd/molecule/default/tasks/openshift_route.yml
new file mode 100644
index 000000000..50056b7e4
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/openshift_route.yml
@@ -0,0 +1,275 @@
+---
+- name: Create Deployment
+ community.okd.k8s:
+ wait: yes
+ 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: docker.io/openshift/hello-openshift
+ ports:
+ - containerPort: 8080
+
+- name: Create Service
+ community.okd.k8s:
+ wait: yes
+ definition:
+ apiVersion: v1
+ kind: Service
+ metadata:
+ name: hello-kubernetes
+ namespace: default
+ spec:
+ ports:
+ - port: 80
+ targetPort: 8080
+ selector:
+ app: hello-kubernetes
+
+- name: Create Route with fewest possible arguments
+ community.okd.openshift_route:
+ service: hello-kubernetes
+ namespace: default
+ register: route
+
+- name: Attempt to hit http URL
+ uri:
+ url: 'http://{{ route.result.spec.host }}'
+ return_content: yes
+ until: result is successful
+ retries: 20
+ register: result
+
+- name: Assert the page content is as expected
+ assert:
+ that:
+ - not result.redirected
+ - result.status == 200
+ - result.content == 'Hello OpenShift!\n'
+
+- name: Delete route
+ community.okd.openshift_route:
+ name: '{{ route.result.metadata.name }}'
+ namespace: default
+ state: absent
+ wait: yes
+
+- name: Create Route with custom name and wait
+ community.okd.openshift_route:
+ service: hello-kubernetes
+ namespace: default
+ name: test1
+ wait: yes
+ register: route
+
+- name: Assert that the condition is properly set
+ assert:
+ that:
+ - route.duration is defined
+ - route.result.status.ingress.0.conditions.0.type == 'Admitted'
+ - route.result.status.ingress.0.conditions.0.status == 'True'
+
+- name: Attempt to hit http URL
+ uri:
+ url: 'http://{{ route.result.spec.host }}'
+ return_content: yes
+ until: result is successful
+ retries: 20
+ register: result
+
+- name: Assert the page content is as expected
+ assert:
+ that:
+ - not result.redirected
+ - result.status == 200
+ - result.content == 'Hello OpenShift!\n'
+
+- name: Delete route
+ community.okd.openshift_route:
+ name: '{{ route.result.metadata.name }}'
+ namespace: default
+ state: absent
+ wait: yes
+
+- name: Create edge-terminated route that allows insecure traffic
+ community.okd.openshift_route:
+ service: hello-kubernetes
+ namespace: default
+ name: hello-kubernetes-https
+ tls:
+ insecure_policy: allow
+ termination: edge
+ register: route
+
+- name: Attempt to hit http URL
+ uri:
+ url: 'http://{{ route.result.spec.host }}'
+ return_content: yes
+ until: result is successful
+ retries: 20
+ register: result
+
+- name: Assert the page content is as expected
+ assert:
+ that:
+ - not result.redirected
+ - result.status == 200
+ - result.content == 'Hello OpenShift!\n'
+
+- name: Attempt to hit https URL
+ uri:
+ url: 'https://{{ route.result.spec.host }}'
+ validate_certs: no
+ return_content: yes
+ until: result is successful
+ retries: 10
+ register: result
+
+- name: Assert the page content is as expected
+ assert:
+ that:
+ - not result.redirected
+ - result.status == 200
+ - result.content == 'Hello OpenShift!\n'
+
+- name: Alter edge-terminated route to redirect insecure traffic
+ community.okd.openshift_route:
+ service: hello-kubernetes
+ namespace: default
+ name: hello-kubernetes-https
+ tls:
+ insecure_policy: redirect
+ termination: edge
+ register: route
+
+- name: Attempt to hit http URL
+ uri:
+ url: 'http://{{ route.result.spec.host }}'
+ return_content: yes
+ validate_certs: no
+ until:
+ - result is successful
+ - result.redirected
+ retries: 10
+ register: result
+
+- name: Assert the page content is as expected
+ assert:
+ that:
+ - result.redirected
+ - result.status == 200
+ - result.content == 'Hello OpenShift!\n'
+
+- name: Attempt to hit https URL
+ uri:
+ url: 'https://{{ route.result.spec.host }}'
+ validate_certs: no
+ return_content: yes
+ until: result is successful
+ retries: 20
+ register: result
+
+- name: Assert the page content is as expected
+ assert:
+ that:
+ - not result.redirected
+ - result.status == 200
+ - result.content == 'Hello OpenShift!\n'
+
+- name: Alter edge-terminated route with insecure traffic disabled
+ community.okd.openshift_route:
+ service: hello-kubernetes
+ namespace: default
+ name: hello-kubernetes-https
+ tls:
+ insecure_policy: disallow
+ termination: edge
+ register: route
+
+- debug: var=route
+
+- name: Attempt to hit https URL
+ uri:
+ url: 'https://{{ route.result.spec.host }}'
+ validate_certs: no
+ return_content: yes
+ until: result is successful
+ retries: 20
+ register: result
+
+- name: Assert the page content is as expected
+ assert:
+ that:
+ - not result.redirected
+ - result.status == 200
+ - result.content == 'Hello OpenShift!\n'
+
+- name: Attempt to hit http URL
+ uri:
+ url: 'http://{{ route.result.spec.host }}'
+ status_code: 503
+ until: result is successful
+ retries: 20
+ register: result
+
+- debug: var=result
+
+- name: Assert the page content is as expected
+ assert:
+ that:
+ - not result.redirected
+ - result.status == 503
+
+- name: Delete route
+ community.okd.openshift_route:
+ name: '{{ route.result.metadata.name }}'
+ namespace: default
+ state: absent
+ wait: yes
+
+# Route with labels and annotations
+- name: Create route with labels and annotations
+ community.okd.openshift_route:
+ service: hello-kubernetes
+ namespace: default
+ name: route-label-annotation
+ labels:
+ ansible: test
+ annotations:
+ haproxy.router.openshift.io/balance: roundrobin
+
+- name: Get route information
+ kubernetes.core.k8s_info:
+ api_version: route.openshift.io/v1
+ kind: Route
+ name: route-label-annotation
+ namespace: default
+ register: route
+
+- assert:
+ that:
+ - route.resources[0].metadata.annotations is defined
+ - '"haproxy.router.openshift.io/balance" in route.resources[0].metadata.annotations'
+ - route.resources[0].metadata.labels is defined
+ - '"ansible" in route.resources[0].metadata.labels'
+
+- name: Delete route
+ community.okd.openshift_route:
+ name: route-label-annotation
+ namespace: default
+ state: absent
+ wait: yes
diff --git a/ansible_collections/community/okd/molecule/default/tasks/validate_installed.yml b/ansible_collections/community/okd/molecule/default/tasks/validate_installed.yml
new file mode 100644
index 000000000..4508efdd6
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/validate_installed.yml
@@ -0,0 +1,122 @@
+---
+- block:
+ - name: Create a project
+ community.okd.k8s:
+ name: "{{ playbook_namespace }}"
+ kind: Project
+ api_version: project.openshift.io/v1
+
+ - name: incredibly simple ConfigMap
+ community.okd.k8s:
+ definition:
+ apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: hello
+ namespace: "{{ playbook_namespace }}"
+ validate:
+ fail_on_error: yes
+ register: k8s_with_validate
+
+ - name: assert that k8s_with_validate succeeds
+ assert:
+ that:
+ - k8s_with_validate is successful
+
+ - name: extra property does not fail without strict
+ community.okd.k8s:
+ src: "files/kuard-extra-property.yml"
+ namespace: "{{ playbook_namespace }}"
+ validate:
+ fail_on_error: yes
+ strict: no
+
+ - name: extra property fails with strict
+ community.okd.k8s:
+ src: "files/kuard-extra-property.yml"
+ namespace: "{{ playbook_namespace }}"
+ validate:
+ fail_on_error: yes
+ strict: yes
+ ignore_errors: yes
+ register: extra_property
+
+ - name: check that extra property fails with strict
+ assert:
+ that:
+ - extra_property is failed
+
+ - name: invalid type fails at validation stage
+ community.okd.k8s:
+ src: "files/kuard-invalid-type.yml"
+ namespace: "{{ playbook_namespace }}"
+ validate:
+ fail_on_error: yes
+ strict: no
+ ignore_errors: yes
+ register: invalid_type
+
+ - name: check that invalid type fails
+ assert:
+ that:
+ - invalid_type is failed
+
+ - name: invalid type fails with warnings when fail_on_error is False
+ community.okd.k8s:
+ src: "files/kuard-invalid-type.yml"
+ namespace: "{{ playbook_namespace }}"
+ validate:
+ fail_on_error: no
+ strict: no
+ ignore_errors: yes
+ register: invalid_type_no_fail
+
+ - name: check that invalid type fails
+ assert:
+ that:
+ - invalid_type_no_fail is failed
+
+ - name: setup custom resource definition
+ community.okd.k8s:
+ src: "files/setup-crd.yml"
+
+ - name: wait a few seconds
+ pause:
+ seconds: 5
+
+ - name: add custom resource definition
+ community.okd.k8s:
+ src: "files/crd-resource.yml"
+ namespace: "{{ playbook_namespace }}"
+ validate:
+ fail_on_error: yes
+ strict: yes
+ register: unknown_kind
+
+ - name: check that unknown kind warns
+ assert:
+ that:
+ - unknown_kind is successful
+
+ always:
+ - name: remove custom resource
+ community.okd.k8s:
+ definition: "{{ lookup('file', 'files/crd-resource.yml') }}"
+ namespace: "{{ playbook_namespace }}"
+ state: absent
+ ignore_errors: yes
+
+ - name: remove custom resource definitions
+ community.okd.k8s:
+ definition: "{{ lookup('file', 'files/setup-crd.yml') }}"
+ state: absent
+
+ - name: Delete namespace
+ community.okd.k8s:
+ state: absent
+ definition:
+ - kind: Project
+ apiVersion: project.openshift.io/v1
+ metadata:
+ name: "{{ playbook_namespace }}"
+ ignore_errors: yes
diff --git a/ansible_collections/community/okd/molecule/default/tasks/validate_not_installed.yml b/ansible_collections/community/okd/molecule/default/tasks/validate_not_installed.yml
new file mode 100644
index 000000000..a64607ce0
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/tasks/validate_not_installed.yml
@@ -0,0 +1,25 @@
+---
+# TODO: Not available in ansible-base
+# - python_requirements_info:
+# dependencies:
+# - openshift
+# - kubernetes
+# - kubernetes-validate
+
+- community.okd.k8s:
+ definition:
+ apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: hello
+ namespace: default
+ validate:
+ fail_on_error: yes
+ ignore_errors: yes
+ register: k8s_no_validate
+
+- name: assert that k8s_no_validate fails gracefully
+ assert:
+ that:
+ - k8s_no_validate is failed
+ - k8s_no_validate.msg.startswith('Failed to import the required Python library (kubernetes-validate)')
diff --git a/ansible_collections/community/okd/molecule/default/vars/main.yml b/ansible_collections/community/okd/molecule/default/vars/main.yml
new file mode 100644
index 000000000..66fb0d33c
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/vars/main.yml
@@ -0,0 +1,94 @@
+---
+k8s_pod_annotations: {}
+
+k8s_pod_metadata:
+ labels:
+ app: '{{ k8s_pod_name }}'
+ annotations: '{{ k8s_pod_annotations }}'
+
+k8s_pod_spec:
+ serviceAccount: "{{ k8s_pod_service_account }}"
+ containers:
+ - image: "{{ k8s_pod_image }}"
+ imagePullPolicy: Always
+ name: "{{ k8s_pod_name }}"
+ command: "{{ k8s_pod_command }}"
+ readinessProbe:
+ initialDelaySeconds: 15
+ exec:
+ command:
+ - /bin/true
+ resources: "{{ k8s_pod_resources }}"
+ ports: "{{ k8s_pod_ports }}"
+ env: "{{ k8s_pod_env }}"
+
+k8s_pod_service_account: default
+
+k8s_pod_resources:
+ limits:
+ cpu: "100m"
+ memory: "100Mi"
+
+k8s_pod_command: []
+
+k8s_pod_ports: []
+
+k8s_pod_env: []
+
+k8s_pod_template:
+ metadata: "{{ k8s_pod_metadata }}"
+ spec: "{{ k8s_pod_spec }}"
+
+k8s_deployment_spec:
+ template: '{{ k8s_pod_template }}'
+ selector:
+ matchLabels:
+ app: '{{ k8s_pod_name }}'
+ replicas: 1
+
+k8s_deployment_template:
+ apiVersion: apps/v1
+ kind: Deployment
+ spec: '{{ k8s_deployment_spec }}'
+
+okd_dc_triggers:
+ - type: ConfigChange
+ - type: ImageChange
+ imageChangeParams:
+ automatic: true
+ containerNames:
+ - '{{ k8s_pod_name }}'
+ from:
+ kind: ImageStreamTag
+ name: '{{ image_name }}:{{ image_tag }}'
+
+okd_dc_spec:
+ template: '{{ k8s_pod_template }}'
+ triggers: '{{ okd_dc_triggers }}'
+ replicas: 1
+ strategy:
+ type: Recreate
+
+okd_dc_template:
+ apiVersion: v1
+ kind: DeploymentConfig
+ spec: '{{ okd_dc_spec }}'
+
+okd_imagestream_template:
+ apiVersion: image.openshift.io/v1
+ kind: ImageStream
+ metadata:
+ name: '{{ image_name }}'
+ spec:
+ lookupPolicy:
+ local: true
+ tags:
+ - annotations: null
+ from:
+ kind: DockerImage
+ name: '{{ image }}'
+ name: '{{ image_tag }}'
+ referencePolicy:
+ type: Source
+
+image_tag: latest
diff --git a/ansible_collections/community/okd/molecule/default/verify.yml b/ansible_collections/community/okd/molecule/default/verify.yml
new file mode 100644
index 000000000..b787062aa
--- /dev/null
+++ b/ansible_collections/community/okd/molecule/default/verify.yml
@@ -0,0 +1,89 @@
+---
+- name: Verify inventory and connection plugins
+ # This group is created by the openshift_inventory plugin
+ # It is automatically configured to use the `oc` connection plugin
+ hosts: namespace_testing_pods
+ gather_facts: no
+ vars:
+ file_content: |
+ Hello world
+ tasks:
+ - name: End play if host not running (TODO should we not add these to the inventory?)
+ meta: end_host
+ when: pod_phase != "Running"
+
+ - setup:
+
+ - debug: var=ansible_facts
+
+ - name: Assert the TEST environment variable was retrieved
+ assert:
+ that: ansible_facts.env.TEST == 'test'
+
+ - name: Copy a file into the host
+ copy:
+ content: '{{ file_content }}'
+ dest: /tmp/test_file
+
+ - name: Retrieve the file from the host
+ slurp:
+ src: /tmp/test_file
+ register: slurped_file
+
+ - name: Assert the file content matches expectations
+ assert:
+ that: (slurped_file.content|b64decode) == file_content
+
+
+- name: Verify
+ hosts: localhost
+ connection: local
+ gather_facts: no
+ vars:
+ ansible_python_interpreter: '{{ virtualenv_interpreter }}'
+
+ tasks:
+ - pip:
+ name: kubernetes-validate==1.12.0
+ virtualenv: "{{ virtualenv }}"
+ virtualenv_command: "{{ virtualenv_command }}"
+ virtualenv_site_packages: no
+
+ - import_tasks: tasks/validate_installed.yml
+
+ - pip:
+ name: kubernetes-validate
+ state: absent
+ virtualenv: "{{ virtualenv }}"
+ virtualenv_command: "{{ virtualenv_command }}"
+ virtualenv_site_packages: no
+
+ - import_tasks: tasks/validate_not_installed.yml
+
+ - import_tasks: tasks/openshift_auth.yml
+ - import_tasks: tasks/openshift_adm_prune_auth_clusterroles.yml
+ - import_tasks: tasks/openshift_adm_prune_auth_roles.yml
+ - import_tasks: tasks/openshift_adm_prune_deployments.yml
+ - import_tasks: tasks/openshift_builds.yml
+ - import_tasks: tasks/openshift_route.yml
+ - import_tasks: tasks/openshift_import_images.yml
+ - import_tasks: tasks/openshift_prune_images.yml
+ - block:
+ - name: Create namespace
+ community.okd.k8s:
+ api_version: v1
+ kind: Namespace
+ name: process-test
+ - import_tasks: tasks/openshift_process.yml
+ vars:
+ files_dir: '{{ playbook_dir }}/files'
+ always:
+ - name: Delete namespace
+ community.okd.k8s:
+ api_version: v1
+ kind: Namespace
+ name: process-test
+ state: absent
+
+ roles:
+ - role: openshift_adm_groups
diff --git a/ansible_collections/community/okd/plugins/connection/oc.py b/ansible_collections/community/okd/plugins/connection/oc.py
new file mode 100644
index 000000000..44236a11a
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/connection/oc.py
@@ -0,0 +1,173 @@
+# Based on the docker connection plugin
+#
+# Connection plugin for configuring kubernetes containers with kubectl
+# (c) 2017, XuXinkun <xuxinkun@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+DOCUMENTATION = '''
+ author:
+ - xuxinkun (@xuxinkun)
+
+ name: oc
+
+ short_description: Execute tasks in pods running on OpenShift.
+
+ description:
+ - Use the oc exec command to run tasks in, or put/fetch files to, pods running on the OpenShift
+ container platform.
+
+
+ requirements:
+ - oc (go binary)
+
+ options:
+ oc_pod:
+ description:
+ - Pod name. Required when the host name does not match pod name.
+ default: ''
+ vars:
+ - name: ansible_oc_pod
+ env:
+ - name: K8S_AUTH_POD
+ oc_container:
+ description:
+ - Container name. Required when a pod contains more than one container.
+ default: ''
+ vars:
+ - name: ansible_oc_container
+ env:
+ - name: K8S_AUTH_CONTAINER
+ oc_namespace:
+ description:
+ - The namespace of the pod
+ default: ''
+ vars:
+ - name: ansible_oc_namespace
+ env:
+ - name: K8S_AUTH_NAMESPACE
+ oc_extra_args:
+ description:
+ - Extra arguments to pass to the oc command line.
+ default: ''
+ vars:
+ - name: ansible_oc_extra_args
+ env:
+ - name: K8S_AUTH_EXTRA_ARGS
+ oc_kubeconfig:
+ description:
+ - Path to a oc config file. Defaults to I(~/.kube/config)
+ default: ''
+ vars:
+ - name: ansible_oc_kubeconfig
+ - name: ansible_oc_config
+ env:
+ - name: K8S_AUTH_KUBECONFIG
+ oc_context:
+ description:
+ - The name of a context found in the K8s config file.
+ default: ''
+ vars:
+ - name: ansible_oc_context
+ env:
+ - name: K8S_AUTH_CONTEXT
+ oc_host:
+ description:
+ - URL for accessing the API.
+ default: ''
+ vars:
+ - name: ansible_oc_host
+ - name: ansible_oc_server
+ env:
+ - name: K8S_AUTH_HOST
+ - name: K8S_AUTH_SERVER
+ oc_token:
+ description:
+ - API authentication bearer token.
+ vars:
+ - name: ansible_oc_token
+ - name: ansible_oc_api_key
+ env:
+ - name: K8S_AUTH_TOKEN
+ - name: K8S_AUTH_API_KEY
+ client_cert:
+ description:
+ - Path to a certificate used to authenticate with the API.
+ default: ''
+ vars:
+ - name: ansible_oc_cert_file
+ - name: ansible_oc_client_cert
+ env:
+ - name: K8S_AUTH_CERT_FILE
+ aliases: [ oc_cert_file ]
+ client_key:
+ description:
+ - Path to a key file used to authenticate with the API.
+ default: ''
+ vars:
+ - name: ansible_oc_key_file
+ - name: ansible_oc_client_key
+ env:
+ - name: K8S_AUTH_KEY_FILE
+ aliases: [ oc_key_file ]
+ ca_cert:
+ description:
+ - Path to a CA certificate used to authenticate with the API.
+ default: ''
+ vars:
+ - name: ansible_oc_ssl_ca_cert
+ - name: ansible_oc_ca_cert
+ env:
+ - name: K8S_AUTH_SSL_CA_CERT
+ aliases: [ oc_ssl_ca_cert ]
+ validate_certs:
+ description:
+ - Whether or not to verify the API server's SSL certificate. Defaults to I(true).
+ default: ''
+ vars:
+ - name: ansible_oc_verify_ssl
+ - name: ansible_oc_validate_certs
+ env:
+ - name: K8S_AUTH_VERIFY_SSL
+ aliases: [ oc_verify_ssl ]
+'''
+
+from ansible_collections.kubernetes.core.plugins.connection.kubectl import Connection as KubectlConnection
+
+
+CONNECTION_TRANSPORT = 'oc'
+
+CONNECTION_OPTIONS = {
+ 'oc_container': '-c',
+ 'oc_namespace': '-n',
+ 'oc_kubeconfig': '--kubeconfig',
+ 'oc_context': '--context',
+ 'oc_host': '--server',
+ 'client_cert': '--client-certificate',
+ 'client_key': '--client-key',
+ 'ca_cert': '--certificate-authority',
+ 'validate_certs': '--insecure-skip-tls-verify',
+ 'oc_token': '--token'
+}
+
+
+class Connection(KubectlConnection):
+ ''' Local oc based connections '''
+ transport = CONNECTION_TRANSPORT
+ connection_options = CONNECTION_OPTIONS
+ documentation = DOCUMENTATION
diff --git a/ansible_collections/community/okd/plugins/inventory/openshift.py b/ansible_collections/community/okd/plugins/inventory/openshift.py
new file mode 100644
index 000000000..f69c652fc
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/inventory/openshift.py
@@ -0,0 +1,215 @@
+# Copyright (c) 2018 Ansible Project
+# 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 = '''
+ name: openshift
+ author:
+ - Chris Houseknecht (@chouseknecht)
+
+ short_description: OpenShift inventory source
+
+ description:
+ - Fetch containers, services and routes for one or more clusters
+ - Groups by cluster name, namespace, namespace_services, namespace_pods, namespace_routes, and labels
+ - Uses openshift.(yml|yaml) YAML configuration file to set parameter values.
+
+ options:
+ plugin:
+ description: token that ensures this is a source file for the 'openshift' plugin.
+ required: True
+ choices: ['openshift', 'community.okd.openshift']
+ connections:
+ description:
+ - Optional list of cluster connection settings. If no connections are provided, the default
+ I(~/.kube/config) and active context will be used, and objects will be returned for all namespaces
+ the active user is authorized to access.
+ suboptions:
+ name:
+ description:
+ - Optional name to assign to the cluster. If not provided, a name is constructed from the server
+ and port.
+ kubeconfig:
+ description:
+ - Path to an existing Kubernetes config file. If not provided, and no other connection
+ options are provided, the Kubernetes client will attempt to load the default
+ configuration file from I(~/.kube/config). Can also be specified via K8S_AUTH_KUBECONFIG
+ environment variable.
+ context:
+ description:
+ - The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment
+ variable.
+ host:
+ description:
+ - Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.
+ api_key:
+ description:
+ - Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment
+ variable.
+ username:
+ description:
+ - Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME
+ environment variable.
+ password:
+ description:
+ - Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD
+ environment variable.
+ client_cert:
+ description:
+ - Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
+ environment variable.
+ aliases: [ cert_file ]
+ client_key:
+ description:
+ - Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE
+ environment variable.
+ aliases: [ key_file ]
+ ca_cert:
+ description:
+ - Path to a CA certificate used to authenticate with the API. Can also be specified via
+ K8S_AUTH_SSL_CA_CERT environment variable.
+ aliases: [ ssl_ca_cert ]
+ 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
+ aliases: [ verify_ssl ]
+ namespaces:
+ description:
+ - List of namespaces. If not specified, will fetch all containers for all namespaces user is authorized
+ to access.
+
+ requirements:
+ - "python >= 3.6"
+ - "kubernetes >= 12.0.0"
+ - "PyYAML >= 3.11"
+'''
+
+EXAMPLES = '''
+# File must be named openshift.yaml or openshift.yml
+
+# Authenticate with token, and return all pods and services for all namespaces
+plugin: community.okd.openshift
+connections:
+ - host: https://192.168.64.4:8443
+ api_key: xxxxxxxxxxxxxxxx
+ verify_ssl: false
+
+# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
+plugin: community.okd.openshift
+connections:
+ - namespaces:
+ - testing
+
+# Use a custom config file, and a specific context.
+plugin: community.okd.openshift
+connections:
+ - kubeconfig: /path/to/config
+ context: 'awx/192-168-64-4:8443/developer'
+'''
+
+try:
+ from ansible_collections.kubernetes.core.plugins.inventory.k8s import K8sInventoryException, InventoryModule as K8sInventoryModule, format_dynamic_api_exc
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import get_api_client
+ HAS_KUBERNETES_COLLECTION = True
+except ImportError as e:
+ HAS_KUBERNETES_COLLECTION = False
+
+
+try:
+ from kubernetes.dynamic.exceptions import DynamicApiError
+except ImportError:
+ pass
+
+
+class InventoryModule(K8sInventoryModule):
+ NAME = 'community.okd.openshift'
+
+ connection_plugin = 'community.okd.oc'
+ transport = 'oc'
+
+ def check_kubernetes_collection(self):
+
+ if not HAS_KUBERNETES_COLLECTION:
+ K8sInventoryException("The kubernetes.core collection must be installed")
+
+ def fetch_objects(self, connections):
+ self.check_kubernetes_collection()
+ super(InventoryModule, self).fetch_objects(connections)
+
+ if connections:
+ if not isinstance(connections, list):
+ raise K8sInventoryException("Expecting connections to be a list.")
+
+ for connection in connections:
+ client = get_api_client(**connection)
+ name = connection.get('name', self.get_default_host_name(client.configuration.host))
+ if connection.get('namespaces'):
+ namespaces = connection['namespaces']
+ else:
+ namespaces = self.get_available_namespaces(client)
+ for namespace in namespaces:
+ self.get_routes_for_namespace(client, name, namespace)
+ else:
+ client = get_api_client()
+ name = self.get_default_host_name(client.configuration.host)
+ namespaces = self.get_available_namespaces(client)
+ for namespace in namespaces:
+ self.get_routes_for_namespace(client, name, namespace)
+
+ def get_routes_for_namespace(self, client, name, namespace):
+ self.check_kubernetes_collection()
+ v1_route = client.resources.get(api_version='route.openshift.io/v1', kind='Route')
+ try:
+ obj = v1_route.get(namespace=namespace)
+ except DynamicApiError as exc:
+ self.display.debug(exc)
+ raise K8sInventoryException('Error fetching Routes list: %s' % format_dynamic_api_exc(exc))
+
+ namespace_group = 'namespace_{0}'.format(namespace)
+ namespace_routes_group = '{0}_routes'.format(namespace_group)
+
+ self.inventory.add_group(name)
+ self.inventory.add_group(namespace_group)
+ self.inventory.add_child(name, namespace_group)
+ self.inventory.add_group(namespace_routes_group)
+ self.inventory.add_child(namespace_group, namespace_routes_group)
+ for route in obj.items:
+ route_name = route.metadata.name
+ route_annotations = {} if not route.metadata.annotations else dict(route.metadata.annotations)
+
+ self.inventory.add_host(route_name)
+
+ if route.metadata.labels:
+ # create a group for each label_value
+ for key, value in route.metadata.labels:
+ group_name = 'label_{0}_{1}'.format(key, value)
+ self.inventory.add_group(group_name)
+ self.inventory.add_child(group_name, route_name)
+ route_labels = dict(route.metadata.labels)
+ else:
+ route_labels = {}
+
+ self.inventory.add_child(namespace_routes_group, route_name)
+
+ # add hostvars
+ self.inventory.set_variable(route_name, 'labels', route_labels)
+ self.inventory.set_variable(route_name, 'annotations', route_annotations)
+ self.inventory.set_variable(route_name, 'cluster_name', route.metadata.clusterName)
+ self.inventory.set_variable(route_name, 'object_type', 'route')
+ self.inventory.set_variable(route_name, 'self_link', route.metadata.selfLink)
+ self.inventory.set_variable(route_name, 'resource_version', route.metadata.resourceVersion)
+ self.inventory.set_variable(route_name, 'uid', route.metadata.uid)
+
+ if route.spec.host:
+ self.inventory.set_variable(route_name, 'host', route.spec.host)
+
+ if route.spec.path:
+ self.inventory.set_variable(route_name, 'path', route.spec.path)
+
+ if hasattr(route.spec.port, 'targetPort') and route.spec.port.targetPort:
+ self.inventory.set_variable(route_name, 'port', dict(route.spec.port))
diff --git a/ansible_collections/community/okd/plugins/module_utils/k8s.py b/ansible_collections/community/okd/plugins/module_utils/k8s.py
new file mode 100644
index 000000000..87ec70d90
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/k8s.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+import operator
+from functools import reduce
+from ansible_collections.community.okd.plugins.module_utils.openshift_common import AnsibleOpenshiftModule
+
+try:
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import create_definitions
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import CoreException
+except ImportError:
+ pass
+
+from ansible.module_utils._text import to_native
+
+try:
+ from kubernetes.dynamic.exceptions import DynamicApiError, NotFoundError, ForbiddenError
+except ImportError as e:
+ pass
+
+
+TRIGGER_ANNOTATION = 'image.openshift.io/triggers'
+TRIGGER_CONTAINER = re.compile(r"(?P<path>.*)\[((?P<index>[0-9]+)|\?\(@\.name==[\"'\\]*(?P<name>[a-z0-9]([-a-z0-9]*[a-z0-9])?))")
+
+
+class OKDRawModule(AnsibleOpenshiftModule):
+
+ def __init__(self, **kwargs):
+
+ super(OKDRawModule, self).__init__(**kwargs)
+
+ @property
+ def module(self):
+ return self._module
+
+ def execute_module(self):
+ results = []
+ changed = False
+
+ try:
+ definitions = create_definitions(self.params)
+ except Exception as e:
+ msg = "Failed to load resource definition: {0}".format(e)
+ raise CoreException(msg) from e
+
+ for definition in definitions:
+ result = {"changed": False, "result": {}}
+ warnings = []
+
+ if self.params.get("state") != 'absent':
+ existing = None
+ name = definition.get("metadata", {}).get("name")
+ namespace = definition.get("metadata", {}).get("namespace")
+ if definition.get("kind") in ['Project', 'ProjectRequest']:
+ try:
+ resource = self.svc.find_resource(kind=definition.get("kind"), api_version=definition.get("apiVersion", "v1"))
+ existing = resource.get(name=name, namespace=namespace).to_dict()
+ except (NotFoundError, ForbiddenError):
+ result = self.create_project_request(definition)
+ changed |= result["changed"]
+ results.append(result)
+ continue
+ except DynamicApiError as exc:
+ self.fail_json(msg='Failed to retrieve requested object: {0}'.format(exc.body),
+ error=exc.status, status=exc.status, reason=exc.reason)
+
+ if definition.get("kind") not in ['Project', 'ProjectRequest']:
+ try:
+ resource = self.svc.find_resource(kind=definition.get("kind"), api_version=definition.get("apiVersion", "v1"))
+ existing = resource.get(name=name, namespace=namespace).to_dict()
+ except Exception:
+ existing = None
+
+ if existing:
+ if resource.kind == 'DeploymentConfig':
+ if definition.get('spec', {}).get('triggers'):
+ definition = self.resolve_imagestream_triggers(existing, definition)
+ elif existing['metadata'].get('annotations', {}).get(TRIGGER_ANNOTATION):
+ definition = self.resolve_imagestream_trigger_annotation(existing, definition)
+
+ if self.params.get("validate") is not None:
+ warnings = self.validate(definition)
+
+ try:
+ result = self.perform_action(definition, self.params)
+ except Exception as e:
+ try:
+ error = e.result
+ except AttributeError:
+ error = {}
+ try:
+ error["reason"] = e.__cause__.reason
+ except AttributeError:
+ pass
+ error["msg"] = to_native(e)
+ if warnings:
+ error.setdefault("warnings", []).extend(warnings)
+
+ if self.params.get("continue_on_error"):
+ result["error"] = error
+ else:
+ self.fail_json(**error)
+
+ if warnings:
+ result.setdefault("warnings", []).extend(warnings)
+ changed |= result["changed"]
+ results.append(result)
+
+ if len(results) == 1:
+ self.exit_json(**results[0])
+
+ self.exit_json(**{"changed": changed, "result": {"results": results}})
+
+ @staticmethod
+ def get_index(desired, objects, keys):
+ """ Iterates over keys, returns the first object from objects where the value of the key
+ matches the value in desired
+ """
+ # pylint: disable=use-a-generator
+ # Use a generator instead 'all(desired.get(key, True) == item.get(key, False) for key in keys)'
+ for i, item in enumerate(objects):
+ if item and all([desired.get(key, True) == item.get(key, False) for key in keys]):
+ return i
+
+ def resolve_imagestream_trigger_annotation(self, existing, definition):
+ import yaml
+
+ def get_from_fields(d, fields):
+ try:
+ return reduce(operator.getitem, fields, d)
+ except Exception:
+ return None
+
+ def set_from_fields(d, fields, value):
+ get_from_fields(d, fields[:-1])[fields[-1]] = value
+
+ if TRIGGER_ANNOTATION in definition['metadata'].get('annotations', {}).keys():
+ triggers = yaml.safe_load(definition['metadata']['annotations'][TRIGGER_ANNOTATION] or '[]')
+ else:
+ triggers = yaml.safe_load(existing['metadata'].get('annotations', '{}').get(TRIGGER_ANNOTATION, '[]'))
+
+ if not isinstance(triggers, list):
+ return definition
+
+ for trigger in triggers:
+ if trigger.get('fieldPath'):
+ parsed = self.parse_trigger_fieldpath(trigger['fieldPath'])
+ path = parsed.get('path', '').split('.')
+ if path:
+ existing_containers = get_from_fields(existing, path)
+ new_containers = get_from_fields(definition, path)
+ if parsed.get('name'):
+ existing_index = self.get_index({'name': parsed['name']}, existing_containers, ['name'])
+ new_index = self.get_index({'name': parsed['name']}, new_containers, ['name'])
+ elif parsed.get('index') is not None:
+ existing_index = new_index = int(parsed['index'])
+ else:
+ existing_index = new_index = None
+ if existing_index is not None and new_index is not None:
+ if existing_index < len(existing_containers) and new_index < len(new_containers):
+ set_from_fields(definition, path + [new_index, 'image'], get_from_fields(existing, path + [existing_index, 'image']))
+ return definition
+
+ def resolve_imagestream_triggers(self, existing, definition):
+
+ existing_triggers = existing.get('spec', {}).get('triggers')
+ new_triggers = definition['spec']['triggers']
+ existing_containers = existing.get('spec', {}).get('template', {}).get('spec', {}).get('containers', [])
+ new_containers = definition.get('spec', {}).get('template', {}).get('spec', {}).get('containers', [])
+ for i, trigger in enumerate(new_triggers):
+ if trigger.get('type') == 'ImageChange' and trigger.get('imageChangeParams'):
+ names = trigger['imageChangeParams'].get('containerNames', [])
+ for name in names:
+ old_container_index = self.get_index({'name': name}, existing_containers, ['name'])
+ new_container_index = self.get_index({'name': name}, new_containers, ['name'])
+ if old_container_index is not None and new_container_index is not None:
+ image = existing['spec']['template']['spec']['containers'][old_container_index]['image']
+ definition['spec']['template']['spec']['containers'][new_container_index]['image'] = image
+
+ existing_index = self.get_index(trigger['imageChangeParams'],
+ [x.get('imageChangeParams') for x in existing_triggers],
+ ['containerNames'])
+ if existing_index is not None:
+ existing_image = existing_triggers[existing_index].get('imageChangeParams', {}).get('lastTriggeredImage')
+ if existing_image:
+ definition['spec']['triggers'][i]['imageChangeParams']['lastTriggeredImage'] = existing_image
+ existing_from = existing_triggers[existing_index].get('imageChangeParams', {}).get('from', {})
+ new_from = trigger['imageChangeParams'].get('from', {})
+ existing_namespace = existing_from.get('namespace')
+ existing_name = existing_from.get('name', False)
+ new_name = new_from.get('name', True)
+ add_namespace = existing_namespace and 'namespace' not in new_from.keys() and existing_name == new_name
+ if add_namespace:
+ definition['spec']['triggers'][i]['imageChangeParams']['from']['namespace'] = existing_from['namespace']
+
+ return definition
+
+ def parse_trigger_fieldpath(self, expression):
+ parsed = TRIGGER_CONTAINER.search(expression).groupdict()
+ if parsed.get('index'):
+ parsed['index'] = int(parsed['index'])
+ return parsed
+
+ def create_project_request(self, definition):
+ definition['kind'] = 'ProjectRequest'
+ result = {'changed': False, 'result': {}}
+ resource = self.svc.find_resource(kind='ProjectRequest', api_version=definition['apiVersion'], fail=True)
+ if not self.check_mode:
+ try:
+ k8s_obj = resource.create(definition)
+ result['result'] = k8s_obj.to_dict()
+ except DynamicApiError as exc:
+ self.fail_json(msg="Failed to create object: {0}".format(exc.body),
+ error=exc.status, status=exc.status, reason=exc.reason)
+ result['changed'] = True
+ result['method'] = 'create'
+ return result
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_auth.py b/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_auth.py
new file mode 100644
index 000000000..e5143ae4e
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_auth.py
@@ -0,0 +1,309 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible.module_utils._text import to_native
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_common import AnsibleOpenshiftModule
+
+try:
+ from kubernetes import client
+ from kubernetes.dynamic.exceptions import DynamicApiError, NotFoundError
+except ImportError:
+ pass
+
+
+class OpenShiftAdmPruneAuth(AnsibleOpenshiftModule):
+ def __init__(self, **kwargs):
+ super(OpenShiftAdmPruneAuth, self).__init__(**kwargs)
+
+ def prune_resource_binding(self, kind, api_version, ref_kind, ref_namespace_names, propagation_policy=None):
+
+ resource = self.find_resource(kind=kind, api_version=api_version, fail=True)
+ candidates = []
+ for ref_namespace, ref_name in ref_namespace_names:
+ try:
+ result = resource.get(name=None, namespace=ref_namespace)
+ result = result.to_dict()
+ result = result.get('items') if 'items' in result else [result]
+ for obj in result:
+ namespace = obj['metadata'].get('namespace', None)
+ name = obj['metadata'].get('name')
+ if ref_kind and obj['roleRef']['kind'] != ref_kind:
+ # skip this binding as the roleRef.kind does not match
+ continue
+ if obj['roleRef']['name'] == ref_name:
+ # select this binding as the roleRef.name match
+ candidates.append((namespace, name))
+ except NotFoundError:
+ continue
+ except DynamicApiError as exc:
+ msg = "Failed to get {kind} resource due to: {msg}".format(kind=kind, msg=exc.body)
+ self.fail_json(msg=msg)
+ except Exception as e:
+ msg = "Failed to get {kind} due to: {msg}".format(kind=kind, msg=to_native(e))
+ self.fail_json(msg=msg)
+
+ if len(candidates) == 0 or self.check_mode:
+ return [y if x is None else x + "/" + y for x, y in candidates]
+
+ delete_options = client.V1DeleteOptions()
+ if propagation_policy:
+ delete_options.propagation_policy = propagation_policy
+
+ for namespace, name in candidates:
+ try:
+ result = resource.delete(name=name, namespace=namespace, body=delete_options)
+ except DynamicApiError as exc:
+ msg = "Failed to delete {kind} {namespace}/{name} due to: {msg}".format(kind=kind, namespace=namespace, name=name, msg=exc.body)
+ self.fail_json(msg=msg)
+ except Exception as e:
+ msg = "Failed to delete {kind} {namespace}/{name} due to: {msg}".format(kind=kind, namespace=namespace, name=name, msg=to_native(e))
+ self.fail_json(msg=msg)
+ return [y if x is None else x + "/" + y for x, y in candidates]
+
+ def update_resource_binding(self, ref_kind, ref_names, namespaced=False):
+
+ kind = 'ClusterRoleBinding'
+ api_version = "rbac.authorization.k8s.io/v1",
+ if namespaced:
+ kind = "RoleBinding"
+ resource = self.find_resource(kind=kind, api_version=api_version, fail=True)
+ result = resource.get(name=None, namespace=None).to_dict()
+ result = result.get('items') if 'items' in result else [result]
+
+ if len(result) == 0:
+ return [], False
+
+ def _update_user_group(binding_namespace, subjects):
+ users, groups = [], []
+ for x in subjects:
+ if x['kind'] == 'User':
+ users.append(x['name'])
+ elif x['kind'] == 'Group':
+ groups.append(x['name'])
+ elif x['kind'] == 'ServiceAccount':
+ namespace = binding_namespace
+ if x.get('namespace') is not None:
+ namespace = x.get('namespace')
+ if namespace is not None:
+ users.append("system:serviceaccount:%s:%s" % (namespace, x['name']))
+ return users, groups
+
+ candidates = []
+ changed = False
+ for item in result:
+ subjects = item.get('subjects', [])
+ retainedSubjects = [x for x in subjects if x['kind'] == ref_kind and x['name'] in ref_names]
+ if len(subjects) != len(retainedSubjects):
+ updated_binding = item
+ updated_binding['subjects'] = retainedSubjects
+ binding_namespace = item['metadata'].get('namespace', None)
+ updated_binding['userNames'], updated_binding['groupNames'] = _update_user_group(binding_namespace, retainedSubjects)
+ candidates.append(binding_namespace + "/" + item['metadata']['name'] if binding_namespace else item['metadata']['name'])
+ changed = True
+ if not self.check_mode:
+ try:
+ resource.apply(updated_binding, namespace=binding_namespace)
+ except DynamicApiError as exc:
+ msg = "Failed to apply object due to: {0}".format(exc.body)
+ self.fail_json(msg=msg)
+ return candidates, changed
+
+ def update_security_context(self, ref_names, key):
+ params = {'kind': 'SecurityContextConstraints', 'api_version': 'security.openshift.io/v1'}
+ sccs = self.kubernetes_facts(**params)
+ if not sccs['api_found']:
+ self.fail_json(msg=sccs['msg'])
+ sccs = sccs.get('resources')
+
+ candidates = []
+ changed = False
+ resource = self.find_resource(kind="SecurityContextConstraints", api_version="security.openshift.io/v1")
+ for item in sccs:
+ subjects = item.get(key, [])
+ retainedSubjects = [x for x in subjects if x not in ref_names]
+ if len(subjects) != len(retainedSubjects):
+ candidates.append(item['metadata']['name'])
+ changed = True
+ if not self.check_mode:
+ upd_sec_ctx = item
+ upd_sec_ctx.update({key: retainedSubjects})
+ try:
+ resource.apply(upd_sec_ctx, namespace=None)
+ except DynamicApiError as exc:
+ msg = "Failed to apply object due to: {0}".format(exc.body)
+ self.fail_json(msg=msg)
+ return candidates, changed
+
+ def auth_prune_roles(self):
+ params = {'kind': 'Role', 'api_version': 'rbac.authorization.k8s.io/v1', 'namespace': self.params.get('namespace')}
+ for attr in ('name', 'label_selectors'):
+ if self.params.get(attr):
+ params[attr] = self.params.get(attr)
+
+ result = self.kubernetes_facts(**params)
+ if not result['api_found']:
+ self.fail_json(msg=result['msg'])
+
+ roles = result.get('resources')
+ if len(roles) == 0:
+ self.exit_json(changed=False, msg="No candidate rolebinding to prune from namespace %s." % self.params.get('namespace'))
+
+ ref_roles = [(x['metadata']['namespace'], x['metadata']['name']) for x in roles]
+ candidates = self.prune_resource_binding(kind="RoleBinding",
+ api_version="rbac.authorization.k8s.io/v1",
+ ref_kind="Role",
+ ref_namespace_names=ref_roles,
+ propagation_policy='Foreground')
+ if len(candidates) == 0:
+ self.exit_json(changed=False, role_binding=candidates)
+
+ self.exit_json(changed=True, role_binding=candidates)
+
+ def auth_prune_clusterroles(self):
+ params = {'kind': 'ClusterRole', 'api_version': 'rbac.authorization.k8s.io/v1'}
+ for attr in ('name', 'label_selectors'):
+ if self.params.get(attr):
+ params[attr] = self.params.get(attr)
+
+ result = self.kubernetes_facts(**params)
+ if not result['api_found']:
+ self.fail_json(msg=result['msg'])
+
+ clusterroles = result.get('resources')
+ if len(clusterroles) == 0:
+ self.exit_json(changed=False, msg="No clusterroles found matching input criteria.")
+
+ ref_clusterroles = [(None, x['metadata']['name']) for x in clusterroles]
+
+ # Prune ClusterRoleBinding
+ candidates_cluster_binding = self.prune_resource_binding(kind="ClusterRoleBinding",
+ api_version="rbac.authorization.k8s.io/v1",
+ ref_kind=None,
+ ref_namespace_names=ref_clusterroles)
+
+ # Prune Role Binding
+ candidates_namespaced_binding = self.prune_resource_binding(kind="RoleBinding",
+ api_version="rbac.authorization.k8s.io/v1",
+ ref_kind='ClusterRole',
+ ref_namespace_names=ref_clusterroles)
+
+ self.exit_json(changed=True,
+ cluster_role_binding=candidates_cluster_binding,
+ role_binding=candidates_namespaced_binding)
+
+ def list_groups(self, params=None):
+ options = {'kind': 'Group', 'api_version': 'user.openshift.io/v1'}
+ if params:
+ for attr in ('name', 'label_selectors'):
+ if params.get(attr):
+ options[attr] = params.get(attr)
+ return self.kubernetes_facts(**options)
+
+ def auth_prune_users(self):
+ params = {'kind': 'User', 'api_version': 'user.openshift.io/v1'}
+ for attr in ('name', 'label_selectors'):
+ if self.params.get(attr):
+ params[attr] = self.params.get(attr)
+
+ users = self.kubernetes_facts(**params)
+ if len(users) == 0:
+ self.exit_json(changed=False, msg="No resource type 'User' found matching input criteria.")
+
+ names = [x['metadata']['name'] for x in users]
+ changed = False
+ # Remove the user role binding
+ rolebinding, changed_role = self.update_resource_binding(ref_kind="User",
+ ref_names=names,
+ namespaced=True)
+ changed = changed or changed_role
+ # Remove the user cluster role binding
+ clusterrolesbinding, changed_cr = self.update_resource_binding(ref_kind="User",
+ ref_names=names)
+ changed = changed or changed_cr
+
+ # Remove the user from security context constraints
+ sccs, changed_sccs = self.update_security_context(names, 'users')
+ changed = changed or changed_sccs
+
+ # Remove the user from groups
+ groups = self.list_groups()
+ deleted_groups = []
+ resource = self.find_resource(kind="Group", api_version="user.openshift.io/v1")
+ for grp in groups:
+ subjects = grp.get('users', [])
+ retainedSubjects = [x for x in subjects if x not in names]
+ if len(subjects) != len(retainedSubjects):
+ deleted_groups.append(grp['metadata']['name'])
+ changed = True
+ if not self.check_mode:
+ upd_group = grp
+ upd_group.update({'users': retainedSubjects})
+ try:
+ resource.apply(upd_group, namespace=None)
+ except DynamicApiError as exc:
+ msg = "Failed to apply object due to: {0}".format(exc.body)
+ self.fail_json(msg=msg)
+
+ # Remove the user's OAuthClientAuthorizations
+ oauth = self.kubernetes_facts(kind='OAuthClientAuthorization', api_version='oauth.openshift.io/v1')
+ deleted_auths = []
+ resource = self.find_resource(kind="OAuthClientAuthorization", api_version="oauth.openshift.io/v1")
+ for authorization in oauth:
+ if authorization.get('userName', None) in names:
+ auth_name = authorization['metadata']['name']
+ deleted_auths.append(auth_name)
+ changed = True
+ if not self.check_mode:
+ try:
+ resource.delete(name=auth_name, namespace=None, body=client.V1DeleteOptions())
+ except DynamicApiError as exc:
+ msg = "Failed to delete OAuthClientAuthorization {name} due to: {msg}".format(name=auth_name, msg=exc.body)
+ self.fail_json(msg=msg)
+ except Exception as e:
+ msg = "Failed to delete OAuthClientAuthorization {name} due to: {msg}".format(name=auth_name, msg=to_native(e))
+ self.fail_json(msg=msg)
+
+ self.exit_json(changed=changed,
+ cluster_role_binding=clusterrolesbinding,
+ role_binding=rolebinding,
+ security_context_constraints=sccs,
+ authorization=deleted_auths,
+ group=deleted_groups)
+
+ def auth_prune_groups(self):
+ groups = self.list_groups(params=self.params)
+ if len(groups) == 0:
+ self.exit_json(changed=False, result="No resource type 'Group' found matching input criteria.")
+
+ names = [x['metadata']['name'] for x in groups]
+
+ changed = False
+ # Remove the groups role binding
+ rolebinding, changed_role = self.update_resource_binding(ref_kind="Group",
+ ref_names=names,
+ namespaced=True)
+ changed = changed or changed_role
+ # Remove the groups cluster role binding
+ clusterrolesbinding, changed_cr = self.update_resource_binding(ref_kind="Group",
+ ref_names=names)
+ changed = changed or changed_cr
+ # Remove the groups security context constraints
+ sccs, changed_sccs = self.update_security_context(names, 'groups')
+ changed = changed or changed_sccs
+
+ self.exit_json(changed=changed,
+ cluster_role_binding=clusterrolesbinding,
+ role_binding=rolebinding,
+ security_context_constraints=sccs)
+
+ def execute_module(self):
+ auth_prune = {
+ 'roles': self.auth_prune_roles,
+ 'clusterroles': self.auth_prune_clusterroles,
+ 'users': self.auth_prune_users,
+ 'groups': self.auth_prune_groups,
+ }
+ auth_prune[self.params.get('resource')]()
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_deployments.py b/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_deployments.py
new file mode 100644
index 000000000..418922d52
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_deployments.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from datetime import datetime, timezone
+import traceback
+
+from ansible.module_utils._text import to_native
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_common import AnsibleOpenshiftModule
+
+try:
+ from kubernetes import client
+ from kubernetes.dynamic.exceptions import DynamicApiError
+except ImportError as e:
+ pass
+
+
+def get_deploymentconfig_for_replicationcontroller(replica_controller):
+ # DeploymentConfigAnnotation is an annotation name used to correlate a deployment with the
+ # DeploymentConfig on which the deployment is based.
+ # This is set on replication controller pod template by deployer controller.
+ DeploymentConfigAnnotation = "openshift.io/deployment-config.name"
+ try:
+ deploymentconfig_name = replica_controller['metadata']['annotations'].get(DeploymentConfigAnnotation)
+ if deploymentconfig_name is None or deploymentconfig_name == "":
+ return None
+ return deploymentconfig_name
+ except Exception:
+ return None
+
+
+class OpenShiftAdmPruneDeployment(AnsibleOpenshiftModule):
+
+ def __init__(self, **kwargs):
+ super(OpenShiftAdmPruneDeployment, self).__init__(**kwargs)
+
+ def filter_replication_controller(self, replicacontrollers):
+ def _deployment(obj):
+ return get_deploymentconfig_for_replicationcontroller(obj) is not None
+
+ def _zeroReplicaSize(obj):
+ return obj['spec']['replicas'] == 0 and obj['status']['replicas'] == 0
+
+ def _complete_failed(obj):
+ DeploymentStatusAnnotation = "openshift.io/deployment.phase"
+ try:
+ # validate that replication controller status is either 'Complete' or 'Failed'
+ deployment_phase = obj['metadata']['annotations'].get(DeploymentStatusAnnotation)
+ return deployment_phase in ('Failed', 'Complete')
+ except Exception:
+ return False
+
+ def _younger(obj):
+ creation_timestamp = datetime.strptime(obj['metadata']['creationTimestamp'], '%Y-%m-%dT%H:%M:%SZ')
+ now = datetime.now(timezone.utc).replace(tzinfo=None)
+ age = (now - creation_timestamp).seconds / 60
+ return age > self.params['keep_younger_than']
+
+ def _orphan(obj):
+ try:
+ # verify if the deploymentconfig associated to the replication controller is still existing
+ deploymentconfig_name = get_deploymentconfig_for_replicationcontroller(obj)
+ params = dict(
+ kind="DeploymentConfig",
+ api_version="apps.openshift.io/v1",
+ name=deploymentconfig_name,
+ namespace=obj["metadata"]["name"],
+ )
+ exists = self.kubernetes_facts(**params)
+ return not (exists.get['api_found'] and len(exists['resources']) > 0)
+ except Exception:
+ return False
+
+ predicates = [_deployment, _zeroReplicaSize, _complete_failed]
+ if self.params['orphans']:
+ predicates.append(_orphan)
+ if self.params['keep_younger_than']:
+ predicates.append(_younger)
+
+ results = replicacontrollers.copy()
+ for pred in predicates:
+ results = filter(pred, results)
+ return list(results)
+
+ def execute_module(self):
+ # list replicationcontroller candidate for pruning
+ kind = 'ReplicationController'
+ api_version = 'v1'
+ resource = self.find_resource(kind=kind, api_version=api_version, fail=True)
+
+ # Get ReplicationController
+ params = dict(
+ kind=kind,
+ api_version="v1",
+ namespace=self.params.get("namespace"),
+ )
+ candidates = self.kubernetes_facts(**params)
+ candidates = self.filter_replication_controller(candidates["resources"])
+
+ if len(candidates) == 0:
+ self.exit_json(changed=False, replication_controllers=[])
+
+ changed = True
+ delete_options = client.V1DeleteOptions(propagation_policy='Background')
+ replication_controllers = []
+ for replica in candidates:
+ try:
+ result = replica
+ if not self.check_mode:
+ name = replica["metadata"]["name"]
+ namespace = replica["metadata"]["namespace"]
+ result = resource.delete(name=name, namespace=namespace, body=delete_options).to_dict()
+ replication_controllers.append(result)
+ except DynamicApiError as exc:
+ msg = "Failed to delete ReplicationController {namespace}/{name} due to: {msg}".format(namespace=namespace, name=name, msg=exc.body)
+ self.fail_json(msg=msg)
+ except Exception as e:
+ msg = "Failed to delete ReplicationController {namespace}/{name} due to: {msg}".format(namespace=namespace, name=name, msg=to_native(e))
+ self.fail_json(msg=msg)
+ self.exit_json(changed=changed, replication_controllers=replication_controllers)
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_images.py b/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_images.py
new file mode 100644
index 000000000..442cf9010
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_adm_prune_images.py
@@ -0,0 +1,477 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from datetime import datetime, timezone, timedelta
+import traceback
+import copy
+
+from ansible.module_utils._text import to_native
+from ansible.module_utils.parsing.convert_bool import boolean
+from ansible.module_utils.six import iteritems
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_common import AnsibleOpenshiftModule
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_images_common import (
+ OpenShiftAnalyzeImageStream,
+ get_image_blobs,
+ is_too_young_object,
+ is_created_after,
+)
+from ansible_collections.community.okd.plugins.module_utils.openshift_docker_image import (
+ parse_docker_image_ref,
+ convert_storage_to_bytes,
+)
+
+try:
+ from kubernetes import client
+ from kubernetes.client import rest
+ from kubernetes.dynamic.exceptions import (
+ DynamicApiError,
+ NotFoundError,
+ ApiException
+ )
+except ImportError:
+ pass
+
+
+ApiConfiguration = {
+ "LimitRange": "v1",
+ "Pod": "v1",
+ "ReplicationController": "v1",
+ "DaemonSet": "apps/v1",
+ "Deployment": "apps/v1",
+ "ReplicaSet": "apps/v1",
+ "StatefulSet": "apps/v1",
+ "Job": "batch/v1",
+ "CronJob": "batch/v1beta1",
+ "DeploymentConfig": "apps.openshift.io/v1",
+ "BuildConfig": "build.openshift.io/v1",
+ "Build": "build.openshift.io/v1",
+ "Image": "image.openshift.io/v1",
+ "ImageStream": "image.openshift.io/v1",
+}
+
+
+def read_object_annotation(obj, name):
+ return obj["metadata"]["annotations"].get(name)
+
+
+def determine_host_registry(module, images, image_streams):
+ # filter managed images
+ def _f_managed_images(obj):
+ value = read_object_annotation(obj, "openshift.io/image.managed")
+ return boolean(value) if value is not None else False
+
+ managed_images = list(filter(_f_managed_images, images))
+
+ # Be sure to pick up the newest managed image which should have an up to date information
+ sorted_images = sorted(managed_images,
+ key=lambda x: x["metadata"]["creationTimestamp"],
+ reverse=True)
+ docker_image_ref = ""
+ if len(sorted_images) > 0:
+ docker_image_ref = sorted_images[0].get("dockerImageReference", "")
+ else:
+ # 2nd try to get the pull spec from any image stream
+ # Sorting by creation timestamp may not get us up to date info. Modification time would be much
+ sorted_image_streams = sorted(image_streams,
+ key=lambda x: x["metadata"]["creationTimestamp"],
+ reverse=True)
+ for i_stream in sorted_image_streams:
+ docker_image_ref = i_stream["status"].get("dockerImageRepository", "")
+ if len(docker_image_ref) > 0:
+ break
+
+ if len(docker_image_ref) == 0:
+ module.exit_json(changed=False, result="no managed image found")
+
+ result, error = parse_docker_image_ref(docker_image_ref, module)
+ return result['hostname']
+
+
+class OpenShiftAdmPruneImages(AnsibleOpenshiftModule):
+ def __init__(self, **kwargs):
+ super(OpenShiftAdmPruneImages, self).__init__(**kwargs)
+
+ self.max_creation_timestamp = self.get_max_creation_timestamp()
+ self._rest_client = None
+ self.registryhost = self.params.get('registry_url')
+ self.changed = False
+
+ def list_objects(self):
+ result = {}
+ for kind, version in iteritems(ApiConfiguration):
+ namespace = None
+ if self.params.get("namespace") and kind.lower() == "imagestream":
+ namespace = self.params.get("namespace")
+ try:
+ result[kind] = self.kubernetes_facts(kind=kind,
+ api_version=version,
+ namespace=namespace).get('resources')
+ except DynamicApiError as e:
+ self.fail_json(
+ msg="An error occurred while trying to list objects.",
+ reason=e.reason,
+ status=e.status,
+ )
+ except Exception as e:
+ self.fail_json(
+ msg="An error occurred while trying to list objects.",
+ error=to_native(e)
+ )
+ return result
+
+ def get_max_creation_timestamp(self):
+ result = None
+ if self.params.get("keep_younger_than"):
+ dt_now = datetime.now(timezone.utc).replace(tzinfo=None)
+ result = dt_now - timedelta(minutes=self.params.get("keep_younger_than"))
+ return result
+
+ @property
+ def rest_client(self):
+ if not self._rest_client:
+ configuration = copy.deepcopy(self.client.configuration)
+ validate_certs = self.params.get('registry_validate_certs')
+ ssl_ca_cert = self.params.get('registry_ca_cert')
+ if validate_certs is not None:
+ configuration.verify_ssl = validate_certs
+ if ssl_ca_cert is not None:
+ configuration.ssl_ca_cert = ssl_ca_cert
+ self._rest_client = rest.RESTClientObject(configuration)
+
+ return self._rest_client
+
+ def delete_from_registry(self, url):
+ try:
+ response = self.rest_client.DELETE(url=url, headers=self.client.configuration.api_key)
+ if response.status == 404:
+ # Unable to delete layer
+ return None
+ # non-2xx/3xx response doesn't cause an error
+ if response.status < 200 or response.status >= 400:
+ return None
+ if response.status != 202 and response.status != 204:
+ self.fail_json(
+ msg="Delete URL {0}: Unexpected status code in response: {1}".format(
+ response.status, url),
+ reason=response.reason
+ )
+ return None
+ except ApiException as e:
+ if e.status != 404:
+ self.fail_json(
+ msg="Failed to delete URL: %s" % url,
+ reason=e.reason,
+ status=e.status,
+ )
+ except Exception as e:
+ self.fail_json(msg="Delete URL {0}: {1}".format(url, type(e)))
+
+ def delete_layers_links(self, path, layers):
+ for layer in layers:
+ url = "%s/v2/%s/blobs/%s" % (self.registryhost, path, layer)
+ self.changed = True
+ if not self.check_mode:
+ self.delete_from_registry(url=url)
+
+ def delete_manifests(self, path, digests):
+ for digest in digests:
+ url = "%s/v2/%s/manifests/%s" % (self.registryhost, path, digest)
+ self.changed = True
+ if not self.check_mode:
+ self.delete_from_registry(url=url)
+
+ def delete_blobs(self, blobs):
+ for blob in blobs:
+ self.changed = True
+ url = "%s/admin/blobs/%s" % (self.registryhost, blob)
+ if not self.check_mode:
+ self.delete_from_registry(url=url)
+
+ def update_image_stream_status(self, definition):
+ kind = definition["kind"]
+ api_version = definition["apiVersion"]
+ namespace = definition["metadata"]["namespace"]
+ name = definition["metadata"]["name"]
+
+ self.changed = True
+ result = definition
+ if not self.check_mode:
+ try:
+ result = self.request(
+ "PUT",
+ "/apis/{api_version}/namespaces/{namespace}/imagestreams/{name}/status".format(
+ api_version=api_version,
+ namespace=namespace,
+ name=name
+ ),
+ body=definition,
+ content_type="application/json",
+ ).to_dict()
+ except DynamicApiError as exc:
+ msg = "Failed to patch object: kind={0} {1}/{2}".format(
+ kind, namespace, name
+ )
+ self.fail_json(msg=msg, status=exc.status, reason=exc.reason)
+ except Exception as exc:
+ msg = "Failed to patch object kind={0} {1}/{2} due to: {3}".format(
+ kind, namespace, name, exc
+ )
+ self.fail_json(msg=msg, error=to_native(exc))
+ return result
+
+ def delete_image(self, image):
+ kind = "Image"
+ api_version = "image.openshift.io/v1"
+ resource = self.find_resource(kind=kind, api_version=api_version)
+ name = image["metadata"]["name"]
+ self.changed = True
+ if not self.check_mode:
+ try:
+ delete_options = client.V1DeleteOptions(grace_period_seconds=0)
+ return resource.delete(name=name, body=delete_options).to_dict()
+ except NotFoundError:
+ pass
+ except DynamicApiError as exc:
+ self.fail_json(
+ msg="Failed to delete object %s/%s due to: %s" % (
+ kind, name, exc.body
+ ),
+ reason=exc.reason,
+ status=exc.status
+ )
+ else:
+ existing = resource.get(name=name)
+ if existing:
+ existing = existing.to_dict()
+ return existing
+
+ def exceeds_limits(self, namespace, image):
+ if namespace not in self.limit_range:
+ return False
+ docker_image_metadata = image.get("dockerImageMetadata")
+ if not docker_image_metadata:
+ return False
+ docker_image_size = docker_image_metadata["Size"]
+
+ for limit in self.limit_range.get(namespace):
+ for item in limit["spec"]["limits"]:
+ if item["type"] != "openshift.io/Image":
+ continue
+ limit_max = item["max"]
+ if not limit_max:
+ continue
+ storage = limit_max["storage"]
+ if not storage:
+ continue
+ if convert_storage_to_bytes(storage) < docker_image_size:
+ # image size is larger than the permitted limit range max size
+ return True
+ return False
+
+ def prune_image_stream_tag(self, stream, tag_event_list):
+ manifests_to_delete, images_to_delete = [], []
+ filtered_items = []
+ tag_event_items = tag_event_list["items"] or []
+ prune_over_size_limit = self.params.get("prune_over_size_limit")
+ stream_namespace = stream["metadata"]["namespace"]
+ stream_name = stream["metadata"]["name"]
+ for idx, item in enumerate(tag_event_items):
+ if is_created_after(item["created"], self.max_creation_timestamp):
+ filtered_items.append(item)
+ continue
+
+ if idx == 0:
+ istag = "%s/%s:%s" % (stream_namespace,
+ stream_name,
+ tag_event_list["tag"])
+ if istag in self.used_tags:
+ # keeping because tag is used
+ filtered_items.append(item)
+ continue
+
+ if item["image"] not in self.image_mapping:
+ # There are few options why the image may not be found:
+ # 1. the image is deleted manually and this record is no longer valid
+ # 2. the imagestream was observed before the image creation, i.e.
+ # this record was created recently and it should be protected by keep_younger_than
+ continue
+
+ image = self.image_mapping[item["image"]]
+ # check prune over limit size
+ if prune_over_size_limit and not self.exceeds_limits(stream_namespace, image):
+ filtered_items.append(item)
+ continue
+
+ image_ref = "%s/%s@%s" % (stream_namespace,
+ stream_name,
+ item["image"])
+ if image_ref in self.used_images:
+ # keeping because tag is used
+ filtered_items.append(item)
+ continue
+
+ images_to_delete.append(item["image"])
+ if self.params.get('prune_registry'):
+ manifests_to_delete.append(image["metadata"]["name"])
+ path = stream_namespace + "/" + stream_name
+ image_blobs, err = get_image_blobs(image)
+ if not err:
+ self.delete_layers_links(path, image_blobs)
+
+ return filtered_items, manifests_to_delete, images_to_delete
+
+ def prune_image_streams(self, stream):
+ name = stream['metadata']['namespace'] + "/" + stream['metadata']['name']
+ if is_too_young_object(stream, self.max_creation_timestamp):
+ # keeping all images because of image stream too young
+ return None, []
+ facts = self.kubernetes_facts(kind="ImageStream",
+ api_version=ApiConfiguration.get("ImageStream"),
+ name=stream["metadata"]["name"],
+ namespace=stream["metadata"]["namespace"])
+ image_stream = facts.get('resources')
+ if len(image_stream) != 1:
+ # skipping because it does not exist anymore
+ return None, []
+ stream = image_stream[0]
+ namespace = self.params.get("namespace")
+ stream_to_update = not namespace or (stream["metadata"]["namespace"] == namespace)
+
+ manifests_to_delete, images_to_delete = [], []
+ deleted_items = False
+
+ # Update Image stream tag
+ if stream_to_update:
+ tags = stream["status"].get("tags", [])
+ for idx, tag_event_list in enumerate(tags):
+ (
+ filtered_tag_event,
+ tag_manifests_to_delete,
+ tag_images_to_delete
+ ) = self.prune_image_stream_tag(stream, tag_event_list)
+ stream['status']['tags'][idx]['items'] = filtered_tag_event
+ manifests_to_delete += tag_manifests_to_delete
+ images_to_delete += tag_images_to_delete
+ deleted_items = deleted_items or (len(tag_images_to_delete) > 0)
+
+ # Deleting tags without items
+ tags = []
+ for tag in stream["status"].get("tags", []):
+ if tag['items'] is None or len(tag['items']) == 0:
+ continue
+ tags.append(tag)
+
+ stream['status']['tags'] = tags
+ result = None
+ # Update ImageStream
+ if stream_to_update:
+ if deleted_items:
+ result = self.update_image_stream_status(stream)
+
+ if self.params.get("prune_registry"):
+ self.delete_manifests(name, manifests_to_delete)
+
+ return result, images_to_delete
+
+ def prune_images(self, image):
+ if not self.params.get("all_images"):
+ if read_object_annotation(image, "openshift.io/image.managed") != "true":
+ # keeping external image because all_images is set to false
+ # pruning only managed images
+ return None
+
+ if is_too_young_object(image, self.max_creation_timestamp):
+ # keeping because of keep_younger_than
+ return None
+
+ # Deleting image from registry
+ if self.params.get("prune_registry"):
+ image_blobs, err = get_image_blobs(image)
+ if err:
+ self.fail_json(msg=err)
+ # add blob for image name
+ image_blobs.append(image["metadata"]["name"])
+ self.delete_blobs(image_blobs)
+
+ # Delete image from cluster
+ return self.delete_image(image)
+
+ def execute_module(self):
+ resources = self.list_objects()
+ if not self.check_mode and self.params.get('prune_registry'):
+ if not self.registryhost:
+ self.registryhost = determine_host_registry(self.module, resources['Image'], resources['ImageStream'])
+ # validate that host has a scheme
+ if "://" not in self.registryhost:
+ self.registryhost = "https://" + self.registryhost
+ # Analyze Image Streams
+ analyze_ref = OpenShiftAnalyzeImageStream(
+ ignore_invalid_refs=self.params.get('ignore_invalid_refs'),
+ max_creation_timestamp=self.max_creation_timestamp,
+ module=self.module
+ )
+ self.used_tags, self.used_images, error = analyze_ref.analyze_image_stream(resources)
+ if error:
+ self.fail_json(msg=error)
+
+ # Create image mapping
+ self.image_mapping = {}
+ for m in resources["Image"]:
+ self.image_mapping[m["metadata"]["name"]] = m
+
+ # Create limit range mapping
+ self.limit_range = {}
+ for limit in resources["LimitRange"]:
+ namespace = limit["metadata"]["namespace"]
+ if namespace not in self.limit_range:
+ self.limit_range[namespace] = []
+ self.limit_range[namespace].append(limit)
+
+ # Stage 1: delete history from image streams
+ updated_image_streams = []
+ deleted_tags_images = []
+ updated_is_mapping = {}
+ for stream in resources['ImageStream']:
+ result, images_to_delete = self.prune_image_streams(stream)
+ if result:
+ updated_is_mapping[result["metadata"]["namespace"] + "/" + result["metadata"]["name"]] = result
+ updated_image_streams.append(result)
+ deleted_tags_images += images_to_delete
+
+ # Create a list with images referenced on image stream
+ self.referenced_images = []
+ for item in self.kubernetes_facts(kind="ImageStream", api_version="image.openshift.io/v1")["resources"]:
+ name = "%s/%s" % (item["metadata"]["namespace"], item["metadata"]["name"])
+ if name in updated_is_mapping:
+ item = updated_is_mapping[name]
+ for tag in item["status"].get("tags", []):
+ self.referenced_images += [t["image"] for t in tag["items"] or []]
+
+ # Stage 2: delete images
+ images = []
+ images_to_delete = [x["metadata"]["name"] for x in resources['Image']]
+ if self.params.get("namespace") is not None:
+ # When namespace is defined, prune only images that were referenced by ImageStream
+ # from the corresponding namespace
+ images_to_delete = deleted_tags_images
+ for name in images_to_delete:
+ if name in self.referenced_images:
+ # The image is referenced in one or more Image stream
+ continue
+ if name not in self.image_mapping:
+ # The image is not existing anymore
+ continue
+ result = self.prune_images(self.image_mapping[name])
+ if result:
+ images.append(result)
+
+ result = {
+ "changed": self.changed,
+ "deleted_images": images,
+ "updated_image_streams": updated_image_streams,
+ }
+ self.exit_json(**result)
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_builds.py b/ansible_collections/community/okd/plugins/module_utils/openshift_builds.py
new file mode 100644
index 000000000..02e60fd2a
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_builds.py
@@ -0,0 +1,409 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from datetime import datetime, timezone, timedelta
+import traceback
+import time
+
+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 as e:
+ pass
+
+
+class OpenShiftBuilds(AnsibleOpenshiftModule):
+ def __init__(self, **kwargs):
+ super(OpenShiftBuilds, self).__init__(**kwargs)
+
+ def get_build_config(self, name, namespace):
+ params = dict(
+ kind="BuildConfig",
+ api_version="build.openshift.io/v1",
+ name=name,
+ namespace=namespace,
+ )
+ result = self.kubernetes_facts(**params)
+ return result["resources"]
+
+ def clone_build(self, name, namespace, request):
+ try:
+ result = self.request(
+ method="POST",
+ path="/apis/build.openshift.io/v1/namespaces/{namespace}/builds/{name}/clone".format(
+ namespace=namespace,
+ name=name
+ ),
+ body=request,
+ content_type="application/json",
+ )
+ return result.to_dict()
+ except DynamicApiError as exc:
+ msg = "Failed to clone Build %s/%s due to: %s" % (namespace, name, exc.body)
+ self.fail_json(msg=msg, status=exc.status, reason=exc.reason)
+ except Exception as e:
+ msg = "Failed to clone Build %s/%s due to: %s" % (namespace, name, to_native(e))
+ self.fail_json(msg=msg, error=to_native(e), exception=e)
+
+ def instantiate_build_config(self, name, namespace, request):
+ try:
+ result = self.request(
+ method="POST",
+ path="/apis/build.openshift.io/v1/namespaces/{namespace}/buildconfigs/{name}/instantiate".format(
+ namespace=namespace,
+ name=name
+ ),
+ body=request,
+ content_type="application/json",
+ )
+ return result.to_dict()
+ except DynamicApiError as exc:
+ msg = "Failed to instantiate BuildConfig %s/%s due to: %s" % (namespace, name, exc.body)
+ self.fail_json(msg=msg, status=exc.status, reason=exc.reason)
+ except Exception as e:
+ msg = "Failed to instantiate BuildConfig %s/%s due to: %s" % (namespace, name, to_native(e))
+ self.fail_json(msg=msg, error=to_native(e), exception=e)
+
+ def start_build(self):
+
+ result = None
+ name = self.params.get("build_config_name")
+ if not name:
+ name = self.params.get("build_name")
+
+ build_request = {
+ "kind": "BuildRequest",
+ "apiVersion": "build.openshift.io/v1",
+ "metadata": {
+ "name": name
+ },
+ "triggeredBy": [
+ {"message": "Manually triggered"}
+ ],
+ }
+
+ # Overrides incremental
+ incremental = self.params.get("incremental")
+ if incremental is not None:
+ build_request.update(
+ {
+ "sourceStrategyOptions": {
+ "incremental": incremental
+ }
+ }
+ )
+
+ # Environment variable
+ if self.params.get("env_vars"):
+ build_request.update(
+ {
+ "env": self.params.get("env_vars")
+ }
+ )
+
+ # Docker strategy option
+ if self.params.get("build_args"):
+ build_request.update(
+ {
+ "dockerStrategyOptions": {
+ "buildArgs": self.params.get("build_args")
+ },
+ }
+ )
+
+ # caching option
+ no_cache = self.params.get("no_cache")
+ if no_cache is not None:
+ build_request.update(
+ {
+ "dockerStrategyOptions": {
+ "noCache": no_cache
+ },
+ }
+ )
+
+ # commit
+ if self.params.get("commit"):
+ build_request.update(
+ {
+ "revision": {
+ "git": {
+ "commit": self.params.get("commit")
+ }
+ }
+ }
+ )
+
+ if self.params.get("build_config_name"):
+ # Instantiate Build from Build config
+ result = self.instantiate_build_config(
+ name=self.params.get("build_config_name"),
+ namespace=self.params.get("namespace"),
+ request=build_request
+ )
+
+ else:
+ # Re-run Build
+ result = self.clone_build(
+ name=self.params.get("build_name"),
+ namespace=self.params.get("namespace"),
+ request=build_request
+ )
+
+ if result and self.params.get("wait"):
+ start = datetime.now()
+
+ def _total_wait_time():
+ return (datetime.now() - start).seconds
+
+ wait_timeout = self.params.get("wait_timeout")
+ wait_sleep = self.params.get("wait_sleep")
+ last_status_phase = None
+ while _total_wait_time() < wait_timeout:
+ params = dict(
+ kind=result["kind"],
+ api_version=result["apiVersion"],
+ name=result["metadata"]["name"],
+ namespace=result["metadata"]["namespace"],
+ )
+ facts = self.kubernetes_facts(**params)
+ if len(facts["resources"]) > 0:
+ last_status_phase = facts["resources"][0]["status"]["phase"]
+ if last_status_phase == "Complete":
+ result = facts["resources"][0]
+ break
+ elif last_status_phase in ("Cancelled", "Error", "Failed"):
+ self.fail_json(
+ msg="Unexpected status for Build %s/%s: %s" % (
+ result["metadata"]["name"],
+ result["metadata"]["namespace"],
+ last_status_phase
+ )
+ )
+ time.sleep(wait_sleep)
+
+ if last_status_phase != "Complete":
+ name = result["metadata"]["name"]
+ namespace = result["metadata"]["namespace"]
+ msg = "Build %s/%s has not complete after %d second(s)," \
+ "current status is %s" % (namespace, name, wait_timeout, last_status_phase)
+
+ self.fail_json(msg=msg)
+
+ result = [result] if result else []
+ self.exit_json(changed=True, builds=result)
+
+ def cancel_build(self, restart):
+
+ kind = 'Build'
+ api_version = 'build.openshift.io/v1'
+
+ namespace = self.params.get("namespace")
+ phases = ["new", "pending", "running"]
+ build_phases = self.params.get("build_phases", [])
+ if build_phases:
+ phases = [p.lower() for p in build_phases]
+
+ names = []
+ if self.params.get("build_name"):
+ names.append(self.params.get("build_name"))
+ else:
+ build_config = self.params.get("build_config_name")
+ # list all builds from namespace
+ params = dict(
+ kind=kind,
+ api_version=api_version,
+ namespace=namespace
+ )
+ resources = self.kubernetes_facts(**params).get("resources", [])
+
+ def _filter_builds(build):
+ config = build["metadata"].get("labels", {}).get("openshift.io/build-config.name")
+ return build_config is None or (build_config is not None and config in build_config)
+
+ for item in list(filter(_filter_builds, resources)):
+ name = item["metadata"]["name"]
+ if name not in names:
+ names.append(name)
+
+ if len(names) == 0:
+ self.exit_json(changed=False, msg="No Build found from namespace %s" % namespace)
+
+ warning = []
+ builds_to_cancel = []
+ for name in names:
+ params = dict(
+ kind=kind,
+ api_version=api_version,
+ name=name,
+ namespace=namespace
+ )
+
+ resource = self.kubernetes_facts(**params).get("resources", [])
+ if len(resource) == 0:
+ warning.append("Build %s/%s not found" % (namespace, name))
+ continue
+
+ resource = resource[0]
+ phase = resource["status"].get("phase").lower()
+
+ # Build status.phase is matching the requested state and is not completed
+ if phase in phases:
+ builds_to_cancel.append(resource)
+ else:
+ warning.append("build %s/%s is not in expected phase, found %s" % (namespace, name, phase))
+
+ changed = False
+ result = []
+ for build in builds_to_cancel:
+ # Set cancelled to true
+ build["status"]["cancelled"] = True
+ name = build["metadata"]["name"]
+ changed = True
+ try:
+ content_type = "application/json"
+ cancelled_build = self.request(
+ "PUT",
+ "/apis/build.openshift.io/v1/namespaces/{0}/builds/{1}".format(
+ namespace, name
+ ),
+ body=build,
+ content_type=content_type,
+ ).to_dict()
+ result.append(cancelled_build)
+ except DynamicApiError as exc:
+ self.fail_json(
+ msg="Failed to cancel Build %s/%s due to: %s" % (namespace, name, exc),
+ reason=exc.reason,
+ status=exc.status
+ )
+ except Exception as e:
+ self.fail_json(
+ msg="Failed to cancel Build %s/%s due to: %s" % (namespace, name, e)
+ )
+
+ # Make sure the build phase is really cancelled.
+ def _wait_until_cancelled(build, wait_timeout, wait_sleep):
+ start = datetime.now()
+ last_phase = None
+ name = build["metadata"]["name"]
+ while (datetime.now() - start).seconds < wait_timeout:
+ params = dict(
+ kind=kind,
+ api_version=api_version,
+ name=name,
+ namespace=namespace
+ )
+ resource = self.kubernetes_facts(**params).get("resources", [])
+ if len(resource) == 0:
+ return None, "Build %s/%s not found" % (namespace, name)
+ resource = resource[0]
+ last_phase = resource["status"]["phase"]
+ if last_phase == "Cancelled":
+ return resource, None
+ time.sleep(wait_sleep)
+ return None, "Build %s/%s is not cancelled as expected, current state is %s" % (namespace, name, last_phase)
+
+ if result and self.params.get("wait"):
+ wait_timeout = self.params.get("wait_timeout")
+ wait_sleep = self.params.get("wait_sleep")
+
+ wait_result = []
+ for build in result:
+ ret, err = _wait_until_cancelled(build, wait_timeout, wait_sleep)
+ if err:
+ self.exit_json(msg=err)
+ wait_result.append(ret)
+ result = wait_result
+
+ if restart:
+ self.start_build()
+
+ self.exit_json(builds=result, changed=changed)
+
+ def execute_module(self):
+ state = self.params.get("state")
+ if state == "started":
+ self.start_build()
+ else:
+ restart = bool(state == "restarted")
+ self.cancel_build(restart=restart)
+
+
+class OpenShiftPruneBuilds(OpenShiftBuilds):
+ def __init__(self, **kwargs):
+ super(OpenShiftPruneBuilds, self).__init__(**kwargs)
+
+ def execute_module(self):
+ # list replicationcontroller candidate for pruning
+ kind = 'Build'
+ api_version = 'build.openshift.io/v1'
+ resource = self.find_resource(kind=kind, api_version=api_version, fail=True)
+
+ self.max_creation_timestamp = None
+ keep_younger_than = self.params.get("keep_younger_than")
+ if keep_younger_than:
+ now = datetime.now(timezone.utc).replace(tzinfo=None)
+ self.max_creation_timestamp = now - timedelta(minutes=keep_younger_than)
+
+ def _prunable_build(build):
+ return build["status"]["phase"] in ("Complete", "Failed", "Error", "Cancelled")
+
+ def _orphan_build(build):
+ if not _prunable_build(build):
+ return False
+
+ config = build["status"].get("config", None)
+ if not config:
+ return True
+ build_config = self.get_build_config(config["name"], config["namespace"])
+ return len(build_config) == 0
+
+ def _younger_build(build):
+ if not self.max_creation_timestamp:
+ return False
+ creation_timestamp = datetime.strptime(build['metadata']['creationTimestamp'], '%Y-%m-%dT%H:%M:%SZ')
+ return creation_timestamp < self.max_creation_timestamp
+
+ predicates = [
+ _prunable_build,
+ ]
+ if self.params.get("orphans"):
+ predicates.append(_orphan_build)
+ if self.max_creation_timestamp:
+ predicates.append(_younger_build)
+
+ # Get ReplicationController
+ params = dict(
+ kind=kind,
+ api_version=api_version,
+ namespace=self.params.get("namespace"),
+ )
+ result = self.kubernetes_facts(**params)
+ candidates = result["resources"]
+ for pred in predicates:
+ candidates = list(filter(pred, candidates))
+
+ if self.check_mode:
+ changed = len(candidates) > 0
+ self.exit_json(changed=changed, builds=candidates)
+
+ changed = False
+ for build in candidates:
+ changed = True
+ try:
+ name = build["metadata"]["name"]
+ namespace = build["metadata"]["namespace"]
+ resource.delete(name=name, namespace=namespace, body={})
+ except DynamicApiError as exc:
+ msg = "Failed to delete Build %s/%s due to: %s" % (namespace, name, exc.body)
+ self.fail_json(msg=msg, status=exc.status, reason=exc.reason)
+ except Exception as e:
+ msg = "Failed to delete Build %s/%s due to: %s" % (namespace, name, to_native(e))
+ self.fail_json(msg=msg, error=to_native(e), exception=e)
+ self.exit_json(changed=changed, builds=candidates)
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_common.py b/ansible_collections/community/okd/plugins/module_utils/openshift_common.py
new file mode 100644
index 000000000..a1318f9a5
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_common.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import traceback
+from abc import abstractmethod
+
+from ansible.module_utils._text import to_native
+
+try:
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import get_api_client
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import AnsibleK8SModule
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
+ K8sService,
+ diff_objects,
+ )
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.runner import (
+ perform_action,
+ validate,
+ )
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
+ create_definitions,
+ merge_params,
+ flatten_list_kind,
+ )
+ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import CoreException
+ HAS_KUBERNETES_COLLECTION = True
+ k8s_collection_import_exception = None
+ K8S_COLLECTION_ERROR = None
+except ImportError as e:
+ HAS_KUBERNETES_COLLECTION = False
+ k8s_collection_import_exception = e
+ K8S_COLLECTION_ERROR = traceback.format_exc()
+
+
+class AnsibleOpenshiftModule(AnsibleK8SModule):
+
+ def __init__(self, **kwargs):
+ super(AnsibleOpenshiftModule, self).__init__(**kwargs)
+
+ self.client = get_api_client(module=self)
+ self.fail = self.fail_json
+
+ self.svc = K8sService(self.client, self._module)
+ self.find_resource = self.svc.find_resource
+ self.kubernetes_facts = self.svc.find
+
+ if not HAS_KUBERNETES_COLLECTION:
+ self.fail_json(
+ msg="The kubernetes.core collection must be installed",
+ exception=K8S_COLLECTION_ERROR,
+ error=to_native(k8s_collection_import_exception),
+ )
+
+ @property
+ def module(self):
+ return self._module
+
+ @abstractmethod
+ def execute_module(self):
+ pass
+
+ def request(self, *args, **kwargs):
+ return self.client.client.request(*args, **kwargs)
+
+ def set_resource_definitions(self):
+ self.resource_definitions = create_definitions(self.params)
+
+ def perform_action(self, definition, params):
+ return perform_action(self.svc, definition, params)
+
+ def validate(self, definition):
+ validate(self.client, self, definition)
+
+ @staticmethod
+ def merge_params(definition, params):
+ return merge_params(definition, params)
+
+ @staticmethod
+ def flatten_list_kind(definition, params):
+ return flatten_list_kind(definition, params)
+
+ @staticmethod
+ def diff_objects(existing, new):
+ return diff_objects(existing, new)
+
+ def run_module(self):
+
+ try:
+ self.execute_module()
+ except CoreException as e:
+ self.fail_from_exception(e)
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_docker_image.py b/ansible_collections/community/okd/plugins/module_utils/openshift_docker_image.py
new file mode 100644
index 000000000..27dbe6cc7
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_docker_image.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+
+
+def convert_storage_to_bytes(value):
+ keys = {
+ "Ki": 1024,
+ "Mi": 1024 * 1024,
+ "Gi": 1024 * 1024 * 1024,
+ "Ti": 1024 * 1024 * 1024 * 1024,
+ "Pi": 1024 * 1024 * 1024 * 1024 * 1024,
+ "Ei": 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
+ }
+ for k in keys:
+ if value.endswith(k) or value.endswith(k[0]):
+ idx = value.find(k[0])
+ return keys.get(k) * int(value[:idx])
+ return int(value)
+
+
+def is_valid_digest(digest):
+
+ digest_algorithm_size = dict(
+ sha256=64, sha384=96, sha512=128,
+ )
+
+ m = re.match(r'[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+', digest)
+ if not m:
+ return "Docker digest does not match expected format %s" % digest
+
+ idx = digest.find(':')
+ # case: "sha256:" with no hex.
+ if idx < 0 or idx == (len(digest) - 1):
+ return "Invalid docker digest %s, no hex value define" % digest
+
+ algorithm = digest[:idx]
+ if algorithm not in digest_algorithm_size:
+ return "Unsupported digest algorithm value %s for digest %s" % (algorithm, digest)
+
+ hex_value = digest[idx + 1:]
+ if len(hex_value) != digest_algorithm_size.get(algorithm):
+ return "Invalid length for digest hex expected %d found %d (digest is %s)" % (
+ digest_algorithm_size.get(algorithm), len(hex_value), digest
+ )
+
+
+def parse_docker_image_ref(image_ref, module=None):
+ """
+ Docker Grammar Reference
+ Reference => name [ ":" tag ] [ "@" digest ]
+ name => [hostname '/'] component ['/' component]*
+ hostname => hostcomponent ['.' hostcomponent]* [':' port-number]
+ hostcomponent => /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
+ port-number => /[0-9]+/
+ component => alpha-numeric [separator alpha-numeric]*
+ alpha-numeric => /[a-z0-9]+/
+ separator => /[_.]|__|[-]*/
+ """
+ idx = image_ref.find("/")
+
+ def _contains_any(src, values):
+ return any(x in src for x in values)
+
+ result = {
+ "tag": None, "digest": None
+ }
+ default_domain = "docker.io"
+ if idx < 0 or (not _contains_any(image_ref[:idx], ":.") and image_ref[:idx] != "localhost"):
+ result["hostname"], remainder = default_domain, image_ref
+ else:
+ result["hostname"], remainder = image_ref[:idx], image_ref[idx + 1:]
+
+ # Parse remainder information
+ idx = remainder.find("@")
+ if idx > 0 and len(remainder) > (idx + 1):
+ # docker image reference with digest
+ component, result["digest"] = remainder[:idx], remainder[idx + 1:]
+ err = is_valid_digest(result["digest"])
+ if err:
+ if module:
+ module.fail_json(msg=err)
+ return None, err
+ else:
+ idx = remainder.find(":")
+ if idx > 0 and len(remainder) > (idx + 1):
+ # docker image reference with tag
+ component, result["tag"] = remainder[:idx], remainder[idx + 1:]
+ else:
+ # name only
+ component = remainder
+ v = component.split("/")
+ namespace = None
+ if len(v) > 1:
+ namespace = v[0]
+ result.update({
+ "namespace": namespace, "name": v[-1]
+ })
+
+ return result, None
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_groups.py b/ansible_collections/community/okd/plugins/module_utils/openshift_groups.py
new file mode 100644
index 000000000..5d1aaadc1
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_groups.py
@@ -0,0 +1,442 @@
+#!/usr/bin/env 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
+
+
+import traceback
+from datetime import datetime
+
+from ansible.module_utils.parsing.convert_bool import boolean
+from ansible.module_utils._text import to_native
+from ansible.module_utils.basic import missing_required_lib
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_ldap import (
+ validate_ldap_sync_config,
+ ldap_split_host_port,
+ OpenshiftLDAPRFC2307,
+ OpenshiftLDAPActiveDirectory,
+ OpenshiftLDAPAugmentedActiveDirectory
+)
+
+try:
+ import ldap
+ HAS_PYTHON_LDAP = True
+ PYTHON_LDAP_ERROR = None
+except ImportError as e:
+ HAS_PYTHON_LDAP = False
+ PYTHON_LDAP_ERROR = e
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_common import AnsibleOpenshiftModule
+
+try:
+ from kubernetes.dynamic.exceptions import DynamicApiError
+except ImportError as e:
+ pass
+
+
+LDAP_OPENSHIFT_HOST_LABEL = "openshift.io/ldap.host"
+LDAP_OPENSHIFT_URL_ANNOTATION = "openshift.io/ldap.url"
+LDAP_OPENSHIFT_UID_ANNOTATION = "openshift.io/ldap.uid"
+LDAP_OPENSHIFT_SYNCTIME_ANNOTATION = "openshift.io/ldap.sync-time"
+
+
+def connect_to_ldap(module, server_uri, bind_dn=None, bind_pw=None, insecure=True, ca_file=None):
+ if insecure:
+ ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
+ elif ca_file:
+ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, ca_file)
+ try:
+ connection = ldap.initialize(server_uri)
+ connection.set_option(ldap.OPT_REFERRALS, 0)
+
+ connection.simple_bind_s(bind_dn, bind_pw)
+ return connection
+ except ldap.LDAPError as e:
+ module.fail_json(msg="Cannot bind to the LDAP server '{0}' due to: {1}".format(server_uri, e))
+
+
+def validate_group_annotation(definition, host_ip):
+ name = definition['metadata']['name']
+ # Validate LDAP URL Annotation
+ annotate_url = definition['metadata'].get('annotations', {}).get(LDAP_OPENSHIFT_URL_ANNOTATION)
+ if host_ip:
+ if not annotate_url:
+ return "group '{0}' marked as having been synced did not have an '{1}' annotation".format(name, LDAP_OPENSHIFT_URL_ANNOTATION)
+ elif annotate_url != host_ip:
+ return "group '{0}' was not synchronized from: '{1}'".format(name, host_ip)
+ # Validate LDAP UID Annotation
+ annotate_uid = definition['metadata']['annotations'].get(LDAP_OPENSHIFT_UID_ANNOTATION)
+ if not annotate_uid:
+ return "group '{0}' marked as having been synced did not have an '{1}' annotation".format(name, LDAP_OPENSHIFT_UID_ANNOTATION)
+ return None
+
+
+class OpenshiftLDAPGroups(object):
+
+ kind = "Group"
+ version = "user.openshift.io/v1"
+
+ def __init__(self, module):
+ self.module = module
+ self.cache = {}
+ self.__group_api = None
+
+ @property
+ def k8s_group_api(self):
+ if not self.__group_api:
+ params = dict(
+ kind=self.kind,
+ api_version=self.version,
+ fail=True
+ )
+ self.__group_api = self.module.find_resource(**params)
+ return self.__group_api
+
+ def get_group_info(self, return_list=False, **kwargs):
+ params = dict(
+ kind=self.kind,
+ api_version=self.version,
+ )
+ params.update(kwargs)
+ result = self.module.kubernetes_facts(**params)
+ if len(result["resources"]) == 0:
+ return None
+ if len(result["resources"]) == 1 and not return_list:
+ return result["resources"][0]
+ else:
+ return result["resources"]
+
+ def list_groups(self):
+ allow_groups = self.module.params.get("allow_groups")
+ deny_groups = self.module.params.get("deny_groups")
+ name_mapping = self.module.config.get("groupUIDNameMapping")
+
+ if name_mapping and (allow_groups or deny_groups):
+
+ def _map_group_names(groups):
+ return [name_mapping.get(value, value) for value in groups]
+
+ allow_groups = _map_group_names(allow_groups)
+ deny_groups = _map_group_names(deny_groups)
+
+ host = self.module.host
+ netlocation = self.module.netlocation
+ groups = []
+ if allow_groups:
+ missing = []
+ for grp in allow_groups:
+ if grp in deny_groups:
+ continue
+ resource = self.get_group_info(name=grp)
+ if not resource:
+ missing.append(grp)
+ continue
+ groups.append(resource)
+
+ if missing:
+ self.module.fail_json(
+ msg="The following groups were not found: %s" % ''.join(missing)
+ )
+ else:
+ label_selector = "%s=%s" % (LDAP_OPENSHIFT_HOST_LABEL, host)
+ resources = self.get_group_info(label_selectors=[label_selector], return_list=True)
+ if not resources:
+ return None, "Unable to find Group matching label selector '%s'" % label_selector
+ groups = resources
+ if deny_groups:
+ groups = [item for item in groups if item["metadata"]["name"] not in deny_groups]
+
+ uids = []
+ for grp in groups:
+ err = validate_group_annotation(grp, netlocation)
+ if err and allow_groups:
+ # We raise an error for group part of the allow_group not matching LDAP sync criteria
+ return None, err
+ group_uid = grp['metadata']['annotations'].get(LDAP_OPENSHIFT_UID_ANNOTATION)
+ self.cache[group_uid] = grp
+ uids.append(group_uid)
+ return uids, None
+
+ def get_group_name_for_uid(self, group_uid):
+ if group_uid not in self.cache:
+ return None, "No mapping found for Group uid: %s" % group_uid
+ return self.cache[group_uid]["metadata"]["name"], None
+
+ def make_openshift_group(self, group_uid, group_name, usernames):
+ group = self.get_group_info(name=group_name)
+ if not group:
+ group = {
+ "apiVersion": "user.openshift.io/v1",
+ "kind": "Group",
+ "metadata": {
+ "name": group_name,
+ "labels": {
+ LDAP_OPENSHIFT_HOST_LABEL: self.module.host
+ },
+ "annotations": {
+ LDAP_OPENSHIFT_URL_ANNOTATION: self.module.netlocation,
+ LDAP_OPENSHIFT_UID_ANNOTATION: group_uid,
+ }
+ }
+ }
+
+ # Make sure we aren't taking over an OpenShift group that is already related to a different LDAP group
+ ldaphost_label = group["metadata"].get("labels", {}).get(LDAP_OPENSHIFT_HOST_LABEL)
+ if not ldaphost_label or ldaphost_label != self.module.host:
+ return None, "Group %s: %s label did not match sync host: wanted %s, got %s" % (
+ group_name, LDAP_OPENSHIFT_HOST_LABEL, self.module.host, ldaphost_label
+ )
+
+ ldapurl_annotation = group["metadata"].get("annotations", {}).get(LDAP_OPENSHIFT_URL_ANNOTATION)
+ if not ldapurl_annotation or ldapurl_annotation != self.module.netlocation:
+ return None, "Group %s: %s annotation did not match sync host: wanted %s, got %s" % (
+ group_name, LDAP_OPENSHIFT_URL_ANNOTATION, self.module.netlocation, ldapurl_annotation
+ )
+
+ ldapuid_annotation = group["metadata"].get("annotations", {}).get(LDAP_OPENSHIFT_UID_ANNOTATION)
+ if not ldapuid_annotation or ldapuid_annotation != group_uid:
+ return None, "Group %s: %s annotation did not match LDAP UID: wanted %s, got %s" % (
+ group_name, LDAP_OPENSHIFT_UID_ANNOTATION, group_uid, ldapuid_annotation
+ )
+
+ # Overwrite Group Users data
+ group["users"] = usernames
+ group["metadata"]["annotations"][LDAP_OPENSHIFT_SYNCTIME_ANNOTATION] = datetime.now().isoformat()
+ return group, None
+
+ def create_openshift_groups(self, groups: list):
+ diffs = []
+ results = []
+ changed = False
+ for definition in groups:
+ name = definition["metadata"]["name"]
+ existing = self.get_group_info(name=name)
+ if not self.module.check_mode:
+ method = "patch" if existing else "create"
+ try:
+ if existing:
+ definition = self.k8s_group_api.patch(definition).to_dict()
+ else:
+ definition = self.k8s_group_api.create(definition).to_dict()
+ except DynamicApiError as exc:
+ self.module.fail_json(msg="Failed to %s Group '%s' due to: %s" % (method, name, exc.body))
+ except Exception as exc:
+ self.module.fail_json(msg="Failed to %s Group '%s' due to: %s" % (method, name, to_native(exc)))
+ equals = False
+ if existing:
+ equals, diff = self.module.diff_objects(existing, definition)
+ diffs.append(diff)
+ changed = changed or not equals
+ results.append(definition)
+ return results, diffs, changed
+
+ def delete_openshift_group(self, name: str):
+ result = dict(
+ kind=self.kind,
+ apiVersion=self.version,
+ metadata=dict(
+ name=name
+ )
+ )
+ if not self.module.check_mode:
+ try:
+ result = self.k8s_group_api.delete(name=name).to_dict()
+ except DynamicApiError as exc:
+ self.module.fail_json(msg="Failed to delete Group '{0}' due to: {1}".format(name, exc.body))
+ except Exception as exc:
+ self.module.fail_json(msg="Failed to delete Group '{0}' due to: {1}".format(name, to_native(exc)))
+ return result
+
+
+class OpenshiftGroupsSync(AnsibleOpenshiftModule):
+
+ def __init__(self, **kwargs):
+
+ super(OpenshiftGroupsSync, self).__init__(**kwargs)
+ self.__k8s_group_api = None
+ self.__ldap_connection = None
+ self.host = None
+ self.port = None
+ self.netlocation = None
+ self.scheme = None
+ self.config = self.params.get("sync_config")
+
+ if not HAS_PYTHON_LDAP:
+ self.fail_json(
+ msg=missing_required_lib('python-ldap'), error=to_native(PYTHON_LDAP_ERROR)
+ )
+
+ @property
+ def k8s_group_api(self):
+ if not self.__k8s_group_api:
+ params = dict(
+ kind="Group",
+ api_version="user.openshift.io/v1",
+ fail=True
+ )
+ self.__k8s_group_api = self.find_resource(**params)
+ return self.__k8s_group_api
+
+ @property
+ def hostIP(self):
+ return self.netlocation
+
+ @property
+ def connection(self):
+ if not self.__ldap_connection:
+ # Create connection object
+ params = dict(
+ module=self,
+ server_uri=self.config.get('url'),
+ bind_dn=self.config.get('bindDN'),
+ bind_pw=self.config.get('bindPassword'),
+ insecure=boolean(self.config.get('insecure')),
+ ca_file=self.config.get('ca')
+ )
+ self.__ldap_connection = connect_to_ldap(**params)
+ return self.__ldap_connection
+
+ def close_connection(self):
+ if self.__ldap_connection:
+ self.__ldap_connection.unbind_s()
+ self.__ldap_connection = None
+
+ def exit_json(self, **kwargs):
+ self.close_connection()
+ self.module.exit_json(**kwargs)
+
+ def fail_json(self, **kwargs):
+ self.close_connection()
+ self.module.fail_json(**kwargs)
+
+ def get_syncer(self):
+ syncer = None
+ if "rfc2307" in self.config:
+ syncer = OpenshiftLDAPRFC2307(self.config, self.connection)
+ elif "activeDirectory" in self.config:
+ syncer = OpenshiftLDAPActiveDirectory(self.config, self.connection)
+ elif "augmentedActiveDirectory" in self.config:
+ syncer = OpenshiftLDAPAugmentedActiveDirectory(self.config, self.connection)
+ else:
+ msg = "No schema-specific config was found, should be one of 'rfc2307', 'activeDirectory', 'augmentedActiveDirectory'"
+ self.fail_json(msg=msg)
+ return syncer
+
+ def synchronize(self):
+
+ sync_group_type = self.module.params.get("type")
+
+ groups_uids = []
+ ldap_openshift_group = OpenshiftLDAPGroups(module=self)
+
+ # Get Synchronize object
+ syncer = self.get_syncer()
+
+ # Determine what to sync : list groups
+ if sync_group_type == "openshift":
+ groups_uids, err = ldap_openshift_group.list_groups()
+ if err:
+ self.fail_json(msg="Failed to list openshift groups", errors=err)
+ else:
+ # List LDAP Group to synchronize
+ groups_uids = self.params.get("allow_groups")
+ if not groups_uids:
+ groups_uids, err = syncer.list_groups()
+ if err:
+ self.module.fail_json(msg=err)
+ deny_groups = self.params.get("deny_groups")
+ if deny_groups:
+ groups_uids = [uid for uid in groups_uids if uid not in deny_groups]
+
+ openshift_groups = []
+ for uid in groups_uids:
+ # Get membership data
+ member_entries, err = syncer.extract_members(uid)
+ if err:
+ self.fail_json(msg=err)
+
+ # Determine usernames for members entries
+ usernames = []
+ for entry in member_entries:
+ name, err = syncer.get_username_for_entry(entry)
+ if err:
+ self.exit_json(
+ msg="Unable to determine username for entry %s: %s" % (entry, err)
+ )
+ if isinstance(name, list):
+ usernames.extend(name)
+ else:
+ usernames.append(name)
+ # Get group name
+ if sync_group_type == "openshift":
+ group_name, err = ldap_openshift_group.get_group_name_for_uid(uid)
+ else:
+ group_name, err = syncer.get_group_name_for_uid(uid)
+ if err:
+ self.exit_json(msg=err)
+
+ # Make Openshift group
+ group, err = ldap_openshift_group.make_openshift_group(uid, group_name, usernames)
+ if err:
+ self.fail_json(msg=err)
+ openshift_groups.append(group)
+
+ # Create Openshift Groups
+ results, diffs, changed = ldap_openshift_group.create_openshift_groups(openshift_groups)
+ self.module.exit_json(changed=True, groups=results)
+
+ def prune(self):
+ ldap_openshift_group = OpenshiftLDAPGroups(module=self)
+ groups_uids, err = ldap_openshift_group.list_groups()
+ if err:
+ self.fail_json(msg="Failed to list openshift groups", errors=err)
+
+ # Get Synchronize object
+ syncer = self.get_syncer()
+
+ changed = False
+ groups = []
+ for uid in groups_uids:
+ # Check if LDAP group exist
+ exists, err = syncer.is_ldapgroup_exists(uid)
+ if err:
+ msg = "Error determining LDAP group existence for group %s: %s" % (uid, err)
+ self.module.fail_json(msg=msg)
+
+ if exists:
+ continue
+
+ # if the LDAP entry that was previously used to create the group doesn't exist, prune it
+ group_name, err = ldap_openshift_group.get_group_name_for_uid(uid)
+ if err:
+ self.module.fail_json(msg=err)
+
+ # Delete Group
+ result = ldap_openshift_group.delete_openshift_group(group_name)
+ groups.append(result)
+ changed = True
+
+ self.exit_json(changed=changed, groups=groups)
+
+ def execute_module(self):
+ # validate LDAP sync configuration
+ error = validate_ldap_sync_config(self.config)
+ if error:
+ self.fail_json(msg="Invalid LDAP Sync config: %s" % error)
+
+ # Split host/port
+ if self.config.get('url'):
+ result, error = ldap_split_host_port(self.config.get('url'))
+ if error:
+ self.fail_json(msg="Failed to parse url='{0}': {1}".format(self.config.get('url'), error))
+ self.netlocation, self.host, self.port = result["netlocation"], result["host"], result["port"]
+ self.scheme = result["scheme"]
+
+ if self.params.get('state') == 'present':
+ self.synchronize()
+ else:
+ self.prune()
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_images_common.py b/ansible_collections/community/okd/plugins/module_utils/openshift_images_common.py
new file mode 100644
index 000000000..67d7123e8
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_images_common.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from datetime import datetime
+from ansible_collections.community.okd.plugins.module_utils.openshift_docker_image import (
+ parse_docker_image_ref,
+)
+from ansible.module_utils.six import iteritems
+
+
+def get_image_blobs(image):
+ blobs = [layer["image"] for layer in image["dockerImageLayers"] if "image" in layer]
+ docker_image_metadata = image.get("dockerImageMetadata")
+ if not docker_image_metadata:
+ return blobs, "failed to read metadata for image %s" % image["metadata"]["name"]
+ media_type_manifest = (
+ "application/vnd.docker.distribution.manifest.v2+json",
+ "application/vnd.oci.image.manifest.v1+json"
+ )
+ media_type_has_config = image['dockerImageManifestMediaType'] in media_type_manifest
+ docker_image_id = docker_image_metadata.get("Id")
+ if media_type_has_config and docker_image_id and len(docker_image_id) > 0:
+ blobs.append(docker_image_id)
+ return blobs, None
+
+
+def is_created_after(creation_timestamp, max_creation_timestamp):
+ if not max_creation_timestamp:
+ return False
+ creationTimestamp = datetime.strptime(creation_timestamp, '%Y-%m-%dT%H:%M:%SZ')
+ return creationTimestamp > max_creation_timestamp
+
+
+def is_too_young_object(obj, max_creation_timestamp):
+ return is_created_after(obj['metadata']['creationTimestamp'],
+ max_creation_timestamp)
+
+
+class OpenShiftAnalyzeImageStream(object):
+
+ def __init__(self, ignore_invalid_refs, max_creation_timestamp, module):
+
+ self.max_creationTimestamp = max_creation_timestamp
+ self.used_tags = {}
+ self.used_images = {}
+ self.ignore_invalid_refs = ignore_invalid_refs
+ self.module = module
+
+ def analyze_reference_image(self, image, referrer):
+ result, error = parse_docker_image_ref(image, self.module)
+ if error:
+ return error
+
+ if not result['hostname'] or not result['namespace']:
+ # image reference does not match hostname/namespace/name pattern - skipping
+ return None
+
+ if not result['digest']:
+ # Attempt to dereference istag. Since we cannot be sure whether the reference refers to the
+ # integrated registry or not, we ignore the host part completely. As a consequence, we may keep
+ # image otherwise sentenced for a removal just because its pull spec accidentally matches one of
+ # our imagestreamtags.
+
+ # set the tag if empty
+ if result['tag'] == "":
+ result['tag'] = 'latest'
+ key = "%s/%s:%s" % (result['namespace'], result['name'], result['tag'])
+ if key not in self.used_tags:
+ self.used_tags[key] = []
+ self.used_tags[key].append(referrer)
+ else:
+ key = "%s/%s@%s" % (result['namespace'], result['name'], result['digest'])
+ if key not in self.used_images:
+ self.used_images[key] = []
+ self.used_images[key].append(referrer)
+
+ def analyze_refs_from_pod_spec(self, podSpec, referrer):
+ for container in podSpec.get('initContainers', []) + podSpec.get('containers', []):
+ image = container.get('image')
+ if len(image.strip()) == 0:
+ # Ignoring container because it has no reference to image
+ continue
+ err = self.analyze_reference_image(image, referrer)
+ if err:
+ return err
+ return None
+
+ def analyze_refs_from_pods(self, pods):
+ for pod in pods:
+ # A pod is only *excluded* from being added to the graph if its phase is not
+ # pending or running. Additionally, it has to be at least as old as the minimum
+ # age threshold defined by the algorithm.
+ too_young = is_too_young_object(pod, self.max_creationTimestamp)
+ if pod['status']['phase'] not in ("Running", "Pending") and too_young:
+ continue
+ referrer = {
+ "kind": pod["kind"],
+ "namespace": pod["metadata"]["namespace"],
+ "name": pod["metadata"]["name"],
+ }
+ err = self.analyze_refs_from_pod_spec(pod['spec'], referrer)
+ if err:
+ return err
+ return None
+
+ def analyze_refs_pod_creators(self, resources):
+ keys = (
+ "ReplicationController", "DeploymentConfig", "DaemonSet",
+ "Deployment", "ReplicaSet", "StatefulSet", "Job", "CronJob"
+ )
+
+ for k, objects in iteritems(resources):
+ if k not in keys:
+ continue
+ for obj in objects:
+ if k == 'CronJob':
+ spec = obj["spec"]["jobTemplate"]["spec"]["template"]["spec"]
+ else:
+ spec = obj["spec"]["template"]["spec"]
+ referrer = {
+ "kind": obj["kind"],
+ "namespace": obj["metadata"]["namespace"],
+ "name": obj["metadata"]["name"],
+ }
+ err = self.analyze_refs_from_pod_spec(spec, referrer)
+ if err:
+ return err
+ return None
+
+ def analyze_refs_from_strategy(self, build_strategy, namespace, referrer):
+ # Determine 'from' reference
+ def _determine_source_strategy():
+ for src in ('sourceStrategy', 'dockerStrategy', 'customStrategy'):
+ strategy = build_strategy.get(src)
+ if strategy:
+ return strategy.get('from')
+ return None
+
+ def _parse_image_stream_image_name(name):
+ v = name.split('@')
+ if len(v) != 2:
+ return None, None, "expected exactly one @ in the isimage name %s" % name
+ name = v[0]
+ tag = v[1]
+ if len(name) == 0 or len(tag) == 0:
+ return None, None, "image stream image name %s must have a name and ID" % name
+ return name, tag, None
+
+ def _parse_image_stream_tag_name(name):
+ if "@" in name:
+ return None, None, "%s is an image stream image, not an image stream tag" % name
+ v = name.split(":")
+ if len(v) != 2:
+ return None, None, "expected exactly one : delimiter in the istag %s" % name
+ name = v[0]
+ tag = v[1]
+ if len(name) == 0 or len(tag) == 0:
+ return None, None, "image stream tag name %s must have a name and a tag" % name
+ return name, tag, None
+
+ from_strategy = _determine_source_strategy()
+ if from_strategy:
+ if from_strategy.get('kind') == "DockerImage":
+ docker_image_ref = from_strategy.get('name').strip()
+ if len(docker_image_ref) > 0:
+ err = self.analyze_reference_image(docker_image_ref, referrer)
+ elif from_strategy.get('kind') == "ImageStreamImage":
+ name, tag, error = _parse_image_stream_image_name(from_strategy.get('name'))
+ if error:
+ if not self.ignore_invalid_refs:
+ return error
+ else:
+ namespace = from_strategy.get('namespace') or namespace
+ self.used_images.append({
+ 'namespace': namespace,
+ 'name': name,
+ 'tag': tag
+ })
+ elif from_strategy.get('kind') == "ImageStreamTag":
+ name, tag, error = _parse_image_stream_tag_name(from_strategy.get('name'))
+ if error:
+ if not self.ignore_invalid_refs:
+ return error
+ else:
+ namespace = from_strategy.get('namespace') or namespace
+ self.used_tags.append({
+ 'namespace': namespace,
+ 'name': name,
+ 'tag': tag
+ })
+
+ def analyze_refs_from_build_strategy(self, resources):
+ # Json Path is always spec.strategy
+ keys = ("BuildConfig", "Build")
+ for k, objects in iteritems(resources):
+ if k not in keys:
+ continue
+ for obj in objects:
+ referrer = {
+ "kind": obj["kind"],
+ "namespace": obj["metadata"]["namespace"],
+ "name": obj["metadata"]["name"],
+ }
+ error = self.analyze_refs_from_strategy(obj['spec']['strategy'],
+ obj['metadata']['namespace'],
+ referrer)
+ if error is not None:
+ return "%s/%s/%s: %s" % (referrer["kind"], referrer["namespace"], referrer["name"], error)
+
+ def analyze_image_stream(self, resources):
+
+ # Analyze image reference from Pods
+ error = self.analyze_refs_from_pods(resources['Pod'])
+ if error:
+ return None, None, error
+
+ # Analyze image reference from Resources creating Pod
+ error = self.analyze_refs_pod_creators(resources)
+ if error:
+ return None, None, error
+
+ # Analyze image reference from Build/BuildConfig
+ error = self.analyze_refs_from_build_strategy(resources)
+ return self.used_tags, self.used_images, error
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_import_image.py b/ansible_collections/community/okd/plugins/module_utils/openshift_import_image.py
new file mode 100644
index 000000000..01bba82af
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_import_image.py
@@ -0,0 +1,390 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import traceback
+import copy
+
+from ansible.module_utils._text import to_native
+from ansible.module_utils.parsing.convert_bool import boolean
+from ansible.module_utils.six import string_types
+
+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.community.okd.plugins.module_utils.openshift_docker_image import (
+ parse_docker_image_ref,
+)
+
+err_stream_not_found_ref = "NotFound reference"
+
+
+def follow_imagestream_tag_reference(stream, tag):
+ multiple = False
+
+ def _imagestream_has_tag():
+ for ref in stream["spec"].get("tags", []):
+ if ref["name"] == tag:
+ return ref
+ return None
+
+ def _imagestream_split_tag(name):
+ parts = name.split(":")
+ name = parts[0]
+ tag = ""
+ if len(parts) > 1:
+ tag = parts[1]
+ if len(tag) == 0:
+ tag = "latest"
+ return name, tag, len(parts) == 2
+
+ content = []
+ err_cross_stream_ref = "tag %s points to an imagestreamtag from another ImageStream" % tag
+ while True:
+ if tag in content:
+ return tag, None, multiple, "tag %s on the image stream is a reference to same tag" % tag
+ content.append(tag)
+ tag_ref = _imagestream_has_tag()
+ if not tag_ref:
+ return None, None, multiple, err_stream_not_found_ref
+
+ if not tag_ref.get("from") or tag_ref["from"]["kind"] != "ImageStreamTag":
+ return tag, tag_ref, multiple, None
+
+ if tag_ref["from"]["namespace"] != "" and tag_ref["from"]["namespace"] != stream["metadata"]["namespace"]:
+ return tag, None, multiple, err_cross_stream_ref
+
+ # The reference needs to be followed with two format patterns:
+ # a) sameis:sometag and b) sometag
+ if ":" in tag_ref["from"]["name"]:
+ name, tagref, result = _imagestream_split_tag(tag_ref["from"]["name"])
+ if not result:
+ return tag, None, multiple, "tag %s points to an invalid imagestreamtag" % tag
+ if name != stream["metadata"]["namespace"]:
+ # anotheris:sometag - this should not happen.
+ return tag, None, multiple, err_cross_stream_ref
+ # sameis:sometag - follow the reference as sometag
+ tag = tagref
+ else:
+ tag = tag_ref["from"]["name"]
+ multiple = True
+
+
+class OpenShiftImportImage(AnsibleOpenshiftModule):
+ def __init__(self, **kwargs):
+ super(OpenShiftImportImage, self).__init__(**kwargs)
+
+ self._rest_client = None
+ self.registryhost = self.params.get('registry_url')
+ self.changed = False
+
+ ref_policy = self.params.get("reference_policy")
+ ref_policy_type = None
+ if ref_policy == "source":
+ ref_policy_type = "Source"
+ elif ref_policy == "local":
+ ref_policy_type = "Local"
+
+ self.ref_policy = {
+ "type": ref_policy_type
+ }
+
+ self.validate_certs = self.params.get("validate_registry_certs")
+ self.cluster_resources = {}
+
+ def create_image_stream_import(self, stream):
+ isi = {
+ "apiVersion": "image.openshift.io/v1",
+ "kind": "ImageStreamImport",
+ "metadata": {
+ "name": stream["metadata"]["name"],
+ "namespace": stream["metadata"]["namespace"],
+ "resourceVersion": stream["metadata"].get("resourceVersion")
+ },
+ "spec": {
+ "import": True
+ }
+ }
+
+ annotations = stream.get("annotations", {})
+ insecure = boolean(annotations.get("openshift.io/image.insecureRepository", True))
+ if self.validate_certs is not None:
+ insecure = not self.validate_certs
+ return isi, insecure
+
+ def create_image_stream_import_all(self, stream, source):
+ isi, insecure = self.create_image_stream_import(stream)
+ isi["spec"]["repository"] = {
+ "from": {
+ "kind": "DockerImage",
+ "name": source,
+ },
+ "importPolicy": {
+ "insecure": insecure,
+ "scheduled": self.params.get("scheduled")
+ },
+ "referencePolicy": self.ref_policy,
+ }
+ return isi
+
+ def create_image_stream_import_tags(self, stream, tags):
+ isi, streamInsecure = self.create_image_stream_import(stream)
+ for k in tags:
+ insecure = streamInsecure
+ scheduled = self.params.get("scheduled")
+
+ old_tag = None
+ for t in stream.get("spec", {}).get("tags", []):
+ if t["name"] == k:
+ old_tag = t
+ break
+
+ if old_tag:
+ insecure = insecure or old_tag["importPolicy"].get("insecure")
+ scheduled = scheduled or old_tag["importPolicy"].get("scheduled")
+
+ images = isi["spec"].get("images", [])
+ images.append({
+ "from": {
+ "kind": "DockerImage",
+ "name": tags.get(k),
+ },
+ "to": {
+ "name": k
+ },
+ "importPolicy": {
+ "insecure": insecure,
+ "scheduled": scheduled
+ },
+ "referencePolicy": self.ref_policy,
+ })
+ isi["spec"]["images"] = images
+ return isi
+
+ def create_image_stream(self, ref):
+ """
+ Create new ImageStream and accompanying ImageStreamImport
+ """
+ source = self.params.get("source")
+ if not source:
+ source = ref["source"]
+
+ stream = dict(
+ apiVersion="image.openshift.io/v1",
+ kind="ImageStream",
+ metadata=dict(
+ name=ref["name"],
+ namespace=self.params.get("namespace"),
+ ),
+ )
+ if self.params.get("all") and not ref["tag"]:
+ spec = dict(
+ dockerImageRepository=source
+ )
+ isi = self.create_image_stream_import_all(stream, source)
+ else:
+ spec = dict(
+ tags=[
+ {
+ "from": {
+ "kind": "DockerImage",
+ "name": source
+ },
+ "referencePolicy": self.ref_policy
+ }
+ ]
+ )
+ tags = {ref["tag"]: source}
+ isi = self.create_image_stream_import_tags(stream, tags)
+ stream.update(
+ dict(spec=spec)
+ )
+ return stream, isi
+
+ def import_all(self, istream):
+ stream = copy.deepcopy(istream)
+ # Update ImageStream appropriately
+ source = self.params.get("source")
+ docker_image_repo = stream["spec"].get("dockerImageRepository")
+ if not source:
+ if docker_image_repo:
+ source = docker_image_repo
+ else:
+ tags = {}
+ for t in stream["spec"].get("tags", []):
+ if t.get("from") and t["from"].get("kind") == "DockerImage":
+ tags[t.get("name")] = t["from"].get("name")
+ if tags == {}:
+ msg = "image stream %s/%s does not have tags pointing to external container images" % (
+ stream["metadata"]["namespace"], stream["metadata"]["name"]
+ )
+ self.fail_json(msg=msg)
+ isi = self.create_image_stream_import_tags(stream, tags)
+ return stream, isi
+
+ if source != docker_image_repo:
+ stream["spec"]["dockerImageRepository"] = source
+ isi = self.create_image_stream_import_all(stream, source)
+ return stream, isi
+
+ def import_tag(self, stream, tag):
+ source = self.params.get("source")
+
+ # Follow any referential tags to the destination
+ final_tag, existing, multiple, err = follow_imagestream_tag_reference(stream, tag)
+ if err:
+ if err == err_stream_not_found_ref:
+ # Create a new tag
+ if not source and tag == "latest":
+ source = stream["spec"].get("dockerImageRepository")
+ # if the from is still empty this means there's no such tag defined
+ # nor we can't create any from .spec.dockerImageRepository
+ if not source:
+ msg = "the tag %s does not exist on the image stream - choose an existing tag to import" % tag
+ self.fail_json(msg=msg)
+ existing = {
+ "from": {
+ "kind": "DockerImage",
+ "name": source,
+ }
+ }
+ else:
+ self.fail_json(msg=err)
+ else:
+ # Disallow re-importing anything other than DockerImage
+ if existing.get("from", {}) and existing["from"].get("kind") != "DockerImage":
+ msg = "tag {tag} points to existing {kind}/={name}, it cannot be re-imported.".format(
+ tag=tag, kind=existing["from"]["kind"], name=existing["from"]["name"]
+ )
+ # disallow changing an existing tag
+ if not existing.get("from", {}):
+ msg = "tag %s already exists - you cannot change the source using this module." % tag
+ self.fail_json(msg=msg)
+ if source and source != existing["from"]["name"]:
+ if multiple:
+ msg = "the tag {0} points to the tag {1} which points to {2} you cannot change the source using this module".format(
+ tag, final_tag, existing["from"]["name"]
+ )
+ else:
+ msg = "the tag %s points to %s you cannot change the source using this module." % (tag, final_tag)
+ self.fail_json(msg=msg)
+
+ # Set the target item to import
+ source = existing["from"].get("name")
+ if multiple:
+ tag = final_tag
+
+ # Clear the legacy annotation
+ tag_to_delete = "openshift.io/image.dockerRepositoryCheck"
+ if existing["annotations"] and tag_to_delete in existing["annotations"]:
+ del existing["annotations"][tag_to_delete]
+
+ # Reset the generation
+ existing["generation"] = 0
+
+ new_stream = copy.deepcopy(stream)
+ new_stream["spec"]["tags"] = []
+ for t in stream["spec"]["tags"]:
+ if t["name"] == tag:
+ new_stream["spec"]["tags"].append(existing)
+ else:
+ new_stream["spec"]["tags"].append(t)
+
+ # Create accompanying ImageStreamImport
+ tags = {tag: source}
+ isi = self.create_image_stream_import_tags(new_stream, tags)
+ return new_stream, isi
+
+ def create_image_import(self, ref):
+ kind = "ImageStream"
+ api_version = "image.openshift.io/v1"
+
+ # Find existing Image Stream
+ params = dict(
+ kind=kind,
+ api_version=api_version,
+ name=ref.get("name"),
+ namespace=self.params.get("namespace")
+ )
+ result = self.kubernetes_facts(**params)
+ if not result["api_found"]:
+ msg = 'Failed to find API for resource with apiVersion "{0}" and kind "{1}"'.format(
+ api_version, kind
+ ),
+ self.fail_json(msg=msg)
+ imagestream = None
+ if len(result["resources"]) > 0:
+ imagestream = result["resources"][0]
+
+ stream, isi = None, None
+ if not imagestream:
+ stream, isi = self.create_image_stream(ref)
+ elif self.params.get("all") and not ref["tag"]:
+ # importing the entire repository
+ stream, isi = self.import_all(imagestream)
+ else:
+ # importing a single tag
+ stream, isi = self.import_tag(imagestream, ref["tag"])
+ return isi
+
+ def parse_image_reference(self, image_ref):
+ result, err = parse_docker_image_ref(image_ref, self.module)
+ if result.get("digest"):
+ self.fail_json(msg="Cannot import by ID, error with definition: %s" % image_ref)
+ tag = result.get("tag") or None
+ if not self.params.get("all") and not tag:
+ tag = "latest"
+ source = self.params.get("source")
+ if not source:
+ source = image_ref
+ return dict(name=result.get("name"), tag=tag, source=image_ref)
+
+ def execute_module(self):
+
+ names = []
+ name = self.params.get("name")
+ if isinstance(name, string_types):
+ names.append(name)
+ elif isinstance(name, list):
+ names = name
+ else:
+ self.fail_json(msg="Parameter name should be provided as list or string.")
+
+ images_refs = [self.parse_image_reference(x) for x in names]
+ images_imports = []
+ for ref in images_refs:
+ isi = self.create_image_import(ref)
+ images_imports.append(isi)
+
+ # Create image import
+ kind = "ImageStreamImport"
+ api_version = "image.openshift.io/v1"
+ namespace = self.params.get("namespace")
+ try:
+ resource = self.find_resource(kind=kind, api_version=api_version, fail=True)
+ result = []
+ for isi in images_imports:
+ if not self.check_mode:
+ isi = resource.create(isi, namespace=namespace).to_dict()
+ result.append(isi)
+ self.exit_json(changed=True, result=result)
+ except DynamicApiError as exc:
+ msg = "Failed to create object {kind}/{namespace}/{name} due to: {error}".format(
+ kind=kind, namespace=namespace, name=isi["metadata"]["name"], error=exc
+ )
+ self.fail_json(
+ msg=msg,
+ error=exc.status,
+ status=exc.status,
+ reason=exc.reason,
+ )
+ except Exception as exc:
+ msg = "Failed to create object {kind}/{namespace}/{name} due to: {error}".format(
+ kind=kind, namespace=namespace, name=isi["metadata"]["name"], error=exc
+ )
+ self.fail_json(msg=msg)
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_ldap.py b/ansible_collections/community/okd/plugins/module_utils/openshift_ldap.py
new file mode 100644
index 000000000..bb9229a72
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_ldap.py
@@ -0,0 +1,777 @@
+#!/usr/bin/env 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
+
+
+import os
+import copy
+
+from ansible.module_utils.parsing.convert_bool import boolean
+from ansible.module_utils.six import iteritems
+
+try:
+ import ldap
+except ImportError as e:
+ pass
+
+
+LDAP_SEARCH_OUT_OF_SCOPE_ERROR = "trying to search by DN for an entry that exists outside of the tree specified with the BaseDN for search"
+
+
+def validate_ldap_sync_config(config):
+ # Validate url
+ url = config.get('url')
+ if not url:
+ return "url should be non empty attribute."
+
+ # Make sure bindDN and bindPassword are both set, or both unset
+ bind_dn = config.get('bindDN', "")
+ bind_password = config.get('bindPassword', "")
+ if (len(bind_dn) == 0) != (len(bind_password) == 0):
+ return "bindDN and bindPassword must both be specified, or both be empty."
+
+ insecure = boolean(config.get('insecure'))
+ ca_file = config.get('ca')
+ if insecure:
+ if url.startswith('ldaps://'):
+ return "Cannot use ldaps scheme with insecure=true."
+ if ca_file:
+ return "Cannot specify a ca with insecure=true."
+ elif ca_file and not os.path.isfile(ca_file):
+ return "could not read ca file: {0}.".format(ca_file)
+
+ nameMapping = config.get('groupUIDNameMapping', {})
+ for k, v in iteritems(nameMapping):
+ if len(k) == 0 or len(v) == 0:
+ return "groupUIDNameMapping has empty key or value"
+
+ schemas = []
+ schema_list = ('rfc2307', 'activeDirectory', 'augmentedActiveDirectory')
+ for schema in schema_list:
+ if schema in config:
+ schemas.append(schema)
+
+ if len(schemas) == 0:
+ return "No schema-specific config was provided, should be one of %s" % ", ".join(schema_list)
+ if len(schemas) > 1:
+ return "Exactly one schema-specific config is required; found (%d) %s" % (len(schemas), ','.join(schemas))
+
+ if schemas[0] == 'rfc2307':
+ return validate_RFC2307(config.get("rfc2307"))
+ elif schemas[0] == 'activeDirectory':
+ return validate_ActiveDirectory(config.get("activeDirectory"))
+ elif schemas[0] == 'augmentedActiveDirectory':
+ return validate_AugmentedActiveDirectory(config.get("augmentedActiveDirectory"))
+
+
+def validate_ldap_query(qry, isDNOnly=False):
+
+ # validate query scope
+ scope = qry.get('scope')
+ if scope and scope not in ("", "sub", "one", "base"):
+ return "invalid scope %s" % scope
+
+ # validate deref aliases
+ derefAlias = qry.get('derefAliases')
+ if derefAlias and derefAlias not in ("never", "search", "base", "always"):
+ return "not a valid LDAP alias dereferncing behavior: %s", derefAlias
+
+ # validate timeout
+ timeout = qry.get('timeout')
+ if timeout and float(timeout) < 0:
+ return "timeout must be equal to or greater than zero"
+
+ # Validate DN only
+ qry_filter = qry.get('filter', "")
+ if isDNOnly:
+ if len(qry_filter) > 0:
+ return 'cannot specify a filter when using "dn" as the UID attribute'
+ else:
+ # validate filter
+ if len(qry_filter) == 0 or qry_filter[0] != '(':
+ return "filter does not start with an '('"
+ return None
+
+
+def validate_RFC2307(config):
+ qry = config.get('groupsQuery')
+ if not qry or not isinstance(qry, dict):
+ return "RFC2307: groupsQuery requires a dictionary"
+ error = validate_ldap_query(qry)
+ if not error:
+ return error
+ for field in ('groupUIDAttribute', 'groupNameAttributes', 'groupMembershipAttributes',
+ 'userUIDAttribute', 'userNameAttributes'):
+ value = config.get(field)
+ if not value:
+ return "RFC2307: {0} is required.".format(field)
+
+ users_qry = config.get('usersQuery')
+ if not users_qry or not isinstance(users_qry, dict):
+ return "RFC2307: usersQuery requires a dictionary"
+
+ isUserDNOnly = (config.get('userUIDAttribute').strip() == 'dn')
+ return validate_ldap_query(users_qry, isDNOnly=isUserDNOnly)
+
+
+def validate_ActiveDirectory(config, label="ActiveDirectory"):
+ users_qry = config.get('usersQuery')
+ if not users_qry or not isinstance(users_qry, dict):
+ return "{0}: usersQuery requires as dictionnary".format(label)
+ error = validate_ldap_query(users_qry)
+ if not error:
+ return error
+
+ for field in ('userNameAttributes', 'groupMembershipAttributes'):
+ value = config.get(field)
+ if not value:
+ return "{0}: {1} is required.".format(field, label)
+
+ return None
+
+
+def validate_AugmentedActiveDirectory(config):
+ error = validate_ActiveDirectory(config, label="AugmentedActiveDirectory")
+ if not error:
+ return error
+ for field in ('groupUIDAttribute', 'groupNameAttributes'):
+ value = config.get(field)
+ if not value:
+ return "AugmentedActiveDirectory: {0} is required".format(field)
+ groups_qry = config.get('groupsQuery')
+ if not groups_qry or not isinstance(groups_qry, dict):
+ return "AugmentedActiveDirectory: groupsQuery requires as dictionnary."
+
+ isGroupDNOnly = (config.get('groupUIDAttribute').strip() == 'dn')
+ return validate_ldap_query(groups_qry, isDNOnly=isGroupDNOnly)
+
+
+def determine_ldap_scope(scope):
+ if scope in ("", "sub"):
+ return ldap.SCOPE_SUBTREE
+ elif scope == 'base':
+ return ldap.SCOPE_BASE
+ elif scope == 'one':
+ return ldap.SCOPE_ONELEVEL
+ return None
+
+
+def determine_deref_aliases(derefAlias):
+ mapping = {
+ "never": ldap.DEREF_NEVER,
+ "search": ldap.DEREF_SEARCHING,
+ "base": ldap.DEREF_FINDING,
+ "always": ldap.DEREF_ALWAYS,
+ }
+ result = None
+ if derefAlias in mapping:
+ result = mapping.get(derefAlias)
+ return result
+
+
+def openshift_ldap_build_base_query(config):
+ qry = {}
+ if config.get('baseDN'):
+ qry['base'] = config.get('baseDN')
+
+ scope = determine_ldap_scope(config.get('scope'))
+ if scope:
+ qry['scope'] = scope
+
+ pageSize = config.get('pageSize')
+ if pageSize and int(pageSize) > 0:
+ qry['sizelimit'] = int(pageSize)
+
+ timeout = config.get('timeout')
+ if timeout and int(timeout) > 0:
+ qry['timeout'] = int(timeout)
+
+ filter = config.get('filter')
+ if filter:
+ qry['filterstr'] = filter
+
+ derefAlias = determine_deref_aliases(config.get('derefAliases'))
+ if derefAlias:
+ qry['derefAlias'] = derefAlias
+ return qry
+
+
+def openshift_ldap_get_attribute_for_entry(entry, attribute):
+ attributes = [attribute]
+ if isinstance(attribute, list):
+ attributes = attribute
+ for k in attributes:
+ if k.lower() == 'dn':
+ return entry[0]
+ v = entry[1].get(k, None)
+ if v:
+ if isinstance(v, list):
+ result = []
+ for x in v:
+ if hasattr(x, 'decode'):
+ result.append(x.decode('utf-8'))
+ else:
+ result.append(x)
+ return result
+ else:
+ return v.decode('utf-8') if hasattr(v, 'decode') else v
+ return ""
+
+
+def ldap_split_host_port(hostport):
+ """
+ ldap_split_host_port splits a network address of the form "host:port",
+ "host%zone:port", "[host]:port" or "[host%zone]:port" into host or
+ host%zone and port.
+ """
+ result = dict(
+ scheme=None, netlocation=None, host=None, port=None
+ )
+ if not hostport:
+ return result, None
+
+ # Extract Scheme
+ netlocation = hostport
+ scheme_l = "://"
+ if "://" in hostport:
+ idx = hostport.find(scheme_l)
+ result["scheme"] = hostport[:idx]
+ netlocation = hostport[idx + len(scheme_l):]
+ result["netlocation"] = netlocation
+
+ if netlocation[-1] == ']':
+ # ipv6 literal (with no port)
+ result["host"] = netlocation
+
+ v = netlocation.rsplit(":", 1)
+ if len(v) != 1:
+ try:
+ result["port"] = int(v[1])
+ except ValueError:
+ return None, "Invalid value specified for port: %s" % v[1]
+ result["host"] = v[0]
+ return result, None
+
+
+def openshift_ldap_query_for_entries(connection, qry, unique_entry=True):
+ # set deref alias (TODO: need to set a default value to reset for each transaction)
+ derefAlias = qry.pop('derefAlias', None)
+ if derefAlias:
+ ldap.set_option(ldap.OPT_DEREF, derefAlias)
+ try:
+ result = connection.search_ext_s(**qry)
+ if not result or len(result) == 0:
+ return None, "Entry not found for base='{0}' and filter='{1}'".format(qry['base'], qry['filterstr'])
+ if len(result) > 1 and unique_entry:
+ if qry.get('scope') == ldap.SCOPE_BASE:
+ return None, "multiple entries found matching dn={0}: {1}".format(qry['base'], result)
+ else:
+ return None, "multiple entries found matching filter {0}: {1}".format(qry['filterstr'], result)
+ return result, None
+ except ldap.NO_SUCH_OBJECT:
+ return None, "search for entry with base dn='{0}' refers to a non-existent entry".format(qry['base'])
+
+
+def openshift_equal_dn_objects(dn_obj, other_dn_obj):
+ if len(dn_obj) != len(other_dn_obj):
+ return False
+
+ for k, v in enumerate(dn_obj):
+ if len(v) != len(other_dn_obj[k]):
+ return False
+ for j, item in enumerate(v):
+ if not (item == other_dn_obj[k][j]):
+ return False
+ return True
+
+
+def openshift_equal_dn(dn, other):
+ dn_obj = ldap.dn.str2dn(dn)
+ other_dn_obj = ldap.dn.str2dn(other)
+
+ return openshift_equal_dn_objects(dn_obj, other_dn_obj)
+
+
+def openshift_ancestorof_dn(dn, other):
+ dn_obj = ldap.dn.str2dn(dn)
+ other_dn_obj = ldap.dn.str2dn(other)
+
+ if len(dn_obj) >= len(other_dn_obj):
+ return False
+ # Take the last attribute from the other DN to compare against
+ return openshift_equal_dn_objects(dn_obj, other_dn_obj[len(other_dn_obj) - len(dn_obj):])
+
+
+class OpenshiftLDAPQueryOnAttribute(object):
+ def __init__(self, qry, attribute):
+ # qry retrieves entries from an LDAP server
+ self.qry = copy.deepcopy(qry)
+ # query_attributes is the attribute for a specific filter that, when conjoined with the common filter,
+ # retrieves the specific LDAP entry from the LDAP server. (e.g. "cn", when formatted with "aGroupName"
+ # and conjoined with "objectClass=groupOfNames", becomes (&(objectClass=groupOfNames)(cn=aGroupName))")
+ self.query_attribute = attribute
+
+ @staticmethod
+ def escape_filter(buffer):
+ """
+ escapes from the provided LDAP filter string the special
+ characters in the set '(', ')', '*', \\ and those out of the range 0 < c < 0x80, as defined in RFC4515.
+ """
+ output = []
+ hex_string = "0123456789abcdef"
+ for c in buffer:
+ if ord(c) > 0x7f or c in ('(', ')', '\\', '*') or c == 0:
+ first = ord(c) >> 4
+ second = ord(c) & 0xf
+ output += ['\\', hex_string[first], hex_string[second]]
+ else:
+ output.append(c)
+ return ''.join(output)
+
+ def build_request(self, ldapuid, attributes):
+ params = copy.deepcopy(self.qry)
+ if self.query_attribute.lower() == 'dn':
+ if ldapuid:
+ if not openshift_equal_dn(ldapuid, params['base']) and not openshift_ancestorof_dn(params['base'], ldapuid):
+ return None, LDAP_SEARCH_OUT_OF_SCOPE_ERROR
+ params['base'] = ldapuid
+ params['scope'] = ldap.SCOPE_BASE
+ # filter that returns all values
+ params['filterstr'] = "(objectClass=*)"
+ params['attrlist'] = attributes
+ else:
+ # Builds the query containing a filter that conjoins the common filter given
+ # in the configuration with the specific attribute filter for which the attribute value is given
+ specificFilter = "%s=%s" % (self.escape_filter(self.query_attribute), self.escape_filter(ldapuid))
+ qry_filter = params.get('filterstr', None)
+ if qry_filter:
+ params['filterstr'] = "(&%s(%s))" % (qry_filter, specificFilter)
+ params['attrlist'] = attributes
+ return params, None
+
+ def ldap_search(self, connection, ldapuid, required_attributes, unique_entry=True):
+ query, error = self.build_request(ldapuid, required_attributes)
+ if error:
+ return None, error
+ # set deref alias (TODO: need to set a default value to reset for each transaction)
+ derefAlias = query.pop('derefAlias', None)
+ if derefAlias:
+ ldap.set_option(ldap.OPT_DEREF, derefAlias)
+
+ try:
+ result = connection.search_ext_s(**query)
+ if not result or len(result) == 0:
+ return None, "Entry not found for base='{0}' and filter='{1}'".format(query['base'], query['filterstr'])
+ if unique_entry:
+ if len(result) > 1:
+ return None, "Multiple Entries found matching search criteria: %s (%s)" % (query, result)
+ result = result[0]
+ return result, None
+ except ldap.NO_SUCH_OBJECT:
+ return None, "Entry not found for base='{0}' and filter='{1}'".format(query['base'], query['filterstr'])
+ except Exception as err:
+ return None, "Request %s failed due to: %s" % (query, err)
+
+
+class OpenshiftLDAPQuery(object):
+ def __init__(self, qry):
+ # Query retrieves entries from an LDAP server
+ self.qry = qry
+
+ def build_request(self, attributes):
+ params = copy.deepcopy(self.qry)
+ params['attrlist'] = attributes
+ return params
+
+ def ldap_search(self, connection, required_attributes):
+ query = self.build_request(required_attributes)
+ # set deref alias (TODO: need to set a default value to reset for each transaction)
+ derefAlias = query.pop('derefAlias', None)
+ if derefAlias:
+ ldap.set_option(ldap.OPT_DEREF, derefAlias)
+
+ try:
+ result = connection.search_ext_s(**query)
+ if not result or len(result) == 0:
+ return None, "Entry not found for base='{0}' and filter='{1}'".format(query['base'], query['filterstr'])
+ return result, None
+ except ldap.NO_SUCH_OBJECT:
+ return None, "search for entry with base dn='{0}' refers to a non-existent entry".format(query['base'])
+
+
+class OpenshiftLDAPInterface(object):
+
+ def __init__(self, connection, groupQuery, groupNameAttributes, groupMembershipAttributes,
+ userQuery, userNameAttributes, config):
+
+ self.connection = connection
+ self.groupQuery = copy.deepcopy(groupQuery)
+ self.groupNameAttributes = groupNameAttributes
+ self.groupMembershipAttributes = groupMembershipAttributes
+ self.userQuery = copy.deepcopy(userQuery)
+ self.userNameAttributes = userNameAttributes
+ self.config = config
+
+ self.tolerate_not_found = boolean(config.get('tolerateMemberNotFoundErrors', False))
+ self.tolerate_out_of_scope = boolean(config.get('tolerateMemberOutOfScopeErrors', False))
+
+ self.required_group_attributes = [self.groupQuery.query_attribute]
+ for x in self.groupNameAttributes + self.groupMembershipAttributes:
+ if x not in self.required_group_attributes:
+ self.required_group_attributes.append(x)
+
+ self.required_user_attributes = [self.userQuery.query_attribute]
+ for x in self.userNameAttributes:
+ if x not in self.required_user_attributes:
+ self.required_user_attributes.append(x)
+
+ self.cached_groups = {}
+ self.cached_users = {}
+
+ def get_group_entry(self, uid):
+ """
+ get_group_entry returns an LDAP group entry for the given group UID by searching the internal cache
+ of the LDAPInterface first, then sending an LDAP query if the cache did not contain the entry.
+ """
+ if uid in self.cached_groups:
+ return self.cached_groups.get(uid), None
+
+ group, err = self.groupQuery.ldap_search(self.connection, uid, self.required_group_attributes)
+ if err:
+ return None, err
+ self.cached_groups[uid] = group
+ return group, None
+
+ def get_user_entry(self, uid):
+ """
+ get_user_entry returns an LDAP group entry for the given user UID by searching the internal cache
+ of the LDAPInterface first, then sending an LDAP query if the cache did not contain the entry.
+ """
+ if uid in self.cached_users:
+ return self.cached_users.get(uid), None
+
+ entry, err = self.userQuery.ldap_search(self.connection, uid, self.required_user_attributes)
+ if err:
+ return None, err
+ self.cached_users[uid] = entry
+ return entry, None
+
+ def exists(self, ldapuid):
+ group, error = self.get_group_entry(ldapuid)
+ return bool(group), error
+
+ def list_groups(self):
+ group_qry = copy.deepcopy(self.groupQuery.qry)
+ group_qry['attrlist'] = self.required_group_attributes
+
+ groups, err = openshift_ldap_query_for_entries(
+ connection=self.connection,
+ qry=group_qry,
+ unique_entry=False
+ )
+ if err:
+ return None, err
+
+ group_uids = []
+ for entry in groups:
+ uid = openshift_ldap_get_attribute_for_entry(entry, self.groupQuery.query_attribute)
+ if not uid:
+ return None, "Unable to find LDAP group uid for entry %s" % entry
+ self.cached_groups[uid] = entry
+ group_uids.append(uid)
+ return group_uids, None
+
+ def extract_members(self, uid):
+ """
+ returns the LDAP member entries for a group specified with a ldapGroupUID
+ """
+ # Get group entry from LDAP
+ group, err = self.get_group_entry(uid)
+ if err:
+ return None, err
+
+ # Extract member UIDs from group entry
+ member_uids = []
+ for attribute in self.groupMembershipAttributes:
+ member_uids += openshift_ldap_get_attribute_for_entry(group, attribute)
+
+ members = []
+ for user_uid in member_uids:
+ entry, err = self.get_user_entry(user_uid)
+ if err:
+ if self.tolerate_not_found and err.startswith("Entry not found"):
+ continue
+ elif err == LDAP_SEARCH_OUT_OF_SCOPE_ERROR:
+ continue
+ return None, err
+ members.append(entry)
+
+ return members, None
+
+
+class OpenshiftLDAPRFC2307(object):
+
+ def __init__(self, config, ldap_connection):
+
+ self.config = config
+ self.ldap_interface = self.create_ldap_interface(ldap_connection)
+
+ def create_ldap_interface(self, connection):
+ segment = self.config.get("rfc2307")
+ groups_base_qry = openshift_ldap_build_base_query(segment['groupsQuery'])
+ users_base_qry = openshift_ldap_build_base_query(segment['usersQuery'])
+
+ groups_query = OpenshiftLDAPQueryOnAttribute(groups_base_qry, segment['groupUIDAttribute'])
+ users_query = OpenshiftLDAPQueryOnAttribute(users_base_qry, segment['userUIDAttribute'])
+
+ params = dict(
+ connection=connection,
+ groupQuery=groups_query,
+ groupNameAttributes=segment['groupNameAttributes'],
+ groupMembershipAttributes=segment['groupMembershipAttributes'],
+ userQuery=users_query,
+ userNameAttributes=segment['userNameAttributes'],
+ config=segment
+ )
+ return OpenshiftLDAPInterface(**params)
+
+ def get_username_for_entry(self, entry):
+ username = openshift_ldap_get_attribute_for_entry(entry, self.ldap_interface.userNameAttributes)
+ if not username:
+ return None, "The user entry (%s) does not map to a OpenShift User name with the given mapping" % entry
+ return username, None
+
+ def get_group_name_for_uid(self, uid):
+
+ # Get name from User defined mapping
+ groupuid_name_mapping = self.config.get("groupUIDNameMapping")
+ if groupuid_name_mapping and uid in groupuid_name_mapping:
+ return groupuid_name_mapping.get(uid), None
+ elif self.ldap_interface.groupNameAttributes:
+ group, err = self.ldap_interface.get_group_entry(uid)
+ if err:
+ return None, err
+ group_name = openshift_ldap_get_attribute_for_entry(group, self.ldap_interface.groupNameAttributes)
+ if not group_name:
+ error = "The group entry (%s) does not map to an OpenShift Group name with the given name attribute (%s)" % (
+ group, self.ldap_interface.groupNameAttributes
+ )
+ return None, error
+ if isinstance(group_name, list):
+ group_name = group_name[0]
+ return group_name, None
+ else:
+ return None, "No OpenShift Group name defined for LDAP group UID: %s" % uid
+
+ def is_ldapgroup_exists(self, uid):
+ group, err = self.ldap_interface.get_group_entry(uid)
+ if err:
+ if err == LDAP_SEARCH_OUT_OF_SCOPE_ERROR or err.startswith("Entry not found") or "non-existent entry" in err:
+ return False, None
+ return False, err
+ if group:
+ return True, None
+ return False, None
+
+ def list_groups(self):
+ return self.ldap_interface.list_groups()
+
+ def extract_members(self, uid):
+ return self.ldap_interface.extract_members(uid)
+
+
+class OpenshiftLDAP_ADInterface(object):
+
+ def __init__(self, connection, user_query, group_member_attr, user_name_attr):
+ self.connection = connection
+ self.userQuery = user_query
+ self.groupMembershipAttributes = group_member_attr
+ self.userNameAttributes = user_name_attr
+
+ self.required_user_attributes = self.userNameAttributes or []
+ for attr in self.groupMembershipAttributes:
+ if attr not in self.required_user_attributes:
+ self.required_user_attributes.append(attr)
+
+ self.cache = {}
+ self.cache_populated = False
+
+ def is_entry_present(self, cache_item, entry):
+ for item in cache_item:
+ if item[0] == entry[0]:
+ return True
+ return False
+
+ def populate_cache(self):
+ if not self.cache_populated:
+ self.cache_populated = True
+ entries, err = self.userQuery.ldap_search(self.connection, self.required_user_attributes)
+ if err:
+ return err
+
+ for entry in entries:
+ for group_attr in self.groupMembershipAttributes:
+ uids = openshift_ldap_get_attribute_for_entry(entry, group_attr)
+ if not isinstance(uids, list):
+ uids = [uids]
+ for uid in uids:
+ if uid not in self.cache:
+ self.cache[uid] = []
+ if not self.is_entry_present(self.cache[uid], entry):
+ self.cache[uid].append(entry)
+ return None
+
+ def list_groups(self):
+ err = self.populate_cache()
+ if err:
+ return None, err
+ result = []
+ if self.cache:
+ result = self.cache.keys()
+ return result, None
+
+ def extract_members(self, uid):
+ # ExtractMembers returns the LDAP member entries for a group specified with a ldapGroupUID
+ # if we already have it cached, return the cached value
+ if uid in self.cache:
+ return self.cache[uid], None
+
+ # This happens in cases where we did not list out every group.
+ # In that case, we're going to be asked about specific groups.
+ users_in_group = []
+ for attr in self.groupMembershipAttributes:
+ query_on_attribute = OpenshiftLDAPQueryOnAttribute(self.userQuery.qry, attr)
+ entries, error = query_on_attribute.ldap_search(self.connection, uid, self.required_user_attributes, unique_entry=False)
+ if error and "not found" not in error:
+ return None, error
+ if not entries:
+ continue
+
+ for entry in entries:
+ if not self.is_entry_present(users_in_group, entry):
+ users_in_group.append(entry)
+
+ self.cache[uid] = users_in_group
+ return users_in_group, None
+
+
+class OpenshiftLDAPActiveDirectory(object):
+
+ def __init__(self, config, ldap_connection):
+
+ self.config = config
+ self.ldap_interface = self.create_ldap_interface(ldap_connection)
+
+ def create_ldap_interface(self, connection):
+ segment = self.config.get("activeDirectory")
+ base_query = openshift_ldap_build_base_query(segment['usersQuery'])
+ user_query = OpenshiftLDAPQuery(base_query)
+
+ return OpenshiftLDAP_ADInterface(
+ connection=connection,
+ user_query=user_query,
+ group_member_attr=segment["groupMembershipAttributes"],
+ user_name_attr=segment["userNameAttributes"],
+ )
+
+ def get_username_for_entry(self, entry):
+ username = openshift_ldap_get_attribute_for_entry(entry, self.ldap_interface.userNameAttributes)
+ if not username:
+ return None, "The user entry (%s) does not map to a OpenShift User name with the given mapping" % entry
+ return username, None
+
+ def get_group_name_for_uid(self, uid):
+ return uid, None
+
+ def is_ldapgroup_exists(self, uid):
+ members, error = self.extract_members(uid)
+ if error:
+ return False, error
+ exists = members and len(members) > 0
+ return exists, None
+
+ def list_groups(self):
+ return self.ldap_interface.list_groups()
+
+ def extract_members(self, uid):
+ return self.ldap_interface.extract_members(uid)
+
+
+class OpenshiftLDAP_AugmentedADInterface(OpenshiftLDAP_ADInterface):
+
+ def __init__(self, connection, user_query, group_member_attr, user_name_attr, group_qry, group_name_attr):
+ super(OpenshiftLDAP_AugmentedADInterface, self).__init__(
+ connection, user_query, group_member_attr, user_name_attr
+ )
+ self.groupQuery = copy.deepcopy(group_qry)
+ self.groupNameAttributes = group_name_attr
+
+ self.required_group_attributes = [self.groupQuery.query_attribute]
+ for x in self.groupNameAttributes:
+ if x not in self.required_group_attributes:
+ self.required_group_attributes.append(x)
+
+ self.cached_groups = {}
+
+ def get_group_entry(self, uid):
+ """
+ get_group_entry returns an LDAP group entry for the given group UID by searching the internal cache
+ of the LDAPInterface first, then sending an LDAP query if the cache did not contain the entry.
+ """
+ if uid in self.cached_groups:
+ return self.cached_groups.get(uid), None
+
+ group, err = self.groupQuery.ldap_search(self.connection, uid, self.required_group_attributes)
+ if err:
+ return None, err
+ self.cached_groups[uid] = group
+ return group, None
+
+ def exists(self, ldapuid):
+ # Get group members
+ members, error = self.extract_members(ldapuid)
+ if error:
+ return False, error
+ group_exists = bool(members)
+
+ # Check group Existence
+ entry, error = self.get_group_entry(ldapuid)
+ if error:
+ if "not found" in error:
+ return False, None
+ else:
+ return False, error
+ else:
+ return group_exists and bool(entry), None
+
+
+class OpenshiftLDAPAugmentedActiveDirectory(OpenshiftLDAPRFC2307):
+
+ def __init__(self, config, ldap_connection):
+
+ self.config = config
+ self.ldap_interface = self.create_ldap_interface(ldap_connection)
+
+ def create_ldap_interface(self, connection):
+ segment = self.config.get("augmentedActiveDirectory")
+ user_base_query = openshift_ldap_build_base_query(segment['usersQuery'])
+ groups_base_qry = openshift_ldap_build_base_query(segment['groupsQuery'])
+
+ user_query = OpenshiftLDAPQuery(user_base_query)
+ groups_query = OpenshiftLDAPQueryOnAttribute(groups_base_qry, segment['groupUIDAttribute'])
+
+ return OpenshiftLDAP_AugmentedADInterface(
+ connection=connection,
+ user_query=user_query,
+ group_member_attr=segment["groupMembershipAttributes"],
+ user_name_attr=segment["userNameAttributes"],
+ group_qry=groups_query,
+ group_name_attr=segment["groupNameAttributes"]
+ )
+
+ def is_ldapgroup_exists(self, uid):
+ return self.ldap_interface.exists(uid)
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_process.py b/ansible_collections/community/okd/plugins/module_utils/openshift_process.py
new file mode 100644
index 000000000..6fa69d13c
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_process.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import os
+import traceback
+
+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
+
+
+class OpenShiftProcess(AnsibleOpenshiftModule):
+ def __init__(self, **kwargs):
+ super(OpenShiftProcess, self).__init__(**kwargs)
+
+ def execute_module(self):
+ v1_templates = self.find_resource(
+ "templates", "template.openshift.io/v1", fail=True
+ )
+ v1_processed_templates = self.find_resource(
+ "processedtemplates", "template.openshift.io/v1", fail=True
+ )
+
+ name = self.params.get("name")
+ namespace = self.params.get("namespace")
+ namespace_target = self.params.get("namespace_target")
+ definition = self.params.get("resource_definition")
+ src = self.params.get("src")
+
+ state = self.params.get("state")
+
+ parameters = self.params.get("parameters") or {}
+ parameter_file = self.params.get("parameter_file")
+
+ if (name and definition) or (name and src) or (src and definition):
+ self.fail_json("Only one of src, name, or definition may be provided")
+
+ if name and not namespace:
+ self.fail_json("namespace is required when name is set")
+
+ template = None
+
+ if src or definition:
+ self.set_resource_definitions()
+ if len(self.resource_definitions) < 1:
+ self.fail_json(
+ "Unable to load a Template resource from src or resource_definition"
+ )
+ elif len(self.resource_definitions) > 1:
+ self.fail_json(
+ "Multiple Template resources found in src or resource_definition, only one Template may be processed at a time"
+ )
+ template = self.resource_definitions[0]
+ template_namespace = template.get("metadata", {}).get("namespace")
+ namespace = template_namespace or namespace or namespace_target or "default"
+ elif name and namespace:
+ try:
+ template = v1_templates.get(name=name, namespace=namespace).to_dict()
+ except DynamicApiError as exc:
+ self.fail_json(
+ msg="Failed to retrieve Template with name '{0}' in namespace '{1}': {2}".format(
+ name, namespace, exc.body
+ ),
+ error=exc.status,
+ status=exc.status,
+ reason=exc.reason,
+ )
+ except Exception as exc:
+ self.fail_json(
+ msg="Failed to retrieve Template with name '{0}' in namespace '{1}': {2}".format(
+ name, namespace, to_native(exc)
+ ),
+ error="",
+ status="",
+ reason="",
+ )
+ else:
+ self.fail_json(
+ "One of resource_definition, src, or name and namespace must be provided"
+ )
+
+ if parameter_file:
+ parameters = self.parse_dotenv_and_merge(parameters, parameter_file)
+
+ for k, v in parameters.items():
+ template = self.update_template_param(template, k, v)
+
+ result = {"changed": False}
+
+ try:
+ response = v1_processed_templates.create(
+ body=template, namespace=namespace
+ ).to_dict()
+ except DynamicApiError as exc:
+ self.fail_json(
+ msg="Server failed to render the Template: {0}".format(exc.body),
+ error=exc.status,
+ status=exc.status,
+ reason=exc.reason,
+ )
+ except Exception as exc:
+ self.fail_json(
+ msg="Server failed to render the Template: {0}".format(to_native(exc)),
+ error="",
+ status="",
+ reason="",
+ )
+ result["message"] = ""
+ if "message" in response:
+ result["message"] = response["message"]
+ result["resources"] = response["objects"]
+
+ if state != "rendered":
+ self.create_resources(response["objects"])
+
+ self.exit_json(**result)
+
+ def create_resources(self, definitions):
+
+ params = {"namespace": self.params.get("namespace_target")}
+
+ self.params["apply"] = False
+ self.params["validate"] = None
+
+ changed = False
+ results = []
+
+ flattened_definitions = []
+ for definition in definitions:
+ if definition is None:
+ continue
+ kind = definition.get("kind")
+ if kind and kind.endswith("List"):
+ flattened_definitions.extend(
+ self.flatten_list_kind(definition, params)
+ )
+ else:
+ flattened_definitions.append(self.merge_params(definition, params))
+
+ for definition in flattened_definitions:
+ result = self.perform_action(definition, self.params)
+ changed = changed or result["changed"]
+ results.append(result)
+
+ if len(results) == 1:
+ self.exit_json(**results[0])
+
+ self.exit_json(**{"changed": changed, "result": {"results": results}})
+
+ def update_template_param(self, template, k, v):
+ for i, param in enumerate(template["parameters"]):
+ if param["name"] == k:
+ template["parameters"][i]["value"] = v
+ return template
+ return template
+
+ def parse_dotenv_and_merge(self, parameters, parameter_file):
+ import re
+
+ DOTENV_PARSER = re.compile(
+ r"(?x)^(\s*(\#.*|\s*|(export\s+)?(?P<key>[A-z_][A-z0-9_.]*)=(?P<value>.+?)?)\s*)[\r\n]*$"
+ )
+ path = os.path.normpath(parameter_file)
+ if not os.path.exists(path):
+ self.fail(msg="Error accessing {0}. Does the file exist?".format(path))
+ try:
+ with open(path, "r") as f:
+ multiline = ""
+ for line in f.readlines():
+ line = line.strip()
+ if line.endswith("\\"):
+ multiline += " ".join(line.rsplit("\\", 1))
+ continue
+ if multiline:
+ line = multiline + line
+ multiline = ""
+ match = DOTENV_PARSER.search(line)
+ if not match:
+ continue
+ match = match.groupdict()
+ if match.get("key"):
+ if match["key"] in parameters:
+ self.fail_json(
+ msg="Duplicate value for '{0}' detected in parameter file".format(
+ match["key"]
+ )
+ )
+ parameters[match["key"]] = match["value"]
+ except IOError as exc:
+ self.fail(msg="Error loading parameter file: {0}".format(exc))
+ return parameters
diff --git a/ansible_collections/community/okd/plugins/module_utils/openshift_registry.py b/ansible_collections/community/okd/plugins/module_utils/openshift_registry.py
new file mode 100644
index 000000000..32a1830df
--- /dev/null
+++ b/ansible_collections/community/okd/plugins/module_utils/openshift_registry.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import traceback
+from urllib.parse import urlparse
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_common import AnsibleOpenshiftModule
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_docker_image import (
+ parse_docker_image_ref,
+)
+
+try:
+ from requests import request
+ from requests.auth import HTTPBasicAuth
+ HAS_REQUESTS_MODULE = True
+ requests_import_exception = None
+except ImportError as e:
+ HAS_REQUESTS_MODULE = False
+ requests_import_exception = e
+ REQUESTS_MODULE_ERROR = traceback.format_exc()
+
+
+class OpenShiftRegistry(AnsibleOpenshiftModule):
+ def __init__(self, **kwargs):
+ super(OpenShiftRegistry, self).__init__(**kwargs)
+ self.check = self.params.get("check")
+
+ def list_image_streams(self, namespace=None):
+ kind = "ImageStream"
+ api_version = "image.openshift.io/v1"
+
+ params = dict(
+ kind=kind,
+ api_version=api_version,
+ namespace=namespace
+ )
+ result = self.kubernetes_facts(**params)
+ imagestream = []
+ if len(result["resources"]) > 0:
+ imagestream = result["resources"]
+ return imagestream
+
+ def find_registry_info(self):
+
+ def _determine_registry(image_stream):
+ public, internal = None, None
+ docker_repo = image_stream["status"].get("publicDockerImageRepository")
+ if docker_repo:
+ ref, err = parse_docker_image_ref(docker_repo, self.module)
+ public = ref["hostname"]
+
+ docker_repo = image_stream["status"].get("dockerImageRepository")
+ if docker_repo:
+ ref, err = parse_docker_image_ref(docker_repo, self.module)
+ internal = ref["hostname"]
+ return internal, public
+
+ # Try to determine registry hosts from Image Stream from 'openshift' namespace
+ for stream in self.list_image_streams(namespace="openshift"):
+ internal, public = _determine_registry(stream)
+ if not public and not internal:
+ self.fail_json(msg="The integrated registry has not been configured")
+ return internal, public
+
+ # Unable to determine registry from 'openshift' namespace, trying with all namespace
+ for stream in self.list_image_streams():
+ internal, public = _determine_registry(stream)
+ if not public and not internal:
+ self.fail_json(msg="The integrated registry has not been configured")
+ return internal, public
+
+ self.fail_json(msg="No Image Streams could be located to retrieve registry info.")
+
+ def execute_module(self):
+ result = {}
+ result["internal_hostname"], result["public_hostname"] = self.find_registry_info()
+
+ if self.check:
+ public_registry = result["public_hostname"]
+ if not public_registry:
+ result["check"] = dict(
+ reached=False,
+ msg="Registry does not have a public hostname."
+ )
+ else:
+ headers = {
+ 'Content-Type': 'application/json'
+ }
+ params = {
+ 'method': 'GET',
+ 'verify': False
+ }
+ if self.client.configuration.api_key:
+ headers.update(self.client.configuration.api_key)
+ elif self.client.configuration.username and self.client.configuration.password:
+ if not HAS_REQUESTS_MODULE:
+ result["check"] = dict(
+ reached=False,
+ msg="The requests python package is missing, try `pip install requests`",
+ error=requests_import_exception
+ )
+ self.exit_json(**result)
+ params.update(
+ dict(auth=HTTPBasicAuth(self.client.configuration.username, self.client.configuration.password))
+ )
+
+ # verify ssl
+ host = urlparse(public_registry)
+ if len(host.scheme) == 0:
+ registry_url = "https://" + public_registry
+
+ if registry_url.startswith("https://") and self.client.configuration.ssl_ca_cert:
+ params.update(
+ dict(verify=self.client.configuration.ssl_ca_cert)
+ )
+ params.update(
+ dict(headers=headers)
+ )
+ last_bad_status, last_bad_reason = None, None
+ for path in ("/", "/healthz"):
+ params.update(
+ dict(url=registry_url + path)
+ )
+ response = request(**params)
+ if response.status_code == 200:
+ result["check"] = dict(
+ reached=True,
+ msg="The local client can contact the integrated registry."
+ )
+ self.exit_json(**result)
+ last_bad_reason = response.reason
+ last_bad_status = response.status_code
+
+ result["check"] = dict(
+ reached=False,
+ msg="Unable to contact the integrated registry using local client. Status=%d, Reason=%s" % (
+ last_bad_status, last_bad_reason
+ )
+ )
+
+ self.exit_json(**result)
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()
diff --git a/ansible_collections/community/okd/requirements.txt b/ansible_collections/community/okd/requirements.txt
new file mode 100644
index 000000000..8de830ea2
--- /dev/null
+++ b/ansible_collections/community/okd/requirements.txt
@@ -0,0 +1,2 @@
+kubernetes>=12.0.0
+requests-oauthlib
diff --git a/ansible_collections/community/okd/requirements.yml b/ansible_collections/community/okd/requirements.yml
new file mode 100644
index 000000000..d7d4e6f7a
--- /dev/null
+++ b/ansible_collections/community/okd/requirements.yml
@@ -0,0 +1,3 @@
+collections:
+ - name: kubernetes.core
+ version: '>=2.4.0'
diff --git a/ansible_collections/community/okd/setup.cfg b/ansible_collections/community/okd/setup.cfg
new file mode 100644
index 000000000..29c924b86
--- /dev/null
+++ b/ansible_collections/community/okd/setup.cfg
@@ -0,0 +1,3 @@
+[flake8]
+max-line-length = 160
+ignore = W503,E402
diff --git a/ansible_collections/community/okd/test-requirements.txt b/ansible_collections/community/okd/test-requirements.txt
new file mode 100644
index 000000000..7f234688f
--- /dev/null
+++ b/ansible_collections/community/okd/test-requirements.txt
@@ -0,0 +1,4 @@
+coverage==4.5.4
+pytest
+pytest-xdist
+pytest-forked
diff --git a/ansible_collections/community/okd/tests/config.yml b/ansible_collections/community/okd/tests/config.yml
new file mode 100644
index 000000000..9e402bda7
--- /dev/null
+++ b/ansible_collections/community/okd/tests/config.yml
@@ -0,0 +1,2 @@
+modules:
+ python_requires: ">=3.6"
diff --git a/ansible_collections/community/okd/tests/sanity/ignore-2.10.txt b/ansible_collections/community/okd/tests/sanity/ignore-2.10.txt
new file mode 100644
index 000000000..54e1d304d
--- /dev/null
+++ b/ansible_collections/community/okd/tests/sanity/ignore-2.10.txt
@@ -0,0 +1,4 @@
+plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
+plugins/modules/k8s.py validate-modules:return-syntax-error
+plugins/module_utils/k8s.py pylint:bad-option-value
+plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc
diff --git a/ansible_collections/community/okd/tests/sanity/ignore-2.11.txt b/ansible_collections/community/okd/tests/sanity/ignore-2.11.txt
new file mode 100644
index 000000000..0ade1e276
--- /dev/null
+++ b/ansible_collections/community/okd/tests/sanity/ignore-2.11.txt
@@ -0,0 +1,169 @@
+plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
+plugins/modules/k8s.py validate-modules:return-syntax-error
+plugins/module_utils/k8s.py pylint:bad-option-value
+plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc
+plugins/connection/oc.py import-2.6!skip
+plugins/connection/oc.py import-2.7!skip
+plugins/connection/oc.py import-3.5!skip
+plugins/connection/oc.py compile-2.6!skip
+plugins/connection/oc.py compile-2.7!skip
+plugins/connection/oc.py compile-3.5!skip
+plugins/inventory/openshift.py import-2.6!skip
+plugins/inventory/openshift.py import-2.7!skip
+plugins/inventory/openshift.py import-3.5!skip
+plugins/inventory/openshift.py compile-2.6!skip
+plugins/inventory/openshift.py compile-2.7!skip
+plugins/inventory/openshift.py compile-3.5!skip
+plugins/modules/openshift_route.py import-2.6!skip
+plugins/modules/openshift_route.py import-2.7!skip
+plugins/modules/openshift_route.py import-3.5!skip
+plugins/modules/openshift_route.py compile-2.6!skip
+plugins/modules/openshift_route.py compile-2.7!skip
+plugins/modules/openshift_route.py compile-3.5!skip
+plugins/modules/k8s.py import-2.6!skip
+plugins/modules/k8s.py import-2.7!skip
+plugins/modules/k8s.py import-3.5!skip
+plugins/modules/k8s.py compile-2.6!skip
+plugins/modules/k8s.py compile-2.7!skip
+plugins/modules/k8s.py compile-3.5!skip
+plugins/modules/openshift_process.py import-2.6!skip
+plugins/modules/openshift_process.py import-2.7!skip
+plugins/modules/openshift_process.py import-3.5!skip
+plugins/modules/openshift_process.py compile-2.6!skip
+plugins/modules/openshift_process.py compile-2.7!skip
+plugins/modules/openshift_process.py compile-3.5!skip
+plugins/modules/openshift_adm_groups_sync.py import-2.6!skip
+plugins/modules/openshift_adm_groups_sync.py import-2.7!skip
+plugins/modules/openshift_adm_groups_sync.py import-3.5!skip
+plugins/modules/openshift_adm_groups_sync.py compile-2.6!skip
+plugins/modules/openshift_adm_groups_sync.py compile-2.7!skip
+plugins/modules/openshift_adm_groups_sync.py compile-3.5!skip
+plugins/modules/openshift_adm_migrate_template_instances.py import-2.6!skip
+plugins/modules/openshift_adm_migrate_template_instances.py import-2.7!skip
+plugins/modules/openshift_adm_migrate_template_instances.py import-3.5!skip
+plugins/modules/openshift_adm_migrate_template_instances.py compile-2.6!skip
+plugins/modules/openshift_adm_migrate_template_instances.py compile-2.7!skip
+plugins/modules/openshift_adm_migrate_template_instances.py compile-3.5!skip
+plugins/modules/openshift_adm_prune_auth.py import-2.6!skip
+plugins/modules/openshift_adm_prune_auth.py import-2.7!skip
+plugins/modules/openshift_adm_prune_auth.py import-3.5!skip
+plugins/modules/openshift_adm_prune_auth.py compile-2.6!skip
+plugins/modules/openshift_adm_prune_auth.py compile-2.7!skip
+plugins/modules/openshift_adm_prune_auth.py compile-3.5!skip
+plugins/modules/openshift_adm_prune_deployments.py import-2.6!skip
+plugins/modules/openshift_adm_prune_deployments.py import-2.7!skip
+plugins/modules/openshift_adm_prune_deployments.py import-3.5!skip
+plugins/modules/openshift_adm_prune_deployments.py compile-2.6!skip
+plugins/modules/openshift_adm_prune_deployments.py compile-2.7!skip
+plugins/modules/openshift_adm_prune_deployments.py compile-3.5!skip
+plugins/modules/openshift_adm_prune_images.py import-2.6!skip
+plugins/modules/openshift_adm_prune_images.py import-2.7!skip
+plugins/modules/openshift_adm_prune_images.py import-3.5!skip
+plugins/modules/openshift_adm_prune_images.py compile-2.6!skip
+plugins/modules/openshift_adm_prune_images.py compile-2.7!skip
+plugins/modules/openshift_adm_prune_images.py compile-3.5!skip
+plugins/modules/openshift_auth.py import-2.6!skip
+plugins/modules/openshift_auth.py import-2.7!skip
+plugins/modules/openshift_auth.py import-3.5!skip
+plugins/modules/openshift_auth.py compile-2.6!skip
+plugins/modules/openshift_auth.py compile-2.7!skip
+plugins/modules/openshift_auth.py compile-3.5!skip
+plugins/modules/openshift_import_image.py import-2.6!skip
+plugins/modules/openshift_import_image.py import-2.7!skip
+plugins/modules/openshift_import_image.py import-3.5!skip
+plugins/modules/openshift_import_image.py compile-2.6!skip
+plugins/modules/openshift_import_image.py compile-2.7!skip
+plugins/modules/openshift_import_image.py compile-3.5!skip
+plugins/modules/openshift_registry_info.py import-2.6!skip
+plugins/modules/openshift_registry_info.py import-2.7!skip
+plugins/modules/openshift_registry_info.py import-3.5!skip
+plugins/modules/openshift_registry_info.py compile-2.6!skip
+plugins/modules/openshift_registry_info.py compile-2.7!skip
+plugins/modules/openshift_registry_info.py compile-3.5!skip
+plugins/module_utils/k8s.py import-2.6!skip
+plugins/module_utils/k8s.py import-2.7!skip
+plugins/module_utils/k8s.py import-3.5!skip
+plugins/module_utils/k8s.py compile-2.6!skip
+plugins/module_utils/k8s.py compile-2.7!skip
+plugins/module_utils/k8s.py compile-3.5!skip
+plugins/module_utils/openshift_process.py import-2.6!skip
+plugins/module_utils/openshift_process.py import-2.7!skip
+plugins/module_utils/openshift_process.py import-3.5!skip
+plugins/module_utils/openshift_process.py compile-2.6!skip
+plugins/module_utils/openshift_process.py compile-2.7!skip
+plugins/module_utils/openshift_process.py compile-3.5!skip
+plugins/module_utils/openshift_adm_prune_auth.py import-2.6!skip
+plugins/module_utils/openshift_adm_prune_auth.py import-2.7!skip
+plugins/module_utils/openshift_adm_prune_auth.py import-3.5!skip
+plugins/module_utils/openshift_adm_prune_auth.py compile-2.6!skip
+plugins/module_utils/openshift_adm_prune_auth.py compile-2.7!skip
+plugins/module_utils/openshift_adm_prune_auth.py compile-3.5!skip
+plugins/module_utils/openshift_adm_prune_deployments.py import-2.6!skip
+plugins/module_utils/openshift_adm_prune_deployments.py import-2.7!skip
+plugins/module_utils/openshift_adm_prune_deployments.py import-3.5!skip
+plugins/module_utils/openshift_adm_prune_deployments.py compile-2.6!skip
+plugins/module_utils/openshift_adm_prune_deployments.py compile-2.7!skip
+plugins/module_utils/openshift_adm_prune_deployments.py compile-3.5!skip
+plugins/module_utils/openshift_ldap.py import-2.6!skip
+plugins/module_utils/openshift_ldap.py import-2.7!skip
+plugins/module_utils/openshift_ldap.py import-3.5!skip
+plugins/module_utils/openshift_ldap.py compile-2.6!skip
+plugins/module_utils/openshift_ldap.py compile-2.7!skip
+plugins/module_utils/openshift_ldap.py compile-3.5!skip
+plugins/module_utils/openshift_adm_prune_images.py import-2.6!skip
+plugins/module_utils/openshift_adm_prune_images.py import-2.7!skip
+plugins/module_utils/openshift_adm_prune_images.py import-3.5!skip
+plugins/module_utils/openshift_adm_prune_images.py compile-2.6!skip
+plugins/module_utils/openshift_adm_prune_images.py compile-2.7!skip
+plugins/module_utils/openshift_adm_prune_images.py compile-3.5!skip
+plugins/module_utils/openshift_docker_image.py import-2.6!skip
+plugins/module_utils/openshift_docker_image.py import-2.7!skip
+plugins/module_utils/openshift_docker_image.py import-3.5!skip
+plugins/module_utils/openshift_docker_image.py compile-2.6!skip
+plugins/module_utils/openshift_docker_image.py compile-2.7!skip
+plugins/module_utils/openshift_docker_image.py compile-3.5!skip
+plugins/module_utils/openshift_import_image.py import-2.6!skip
+plugins/module_utils/openshift_import_image.py import-2.7!skip
+plugins/module_utils/openshift_import_image.py import-3.5!skip
+plugins/module_utils/openshift_import_image.py compile-2.6!skip
+plugins/module_utils/openshift_import_image.py compile-2.7!skip
+plugins/module_utils/openshift_import_image.py compile-3.5!skip
+plugins/module_utils/openshift_groups.py import-2.6!skip
+plugins/module_utils/openshift_groups.py import-2.7!skip
+plugins/module_utils/openshift_groups.py import-3.5!skip
+plugins/module_utils/openshift_groups.py compile-2.6!skip
+plugins/module_utils/openshift_groups.py compile-2.7!skip
+plugins/module_utils/openshift_groups.py compile-3.5!skip
+plugins/module_utils/openshift_images_common.py import-2.6!skip
+plugins/module_utils/openshift_images_common.py import-2.7!skip
+plugins/module_utils/openshift_images_common.py import-3.5!skip
+plugins/module_utils/openshift_images_common.py compile-2.6!skip
+plugins/module_utils/openshift_images_common.py compile-2.7!skip
+plugins/module_utils/openshift_images_common.py compile-3.5!skip
+plugins/module_utils/openshift_registry.py import-2.6!skip
+plugins/module_utils/openshift_registry.py import-2.7!skip
+plugins/module_utils/openshift_registry.py import-3.5!skip
+plugins/module_utils/openshift_registry.py compile-2.6!skip
+plugins/module_utils/openshift_registry.py compile-2.7!skip
+plugins/module_utils/openshift_registry.py compile-3.5!skip
+plugins/module_utils/openshift_builds.py import-2.6!skip
+plugins/module_utils/openshift_builds.py import-2.7!skip
+plugins/module_utils/openshift_builds.py import-3.5!skip
+plugins/module_utils/openshift_builds.py compile-2.6!skip
+plugins/module_utils/openshift_builds.py compile-2.7!skip
+plugins/module_utils/openshift_builds.py compile-3.5!skip
+plugins/modules/openshift_build.py import-2.6!skip
+plugins/modules/openshift_build.py import-2.7!skip
+plugins/modules/openshift_build.py import-3.5!skip
+plugins/modules/openshift_build.py compile-2.6!skip
+plugins/modules/openshift_build.py compile-2.7!skip
+plugins/modules/openshift_build.py compile-3.5!skip
+plugins/modules/openshift_adm_prune_builds.py import-2.6!skip
+plugins/modules/openshift_adm_prune_builds.py import-2.7!skip
+plugins/modules/openshift_adm_prune_builds.py import-3.5!skip
+plugins/modules/openshift_adm_prune_builds.py compile-2.6!skip
+plugins/modules/openshift_adm_prune_builds.py compile-2.7!skip
+plugins/modules/openshift_adm_prune_builds.py compile-3.5!skip
+plugins/module_utils/openshift_common.py import-2.6!skip
+plugins/module_utils/openshift_common.py import-2.7!skip
+plugins/module_utils/openshift_common.py import-3.5!skip \ No newline at end of file
diff --git a/ansible_collections/community/okd/tests/sanity/ignore-2.12.txt b/ansible_collections/community/okd/tests/sanity/ignore-2.12.txt
new file mode 100644
index 000000000..2fd2bdc90
--- /dev/null
+++ b/ansible_collections/community/okd/tests/sanity/ignore-2.12.txt
@@ -0,0 +1,3 @@
+plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
+plugins/modules/k8s.py validate-modules:return-syntax-error
+plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc \ No newline at end of file
diff --git a/ansible_collections/community/okd/tests/sanity/ignore-2.13.txt b/ansible_collections/community/okd/tests/sanity/ignore-2.13.txt
new file mode 100644
index 000000000..2fd2bdc90
--- /dev/null
+++ b/ansible_collections/community/okd/tests/sanity/ignore-2.13.txt
@@ -0,0 +1,3 @@
+plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
+plugins/modules/k8s.py validate-modules:return-syntax-error
+plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc \ No newline at end of file
diff --git a/ansible_collections/community/okd/tests/sanity/ignore-2.14.txt b/ansible_collections/community/okd/tests/sanity/ignore-2.14.txt
new file mode 100644
index 000000000..2fd2bdc90
--- /dev/null
+++ b/ansible_collections/community/okd/tests/sanity/ignore-2.14.txt
@@ -0,0 +1,3 @@
+plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
+plugins/modules/k8s.py validate-modules:return-syntax-error
+plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc \ No newline at end of file
diff --git a/ansible_collections/community/okd/tests/sanity/ignore-2.15.txt b/ansible_collections/community/okd/tests/sanity/ignore-2.15.txt
new file mode 100644
index 000000000..2fd2bdc90
--- /dev/null
+++ b/ansible_collections/community/okd/tests/sanity/ignore-2.15.txt
@@ -0,0 +1,3 @@
+plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
+plugins/modules/k8s.py validate-modules:return-syntax-error
+plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc \ No newline at end of file
diff --git a/ansible_collections/community/okd/tests/sanity/ignore-2.9.txt b/ansible_collections/community/okd/tests/sanity/ignore-2.9.txt
new file mode 100644
index 000000000..a46997074
--- /dev/null
+++ b/ansible_collections/community/okd/tests/sanity/ignore-2.9.txt
@@ -0,0 +1,3 @@
+plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
+plugins/module_utils/k8s.py pylint:bad-option-value
+plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc
diff --git a/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_ldap_dn.py b/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_ldap_dn.py
new file mode 100644
index 000000000..5835f36d7
--- /dev/null
+++ b/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_ldap_dn.py
@@ -0,0 +1,32 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_ldap import (
+ openshift_equal_dn,
+ openshift_ancestorof_dn
+)
+import pytest
+
+try:
+ import ldap
+except ImportError:
+ pytestmark = pytest.mark.skip("This test requires the python-ldap library")
+
+
+def test_equal_dn():
+
+ assert openshift_equal_dn("cn=unit,ou=users,dc=ansible,dc=com", "cn=unit,ou=users,dc=ansible,dc=com")
+ assert not openshift_equal_dn("cn=unit,ou=users,dc=ansible,dc=com", "cn=units,ou=users,dc=ansible,dc=com")
+ assert not openshift_equal_dn("cn=unit,ou=users,dc=ansible,dc=com", "cn=unit,ou=user,dc=ansible,dc=com")
+ assert not openshift_equal_dn("cn=unit,ou=users,dc=ansible,dc=com", "cn=unit,ou=users,dc=ansible,dc=org")
+
+
+def test_ancestor_of_dn():
+
+ assert not openshift_ancestorof_dn("cn=unit,ou=users,dc=ansible,dc=com", "cn=unit,ou=users,dc=ansible,dc=com")
+ assert not openshift_ancestorof_dn("cn=unit,ou=users,dc=ansible,dc=com", "cn=units,ou=users,dc=ansible,dc=com")
+ assert openshift_ancestorof_dn("ou=users,dc=ansible,dc=com", "cn=john,ou=users,dc=ansible,dc=com")
+ assert openshift_ancestorof_dn("ou=users,dc=ansible,dc=com", "cn=mathew,ou=users,dc=ansible,dc=com")
+ assert not openshift_ancestorof_dn("ou=users,dc=ansible,dc=com", "cn=mathew,ou=users,dc=ansible,dc=org")
diff --git a/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_ldap_sync_config.py b/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_ldap_sync_config.py
new file mode 100644
index 000000000..1a8ef67e2
--- /dev/null
+++ b/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_ldap_sync_config.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_ldap import (
+ validate_ldap_sync_config,
+)
+
+
+def test_missing_url():
+ config = dict(
+ kind="LDAPSyncConfig",
+ apiVersion="v1",
+ insecure=True
+ )
+ err = validate_ldap_sync_config(config)
+ assert err == "url should be non empty attribute."
+
+
+def test_binddn_and_bindpwd_linked():
+ """
+ one of bind_dn and bind_pwd cannot be set alone
+ """
+ config = dict(
+ kind="LDAPSyncConfig",
+ apiVersion="v1",
+ url="ldap://LDAP_SERVICE_IP:389",
+ insecure=True,
+ bindDN="cn=admin,dc=example,dc=org"
+ )
+
+ credentials_error = "bindDN and bindPassword must both be specified, or both be empty."
+
+ assert validate_ldap_sync_config(config) == credentials_error
+
+ config = dict(
+ kind="LDAPSyncConfig",
+ apiVersion="v1",
+ url="ldap://LDAP_SERVICE_IP:389",
+ insecure=True,
+ bindPassword="testing1223"
+ )
+
+ assert validate_ldap_sync_config(config) == credentials_error
+
+
+def test_insecure_connection():
+ config = dict(
+ kind="LDAPSyncConfig",
+ apiVersion="v1",
+ url="ldaps://LDAP_SERVICE_IP:389",
+ insecure=True,
+ )
+
+ assert validate_ldap_sync_config(config) == "Cannot use ldaps scheme with insecure=true."
+
+ config.update(dict(
+ url="ldap://LDAP_SERVICE_IP:389",
+ ca="path/to/ca/file"
+ ))
+
+ assert validate_ldap_sync_config(config) == "Cannot specify a ca with insecure=true."
diff --git a/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_openshift_docker_image.py b/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_openshift_docker_image.py
new file mode 100644
index 000000000..99cf15cc5
--- /dev/null
+++ b/ansible_collections/community/okd/tests/unit/plugins/module_utils/test_openshift_docker_image.py
@@ -0,0 +1,152 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+from ansible_collections.community.okd.plugins.module_utils.openshift_docker_image import (
+ convert_storage_to_bytes,
+ parse_docker_image_ref,
+)
+import pytest
+
+
+def test_convert_storage_to_bytes():
+
+ data = [
+ ("1000", 1000),
+ ("1000Ki", 1000 * 1024),
+ ("890Ki", 890 * 1024),
+ ("890Mi", 890 * 1024 * 1024),
+ ("90Gi", 90 * 1024 * 1024 * 1024),
+ ("192Ti", 192 * 1024 * 1024 * 1024 * 1024),
+ ("452Pi", 452 * 1024 * 1024 * 1024 * 1024 * 1024),
+ ("23Ei", 23 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024),
+ ]
+
+ for x in data:
+ result = convert_storage_to_bytes(x[0])
+ assert result == x[1]
+
+ failed = "123ki"
+ with pytest.raises(ValueError):
+ convert_storage_to_bytes(failed)
+
+
+def validate_docker_response(resp, **kwargs):
+ assert isinstance(resp, dict)
+ for key in ("hostname", "digest", "tag", "name", "namespace"):
+ assert key in resp
+
+ hostname = kwargs.get("hostname", "docker.io")
+ assert resp["hostname"] == hostname
+
+ namespace = kwargs.get("namespace")
+ assert resp["namespace"] == namespace
+
+ name = kwargs.get("name")
+ assert resp["name"] == name
+
+ digest = kwargs.get("digest")
+ assert resp["digest"] == digest
+
+ tag = kwargs.get("tag")
+ assert resp["tag"] == tag
+
+
+def test_parse_docker_image_ref_valid_image_with_digest():
+
+ image = "registry.access.redhat.com/ubi8/dotnet-21@sha256:f7718f5efd3436e781ee4322c92ab0c4ae63e61f5b36f1473a57874cc3522669"
+ response, err = parse_docker_image_ref(image)
+ assert err is None
+
+ validate_docker_response(response,
+ hostname="registry.access.redhat.com",
+ namespace="ubi8",
+ name="dotnet-21",
+ digest="sha256:f7718f5efd3436e781ee4322c92ab0c4ae63e61f5b36f1473a57874cc3522669")
+
+
+def test_parse_docker_image_ref_valid_image_with_tag_latest():
+
+ image = "registry.access.redhat.com/ubi8/dotnet-21:latest"
+ response, err = parse_docker_image_ref(image)
+ assert err is None
+
+ validate_docker_response(response,
+ hostname="registry.access.redhat.com",
+ namespace="ubi8",
+ name="dotnet-21",
+ tag="latest")
+
+
+def test_parse_docker_image_ref_valid_image_with_tag_int():
+
+ image = "registry.access.redhat.com/ubi8/dotnet-21:0.0.1"
+ response, err = parse_docker_image_ref(image)
+ assert err is None
+
+ validate_docker_response(response,
+ hostname="registry.access.redhat.com",
+ namespace="ubi8",
+ name="dotnet-21",
+ tag="0.0.1")
+
+
+def test_parse_docker_image_ref_invalid_image():
+
+ # The hex value of the sha256 is not valid
+ image = "registry.access.redhat.com/dotnet-21@sha256:f7718f5efd3436e781ee4322c92ab0c4ae63e61f5b36f1473a57874cc3522"
+ response, err = parse_docker_image_ref(image)
+ assert err and err.startswith("Invalid length for digest hex")
+
+
+def test_parse_docker_image_ref_valid_image_without_hostname():
+
+ image = "ansible:2.10.0"
+ response, err = parse_docker_image_ref(image)
+ assert err is None
+
+ validate_docker_response(response, name="ansible", tag="2.10.0")
+
+
+def test_parse_docker_image_ref_valid_image_without_hostname_and_with_digest():
+
+ image = "ansible@sha256:f7718f5efd3436e781ee4322c92ab0c4ae63e61f5b36f1473a57874cc3522669"
+ response, err = parse_docker_image_ref(image)
+ assert err is None
+
+ validate_docker_response(response, name="ansible", digest="sha256:f7718f5efd3436e781ee4322c92ab0c4ae63e61f5b36f1473a57874cc3522669")
+
+
+def test_parse_docker_image_ref_valid_image_with_name_only():
+
+ image = "ansible"
+ response, err = parse_docker_image_ref(image)
+ assert err is None
+
+ validate_docker_response(response, name="ansible")
+
+
+def test_parse_docker_image_ref_valid_image_without_hostname_with_namespace_and_name():
+
+ image = "ibmcom/pause@sha256:fcaff905397ba63fd376d0c3019f1f1cb6e7506131389edbcb3d22719f1ae54d"
+ response, err = parse_docker_image_ref(image)
+ assert err is None
+
+ validate_docker_response(response,
+ name="pause",
+ namespace="ibmcom",
+ digest="sha256:fcaff905397ba63fd376d0c3019f1f1cb6e7506131389edbcb3d22719f1ae54d")
+
+
+def test_parse_docker_image_ref_valid_image_with_complex_namespace_name():
+
+ image = "registry.redhat.io/jboss-webserver-5/webserver54-openjdk11-tomcat9-openshift-rhel7:1.0"
+ response, err = parse_docker_image_ref(image)
+ assert err is None
+
+ validate_docker_response(response,
+ hostname="registry.redhat.io",
+ name="webserver54-openjdk11-tomcat9-openshift-rhel7",
+ namespace="jboss-webserver-5",
+ tag="1.0")
diff --git a/ansible_collections/community/okd/tests/unit/plugins/modules/test_openshift_adm_migrate_template_instances.py b/ansible_collections/community/okd/tests/unit/plugins/modules/test_openshift_adm_migrate_template_instances.py
new file mode 100644
index 000000000..256bef0aa
--- /dev/null
+++ b/ansible_collections/community/okd/tests/unit/plugins/modules/test_openshift_adm_migrate_template_instances.py
@@ -0,0 +1,236 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+import pytest
+
+from ansible_collections.community.okd.plugins.modules.openshift_adm_migrate_template_instances import (
+ OpenShiftMigrateTemplateInstances,
+)
+
+
+testdata = [
+ ("input", "output"),
+ [
+ (
+ {
+ "status": {
+ "objects": [
+ {"ref": {"kind": "DeploymentConfig", "apiVersion": "v1"}}
+ ]
+ }
+ },
+ [
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "DeploymentConfig",
+ "apiVersion": "apps.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ }
+ ],
+ ),
+ (
+ {"status": {"objects": [{"ref": {"kind": "DeploymentConfig"}}]}},
+ [
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "DeploymentConfig",
+ "apiVersion": "apps.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ }
+ ],
+ ),
+ (
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "DeploymentConfig",
+ "apiVersion": "apps.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ },
+ [],
+ ),
+ (
+ {
+ "status": {
+ "objects": [{"ref": {"kind": "BuildConfig", "apiVersion": "v1"}}]
+ }
+ },
+ [
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "BuildConfig",
+ "apiVersion": "build.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ }
+ ],
+ ),
+ (
+ {"status": {"objects": [{"ref": {"kind": "BuildConfig"}}]}},
+ [
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "BuildConfig",
+ "apiVersion": "build.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ }
+ ],
+ ),
+ (
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "BuildConfig",
+ "apiVersion": "build.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ },
+ [],
+ ),
+ (
+ {"status": {"objects": [{"ref": {"kind": "Build", "apiVersion": "v1"}}]}},
+ [
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "Build",
+ "apiVersion": "build.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ }
+ ],
+ ),
+ (
+ {"status": {"objects": [{"ref": {"kind": "Build"}}]}},
+ [
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "Build",
+ "apiVersion": "build.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ }
+ ],
+ ),
+ (
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "Build",
+ "apiVersion": "build.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ },
+ [],
+ ),
+ (
+ {"status": {"objects": [{"ref": {"kind": "Route", "apiVersion": "v1"}}]}},
+ [
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "Route",
+ "apiVersion": "route.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ }
+ ],
+ ),
+ (
+ {"status": {"objects": [{"ref": {"kind": "Route"}}]}},
+ [
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "Route",
+ "apiVersion": "route.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ }
+ ],
+ ),
+ (
+ {
+ "status": {
+ "objects": [
+ {
+ "ref": {
+ "kind": "Route",
+ "apiVersion": "route.openshift.io/v1",
+ }
+ }
+ ]
+ }
+ },
+ [],
+ ),
+ (
+ {
+ "status": {
+ "objects": [{"ref": {"kind": "FakeKind", "apiVersion": "v1"}}]
+ }
+ },
+ [],
+ ),
+ ],
+]
+
+
+@pytest.mark.parametrize(*testdata)
+def test_templateinstance_migrations(input, output):
+ assert OpenShiftMigrateTemplateInstances.perform_migrations(input) == output
diff --git a/ansible_collections/community/okd/tests/unit/requirements.txt b/ansible_collections/community/okd/tests/unit/requirements.txt
new file mode 100644
index 000000000..e079f8a60
--- /dev/null
+++ b/ansible_collections/community/okd/tests/unit/requirements.txt
@@ -0,0 +1 @@
+pytest