Add test_k8s_dashboard part2

a follow-up to #941156, adding an extra function(which is not finished
yet) to create a k8s dashboard, but it has the starting steps like
crating a secret and adding the https certificates, copying another
necessary files too.
Also, the abstraction for k8s secrets had to be improved by adding the
secret, table parser and output classes.
there was a typo in the kubectl_get_pods_output script which is now
fixed.

Change-Id: Ie6d60b893ec5b686e19dae8ea6197c3991e5b034
Signed-off-by: Gabriel Calixto de Paula <gabrielcalixto9@gmail.com>
This commit is contained in:
Gabriel Calixto de Paula
2025-02-13 10:37:57 -05:00
parent 52e12dc183
commit 516409f287
9 changed files with 506 additions and 20 deletions

View File

@@ -25,7 +25,7 @@ class KubectlGetPodsOutput:
pod = KubectlPodObject(pod_dict['NAME'])
if 'NAMESPACE' in pod_dict:
pod.set_ready(pod_dict['NAMESPACE'])
pod.set_namespace(pod_dict['NAMESPACE'])
if 'READY' in pod_dict:
pod.set_ready(pod_dict['READY'])

View File

@@ -32,3 +32,14 @@ class KubectlCreateSecretsKeywords(BaseKeyword):
self.ssh_connection.send(
export_k8s_config(f"kubectl create secret docker-registry {secret_name} --docker-server={docker_server} " f"--docker-username={user_name} --docker-password={password}")
)
def create_secret_generic(self, secret_name: str, tls_crt: str, tls_key: str):
"""
Create a generic secret
Args:
secret_name (str): the secret name
tls_crt (str): tls_crt file name
tls_key (str): tls_key file name
"""
self.ssh_connection.send(export_k8s_config(f"kubectl create secret {secret_name} --from-file=tls.crt={tls_crt} --from-file=tls.key={tls_key}"))

View File

@@ -0,0 +1,32 @@
from keywords.k8s.secret.object.kubectl_get_secret_table_parser import KubectlGetSecretsTableParser
from keywords.k8s.secret.object.kubectl_secret_object import KubectlSecretObject
class KubectlGetSecretOutput:
def __init__(self, kubectl_get_secrets_output: str):
"""_summary_
Args:
kubectl_get_secrets_output (str): Raw string output from running "kubectl get secrets" command
"""
self.kubectl_secret: [KubectlSecretObject] = []
kubectl_get_secrets_table_parser = KubectlGetSecretsTableParser(kubectl_get_secrets_output)
output_values_list = kubectl_get_secrets_table_parser.get_output_values_list()
for secret_dict in output_values_list:
if 'NAME' not in secret_dict:
raise ValueError(f"There is no NAME associated with the secret: {secret_dict}")
secret = KubectlSecretObject(secret_dict['NAME'])
if 'TYPE' in secret_dict:
secret.set_type(secret_dict['TYPE'])
if 'DATA' in secret_dict:
secret.set_data(secret_dict['DATA'])
if 'AGE' in secret_dict:
secret.set_age(secret_dict['AGE'])
self.kubectl_secret.append(secret)

View File

@@ -0,0 +1,21 @@
from keywords.k8s.k8s_table_parser_base import K8sTableParserBase
class KubectlGetSecretsTableParser(K8sTableParserBase):
"""
Class for parsing the output of "kubectl get secret" commands.
"""
def __init__(self, k8s_output):
"""_
Constructor
Args:
k8s_output (_type_): _description_
"""
super().__init__(k8s_output)
self.possible_headers = [
"NAME",
"TYPE",
"DATA",
"AGE",
]

View File

@@ -0,0 +1,74 @@
class KubectlSecretObject:
"""
Class to hold attributes of a 'kubectl get secrets' command entry
"""
def __init__(self, name: str):
"""
Constructor
Args:
name (str): secret name
"""
self.name = name
self.type = None
self.data = None
self.age = None
def get_name(self) -> str:
"""
Getter for NAME entry
"""
return self.name
def set_name(self, name: str) -> None:
"""
Setter for NAME entry
Args:
name (str): secret name
"""
self.name = name
def get_type(self) -> str:
"""
Getter for TYPE entry
"""
return self.type
def set_type(self, type: str) -> None:
"""
Setter for TYPE entry
Args:
type (str): secret type
"""
self.type = type
def get_data(self) -> int:
"""
Getter for DATA entry
"""
return self.data
def set_data(self, data: int) -> None:
"""
Setter for DATA entry
Args:
data (int): secret data
"""
self.data = data
def get_age(self) -> str:
"""
Getter for AGE entry
"""
return self.age
def set_age(self, age: str) -> None:
"""
Setter for AGE entry
Args:
age (str): secret age
"""
self.age = age

