Files
kubernetes.core/plugins/modules/k8s_exec.py
Mike Graves 68d45af767 Upgrade black version (#424) (#446)
[backport/2.3] Upgrade black version (#424)

Depends-On: ansible/ansible-zuul-jobs#1515
Upgrade black version
SUMMARY
Move off of beta version of black and pin to current calendar year
version.
The only manual changes here are to tox.ini. Everything else is from running the new version of black.
ISSUE TYPE
COMPONENT NAME
ADDITIONAL INFORMATION
Reviewed-by: Abhijeet Kasurde 
(cherry picked from commit 7c71436)
2022-04-29 17:47:08 +00:00

246 lines
6.6 KiB
Python

#!/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
DOCUMENTATION = r"""
module: k8s_exec
short_description: Execute command in Pod
version_added: "0.10.0"
author: "Tristan de Cacqueray (@tristanC)"
description:
- Use the Kubernetes Python client to execute command on K8s pods.
extends_documentation_fragment:
- kubernetes.core.k8s_auth_options
requirements:
- "python >= 3.6"
- "kubernetes >= 12.0.0"
- "PyYAML >= 3.11"
notes:
- Return code C(rc) for the command executed is added in output in version 2.2.0, and deprecates return code C(return_code).
- Return code C(return_code) for the command executed is added in output in version 1.0.0.
- The authenticated user must have at least read access to the pods resource and write access to the pods/exec resource.
options:
proxy:
description:
- The URL of an HTTP proxy to use for the connection.
- Can also be specified via I(K8S_AUTH_PROXY) environment variable.
- Please note that this module does not pick up typical proxy settings from the environment (for example, HTTP_PROXY).
type: str
namespace:
description:
- The pod namespace name.
type: str
required: yes
pod:
description:
- The pod name.
type: str
required: yes
container:
description:
- The name of the container in the pod to connect to.
- Defaults to only container if there is only one container in the pod.
- If not specified, will choose the first container from the given pod as kubectl cmdline does.
type: str
required: no
command:
description:
- The command to execute.
type: str
required: yes
"""
EXAMPLES = r"""
- name: Execute a command
kubernetes.core.k8s_exec:
namespace: myproject
pod: zuul-scheduler
command: zuul-scheduler full-reconfigure
- name: Check RC status of command executed
kubernetes.core.k8s_exec:
namespace: myproject
pod: busybox-test
command: cmd_with_non_zero_exit_code
register: command_status
ignore_errors: True
- name: Check last command status
debug:
msg: "cmd failed"
when: command_status.rc != 0
- name: Specify a container name to execute the command on
kubernetes.core.k8s_exec:
namespace: myproject
pod: busybox-test
container: manager
command: echo "hello"
"""
RETURN = r"""
result:
description:
- The command object
returned: success
type: complex
contains:
stdout:
description: The command stdout
type: str
stdout_lines:
description: The command stdout
type: str
stderr:
description: The command stderr
type: str
stderr_lines:
description: The command stderr
type: str
rc:
description: The command status code
type: int
version_added: 2.2.0
return_code:
description: The command status code. This attribute is deprecated and will be removed in a future release. Please use rc instead.
type: int
"""
import copy
import shlex
try:
import yaml
except ImportError:
# ImportError are managed by the common module already.
pass
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule,
)
from ansible.module_utils._text import to_native
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
AUTH_ARG_SPEC,
)
try:
from kubernetes.client.apis import core_v1_api
from kubernetes.stream import stream
from kubernetes.client.exceptions import ApiException
except ImportError:
# ImportError are managed by the common module already.
pass
def argspec():
spec = copy.deepcopy(AUTH_ARG_SPEC)
spec["namespace"] = dict(type="str", required=True)
spec["pod"] = dict(type="str", required=True)
spec["container"] = dict(type="str")
spec["command"] = dict(type="str", required=True)
return spec
def execute_module(module, k8s_ansible_mixin):
# Load kubernetes.client.Configuration
api = core_v1_api.CoreV1Api(k8s_ansible_mixin.client.client)
# hack because passing the container as None breaks things
optional_kwargs = {}
if module.params.get("container"):
optional_kwargs["container"] = module.params["container"]
else:
# default to the first container available on pod
resp = None
try:
resp = api.read_namespaced_pod(
name=module.params["pod"], namespace=module.params["namespace"]
)
except ApiException:
pass
if resp and len(resp.spec.containers) >= 1:
optional_kwargs["container"] = resp.spec.containers[0].name
try:
resp = stream(
api.connect_get_namespaced_pod_exec,
module.params["pod"],
module.params["namespace"],
command=shlex.split(module.params["command"]),
stdout=True,
stderr=True,
stdin=False,
tty=False,
_preload_content=False,
**optional_kwargs
)
except Exception as e:
module.fail_json(
msg="Failed to execute on pod %s"
" due to : %s" % (module.params.get("pod"), to_native(e))
)
stdout, stderr, rc = [], [], 0
while resp.is_open():
resp.update(timeout=1)
if resp.peek_stdout():
stdout.append(resp.read_stdout())
if resp.peek_stderr():
stderr.append(resp.read_stderr())
err = resp.read_channel(3)
err = yaml.safe_load(err)
if err["status"] == "Success":
rc = 0
else:
rc = int(err["details"]["causes"][0]["message"])
module.deprecate(
"The 'return_code' return key is deprecated. Please use 'rc' instead.",
version="4.0.0",
collection_name="kubernetes.core",
)
module.exit_json(
# Some command might change environment, but ultimately failing at end
changed=True,
stdout="".join(stdout),
stderr="".join(stderr),
rc=rc,
return_code=rc,
)
def main():
module = AnsibleModule(
argument_spec=argspec(),
supports_check_mode=True,
)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
)
k8s_ansible_mixin = K8sAnsibleMixin(module)
k8s_ansible_mixin.client = get_api_client(module=module)
execute_module(module, k8s_ansible_mixin)
if __name__ == "__main__":
main()