From 80a3c82fba8b3069fcee4eda2557a098dd4c8921 Mon Sep 17 00:00:00 2001 From: Gabriel Calixto Date: Tue, 11 Mar 2025 16:39:28 -0300 Subject: [PATCH] add test_k8s_dashboard_access test steps part1 with the test setup done, this change adds the necessary code to create a k8s token which will be used to login via the k8s dashboard URL. Change-Id: I44b3690306f1522c4f2b94af83e2a816f22e9573 Signed-off-by: Gabriel Calixto --- .../k8s/files/kubectl_file_apply_keywords.py | 22 +++---- .../kubectl_delete_serviceaccount_keywords.py | 60 +++++++++++++++++++ .../token/kubectl_create_token_keywords.py | 35 +++++++++++ .../containers/test_k8s_dashboard.py | 58 ++++++++++++++++-- 4 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 keywords/k8s/serviceaccount/kubectl_delete_serviceaccount_keywords.py create mode 100644 keywords/k8s/token/kubectl_create_token_keywords.py diff --git a/keywords/k8s/files/kubectl_file_apply_keywords.py b/keywords/k8s/files/kubectl_file_apply_keywords.py index d07bcedf..aeaf9b0c 100644 --- a/keywords/k8s/files/kubectl_file_apply_keywords.py +++ b/keywords/k8s/files/kubectl_file_apply_keywords.py @@ -4,23 +4,25 @@ from keywords.k8s.k8s_command_wrapper import export_k8s_config class KubectlFileApplyKeywords(BaseKeyword): - """_summary_ - - Args: - BaseKeyword (_type_): _description_ + """ + K8s file apply keywords """ def __init__(self, ssh_connection: SSHConnection): + """ + Initializes the class with an SSH connection. + + Args: + ssh_connection (SSHConnection): An instance of SSHConnection to be used for SSH operations. + """ self.ssh_connection = ssh_connection - def dashboard_apply_from_yaml(self, yaml_file: str): + def apply_resource_from_yaml(self, yaml_file: str): """ - Does a dashboard apply using the given yaml file + Applies a Kubernetes resource using the given YAML file. + Args: - yaml_file (): the yaml file to appy - - Returns: - + yaml_file (str): The path to the YAML file containing the resource definition. """ self.ssh_connection.send(export_k8s_config(f"kubectl apply -f {yaml_file}")) self.validate_success_return_code(self.ssh_connection) diff --git a/keywords/k8s/serviceaccount/kubectl_delete_serviceaccount_keywords.py b/keywords/k8s/serviceaccount/kubectl_delete_serviceaccount_keywords.py new file mode 100644 index 00000000..a15589b0 --- /dev/null +++ b/keywords/k8s/serviceaccount/kubectl_delete_serviceaccount_keywords.py @@ -0,0 +1,60 @@ +from framework.logging.automation_logger import get_logger +from framework.ssh.ssh_connection import SSHConnection +from keywords.base_keyword import BaseKeyword +from keywords.k8s.k8s_command_wrapper import export_k8s_config + + +class KubectlDeleteServiceAccountKeywords(BaseKeyword): + """ + Delete ServiceAccount keywords + """ + + def __init__(self, ssh_connection: SSHConnection): + """ + Constructor for KubectlDeleteServiceAccountKeywords. + + Args: + ssh_connection (SSHConnection): An SSH connection object used to execute commands on the Kubernetes cluster. + """ + self.ssh_connection = ssh_connection + + def delete_serviceaccount(self, serviceaccount_name: str, nspace: str = None) -> str: + """ + Deletes the specified Kubernetes service account. + + Args: + serviceaccount_name (str): The name of the service account to delete. + nspace (str, optional): The namespace of the service account. Defaults to None. + + Returns: + str: The output of the kubectl delete command. + """ + args = "" + if nspace: + args += f" -n {nspace} " + args += f"{serviceaccount_name}" + output = self.ssh_connection.send(export_k8s_config(f"kubectl delete serviceaccount {args}")) + self.validate_success_return_code(self.ssh_connection) + + return output + + def cleanup_serviceaccount(self, serviceaccount_name: str, nspace: str = None) -> str: + """ + Deletes a Kubernetes ServiceAccount,method is used for cleanup purposes. + + Args: + serviceaccount_name (str): The name of the ServiceAccount to delete. + nspace (str, optional): The namespace of the ServiceAccount. Defaults to None. + + Returns: + str: The output of the command. + """ + args = "" + if nspace: + args += f" -n {nspace} " + args += f"{serviceaccount_name}" + self.ssh_connection.send(export_k8s_config(f"kubectl delete serviceaccount {args}")) + rc = self.ssh_connection.get_return_code() + if rc != 0: + get_logger().log_error(f"ServiceAccount {serviceaccount_name} failed to delete") + return rc diff --git a/keywords/k8s/token/kubectl_create_token_keywords.py b/keywords/k8s/token/kubectl_create_token_keywords.py new file mode 100644 index 00000000..4d3e0c1d --- /dev/null +++ b/keywords/k8s/token/kubectl_create_token_keywords.py @@ -0,0 +1,35 @@ +from framework.ssh.ssh_connection import SSHConnection +from keywords.base_keyword import BaseKeyword +from keywords.k8s.k8s_command_wrapper import export_k8s_config + + +class KubectlCreateTokenKeywords(BaseKeyword): + """ + Keywords for creating a token with kubectl + """ + + def __init__(self, ssh_connection: SSHConnection): + """ + Initializes the KubectlCreateTokenKeywords class with an SSH connection. + + Args: + ssh_connection (SSHConnection): An object representing the SSH connection. + """ + self.ssh_connection = ssh_connection + + def create_token(self, nspace: str, user: str) -> str: + """ + Creates a Kubernetes token for a specified user in a given namespace. + + Args: + nspace (str): The Kubernetes namespace where the token will be created. + user (str): The user for whom the token will be created. + + Returns: + str: The output from the command execution. + + """ + args = f"{user} -n {nspace}" + output = self.ssh_connection.send(export_k8s_config(f"kubectl create token {args}")) + self.validate_success_return_code(self.ssh_connection) + return output diff --git a/testcases/cloud_platform/regression/containers/test_k8s_dashboard.py b/testcases/cloud_platform/regression/containers/test_k8s_dashboard.py index 2a4fd21e..91dcae70 100644 --- a/testcases/cloud_platform/regression/containers/test_k8s_dashboard.py +++ b/testcases/cloud_platform/regression/containers/test_k8s_dashboard.py @@ -20,6 +20,8 @@ from keywords.k8s.namespace.kubectl_get_namespaces_keywords import KubectlGetNam from keywords.k8s.patch.kubectl_apply_patch_keywords import KubectlApplyPatchKeywords from keywords.k8s.secret.kubectl_create_secret_keywords import KubectlCreateSecretsKeywords from keywords.k8s.secret.kubectl_delete_secret_keywords import KubectlDeleteSecretsKeywords +from keywords.k8s.serviceaccount.kubectl_delete_serviceaccount_keywords import KubectlDeleteServiceAccountKeywords +from keywords.k8s.token.kubectl_create_token_keywords import KubectlCreateTokenKeywords from keywords.openssl.openssl_keywords import OpenSSLKeywords @@ -38,11 +40,12 @@ def check_url_access(url: str) -> tuple: return req.response.status_code, req.response.text -def copy_k8s_files(ssh_connection: SSHConnection): +def copy_k8s_files(request: fixture, ssh_connection: SSHConnection): """ Copy the necessary k8s dashboard yaml files Args: + request (fixture): pytest fixture ssh_connection (SSHConnection): ssh connection object """ k8s_dashboard_dir = "k8s_dashboard" @@ -53,6 +56,12 @@ def copy_k8s_files(ssh_connection: SSHConnection): local_path = get_stx_resource_path(f"resources/cloud_platform/containers/k8s_dashboard/{dashboard_file_name}") FileKeywords(ssh_connection).upload_file(local_path, f"/home/sysadmin/{k8s_dashboard_dir}/{dashboard_file_name}") + def teardown(): + get_logger().log_info("Deleting k8s_dashboard directory") + FileKeywords(ssh_connection).delete_folder_with_sudo(f"/home/sysadmin/{k8s_dashboard_dir}") + + request.addfinalizer(teardown) + def create_k8s_dashboard(request: fixture, namespace: str, con_ssh: SSHConnection): """ @@ -91,14 +100,12 @@ def create_k8s_dashboard(request: fixture, namespace: str, con_ssh: SSHConnectio KubectlCreateSecretsKeywords(ssh_connection=con_ssh).create_secret_generic(secret_name=secrets_name, tls_crt=crt, tls_key=key, namespace=namespace) get_logger().log_info(f"Creating resource from file {k8s_dashboard_file_path}") - KubectlFileApplyKeywords(ssh_connection=con_ssh).dashboard_apply_from_yaml(k8s_dashboard_file_path) + KubectlFileApplyKeywords(ssh_connection=con_ssh).apply_resource_from_yaml(k8s_dashboard_file_path) def teardown(): KubectlFileDeleteKeywords(ssh_connection=con_ssh).delete_resources(k8s_dashboard_file_path) # delete created dashboard secret KubectlDeleteSecretsKeywords(con_ssh).cleanup_secret(namespace=namespace, secret_name=secrets_name) - get_logger().log_info("Deleting k8s_dashboard directory") - con_ssh.send(f"rm -rf {home_k8s}") get_logger().log_info(f"Updating {name} service to be exposed on port {port}") arg_port = '{"spec":{"type":"NodePort","ports":[{"port":443, "nodePort": ' + str(port) + "}]}}" @@ -115,6 +122,44 @@ def create_k8s_dashboard(request: fixture, namespace: str, con_ssh: SSHConnectio raise KeywordException(detailed_message=f"Kubernetes dashboard returned status code {status_code}") +def get_k8s_token(request: fixture, con_ssh: SSHConnection) -> str: + """ + Get token for login to dashboard. + + For Kubernetes versions above 1.24.4, create an admin-user service-account + in the kube-system namespace and bind the cluster-admin ClusterRoleBinding + to this user. Then, create a token for this user in the kube-system namespace. + + Args: + request (fixture): pytest fixture + con_ssh (SSHConnection): SSH connection object + + Returns: + str: Token for login to the dashboard + """ + get_logger().log_info("Create the admin-user service-account in kube-system and bind the " "cluster-admin ClusterRoleBinding to this user") + adminuserfile = "admin-user.yaml" + serviceaccount = "admin-user" + home_k8s = "/home/sysadmin/k8s_dashboard" + + admin_user_file_path = os.path.join(home_k8s, adminuserfile) + + get_logger().log_info("Creating the admin-user service-account") + KubectlFileApplyKeywords(ssh_connection=con_ssh).apply_resource_from_yaml(admin_user_file_path) + + get_logger().log_info("Creating the token for admin-user") + token = KubectlCreateTokenKeywords(ssh_connection=con_ssh).create_token(nspace="kube-system", user=serviceaccount) + + def teardown(): + get_logger().log_info(f"Removing serviceaccount {serviceaccount} in kube-system") + KubectlDeleteServiceAccountKeywords(ssh_connection=con_ssh).cleanup_serviceaccount(serviceaccount_name=serviceaccount, nspace="kube-system") + + request.addfinalizer(teardown) + + get_logger().log_info(f"Token for login to dashboard: {token}") + return token + + @mark.p0 def test_k8s_dashboard_access(request): """ @@ -139,7 +184,7 @@ def test_k8s_dashboard_access(request): # Defines dashboard file name, source (local) and destination (remote) file paths. # Opens an SSH session to active controller. ssh_connection = LabConnectionKeywords().get_active_controller_ssh() - copy_k8s_files(ssh_connection) + copy_k8s_files(request, ssh_connection) # Create Dashboard namespace namespace_name = "kubernetes-dashboard" kubectl_create_ns_keyword = KubectlCreateNamespacesKeywords(ssh_connection) @@ -159,3 +204,6 @@ def test_k8s_dashboard_access(request): # Step 2: Create the necessary k8s dashboard resources test_namespace = "kubernetes-dashboard" create_k8s_dashboard(request, namespace=test_namespace, con_ssh=ssh_connection) + + # Step 3: Create the token for the dashboard + get_k8s_token(request=request, con_ssh=ssh_connection)