View File

@@ -0,0 +1,297 @@
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
---
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
ports:
- port: 443
targetPort: 8443
selector:
k8s-app: kubernetes-dashboard
---
apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-certs
namespace: kubernetes-dashboard
type: Opaque
---
apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-csrf
namespace: kubernetes-dashboard
type: Opaque
data:
csrf: ""
---
apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-key-holder
namespace: kubernetes-dashboard
type: Opaque
---
kind: ConfigMap
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-settings
namespace: kubernetes-dashboard
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
rules:
# Allow Dashboard to get, update and delete Dashboard exclusive secrets.
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf"]
verbs: ["get", "update", "delete"]
# Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map.
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["kubernetes-dashboard-settings"]
verbs: ["get", "update"]
# Allow Dashboard to get metrics.
- apiGroups: [""]
resources: ["services"]
resourceNames: ["heapster", "dashboard-metrics-scraper"]
verbs: ["proxy"]
- apiGroups: [""]
resources: ["services/proxy"]
resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"]
verbs: ["get"]
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
rules:
# Allow Metrics Scraper to get metrics from the Metrics server
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: kubernetes-dashboard
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kubernetes-dashboard
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard
---
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: kubernetes-dashboard
template:
metadata:
labels:
k8s-app: kubernetes-dashboard
spec:
containers:
- name: kubernetes-dashboard
image: kubernetesui/dashboard:v2.0.0
imagePullPolicy: Always
ports:
- containerPort: 8443
protocol: TCP
args:
#- --auto-generate-certificates
- --namespace=kubernetes-dashboard
- --tls-cert-file=/tls.crt
- --tls-key-file=/tls.key
# Uncomment the following line to manually specify Kubernetes API server Host
# If not specified, Dashboard will attempt to auto discover the API server and connect
# to it. Uncomment only if the default does not work.
# - --apiserver-host=http://my-address:port
volumeMounts:
- name: kubernetes-dashboard-certs
mountPath: /certs
# Create on-disk volume to store exec logs
- mountPath: /tmp
name: tmp-volume
livenessProbe:
httpGet:
scheme: HTTPS
path: /
port: 8443
initialDelaySeconds: 30
timeoutSeconds: 30
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1001
runAsGroup: 2001
volumes:
- name: kubernetes-dashboard-certs
secret:
secretName: kubernetes-dashboard-certs
- name: tmp-volume
emptyDir: {}
serviceAccountName: kubernetes-dashboard
nodeSelector:
"kubernetes.io/os": linux
# Comment the following tolerations if Dashboard must not be deployed on master
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
---
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard
spec:
ports:
- port: 8000
targetPort: 8000
selector:
k8s-app: dashboard-metrics-scraper
---
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: dashboard-metrics-scraper
template:
metadata:
labels:
k8s-app: dashboard-metrics-scraper
annotations:
seccomp.security.alpha.kubernetes.io/pod: 'runtime/default'
spec:
containers:
- name: dashboard-metrics-scraper
image: kubernetesui/metrics-scraper:v1.0.4
ports:
- containerPort: 8000
protocol: TCP
livenessProbe:
httpGet:
scheme: HTTP
path: /
port: 8000
initialDelaySeconds: 30
timeoutSeconds: 30
volumeMounts:
- mountPath: /tmp
name: tmp-volume
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1001
runAsGroup: 2001
serviceAccountName: kubernetes-dashboard
nodeSelector:
"kubernetes.io/os": linux
# Comment the following tolerations if Dashboard must not be deployed on master
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
volumes:
- name: tmp-volume
emptyDir: {}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
token: {}

View File

