Skip to content
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

Health check #37

Closed
wants to merge 11 commits into from
87 changes: 10 additions & 77 deletions src/KubeLibrary/KubeLibrary.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import ssl
import urllib3
from kubernetes import client, config
import requests
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requests is not needed, see the comment below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request module removed.


from robot.api import logger

# supressing SSL warnings when using self-signed certs
Expand All @@ -11,27 +13,19 @@

class KubeLibrary(object):
"""KubeLibrary is a Robot Framework test library for Kubernetes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please leave those empty lines as they improve readability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

The approach taken by this library is to provide easy to access kubernetes objects representation that can
be then accessed to define highlevel keywords for tests.

= Kubeconfigs =

By default ~/.kube/config is used. Kubeconfig location
can also be passed by setting KUBECONFIG environment variable or as Library argument.

| ***** Settings *****
| Library KubeLibrary /path/to/kubeconfig

= In cluster execution =

If tests are supposed to be executed from within cluster, KubeLibrary can be configured to use standard
token authentication. Just set incluster parameter to True. If True then kubeconfigs are not used,
even if provided.

| ***** Settings *****
| Library KubeLibrary None True

"""
def __init__(self, kube_config=None, incluster=False, cert_validation=True):
"""KubeLibrary can be configured with several optional arguments.
Expand Down Expand Up @@ -73,7 +67,6 @@ def reload_config(self, kube_config=None, incluster=False, cert_validation=True)

def k8s_api_ping(self):
"""Performs GET on /api/v1/ for simple check of API availability.

Returns tuple of (response data, response status, response headers). Can be used as prerequisite in tests.
"""
path_params = {}
Expand All @@ -92,19 +85,15 @@ def k8s_api_ping(self):

def get_namespaces(self, label_selector=""):
"""Gets a list of available namespaces.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of namespaces.
"""
ret = self.v1.list_namespace(watch=False, label_selector=label_selector)
return [item.metadata.name for item in ret.items]

def get_healthy_nodes_count(self, label_selector=""):
"""Counts node with KubeletReady and status True.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Can be used to check number of healthy nodes. Can be used as prerequisite in tests.
"""
ret = self.v1.list_node(watch=False, label_selector=label_selector)
Expand All @@ -117,11 +106,8 @@ def get_healthy_nodes_count(self, label_selector=""):

def get_pod_names_in_namespace(self, name_pattern, namespace, label_selector=""):
"""Gets pod name matching pattern in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of strings.

- ``name_pattern``:
Pod name pattern to check
- ``namespace``:
Expand All @@ -133,11 +119,8 @@ def get_pod_names_in_namespace(self, name_pattern, namespace, label_selector="")

def get_pods_in_namespace(self, name_pattern, namespace, label_selector=""):
"""Gets pods matching pattern in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of pods.

- ``name_pattern``:
Pod name pattern to check
- ``namespace``:
Expand All @@ -150,9 +133,7 @@ def get_pods_in_namespace(self, name_pattern, namespace, label_selector=""):

def get_pod_logs(self, name, namespace, container):
"""Gets container logs of given pod in given namespace.

Returns logs.

- ``name``:
Pod name to check
- ``namespace``:
Expand All @@ -165,11 +146,8 @@ def get_pod_logs(self, name, namespace, container):

def get_configmaps_in_namespace(self, name_pattern, namespace, label_selector=""):
"""Gets configmaps matching pattern in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of configmaps.

- ``name_pattern``:
configmap name pattern to check
- ``namespace``:
Expand All @@ -182,11 +160,8 @@ def get_configmaps_in_namespace(self, name_pattern, namespace, label_selector=""

def get_service_accounts_in_namespace(self, name_pattern, namespace, label_selector=""):
"""Gets service accounts matching pattern in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of service accounts.

