-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Retrieve application and pod statuses #1332
Changes from 11 commits
a615134
2573031
568a593
3af93a4
7d0e88e
7019610
6eb2a30
fbb8041
2401905
89a5efd
df8a6d9
ffd6500
9ed594c
76dfbca
414220a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import subprocess | ||
import sys | ||
import json | ||
from crmetrics import CRBase | ||
|
||
''' | ||
new plugin app-status -- takes in kind and instance and displays | ||
kind, namespace, name and status and lists pods in that application | ||
with their status | ||
|
||
application name is the namespace and pods will be under this namespace | ||
for application status itself, name would just be the helm release | ||
|
||
Input: name of kind and name of application instance | ||
|
||
TODO: reexamine all error checks for plugins | ||
''' | ||
|
||
|
||
class AppStatusFinder(CRBase): | ||
|
||
def get_app_instance_status(self, kind, instance, kubeconfig): | ||
cmd = 'kubectl get %s %s -o json %s' % (kind, instance, kubeconfig) | ||
out, err = self._run_command(cmd) | ||
if err != "": | ||
print("Something went wrong while getting app instance status.") | ||
print(err) | ||
exit(1) | ||
''' | ||
with response, check if status exists | ||
check if helmrelease exists and extract name of instance | ||
or check if status contains an error | ||
otherwise (i.e. status missing), display "App not deployed properly" | ||
''' | ||
# response = json.dumps(json.loads(out), indent=4) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete. |
||
response = json.loads(out) | ||
if 'status' in response: | ||
if 'helmrelease' in response['status']: | ||
helm_release = response['status']['helmrelease'].strip('\n') | ||
ns, name = helm_release.split(':') | ||
return name, ns, True, None | ||
else: | ||
# an error has occurred | ||
status = response['status'] | ||
return status, None, False, None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest defining variables for the second and third return parameters and setting their values after lines 21 and 25. That way, by looking at the return statement it will be clear what parameters are being returned. |
||
|
||
else: | ||
return '', 'Application not deployed properly', False, None | ||
|
||
|
||
|
||
|
||
def get_app_pods(self, namespace, kubeconfig): | ||
cmd = 'kubectl get pods -n %s %s -o json' % (namespace, kubeconfig) | ||
# pods = self._get_resources(None, 'pods', namespace, kubeconfig) | ||
out, err = self._run_command(cmd) | ||
# format? | ||
response = json.loads(out) | ||
pods = [] | ||
for pod in response['items']: | ||
name = pod['metadata']['name'] | ||
typ = pod['kind'] | ||
ns = pod['metadata']['namespace'] | ||
phase = pod['status']['phase'] | ||
pods.append((name, typ, ns, phase)) | ||
return pods | ||
|
||
|
||
if __name__ == '__main__': | ||
appStatusFinder = AppStatusFinder() | ||
kind = sys.argv[1] | ||
instance = sys.argv[2] | ||
kubeconfig = sys.argv[3] | ||
|
||
valid_consumer_api = appStatusFinder.verify_kind_is_consumerapi(kind, kubeconfig) | ||
if not valid_consumer_api: | ||
print(("{} is not a valid Consumer API.").format(kind)) | ||
exit(0) | ||
|
||
res_exists, ns, err = appStatusFinder.check_res_exists(kind, instance, kubeconfig) | ||
if not res_exists: | ||
print(err) | ||
exit(0) | ||
|
||
working, error = appStatusFinder.validate_kind_and_instance(kind, instance, ns) | ||
if working == False: | ||
print(err) | ||
exit(1) | ||
|
||
release_name_or_status, release_ns, deployed, err = appStatusFinder.get_app_instance_status(kind, instance, kubeconfig) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't need the last parameter since it is always returned as None. |
||
if err is not None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't need this check. The error check is happening in lines 12-15. |
||
print(err) | ||
exit(1) | ||
|
||
if deployed: | ||
deploy_str = 'Deployed' | ||
else: | ||
print(release_name_or_status) | ||
exit(1) | ||
|
||
# if not deployed, there's probably an error -- maybe display a different response in this case | ||
pods = appStatusFinder.get_app_pods(instance, kubeconfig) | ||
|
||
print("{:<35} {:<35} {:<35} {:<35}".format("NAME", "TYPE", "NAMESPACE", "STATUS")) | ||
print("{:<35} {:<35} {:<35} {:<35}".format(release_name_or_status, 'helmrelease', release_ns, deploy_str)) | ||
for pod_name, typ, pod_ns, phase in pods: | ||
print("{:<35} {:<35} {:<35} {:<35}".format(pod_name, typ, pod_ns, phase)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,10 +6,15 @@ | |
import platform | ||
import pprint | ||
import time | ||
|
||
import yaml | ||
import utils | ||
|
||
class CRBase(object): | ||
def _run_command(self, cmd): | ||
cmdOut = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate() | ||
out = cmdOut[0].decode('utf-8') | ||
err = cmdOut[1].decode('utf-8') | ||
return out, err | ||
|
||
def parse_pod_details(self, out, instance): | ||
pod_list = [] | ||
|
@@ -114,6 +119,64 @@ def get_resources_connections(self, kind, instance, namespace, kubeconfig): | |
print(e) | ||
return json_output | ||
|
||
def _get_resources(self, kind, plural, targetNS, kubeconfig): | ||
cmd = "kubectl get " + plural + " -n " + targetNS + " " + kubeconfig | ||
out, err = self._run_command(cmd) | ||
resources = [] | ||
for line in out.split("\n"): | ||
res_details = {} | ||
line = line.strip() | ||
if 'NAME' not in line and line != '' and line != '\n': | ||
line1 = ' '.join(line.split()) | ||
parts = line1.split(" ") | ||
res_name = parts[0].strip() | ||
res_details['name'] = res_name | ||
res_details['namespace'] = targetNS | ||
res_details['kind'] = kind | ||
resources.append(res_details) | ||
return resources | ||
|
||
def check_res_exists(self, kind, instance, kubeconfig): | ||
cmd = 'kubectl get ' + kind + ' -A ' + kubeconfig | ||
out, err = self._run_command(cmd) | ||
for line in out.split("\n"): | ||
if instance in line: | ||
parts = line.split(" ") | ||
ns = parts[0].strip() | ||
return True, ns, '' | ||
return False, '', kind + ' ' + instance + ' not found.' | ||
|
||
def verify_kind_is_consumerapi(self, kind, kubeconfig): | ||
|
||
if kind.lower() in 'resourcecompositions': | ||
return False | ||
|
||
cmd = 'kubectl get crds ' + kubeconfig | ||
out, err = self._run_command(cmd) | ||
for line in out.split("\n"): | ||
parts = line.split(" ") | ||
fqn = parts[0].strip() | ||
parts1 = fqn.split(".") | ||
plural = parts1[0] | ||
singular = plural[0:len(plural) - 1] | ||
if kind.lower() == singular: | ||
return True | ||
return False | ||
|
||
''' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this TODO? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I'm removing it now -- the method that that TODO describes was written right below it. |
||
TODO: add method that accepts kind name (HelloWorldService), instance name (hs1), and namespace | ||
(KUBEPLUS_NS) | ||
it will run `kubectl get kind_name instance_name -n namespace` and if this errors out, no further check | ||
is needed and the user must be informed | ||
otherwise, proceed as normal | ||
''' | ||
def validate_kind_and_instance(self, kind, instance, namespace): | ||
cmd = 'kubectl get %s %s -n %s' % (kind, instance, namespace) | ||
_, err = self._run_command(cmd) | ||
if err == '': # or None? | ||
return True, None | ||
return False, err | ||
|
||
|
||
class CRMetrics(CRBase): | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#!/bin/bash | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have made this file executable, right? (chmod +x kubectl-appstatus) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. |
||
|
||
source utils.sh | ||
|
||
print_help () { | ||
echo "NAME" | ||
echo " kubectl appstatus" | ||
echo "" | ||
echo "SYNOPSIS" | ||
echo " kubectl appstatus <Kind> <Instance> -k <Absolute path to kubeconfig>" | ||
echo "" | ||
echo "DESCRIPTION" | ||
echo " kubectl appstatus shows the status of the application instance and its pods" | ||
exit 0 | ||
} | ||
|
||
if (( $# < 2 )); then | ||
print_help | ||
fi | ||
|
||
kind=$1 | ||
instance=$2 | ||
kubeconfig="" | ||
|
||
shift; | ||
shift; | ||
|
||
while getopts ":k:" opt; do | ||
case ${opt} in | ||
k ) | ||
kubeconfig1=$OPTARG | ||
if [ ! -f $kubeconfig1 ]; then | ||
echo "Kubeconfig $kubeconfig1 does not exist." | ||
exit 0 | ||
fi;; | ||
? ) | ||
echo "Invalid option: ${1} " 1>&2 | ||
print_help | ||
exit 0 | ||
;; | ||
esac | ||
done | ||
|
||
kubeconfig="--kubeconfig="$kubeconfig1 | ||
|
||
canonicalKind=$(get_canonical_kind $kind) | ||
|
||
if [[ $canonicalKind == *"Unknown"* ]]; then | ||
echo "$canonicalKind" | ||
exit 0 | ||
fi | ||
|
||
python3 /$KUBEPLUS_HOME/plugins/appstatus.py $canonicalKind $instance $kubeconfig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Get rid of TODO