@@ -1,3 +1,5 @@
import os
from config.configuration_manager import ConfigurationManager
from framework.resources.resource_finder import get_stx_resource_path
from framework.ssh.secure_transfer_file.secure_transfer_file import SecureTransferFile
@@ -7,9 +9,42 @@ from keywords.cloud_platform.ssh.lab_connection_keywords import LabConnectionKey
from keywords.k8s.namespace.kubectl_create_namespace_keywords import KubectlCreateNamespacesKeywords
from keywords.k8s.namespace.kubectl_delete_namespace_keywords import KubectlDeleteNamespaceKeywords
from keywords.k8s.namespace.kubectl_get_namespaces_keywords import KubectlGetNamespacesKeywords
from keywords.k8s.secret.kubectl_create_secrete_keywords import KubectlCreateSecretsKeywords
from pytest import mark
def create_k8s_dashboard(namespace, con_ssh):
"""
Create all necessary resources for the k8s dashboard
Args:
namespace (str): kubernetes_dashboard namespace name
con_ssh (SSHConnection): the SSH connection
"""
# k8s_dashboard_file = "k8s_dashboard.yaml"
# cert = 'k8s_dashboard_certs'
dashboard_key = 'k8s_dashboard_certs/dashboard.key'
dashboard_cert = 'k8s_dashboard_certs/dashboard.crt'
# port = 30000
# secrets_name = 'kubernetes-dashboard-certs'
# kubeconfig_file_path = "utils/test_files/kubeconfig.yaml"
home = "/home/sysadmin/"
path_cert = os.path.join(home, dashboard_cert)
key = os.path.join(home, dashboard_key)
crt = os.path.join(home, dashboard_cert)
sys_domain_name = ConfigurationManager.get_lab_config().get_floating_ip()
con_ssh.send('mkdir -p {}'.format(path_cert), fail_ok=False)
con_ssh.send(
'openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout {} -out {} ' '-subj "/CN={}"'.format(key, crt, sys_domain_name),
fail_ok=False,
)
KubectlCreateSecretsKeywords.create_secret_generic(namespace=namespace, tls_crt=crt, tls_key=key)
@mark.p0
def test_k8s_dashboard_access(request):
"""
@@ -33,30 +68,31 @@ def test_k8s_dashboard_access(request):
# Defines dashboard file name, source (local) and destination (remote) file paths.
dashboard_file_name = 'admin-user.yaml'
local_path = get_stx_resource_path(f'resources/cloud_platform/containers/k8s_dashboard/{dashboard_file_name}')
remote_path = f'/home/{ConfigurationManager.get_lab_config().get_admin_credentials().get_user_name()}/{dashboard_file_name}'
dashboard_file_names = ['admin-user.yaml', 'k8s_dashboard.yaml']
for dashboard_file_name in dashboard_file_names:
local_path = get_stx_resource_path(f'resources/cloud_platform/containers/k8s_dashboard/{dashboard_file_name}')
remote_path = f'/home/{ConfigurationManager.get_lab_config().get_admin_credentials().get_user_name()}/{dashboard_file_name}'
# Opens an SSH session to active controller.
ssh_connection = LabConnectionKeywords().get_active_controller_ssh()
# Opens an SSH session to active controller.
ssh_connection = LabConnectionKeywords().get_active_controller_ssh()
# Opens an SFTP session to active controller.
sftp_client = ssh_connection.get_sftp_client()
# Opens an SFTP session to active controller.
sftp_client = ssh_connection.get_sftp_client()
# Sets the parameters for the app file transfer through a new instance of SecureTransferFileInputObject.
secure_transfer_file_input_object = SecureTransferFileInputObject()
secure_transfer_file_input_object.set_sftp_client(sftp_client)
secure_transfer_file_input_object.set_origin_path(local_path)
secure_transfer_file_input_object.set_destination_path(remote_path)
secure_transfer_file_input_object.set_transfer_direction(TransferDirection.FROM_LOCAL_TO_REMOTE)
secure_transfer_file_input_object.set_force(True)
# Sets the parameters for the app file transfer through a new instance of SecureTransferFileInputObject.
secure_transfer_file_input_object = SecureTransferFileInputObject()
secure_transfer_file_input_object.set_sftp_client(sftp_client)
secure_transfer_file_input_object.set_origin_path(local_path)
secure_transfer_file_input_object.set_destination_path(remote_path)
secure_transfer_file_input_object.set_transfer_direction(TransferDirection.FROM_LOCAL_TO_REMOTE)
secure_transfer_file_input_object.set_force(True)
# Transfers the dashboard file from local path to remote path.
secure_transfer_file = SecureTransferFile(secure_transfer_file_input_object)
file_transfer_succeeded = secure_transfer_file.transfer_file()
# Transfers the dashboard file from local path to remote path.
secure_transfer_file = SecureTransferFile(secure_transfer_file_input_object)
file_transfer_succeeded = secure_transfer_file.transfer_file()
# Asserts the file was really transferred.
assert file_transfer_succeeded
# Asserts the file was really transferred.
assert file_transfer_succeeded
# Create Dashboard namespace
namespace_name = 'kubernetes-dashboard'