mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-08-03 04:34:24 -07:00
Merge aa6ed971a3
into 84b5d38c51
This commit is contained in:
commit
a72fc67016
5 changed files with 1101 additions and 0 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -1346,6 +1346,8 @@ files:
|
||||||
maintainers: precurse
|
maintainers: precurse
|
||||||
$modules/taiga_issue.py:
|
$modules/taiga_issue.py:
|
||||||
maintainers: lekum
|
maintainers: lekum
|
||||||
|
$modules/tanzu_mission_control_secret.py:
|
||||||
|
maintainers: massix
|
||||||
$modules/telegram.py:
|
$modules/telegram.py:
|
||||||
maintainers: tyouxa loms lomserman
|
maintainers: tyouxa loms lomserman
|
||||||
$modules/terraform.py:
|
$modules/terraform.py:
|
||||||
|
|
798
plugins/modules/tanzu_mission_control_secret.py
Normal file
798
plugins/modules/tanzu_mission_control_secret.py
Normal file
|
@ -0,0 +1,798 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2025, Massimo Gengarelli (massimo.gengarelli@proton.me)
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
DOCUMENTATION = r"""
|
||||||
|
module: tanzu_mission_control_secret
|
||||||
|
short_description: Manages Cluster, Cluster Group Secrets and SecretExports in Tanzu Mission Control
|
||||||
|
version_added: 11.0.0
|
||||||
|
description:
|
||||||
|
- Create and revokes Cluster and Cluster Group Secrets in Tanzu Mission Control for a given Cluster.
|
||||||
|
- Creates and removes SecretExports for the given secret in Tanzu Mission Control.
|
||||||
|
author:
|
||||||
|
- Massimo Gengarelli (@massix)
|
||||||
|
notes:
|
||||||
|
- To obtain an O(api_token), login to Tanzu Mission Control, click on your user, then I(Settings) and then I(Generate an
|
||||||
|
API token).
|
||||||
|
- The generated API token B(must) have the rights to create I(Secrets) and I(SecretExports) in Tanzu Mission Control.
|
||||||
|
- The rights are granted by the administrator of the TMC Instance to single users or groups.
|
||||||
|
- When creating the token, make sure you select the C(tmc_user) service role for all the required organizations.
|
||||||
|
- This has only been tested with I(TMC SaaS), but it should also work with TMC Self Hosted.
|
||||||
|
options:
|
||||||
|
api_host:
|
||||||
|
description:
|
||||||
|
- 'Full URL of the TMC instance without the protocol, for example: V(my-organization.tmc.tanzu.broadcom.com).'
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
api_token:
|
||||||
|
description:
|
||||||
|
- API Token used to access the APIs.
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
management_cluster_name:
|
||||||
|
description:
|
||||||
|
- When creating a secret for a single cluster, this is the name of the management cluster.
|
||||||
|
- Required only when using O(cluster_name).
|
||||||
|
- Ignored when using O(cluster_group).
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
provisioner_name:
|
||||||
|
description:
|
||||||
|
- When creating a secret for a single cluster, this is the name of the provisioner.
|
||||||
|
- Required only when using O(cluster_name).
|
||||||
|
- Ignored when using O(cluster_group).
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
cluster_name:
|
||||||
|
description:
|
||||||
|
- Name of the existing cluster where the secret will be created.
|
||||||
|
- Mutually exclusive with O(cluster_group).
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
cluster_group:
|
||||||
|
description:
|
||||||
|
- Name of the existing cluster group where the secret will be created.
|
||||||
|
- Mutually exclusive with O(cluster_name).
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
cluster_namespace:
|
||||||
|
description:
|
||||||
|
- The namespace where the secret will be created.
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
secret_type:
|
||||||
|
description:
|
||||||
|
- Set this to V(opaque) to create an opaque secret (key/value pairs) or V(docker_config) to create a Docker Config secret.
|
||||||
|
- When O(secret_type=docker_config), O(registry_host), O(registry_username), and O(registry_password) are required.
|
||||||
|
- When O(secret_type=opaque), O(data) is required.
|
||||||
|
type: str
|
||||||
|
choices: ["opaque", "docker_config"]
|
||||||
|
default: opaque
|
||||||
|
required: false
|
||||||
|
registry_host:
|
||||||
|
description:
|
||||||
|
- Required when O(secret_type=docker_config), this is the Docker registry host.
|
||||||
|
- Mutually exclusive with O(data).
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
registry_username:
|
||||||
|
description:
|
||||||
|
- Required when O(secret_type=docker_config), this is the username of the Docker registry.
|
||||||
|
- Mutually exclusive with O(data).
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
registry_password:
|
||||||
|
description:
|
||||||
|
- Required when O(secret_type=docker_config), this is the password of the Docker registry.
|
||||||
|
- Mutually exclusive with O(data).
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
secret_name:
|
||||||
|
description:
|
||||||
|
- Name of the secret.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
data:
|
||||||
|
description:
|
||||||
|
- Dictionary of key/value pairs for the secret.
|
||||||
|
- The values B(must not) be encoded in C(base64), the module will do that for you.
|
||||||
|
- Required when O(secret_type=opaque).
|
||||||
|
- Mutually exclusive with O(registry_host), O(registry_username), and O(registry_password).
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: {}
|
||||||
|
export:
|
||||||
|
description:
|
||||||
|
- Whether or not to export the secret across all namespaces, using a C(SecretExport).
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- When V(present) the secret will be added to the cluster if it does not exist.
|
||||||
|
- When V(absent) it will be removed if it exists.
|
||||||
|
- When V(update) it will be updated if it exists and created if it does not.
|
||||||
|
- When V(update) the old fields will be B(erased), so make sure you also specify the old fields!
|
||||||
|
default: present
|
||||||
|
type: str
|
||||||
|
choices: ["present", "absent", "update"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = r"""
|
||||||
|
# Create a new secret at Cluster level
|
||||||
|
- name: Create and export a new cluster secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: example.tmc.tanzu.broadcom.com
|
||||||
|
api_token: "super-secret-api-token"
|
||||||
|
cluster_name: test-cluster
|
||||||
|
cluster_namespace: default
|
||||||
|
management_cluster_name: management-cluster
|
||||||
|
provisioner_name: provisioner-name
|
||||||
|
secret_name: my-very-secret-secret
|
||||||
|
secret_type: opaque
|
||||||
|
data:
|
||||||
|
first_field: with a value
|
||||||
|
multiline_field: |
|
||||||
|
You can also create multiline fields (if you want to embed files, for examples)
|
||||||
|
export: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
# This will lead to a no change, if you want to update a secret you *must* use state=update
|
||||||
|
- name: Try to recreate the same secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: example.tmc.tanzu.broadcom.com
|
||||||
|
api_token: "super-secret-api-token"
|
||||||
|
cluster_name: test-cluster
|
||||||
|
cluster_namespace: default
|
||||||
|
management_cluster_name: management-cluster
|
||||||
|
provisioner_name: provisioner-name
|
||||||
|
secret_name: my-very-secret-secret
|
||||||
|
data:
|
||||||
|
some_new_field: some new value
|
||||||
|
state: present
|
||||||
|
|
||||||
|
# This will erase the previous fields (there is no way to retrieve the old values)
|
||||||
|
- name: Update a secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: example.tmc.tanzu.broadcom.com
|
||||||
|
api_token: "super-secret-api-token"
|
||||||
|
cluster_name: test-cluster
|
||||||
|
cluster_namespace: default
|
||||||
|
management_cluster_name: management-cluster
|
||||||
|
provisioner_name: provisioner-name
|
||||||
|
secret_name: my-very-secret-secret
|
||||||
|
data:
|
||||||
|
first_field: with a value
|
||||||
|
some_new_field: some new value
|
||||||
|
state: update
|
||||||
|
|
||||||
|
- name: Delete a secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: example.tmc.tanzu.broadcom.com
|
||||||
|
api_token: "super-secret-api-token"
|
||||||
|
cluster_name: test-cluster
|
||||||
|
cluster_namespace: default
|
||||||
|
management_cluster_name: management-cluster
|
||||||
|
provisioner_name: provisioner-name
|
||||||
|
secret_name: my-very-secret-secret
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
# This will create a secret of type "kubernetes.io/dockerconfigjson" with the .dockerconfigjson field
|
||||||
|
- name: Create and export a ClusterGroup Registry secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: example.tmc.tanzu.broadcom.com
|
||||||
|
api_token: "super-secret-api-token"
|
||||||
|
cluster_group: test-cluster-group
|
||||||
|
cluster_namespace: default
|
||||||
|
secret_name: my-very-secret-secret
|
||||||
|
secret_type: docker_config
|
||||||
|
registry_host: docker.io
|
||||||
|
registry_username: some_username
|
||||||
|
registry_password: some_password
|
||||||
|
export: true
|
||||||
|
state: present
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = ""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from base64 import b64encode
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||||
|
from ansible.module_utils.six.moves.urllib.error import HTTPError
|
||||||
|
from ansible.module_utils.urls import open_url
|
||||||
|
from ansible.module_utils.common.text.converters import to_bytes, to_native
|
||||||
|
|
||||||
|
|
||||||
|
class TMCSecret(object):
|
||||||
|
"""Represents a generic secret (either Cluster or ClusterGroup) in TMC."""
|
||||||
|
|
||||||
|
def __init__(self, secret_name, secret_type, cluster_namespace):
|
||||||
|
self._secret_name = secret_name
|
||||||
|
self._secret_type = (
|
||||||
|
"SECRET_TYPE_OPAQUE"
|
||||||
|
if secret_type == "opaque"
|
||||||
|
else "SECRET_TYPE_DOCKERCONFIGJSON"
|
||||||
|
)
|
||||||
|
self._cluster_namespace = cluster_namespace
|
||||||
|
|
||||||
|
def exists(self, api_host, access_token):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def is_exported(self, api_host, access_token):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def create(self, api_host, access_token, payload):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def update(self, api_host, access_token, payload):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def delete(self, api_host, access_token):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def create_export(self, api_host, access_token):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def delete_export(self, api_host, access_token):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class TMCClusterGroupSecret(TMCSecret):
|
||||||
|
"""Represents a ClusterGroup secret in TMC."""
|
||||||
|
|
||||||
|
def __init__(self, cluster_group, cluster_namespace, secret_name, secret_type):
|
||||||
|
super().__init__(secret_name, secret_type, cluster_namespace)
|
||||||
|
self._cluster_group = cluster_group
|
||||||
|
|
||||||
|
def _generate_query_params(self):
|
||||||
|
return urlencode({"fullName.namespaceName": self._cluster_namespace})
|
||||||
|
|
||||||
|
def _generate_url(self, api_host):
|
||||||
|
return "https://{}/v1alpha1/clustergroups/{}/namespace/secrets".format(
|
||||||
|
api_host, self._cluster_group
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_current_spec(self, api_host, access_token):
|
||||||
|
url = "{}/{}?{}".format(
|
||||||
|
self._generate_url(api_host),
|
||||||
|
self._secret_name,
|
||||||
|
self._generate_query_params(),
|
||||||
|
)
|
||||||
|
response = open_url(
|
||||||
|
url,
|
||||||
|
headers={"Authorization": "Bearer {}".format(access_token)},
|
||||||
|
)
|
||||||
|
|
||||||
|
return json.loads(response.read())
|
||||||
|
|
||||||
|
def _generate_export_url(self, api_host, operation="get"):
|
||||||
|
if operation == "get":
|
||||||
|
return "https://{}/v1alpha1/clustergroups/{}/namespaces/{}/secretexports".format(
|
||||||
|
api_host, self._cluster_namespace, self._cluster_group
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return (
|
||||||
|
"https://{}/v1alpha1/clustergroups/{}/namespace/secretexports".format(
|
||||||
|
api_host, self._cluster_group
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _prepare_request(self, payload):
|
||||||
|
new_payload = {}
|
||||||
|
for key, value in payload.items():
|
||||||
|
new_payload[key] = to_native(b64encode(to_bytes(value)))
|
||||||
|
|
||||||
|
complete_payload = {
|
||||||
|
"secret": {
|
||||||
|
"fullName": {
|
||||||
|
"clusterGroupName": self._cluster_group,
|
||||||
|
"name": self._secret_name,
|
||||||
|
"namespaceName": self._cluster_namespace,
|
||||||
|
},
|
||||||
|
"meta": {},
|
||||||
|
"status": {},
|
||||||
|
"spec": {
|
||||||
|
"atomicSpec": {"secretType": self._secret_type, "data": new_payload}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return complete_payload
|
||||||
|
|
||||||
|
def exists(self, api_host, access_token):
|
||||||
|
url = "{}/{}?{}".format(
|
||||||
|
self._generate_url(api_host),
|
||||||
|
self._secret_name,
|
||||||
|
self._generate_query_params(),
|
||||||
|
)
|
||||||
|
auhorization_header = "Bearer {}".format(access_token)
|
||||||
|
try:
|
||||||
|
open_url(
|
||||||
|
url,
|
||||||
|
headers={"Authorization": auhorization_header},
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except HTTPError as e:
|
||||||
|
if e.getcode() == 404:
|
||||||
|
return False
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def create(self, api_host, access_token, payload):
|
||||||
|
url = self._generate_url(api_host)
|
||||||
|
all_headers = {"Authorization": "Bearer {}".format(access_token)}
|
||||||
|
new_payload = to_bytes(json.dumps(self._prepare_request(payload)))
|
||||||
|
|
||||||
|
open_url(url, headers=all_headers, data=new_payload)
|
||||||
|
return True, "Secret {}/{} created".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def update(self, api_host, access_token, payload):
|
||||||
|
url = "{}/{}".format(self._generate_url(api_host), self._secret_name)
|
||||||
|
authorization_header = "Bearer {}".format(access_token)
|
||||||
|
new_payload = self._prepare_request(payload)
|
||||||
|
all_headers = {"Authorization": authorization_header}
|
||||||
|
|
||||||
|
current_spec = self._get_current_spec(api_host, access_token)
|
||||||
|
|
||||||
|
new_payload["secret"]["meta"] = current_spec["secret"]["meta"]
|
||||||
|
new_payload["secret"]["type"] = current_spec["secret"]["type"]
|
||||||
|
new_payload["secret"]["fullName"] = current_spec["secret"]["fullName"]
|
||||||
|
|
||||||
|
new_payload = to_bytes(json.dumps(new_payload))
|
||||||
|
|
||||||
|
open_url(url, method="PUT", headers=all_headers, data=new_payload)
|
||||||
|
return True, "Secret {}/{} updated".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self, api_host, access_token):
|
||||||
|
url = "{}/{}?{}".format(
|
||||||
|
self._generate_url(api_host),
|
||||||
|
self._secret_name,
|
||||||
|
self._generate_query_params(),
|
||||||
|
)
|
||||||
|
authorization_header = "Bearer {}".format(access_token)
|
||||||
|
|
||||||
|
open_url(
|
||||||
|
url,
|
||||||
|
method="DELETE",
|
||||||
|
headers={"Authorization": authorization_header},
|
||||||
|
)
|
||||||
|
return True, "Secret {}/{} deleted".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_exported(self, api_host, access_token):
|
||||||
|
url = "{}/{}".format(self._generate_export_url(api_host), self._secret_name)
|
||||||
|
authorization_header = {"Authorization": "Bearer {}".format(access_token)}
|
||||||
|
|
||||||
|
try:
|
||||||
|
open_url(url, headers=authorization_header)
|
||||||
|
return True
|
||||||
|
except HTTPError as e:
|
||||||
|
if e.getcode() == 404:
|
||||||
|
return False
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def create_export(self, api_host, access_token):
|
||||||
|
url = self._generate_export_url(api_host, operation="create")
|
||||||
|
authorization_header = {"Authorization": "Bearer {}".format(access_token)}
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"secretExport": {
|
||||||
|
"fullName": {
|
||||||
|
"clusterGroupName": self._cluster_group,
|
||||||
|
"name": self._secret_name,
|
||||||
|
"namespaceName": self._cluster_namespace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = to_bytes(json.dumps(payload))
|
||||||
|
|
||||||
|
open_url(url, headers=authorization_header, data=payload)
|
||||||
|
return True, "SecretExport for {}/{} created".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_export(self, api_host, access_token):
|
||||||
|
url = "{}/{}".format(self._generate_export_url(api_host), self._secret_name)
|
||||||
|
authorization_header = {"Authorization": "Bearer {}".format(access_token)}
|
||||||
|
|
||||||
|
open_url(url, method="DELETE", headers=authorization_header)
|
||||||
|
return True, "SecretExport for {}/{} deleted".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TMCClusterSecret(TMCSecret):
|
||||||
|
"""Represents a Cluster Secret in TMC."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
management_cluster_name,
|
||||||
|
provisioner_name,
|
||||||
|
cluster_name,
|
||||||
|
cluster_namespace,
|
||||||
|
secret_name,
|
||||||
|
secret_type,
|
||||||
|
):
|
||||||
|
super().__init__(secret_name, secret_type, cluster_namespace)
|
||||||
|
self._management_cluster_name = management_cluster_name
|
||||||
|
self._provisioner_name = provisioner_name
|
||||||
|
self._cluster_name = cluster_name
|
||||||
|
|
||||||
|
def _get_current_spec(self, api_host, access_token):
|
||||||
|
url = "{}/{}?{}".format(
|
||||||
|
self._generate_url(api_host),
|
||||||
|
self._secret_name,
|
||||||
|
self._generate_query_params(),
|
||||||
|
)
|
||||||
|
response = open_url(
|
||||||
|
url,
|
||||||
|
headers={"Authorization": "Bearer {}".format(access_token)},
|
||||||
|
)
|
||||||
|
|
||||||
|
return json.loads(response.read())
|
||||||
|
|
||||||
|
def _generate_export_url(self, api_host):
|
||||||
|
return "https://{}/v1alpha1/clusters/{}/namespaces/{}/secretexports".format(
|
||||||
|
api_host, self._cluster_name, self._cluster_namespace
|
||||||
|
)
|
||||||
|
|
||||||
|
def _generate_url(self, api_host):
|
||||||
|
return "https://{}/v1alpha1/clusters/{}/namespaces/{}/secrets".format(
|
||||||
|
api_host, self._cluster_name, self._cluster_namespace
|
||||||
|
)
|
||||||
|
|
||||||
|
def _generate_query_params(self):
|
||||||
|
return urlencode(
|
||||||
|
{
|
||||||
|
"fullName.managementClusterName": self._management_cluster_name,
|
||||||
|
"fullName.provisionerName": self._provisioner_name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def _prepare_request(self, payload):
|
||||||
|
new_payload = {}
|
||||||
|
for key, value in payload.items():
|
||||||
|
new_payload[key] = to_native(b64encode(to_bytes(value)))
|
||||||
|
|
||||||
|
complete_payload = {
|
||||||
|
"secret": {
|
||||||
|
"fullName": {
|
||||||
|
"clusterName": self._cluster_name,
|
||||||
|
"managementClusterName": self._management_cluster_name,
|
||||||
|
"name": self._secret_name,
|
||||||
|
"namespaceName": self._cluster_namespace,
|
||||||
|
"provisionerName": self._provisioner_name,
|
||||||
|
},
|
||||||
|
"meta": {},
|
||||||
|
"status": {},
|
||||||
|
"spec": {"secretType": self._secret_type, "data": new_payload},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return complete_payload
|
||||||
|
|
||||||
|
def exists(self, api_host, access_token):
|
||||||
|
url = "{}/{}?{}".format(
|
||||||
|
self._generate_url(api_host),
|
||||||
|
self._secret_name,
|
||||||
|
self._generate_query_params(),
|
||||||
|
)
|
||||||
|
authorization_header = "Bearer {}".format(access_token)
|
||||||
|
try:
|
||||||
|
open_url(
|
||||||
|
url,
|
||||||
|
headers={"Authorization": authorization_header},
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except HTTPError as e:
|
||||||
|
if e.getcode() == 404:
|
||||||
|
return False
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def create(self, api_host, access_token, payload):
|
||||||
|
url = self._generate_url(api_host)
|
||||||
|
authorization_header = "Bearer {}".format(access_token)
|
||||||
|
new_payload = to_bytes(json.dumps(self._prepare_request(payload)))
|
||||||
|
all_headers = {"Authorization": authorization_header}
|
||||||
|
|
||||||
|
open_url(url, headers=all_headers, data=new_payload)
|
||||||
|
return True, "Secret {}/{} created".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def update(self, api_host, access_token, payload):
|
||||||
|
url = "{}/{}".format(self._generate_url(api_host), self._secret_name)
|
||||||
|
authorization_header = "Bearer {}".format(access_token)
|
||||||
|
new_payload = self._prepare_request(payload)
|
||||||
|
all_headers = {"Authorization": authorization_header}
|
||||||
|
|
||||||
|
current_spec = self._get_current_spec(api_host, access_token)
|
||||||
|
|
||||||
|
new_payload["secret"]["meta"] = current_spec["secret"]["meta"]
|
||||||
|
new_payload["secret"]["type"] = current_spec["secret"]["type"]
|
||||||
|
new_payload["secret"]["fullName"] = current_spec["secret"]["fullName"]
|
||||||
|
|
||||||
|
new_payload = to_bytes(json.dumps(new_payload))
|
||||||
|
|
||||||
|
open_url(url, method="PUT", headers=all_headers, data=new_payload)
|
||||||
|
return True, "Secret {}/{} updated".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self, api_host, access_token):
|
||||||
|
url = "{}/{}?{}".format(
|
||||||
|
self._generate_url(api_host),
|
||||||
|
self._secret_name,
|
||||||
|
self._generate_query_params(),
|
||||||
|
)
|
||||||
|
authorization_header = "Bearer {}".format(access_token)
|
||||||
|
|
||||||
|
open_url(
|
||||||
|
url,
|
||||||
|
method="DELETE",
|
||||||
|
headers={"Authorization": authorization_header},
|
||||||
|
)
|
||||||
|
return True, "Secret {}/{} deleted".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_exported(self, api_host, access_token):
|
||||||
|
url = "{}/{}?{}".format(
|
||||||
|
self._generate_export_url(api_host),
|
||||||
|
self._secret_name,
|
||||||
|
self._generate_query_params(),
|
||||||
|
)
|
||||||
|
authorization_header = {"Authorization": "Bearer {}".format(access_token)}
|
||||||
|
|
||||||
|
try:
|
||||||
|
open_url(
|
||||||
|
url,
|
||||||
|
headers=authorization_header,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except HTTPError as e:
|
||||||
|
if e.getcode() == 404:
|
||||||
|
return False
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def create_export(self, api_host, access_token):
|
||||||
|
url = self._generate_export_url(api_host)
|
||||||
|
authorization_header = {"Authorization": "Bearer {}".format(access_token)}
|
||||||
|
payload = {
|
||||||
|
"secretExport": {
|
||||||
|
"fullName": {
|
||||||
|
"clusterName": self._cluster_name,
|
||||||
|
"managementClusterName": self._management_cluster_name,
|
||||||
|
"provisionerName": self._provisioner_name,
|
||||||
|
"name": self._secret_name,
|
||||||
|
"namespaceName": self._cluster_namespace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = to_bytes(json.dumps(payload))
|
||||||
|
|
||||||
|
open_url(url, headers=authorization_header, data=payload)
|
||||||
|
return True, "SecretExport for {}/{} created".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_export(self, api_host, access_token):
|
||||||
|
url = "{}/{}?{}".format(
|
||||||
|
self._generate_export_url(api_host),
|
||||||
|
self._secret_name,
|
||||||
|
self._generate_query_params(),
|
||||||
|
)
|
||||||
|
authorization_header = {"Authorization": "Bearer {}".format(access_token)}
|
||||||
|
|
||||||
|
open_url(
|
||||||
|
url,
|
||||||
|
method="DELETE",
|
||||||
|
headers=authorization_header,
|
||||||
|
)
|
||||||
|
return True, "SecretExport for {}/{} deleted".format(
|
||||||
|
self._cluster_namespace, self._secret_name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def exchange_token(api_token):
|
||||||
|
exchange_url = "https://console.tanzu.broadcom.com/csp/gateway/am/api/auth/api-tokens/authorize"
|
||||||
|
headers = {"Content-type": "application/x-www-form-urlencoded"}
|
||||||
|
data = urlencode({"api_token": api_token})
|
||||||
|
|
||||||
|
exchange_result = open_url(
|
||||||
|
exchange_url,
|
||||||
|
data=data,
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
|
||||||
|
raw_data = json.loads(exchange_result.read())
|
||||||
|
return None if "access_token" not in raw_data else raw_data["access_token"]
|
||||||
|
|
||||||
|
|
||||||
|
def handle_logic(secret_status, wished_state):
|
||||||
|
if secret_status and wished_state == "present":
|
||||||
|
return "do_nothing"
|
||||||
|
|
||||||
|
if secret_status and wished_state == "update":
|
||||||
|
return "force_update"
|
||||||
|
|
||||||
|
if secret_status and wished_state == "absent":
|
||||||
|
return "delete"
|
||||||
|
|
||||||
|
if not secret_status and wished_state == "present" or wished_state == "update":
|
||||||
|
return "create"
|
||||||
|
|
||||||
|
if not secret_status and wished_state == "absent":
|
||||||
|
return "do_nothing"
|
||||||
|
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
|
||||||
|
def load_module():
|
||||||
|
argument_spec = dict(
|
||||||
|
api_host=dict(type="str", required=True),
|
||||||
|
api_token=dict(type="str", required=True, no_log=True),
|
||||||
|
management_cluster_name=dict(type="str"),
|
||||||
|
provisioner_name=dict(type="str"),
|
||||||
|
cluster_name=dict(type="str"),
|
||||||
|
cluster_namespace=dict(type="str", required=True),
|
||||||
|
cluster_group=dict(type="str"),
|
||||||
|
secret_name=dict(type="str", required=True),
|
||||||
|
registry_host=dict(type="str"),
|
||||||
|
registry_username=dict(type="str"),
|
||||||
|
registry_password=dict(type="str", no_log=True),
|
||||||
|
secret_type=dict(
|
||||||
|
type="str",
|
||||||
|
choices=["opaque", "docker_config"],
|
||||||
|
default="opaque",
|
||||||
|
),
|
||||||
|
data=dict(type="dict", default={}),
|
||||||
|
export=dict(type="bool", default=False),
|
||||||
|
state=dict(
|
||||||
|
type="str", default="present", choices=["present", "absent", "update"]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return AnsibleModule(
|
||||||
|
argument_spec=argument_spec,
|
||||||
|
supports_check_mode=True,
|
||||||
|
mutually_exclusive=[
|
||||||
|
["cluster_group", "cluster_name"],
|
||||||
|
["cluster_group", "management_cluster_name"],
|
||||||
|
["cluster_group", "provisioner_name"],
|
||||||
|
["data", "registry_host"],
|
||||||
|
["data", "registry_username"],
|
||||||
|
["data", "registry_password"],
|
||||||
|
],
|
||||||
|
required_one_of=[
|
||||||
|
["cluster_name", "cluster_group"],
|
||||||
|
["data", "registry_host"],
|
||||||
|
["data", "registry_username"],
|
||||||
|
["data", "registry_password"],
|
||||||
|
],
|
||||||
|
required_together=[
|
||||||
|
["cluster_name", "management_cluster_name", "provisioner_name"],
|
||||||
|
["registry_host", "registry_username", "registry_password"],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_dockerconfig(registry_host, registry_username, registry_password):
|
||||||
|
payload = {
|
||||||
|
"auths": {
|
||||||
|
registry_host: {
|
||||||
|
"username": registry_username,
|
||||||
|
"password": registry_password,
|
||||||
|
"auth": to_native(
|
||||||
|
b64encode(
|
||||||
|
to_bytes("{}:{}".format(registry_username, registry_password))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {".dockerconfigjson": json.dumps(payload)}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = load_module()
|
||||||
|
|
||||||
|
state = module.params["state"]
|
||||||
|
api_host = module.params["api_host"]
|
||||||
|
api_token = module.params["api_token"]
|
||||||
|
cluster_name = module.params["cluster_name"]
|
||||||
|
cluster_namespace = module.params["cluster_namespace"]
|
||||||
|
management_cluster_name = module.params["management_cluster_name"]
|
||||||
|
provisioner_name = module.params["provisioner_name"]
|
||||||
|
secret_name = module.params["secret_name"]
|
||||||
|
data = module.params["data"]
|
||||||
|
cluster_group = module.params["cluster_group"]
|
||||||
|
secret_type = module.params["secret_type"]
|
||||||
|
export = module.params["export"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
access_token = exchange_token(api_token)
|
||||||
|
if access_token is None:
|
||||||
|
return module.fail_json(msg="Failed to exchange token, check credentials")
|
||||||
|
except HTTPError as e:
|
||||||
|
return module.fail_json(msg=to_native(e.read()))
|
||||||
|
|
||||||
|
tmc_secret = None
|
||||||
|
|
||||||
|
if cluster_name is not None:
|
||||||
|
tmc_secret = TMCClusterSecret(
|
||||||
|
management_cluster_name,
|
||||||
|
provisioner_name,
|
||||||
|
cluster_name,
|
||||||
|
cluster_namespace,
|
||||||
|
secret_name,
|
||||||
|
secret_type,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
tmc_secret = TMCClusterGroupSecret(
|
||||||
|
cluster_group, cluster_namespace, secret_name, secret_type
|
||||||
|
)
|
||||||
|
|
||||||
|
if secret_type == "SECRET_TYPE_OPAQUE":
|
||||||
|
payload = data
|
||||||
|
else:
|
||||||
|
payload = create_dockerconfig(
|
||||||
|
module.params["registry_host"],
|
||||||
|
module.params["registry_username"],
|
||||||
|
module.params["registry_password"],
|
||||||
|
)
|
||||||
|
|
||||||
|
secret_status = tmc_secret.exists(api_host, access_token)
|
||||||
|
action = handle_logic(secret_status, state)
|
||||||
|
created, create_msg = False, ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
if action == "do_nothing":
|
||||||
|
return module.exit_json(changed=False)
|
||||||
|
|
||||||
|
if action == "delete":
|
||||||
|
changed, msg = tmc_secret.delete(api_host, access_token)
|
||||||
|
return module.exit_json(changed=changed, msg=msg)
|
||||||
|
|
||||||
|
if action == "force_update":
|
||||||
|
created, create_msg = tmc_secret.update(api_host, access_token, payload)
|
||||||
|
elif action == "create":
|
||||||
|
created, create_msg = tmc_secret.create(api_host, access_token, payload)
|
||||||
|
else:
|
||||||
|
return module.fail_json(msg="Unknown action {}".format(action))
|
||||||
|
|
||||||
|
exported, export_msg = False, ""
|
||||||
|
|
||||||
|
if export and not tmc_secret.is_exported(api_host, access_token):
|
||||||
|
exported, export_msg = tmc_secret.create_export(api_host, access_token)
|
||||||
|
|
||||||
|
if not export and tmc_secret.is_exported(api_host, access_token):
|
||||||
|
exported, export_msg = tmc_secret.delete_export(api_host, access_token)
|
||||||
|
|
||||||
|
final_changed = created or exported
|
||||||
|
final_msg = (
|
||||||
|
create_msg
|
||||||
|
if export_msg == ""
|
||||||
|
else "{} and {}".format(create_msg, export_msg)
|
||||||
|
)
|
||||||
|
return module.exit_json(changed=final_changed, msg=final_msg)
|
||||||
|
except HTTPError as e:
|
||||||
|
return module.fail_json(msg=to_native(e.read()))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Copyright (c) Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
unsupported
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
####################################################################
|
||||||
|
# WARNING: These are designed specifically for Ansible tests #
|
||||||
|
# and should not be used as examples of how to write Ansible roles #
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
# Copyright (c) 2025, Massimo Gengarelli <massimo.gengarelli@proton.me>
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
api_host:
|
||||||
|
api_token:
|
||||||
|
management_cluster_name:
|
||||||
|
provisioner_name:
|
||||||
|
cluster_name:
|
||||||
|
cluster_namespace:
|
||||||
|
cluster_group:
|
||||||
|
secret_name:
|
||||||
|
secret_type:
|
||||||
|
state:
|
|
@ -0,0 +1,276 @@
|
||||||
|
####################################################################
|
||||||
|
# WARNING: These are designed specifically for Ansible tests #
|
||||||
|
# and should not be used as examples of how to write Ansible roles #
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
# Copyright (c) 2025, Massimo Gengarelli <massimo.gengarelli@proton.me>
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- name: Cluster Secret | Create a new secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data:
|
||||||
|
key1: value1
|
||||||
|
key2: value2
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Cluster Secret | Create and export a new secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: exported-secret
|
||||||
|
data:
|
||||||
|
key1: value1
|
||||||
|
key2: value2
|
||||||
|
export: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Cluster Secret | Update and un-export a secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: exported-secret
|
||||||
|
data:
|
||||||
|
key1: value1
|
||||||
|
key2: value2
|
||||||
|
export: false
|
||||||
|
state: update
|
||||||
|
|
||||||
|
- name: Cluster Secret | Do not update an already existing secret (state=present)
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data: {}
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Cluster Secret | Update a secret (state=update)
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data:
|
||||||
|
key3: value3
|
||||||
|
key4: value4
|
||||||
|
state: update
|
||||||
|
export: true
|
||||||
|
|
||||||
|
- name: Cluster Secret | Create a new secret (using state=update)
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: very-new-secret
|
||||||
|
export: true
|
||||||
|
data:
|
||||||
|
key1: hello world
|
||||||
|
key2: |
|
||||||
|
multiline string with
|
||||||
|
newlines respected.
|
||||||
|
state: update
|
||||||
|
|
||||||
|
- name: Cluster Secret | Delete the exported secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: exported-secret
|
||||||
|
data: {}
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Cluster Secret | Delete the created secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: very-new-secret
|
||||||
|
data: {}
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Cluster Secret | Delete the first secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data: {}
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Cluster Secret | Deleting a non existing secret should yield no result
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data: {}
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: ClusterGroup Secret | Create a new secret (state=present)
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_group: "{{ cluster_group }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data:
|
||||||
|
key1: value1
|
||||||
|
key2: value2
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: ClusterGroup Secret | Avoid updating the secret (state=present)
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_group: "{{ cluster_group }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data: {}
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: ClusterGroup Secret | Create new secret (state=update)
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_group: "{{ cluster_group }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
secret_name: very-new-secret
|
||||||
|
data:
|
||||||
|
key1: hello world
|
||||||
|
key2: |
|
||||||
|
multiline string with
|
||||||
|
newlines respected.
|
||||||
|
state: update
|
||||||
|
|
||||||
|
- name: ClusterGroup Secret | Create and export a new secret (export=true)
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_group: "{{ cluster_group }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
secret_name: exported-secret
|
||||||
|
data:
|
||||||
|
key1: hello world
|
||||||
|
key2: |
|
||||||
|
multiline string with
|
||||||
|
newlines respected.
|
||||||
|
export: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: ClusterGroup Secret | Update an existing secret (state=update, export=true)
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_group: "{{ cluster_group }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data:
|
||||||
|
key3: value3
|
||||||
|
key4: value4
|
||||||
|
export: true
|
||||||
|
state: update
|
||||||
|
|
||||||
|
- name: ClusterGroup Secret | Delete a secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_group: "{{ cluster_group }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
secret_name: "{{ item }}"
|
||||||
|
data: {}
|
||||||
|
secret_type: opaque
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- "{{ secret_name }}"
|
||||||
|
- very-new-secret
|
||||||
|
- non-existant
|
||||||
|
- exported-secret
|
||||||
|
|
||||||
|
- name: Cluster Secret | Create a Docker secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
secret_type: docker_config
|
||||||
|
registry_host: docker.io
|
||||||
|
registry_username: some_username
|
||||||
|
registry_password: some_password
|
||||||
|
export: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Cluster Group Secret | Create a Docker secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_group: "{{ cluster_group }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
secret_type: docker_config
|
||||||
|
registry_host: docker.io
|
||||||
|
registry_username: some_username
|
||||||
|
registry_password: some_password
|
||||||
|
export: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Cluster Group Secret | Delete the Docker secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_group: "{{ cluster_group }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data: {}
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Cluster Secret | Delete the Docker secret
|
||||||
|
community.general.tanzu_mission_control_secret:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_namespace: "{{ cluster_namespace }}"
|
||||||
|
management_cluster_name: "{{ management_cluster_name }}"
|
||||||
|
provisioner_name: "{{ provisioner_name }}"
|
||||||
|
secret_name: "{{ secret_name }}"
|
||||||
|
data: {}
|
||||||
|
state: absent
|
Loading…
Add table
Add a link
Reference in a new issue