- ``name_pattern``:
Service Account name pattern to check
- ``namespace``:
Expand All @@ -199,11 +174,8 @@ def get_service_accounts_in_namespace(self, name_pattern, namespace, label_selec

def get_deployments_in_namespace(self, name_pattern, namespace, label_selector=""):
"""Gets deployments matching pattern in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of deployments.

- ``name_pattern``:
deployment name pattern to check
- ``namespace``:
Expand All @@ -216,11 +188,8 @@ def get_deployments_in_namespace(self, name_pattern, namespace, label_selector="

def get_jobs_in_namespace(self, name_pattern, namespace, label_selector=""):
"""Gets jobs matching pattern in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of jobs.

- ``name_pattern``:
job name pattern to check
- ``namespace``:
Expand All @@ -233,11 +202,8 @@ def get_jobs_in_namespace(self, name_pattern, namespace, label_selector=""):

def get_secrets_in_namespace(self, name_pattern, namespace, label_selector=""):
"""Gets secrets matching pattern in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of secrets.

- ``name_pattern``:
secret name pattern to check
- ``namespace``:
Expand All @@ -250,29 +216,23 @@ def get_secrets_in_namespace(self, name_pattern, namespace, label_selector=""):

def filter_pods_names(self, pods):
"""Filter pod names for list of pods.

Returns list of strings.

- ``pods``:
List of pods objects
"""
return [pod.metadata.name for pod in pods]

def filter_service_accounts_names(self, service_accounts):
"""Filter service accounts names for list of service accounts.

Returns list of strings.

- ``service_accounts``:
List of service accounts objects
"""
return [sa.metadata.name for sa in service_accounts]

def filter_pods_containers_by_name(self, pods, name_pattern):
"""Filters pods containers by name for given list of pods.

Returns lists of containers (flattens).

- ``pods``:
List of pods objects
"""
Expand All @@ -286,29 +246,23 @@ def filter_pods_containers_by_name(self, pods, name_pattern):

def filter_containers_images(self, containers):
"""Filters container images for given lists of containers.

Returns list of images.

- ``containers``:
List of containers
"""
return [container.image for container in containers]

def filter_containers_resources(self, containers):
"""Filters container resources for given lists of containers.

Returns list of resources.

- ``containers``:
List of containers
"""
return [container.resources for container in containers]

def filter_pods_containers_statuses_by_name(self, pods, name_pattern):
"""Filters pods containers statuses by container name for given list of pods.

Returns lists of containers statuses.

- ``pods``:
List of pods objects
"""
Expand All @@ -322,7 +276,6 @@ def filter_pods_containers_statuses_by_name(self, pods, name_pattern):

def get_pod_status_in_namespace(self, name, namespace):
"""Gets pod status in given namespace.

- ``name``:
Name of pod.
- ``namespace``:
Expand All @@ -333,9 +286,7 @@ def get_pod_status_in_namespace(self, name, namespace):

def assert_pod_has_labels(self, pod, labels_json):
"""Assert pod has labels.

Returns True/False

- ``pod``:
Pod object.
- ``labels_json``:
Expand All @@ -358,9 +309,7 @@ def assert_pod_has_labels(self, pod, labels_json):

def assert_pod_has_annotations(self, pod, annotations_json):
"""Assert pod has annotations.

Returns True/False

- ``pod``:
Pod object.
- ``annotations_json``:
Expand All @@ -383,9 +332,7 @@ def assert_pod_has_annotations(self, pod, annotations_json):

def assert_container_has_env_vars(self, container, env_vars_json):
"""Assert container has env vars.

Returns True/False

- ``container``:
Container object.
- ``env_var_json``:
Expand All @@ -412,11 +359,8 @@ def assert_container_has_env_vars(self, container, env_vars_json):

def get_services_in_namespace(self, namespace, label_selector=""):
"""Gets services in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of strings.

- ``namespace``:
Namespace to check
"""
Expand All @@ -425,11 +369,8 @@ def get_services_in_namespace(self, namespace, label_selector=""):

def get_service_details_in_namespace(self, name, namespace):
"""Gets service details in given namespace.

Returns Service object representation. Can be accessed using

| Should Be Equal As integers | ${service_details.spec.ports[0].port} | 8080 |

- ``name``:
Name of service.
- ``namespace``:
Expand All @@ -440,11 +381,8 @@ def get_service_details_in_namespace(self, name, namespace):

def get_endpoints_in_namespace(self, name, namespace):
"""Gets endpoint details in given namespace.

Returns Endpoint object representation. Can be accessed using

| Should Match | ${endpoint_details.subsets[0].addresses[0].target_ref.name} | pod-name-123456 |

- ``name``:
Name of endpoint.
- ``namespace``:
Expand All @@ -455,11 +393,8 @@ def get_endpoints_in_namespace(self, name, namespace):

def get_pvc_in_namespace(self, namespace, label_selector=""):
"""Gets pvcs in given namespace.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of strings.

- ``namespace``:
Namespace to check
"""
Expand All @@ -468,11 +403,8 @@ def get_pvc_in_namespace(self, namespace, label_selector=""):

def get_pvc_capacity(self, name, namespace):
"""Gets PVC details in given namespace.

Returns PVC object representation. Can be accessed using

| Should Be Equal As strings | ${pvc.status.capacity.storage} | 1Gi |

- ``name``:
Name of PVC.
- ``namespace``:
Expand All @@ -483,19 +415,15 @@ def get_pvc_capacity(self, name, namespace):

def get_kubelet_version(self, label_selector=""):
"""Gets list of kubelet versions on each node.

Can be optionally filtered by label. e.g. label_selector=label_key=label_value

Returns list of strings.
"""
ret = self.v1.list_node(watch=False, label_selector=label_selector)
return [item.status.node_info.kubelet_version for item in ret.items]

def create_service_account_in_namespace(self, namespace, body):
"""Creates service account in a namespace

Returns created service account

- ``body``:
Service Account object.
- ``namespace``:
Expand All @@ -506,14 +434,19 @@ def create_service_account_in_namespace(self, namespace, body):

def delete_service_account_in_namespace(self, name, namespace):
"""Deletes service account in a namespace

Returns V1status


- ``name``:
Service Account name
- ``namespace``:
Namespace to check
"""
ret = self.v1.delete_namespaced_service_account(name=name, namespace=namespace)
return ret

def get_healthcheck(self):
"""check cluster leverl healthcheck
Can be used to verify the readiness/current status of the API server
"""
output = requests.get('https://localhost:6443/readyz?verbose=', verify = False)
satish-nubolab marked this conversation as resolved.
Show resolved Hide resolved
logger.debug(output)
return output
8 changes: 8 additions & 0 deletions testcases/healthcheck/healthcheck.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*** Settings ***
Resource ./healthcheck_kw.robot

*** Test Cases ***
Healthcheck
[Tags] other
Healthcheck

15 changes: 15 additions & 0 deletions testcases/healthcheck/healthcheck_kw.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
*** Settings ***
Library Collections
Library RequestsLibrary
Library String
# For regular execution
Library KubeLibrary
# For incluster execution
#Library KubeLibrary None True False
# For development
#Library ../../src/KubeLibrary/KubeLibrary.py ~/.kube/k3d

*** Keywords ***
Healthcheck
${output}= Get Healthcheck
Log \nHealthcheck ${output}: console=True
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if your example keyword could also verify if all checks have passed with ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Evaluated the response and verified if all checks have passed with ok.