mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-03-27 05:43:02 +00:00
minor(doc): use the same style of version_added across repo SUMMARY Currently is no single style of version_added, in some places it's unquoted, somewhere single quote is used, in another places it's double quoted. Moreover, some file had different styles in one single file. The aim of this PR is to update whole repo to single style for version_added ISSUE TYPE Docs Pull Request COMPONENT NAME kustomize helm helm_info helm_plugin helm_plugin_info helm_pull helm_repository helm_template k8s_cluster_info k8s_cp k8s_drain k8s_exec k8s_log k8s_rollback k8s_taint ADDITIONAL INFORMATION The same style is proposed as used in amazon.aws collections Reviewed-by: Kelv Gooding Reviewed-by: Alina Buzachis Reviewed-by: Mike Graves <mgraves@redhat.com>
277 lines
7.7 KiB
Python
277 lines
7.7 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright: (c) 2020, Julien Huon <@julienhuon> Institut National de l'Audiovisuel
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
DOCUMENTATION = r"""
|
|
module: k8s_rollback
|
|
short_description: Rollback Kubernetes (K8S) Deployments and DaemonSets
|
|
version_added: 1.0.0
|
|
author:
|
|
- "Julien Huon (@julienhuon)"
|
|
description:
|
|
- Use the Kubernetes Python client to perform the Rollback.
|
|
- Authenticate using either a config file, certificates, password or token.
|
|
- Similar to the C(kubectl rollout undo) command.
|
|
options:
|
|
label_selectors:
|
|
description: List of label selectors to use to filter results.
|
|
type: list
|
|
elements: str
|
|
default: []
|
|
field_selectors:
|
|
description: List of field selectors to use to filter results.
|
|
type: list
|
|
elements: str
|
|
default: []
|
|
extends_documentation_fragment:
|
|
- kubernetes.core.k8s_auth_options
|
|
- kubernetes.core.k8s_name_options
|
|
requirements:
|
|
- "python >= 3.9"
|
|
- "kubernetes >= 24.2.0"
|
|
- "PyYAML >= 3.11"
|
|
"""
|
|
|
|
EXAMPLES = r"""
|
|
- name: Rollback a failed deployment
|
|
kubernetes.core.k8s_rollback:
|
|
api_version: apps/v1
|
|
kind: Deployment
|
|
name: web
|
|
namespace: testing
|
|
"""
|
|
|
|
RETURN = r"""
|
|
rollback_info:
|
|
description:
|
|
- The object that was rolled back.
|
|
returned: success
|
|
type: complex
|
|
contains:
|
|
api_version:
|
|
description: The versioned schema of this representation of an object.
|
|
returned: success
|
|
type: str
|
|
code:
|
|
description: The HTTP Code of the response
|
|
returned: success
|
|
type: str
|
|
kind:
|
|
description: Status
|
|
returned: success
|
|
type: str
|
|
metadata:
|
|
description:
|
|
- Standard object metadata.
|
|
- Includes name, namespace, annotations, labels, etc.
|
|
returned: success
|
|
type: dict
|
|
status:
|
|
description: Current status details for the object.
|
|
returned: success
|
|
type: dict
|
|
"""
|
|
|
|
import copy
|
|
|
|
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
|
|
AnsibleModule,
|
|
)
|
|
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
|
|
AUTH_ARG_SPEC,
|
|
NAME_ARG_SPEC,
|
|
)
|
|
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.exceptions import (
|
|
CoreException,
|
|
)
|
|
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
|
|
K8sService,
|
|
)
|
|
|
|
|
|
def get_managed_resource(kind):
|
|
managed_resource = {}
|
|
|
|
if kind == "DaemonSet":
|
|
managed_resource["kind"] = "ControllerRevision"
|
|
managed_resource["api_version"] = "apps/v1"
|
|
elif kind == "Deployment":
|
|
managed_resource["kind"] = "ReplicaSet"
|
|
managed_resource["api_version"] = "apps/v1"
|
|
else:
|
|
raise CoreException(
|
|
"Cannot perform rollback on resource of kind {0}".format(kind)
|
|
)
|
|
return managed_resource
|
|
|
|
|
|
def execute_module(svc):
|
|
results = []
|
|
module = svc.module
|
|
|
|
resources = svc.find(
|
|
module.params["kind"],
|
|
module.params["api_version"],
|
|
module.params["name"],
|
|
module.params["namespace"],
|
|
module.params["label_selectors"],
|
|
module.params["field_selectors"],
|
|
)
|
|
|
|
changed = False
|
|
for resource in resources["resources"]:
|
|
result = perform_action(svc, resource)
|
|
changed = result["changed"] or changed
|
|
results.append(result)
|
|
|
|
module.exit_json(**{"changed": changed, "rollback_info": results})
|
|
|
|
|
|
def perform_action(svc, resource):
|
|
module = svc.module
|
|
|
|
if module.params["kind"] == "DaemonSet":
|
|
current_revision = resource["metadata"]["generation"]
|
|
elif module.params["kind"] == "Deployment":
|
|
current_revision = resource["metadata"]["annotations"][
|
|
"deployment.kubernetes.io/revision"
|
|
]
|
|
|
|
managed_resource = get_managed_resource(module.params["kind"])
|
|
managed_resources = svc.find(
|
|
managed_resource["kind"],
|
|
managed_resource["api_version"],
|
|
"",
|
|
module.params["namespace"],
|
|
resource["spec"]["selector"]["matchLabels"],
|
|
"",
|
|
)
|
|
|
|
prev_managed_resource = get_previous_revision(
|
|
managed_resources["resources"], current_revision
|
|
)
|
|
if not prev_managed_resource:
|
|
warn = "No rollout history found for resource %s/%s" % (
|
|
module.params["kind"],
|
|
resource["metadata"]["name"],
|
|
)
|
|
result = {"changed": False, "warnings": [warn]}
|
|
return result
|
|
|
|
if module.params["kind"] == "Deployment":
|
|
del prev_managed_resource["spec"]["template"]["metadata"]["labels"][
|
|
"pod-template-hash"
|
|
]
|
|
|
|
resource_patch = [
|
|
{
|
|
"op": "replace",
|
|
"path": "/spec/template",
|
|
"value": prev_managed_resource["spec"]["template"],
|
|
},
|
|
{
|
|
"op": "replace",
|
|
"path": "/metadata/annotations",
|
|
"value": {
|
|
"deployment.kubernetes.io/revision": prev_managed_resource[
|
|
"metadata"
|
|
]["annotations"]["deployment.kubernetes.io/revision"]
|
|
},
|
|
},
|
|
]
|
|
|
|
api_target = "deployments"
|
|
content_type = "application/json-patch+json"
|
|
elif module.params["kind"] == "DaemonSet":
|
|
resource_patch = prev_managed_resource["data"]
|
|
|
|
api_target = "daemonsets"
|
|
content_type = "application/strategic-merge-patch+json"
|
|
|
|
rollback = resource
|
|
if not module.check_mode:
|
|
rollback = svc.client.client.request(
|
|
"PATCH",
|
|
"/apis/{0}/namespaces/{1}/{2}/{3}".format(
|
|
module.params["api_version"],
|
|
module.params["namespace"],
|
|
api_target,
|
|
module.params["name"],
|
|
),
|
|
body=resource_patch,
|
|
content_type=content_type,
|
|
).to_dict()
|
|
|
|
result = {"changed": True}
|
|
result["method"] = "patch"
|
|
result["body"] = resource_patch
|
|
result["resources"] = rollback
|
|
return result
|
|
|
|
|
|
def argspec():
|
|
args = copy.deepcopy(AUTH_ARG_SPEC)
|
|
args.update(NAME_ARG_SPEC)
|
|
args.update(
|
|
dict(
|
|
label_selectors=dict(type="list", elements="str", default=[]),
|
|
field_selectors=dict(type="list", elements="str", default=[]),
|
|
)
|
|
)
|
|
return args
|
|
|
|
|
|
def get_previous_revision(all_resources, current_revision):
|
|
for resource in all_resources:
|
|
if resource["kind"] == "ReplicaSet":
|
|
if (
|
|
int(
|
|
resource["metadata"]["annotations"][
|
|
"deployment.kubernetes.io/revision"
|
|
]
|
|
)
|
|
== int(current_revision) - 1
|
|
):
|
|
return resource
|
|
elif resource["kind"] == "ControllerRevision":
|
|
if (
|
|
int(
|
|
resource["metadata"]["annotations"][
|
|
"deprecated.daemonset.template.generation"
|
|
]
|
|
)
|
|
== int(current_revision) - 1
|
|
):
|
|
return resource
|
|
return None
|
|
|
|
|
|
def main():
|
|
module = AnsibleK8SModule(
|
|
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
|
|
)
|
|
|
|
try:
|
|
client = get_api_client(module=module)
|
|
svc = K8sService(client, module)
|
|
execute_module(svc)
|
|
except CoreException as e:
|
|
module.fail_from_exception(e)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|