github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/pkg/utils/o11y/controller.go (about)

     1  package o11y
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"os/exec"
     9  	"regexp"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	kubeCl "github.com/redhat-appstudio/e2e-tests/pkg/apis/kubernetes"
    15  	"github.com/redhat-appstudio/e2e-tests/pkg/constants"
    16  	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
    17  	appsv1 "k8s.io/api/apps/v1"
    18  	corev1 "k8s.io/api/core/v1"
    19  	"k8s.io/apimachinery/pkg/api/resource"
    20  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    21  	"k8s.io/apimachinery/pkg/labels"
    22  	"k8s.io/apimachinery/pkg/selection"
    23  	"k8s.io/apimachinery/pkg/util/wait"
    24  	"k8s.io/utils/pointer"
    25  	"sigs.k8s.io/controller-runtime/pkg/client"
    26  )
    27  
    28  type SuiteController struct {
    29  	*kubeCl.CustomClient
    30  }
    31  
    32  type MetricResult struct {
    33  	Metric map[string]string `json:"metric"`
    34  	Value  []interface{}     `json:"value"`
    35  }
    36  
    37  func NewSuiteController(kube *kubeCl.CustomClient) (*SuiteController, error) {
    38  	return &SuiteController{
    39  		kube,
    40  	}, nil
    41  }
    42  
    43  func (h *SuiteController) convertBytesToMB(valueInBytes float64) int {
    44  	valueInMegabytes := valueInBytes / (1000 * 1000)
    45  	return int(valueInMegabytes)
    46  }
    47  
    48  // Fetch metrics for given query
    49  func (h *SuiteController) GetMetrics(query string) ([]MetricResult, error) {
    50  
    51  	var result struct {
    52  		Data struct {
    53  			Result []MetricResult `json:"result"`
    54  		} `json:"data"`
    55  	}
    56  
    57  	// Temporary way to fetch the metrics, will be replaced by golang http client library
    58  	// curl -X GET -kG "https://$THANOS_QUERIER_HOST/api/v1/query?" --data-urlencode "query="+query -H "Authorization: Bearer $TOKEN"
    59  	curlCmd := exec.Command("curl", "-X", "GET", "-kG", "http://localhost:8080/api/v1/query", "--data-urlencode", "query="+query)
    60  	output, err := curlCmd.Output()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	err = json.Unmarshal(output, &result)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	return result.Data.Result, nil
    71  }
    72  
    73  func (h *SuiteController) GetRegexPodNameWithResult(podNameRegex string, results []MetricResult) (map[string]string, error) {
    74  	podNamesWithResult := make(map[string]string)
    75  	regex, err := regexp.Compile(podNameRegex)
    76  	if err != nil {
    77  		return podNamesWithResult, fmt.Errorf("error compiling regex pattern: %v", err)
    78  	}
    79  
    80  	for _, res := range results {
    81  		if podName, ok := res.Metric["pod"]; ok {
    82  			if regex.MatchString(podName) {
    83  				value := res.Value[1].(string)
    84  				podNamesWithResult[podName] = value
    85  			}
    86  		}
    87  	}
    88  
    89  	if len(podNamesWithResult) == 0 {
    90  		return nil, fmt.Errorf("no pods matching the regex pattern were found")
    91  	}
    92  
    93  	return podNamesWithResult, nil
    94  }
    95  
    96  func (h *SuiteController) ConvertValuesToMB(podNamesWithResult map[string]string) (map[string]int, error) {
    97  	podNameWithMB := make(map[string]int)
    98  
    99  	for podName, value := range podNamesWithResult {
   100  		valueStr := value
   101  
   102  		valueInBytes, err := strconv.ParseFloat(valueStr, 64)
   103  		if err != nil {
   104  			return nil, fmt.Errorf("error parsing value for %s: %s", podName, err)
   105  		}
   106  
   107  		valueInMegabytes := h.convertBytesToMB(valueInBytes)
   108  		podNameWithMB[podName] = int(valueInMegabytes)
   109  	}
   110  
   111  	return podNameWithMB, nil
   112  }
   113  
   114  func labelsToSelector(labelMap map[string]string) labels.Selector {
   115  	selector := labels.NewSelector()
   116  	for key, value := range labelMap {
   117  		req, _ := labels.NewRequirement(key, selection.Equals, []string{value})
   118  		selector = selector.Add(*req)
   119  	}
   120  	return selector
   121  }
   122  
   123  func (s *SuiteController) WaitForScriptCompletion(deployment *appsv1.Deployment, successMessage string, timeout time.Duration) error {
   124  	namespace := deployment.Namespace
   125  	deploymentName := deployment.Name
   126  
   127  	// Get the pod associated with the deployment
   128  	podList := &corev1.PodList{}
   129  	labels := deployment.Spec.Selector.MatchLabels
   130  	labelSelector := labelsToSelector(labels)
   131  	err := s.KubeRest().List(context.Background(), podList, client.InNamespace(namespace), client.MatchingLabelsSelector{Selector: labelSelector})
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	if len(podList.Items) == 0 {
   137  		return fmt.Errorf("no pods found for deployment %s", deploymentName)
   138  	}
   139  
   140  	pod := podList.Items[0]
   141  
   142  	// Wait for the success message in the pod's log output
   143  	podLogOpts := &corev1.PodLogOptions{}
   144  	req := s.KubeInterface().CoreV1().Pods(namespace).GetLogs(pod.Name, podLogOpts)
   145  
   146  	err = wait.PollImmediate(time.Second, timeout, func() (bool, error) {
   147  		readCloser, err := req.Stream(context.Background())
   148  		if err != nil {
   149  			return false, err
   150  		}
   151  		defer readCloser.Close()
   152  
   153  		scanner := bufio.NewScanner(readCloser)
   154  		for scanner.Scan() {
   155  			if strings.Contains(scanner.Text(), successMessage) {
   156  				return true, nil
   157  			}
   158  		}
   159  		return false, nil
   160  	})
   161  
   162  	return err
   163  }
   164  
   165  func (h *SuiteController) getImagePushScript(secret, quayOrg string) string {
   166  	return fmt.Sprintf(`#!/bin/sh
   167  authFilePath="/tekton/creds-secrets/%s/.dockerconfigjson"
   168  destImageRef="quay.io/%s/o11y-workloads"
   169  # Set Permissions
   170  sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf
   171  echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid
   172  # Pull Image
   173  echo -e "FROM quay.io/libpod/alpine:latest\nRUN dd if=/dev/urandom of=/100mbfile bs=1M count=100" > Dockerfile
   174  unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah bud --tls-verify=false --no-cache -f ./Dockerfile -t "$destImageRef" .
   175  IMAGE_SHA_DIGEST=$(buildah images --digests | grep ${destImageRef} | awk '{print $4}')
   176  TAGGED_IMAGE_NAME="${destImageRef}:${IMAGE_SHA_DIGEST}"
   177  buildah tag ${destImageRef} ${TAGGED_IMAGE_NAME}
   178  buildah images
   179  buildah push --authfile "$authFilePath" --disable-compression --tls-verify=false ${TAGGED_IMAGE_NAME}
   180  if [ $? -eq 0 ]; then
   181    # Scraping Interval Period, Pod must stay alive
   182    sleep 1m
   183    echo "Image push completed"
   184  else
   185    echo "Image push failed"
   186    exit 1
   187  fi`, secret, quayOrg)
   188  }
   189  
   190  func (h *SuiteController) QuayImagePushPipelineRun(quayOrg, secret, namespace string) (*v1beta1.PipelineRun, error) {
   191  	pipelineRun := &v1beta1.PipelineRun{
   192  		ObjectMeta: metav1.ObjectMeta{
   193  			GenerateName: "pipelinerun-egress-",
   194  			Namespace:    namespace,
   195  			Labels: map[string]string{
   196  				"pipelines.appstudio.openshift.io/type": "test",
   197  			},
   198  		},
   199  		Spec: v1beta1.PipelineRunSpec{
   200  			PipelineSpec: &v1beta1.PipelineSpec{
   201  				Tasks: []v1beta1.PipelineTask{
   202  					{
   203  						Name: "buildah-quay",
   204  						TaskSpec: &v1beta1.EmbeddedTask{
   205  							TaskSpec: v1beta1.TaskSpec{
   206  								Steps: []v1beta1.Step{
   207  									{
   208  										Name:  "pull-and-push-image",
   209  										Image: "quay.io/redhat-appstudio/buildah:v1.28",
   210  										Env: []corev1.EnvVar{
   211  											{Name: "BUILDAH_FORMAT", Value: "oci"},
   212  											{Name: "STORAGE_DRIVER", Value: "vfs"},
   213  										},
   214  										Script: h.getImagePushScript(secret, quayOrg),
   215  										SecurityContext: &corev1.SecurityContext{
   216  											RunAsUser: pointer.Int64(0),
   217  											Capabilities: &corev1.Capabilities{
   218  												Add: []corev1.Capability{
   219  													"SETFCAP",
   220  												},
   221  											},
   222  										},
   223  									},
   224  								},
   225  							},
   226  						},
   227  					},
   228  				},
   229  			},
   230  		},
   231  	}
   232  
   233  	if err := h.KubeRest().Create(context.Background(), pipelineRun); err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	return pipelineRun, nil
   238  }
   239  
   240  func (h *SuiteController) VCPUMinutesPipelineRun(namespace string) (*v1beta1.PipelineRun, error) {
   241  	pipelineRun := &v1beta1.PipelineRun{
   242  		ObjectMeta: metav1.ObjectMeta{
   243  			GenerateName: "pipelinerun-vcpu-",
   244  			Namespace:    namespace,
   245  			Labels: map[string]string{
   246  				"pipelines.appstudio.openshift.io/type": "test",
   247  			},
   248  		},
   249  		Spec: v1beta1.PipelineRunSpec{
   250  			PipelineSpec: &v1beta1.PipelineSpec{
   251  				Tasks: []v1beta1.PipelineTask{
   252  					{
   253  						Name: "vcpu-minutes",
   254  						TaskSpec: &v1beta1.EmbeddedTask{
   255  							TaskSpec: v1beta1.TaskSpec{
   256  								Steps: []v1beta1.Step{
   257  									{
   258  										Name:   "resource-constraint",
   259  										Image:  "registry.access.redhat.com/ubi9/ubi-micro",
   260  										Script: "#!/usr/bin/env bash\nsleep 1m\necho 'vCPU Deployment Completed'\n",
   261  										Resources: corev1.ResourceRequirements{
   262  											Requests: corev1.ResourceList{
   263  												corev1.ResourceMemory: resource.MustParse("200Mi"),
   264  												corev1.ResourceCPU:    resource.MustParse("200m"),
   265  											},
   266  											Limits: corev1.ResourceList{
   267  												corev1.ResourceMemory: resource.MustParse("200Mi"),
   268  												corev1.ResourceCPU:    resource.MustParse("200m"),
   269  											},
   270  										},
   271  									},
   272  								},
   273  							},
   274  						},
   275  					},
   276  				},
   277  			},
   278  		},
   279  	}
   280  
   281  	if err := h.KubeRest().Create(context.Background(), pipelineRun); err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	return pipelineRun, nil
   286  }
   287  
   288  func (h *SuiteController) QuayImagePushDeployment(quayOrg, secret, namespace string) (*appsv1.Deployment, error) {
   289  	Deployment := &appsv1.Deployment{
   290  		ObjectMeta: metav1.ObjectMeta{
   291  			Name:      "deployment-egress",
   292  			Namespace: namespace,
   293  		},
   294  		Spec: appsv1.DeploymentSpec{
   295  			Replicas: pointer.Int32(1),
   296  			Selector: &metav1.LabelSelector{
   297  				MatchLabels: map[string]string{
   298  					"app": "deployment-egress",
   299  				},
   300  			},
   301  			Template: corev1.PodTemplateSpec{
   302  				ObjectMeta: metav1.ObjectMeta{
   303  					Labels: map[string]string{
   304  						"app": "deployment-egress",
   305  					},
   306  				},
   307  				Spec: corev1.PodSpec{
   308  					ImagePullSecrets: []corev1.LocalObjectReference{
   309  						{
   310  							Name: secret,
   311  						},
   312  					},
   313  					ServiceAccountName: constants.DefaultPipelineServiceAccount,
   314  					Containers: []corev1.Container{
   315  						{
   316  							Name:  "quay-image-push-container",
   317  							Image: "quay.io/redhat-appstudio/buildah:v1.28",
   318  							VolumeMounts: []corev1.VolumeMount{
   319  								{
   320  									Name:      "docker-config",
   321  									MountPath: "/tekton/creds-secrets/o11y-tests-token/",
   322  									ReadOnly:  true,
   323  								},
   324  							},
   325  							Env: []corev1.EnvVar{
   326  								{Name: "BUILDAH_FORMAT", Value: "oci"},
   327  								{Name: "STORAGE_DRIVER", Value: "vfs"},
   328  							},
   329  							Command: []string{"/bin/sh", "-c"},
   330  							Args:    []string{h.getImagePushScript(secret, quayOrg)},
   331  							SecurityContext: &corev1.SecurityContext{
   332  								RunAsUser: pointer.Int64(0),
   333  								Capabilities: &corev1.Capabilities{
   334  									Add: []corev1.Capability{
   335  										"SETFCAP",
   336  									},
   337  								},
   338  							},
   339  						},
   340  					},
   341  					Volumes: []corev1.Volume{
   342  						{
   343  							Name: "docker-config",
   344  							VolumeSource: corev1.VolumeSource{
   345  								Secret: &corev1.SecretVolumeSource{
   346  									SecretName: secret,
   347  								},
   348  							},
   349  						},
   350  					},
   351  				},
   352  			},
   353  		},
   354  	}
   355  
   356  	if err := h.KubeRest().Create(context.Background(), Deployment); err != nil {
   357  		return &appsv1.Deployment{}, err
   358  	}
   359  
   360  	return Deployment, nil
   361  }
   362  
   363  func (h *SuiteController) VCPUMinutesDeployment(namespace string) (*appsv1.Deployment, error) {
   364  	Deployment := &appsv1.Deployment{
   365  		ObjectMeta: metav1.ObjectMeta{
   366  			Name:      "deployment-vcpu",
   367  			Namespace: namespace,
   368  		},
   369  		Spec: appsv1.DeploymentSpec{
   370  			Replicas: pointer.Int32(1),
   371  			Selector: &metav1.LabelSelector{
   372  				MatchLabels: map[string]string{
   373  					"app": "deployment-vcpu",
   374  				},
   375  			},
   376  			Template: corev1.PodTemplateSpec{
   377  				ObjectMeta: metav1.ObjectMeta{
   378  					Labels: map[string]string{
   379  						"app": "deployment-vcpu",
   380  					},
   381  				},
   382  				Spec: corev1.PodSpec{
   383  					Containers: []corev1.Container{
   384  						{
   385  							Name:  "vcpu-minutes",
   386  							Image: "registry.access.redhat.com/ubi9/ubi-micro",
   387  							Command: []string{
   388  								"/bin/bash",
   389  								"-c",
   390  								"sleep 1m ; echo 'vCPU Deployment Completed'",
   391  							},
   392  							Resources: corev1.ResourceRequirements{
   393  								Requests: corev1.ResourceList{
   394  									corev1.ResourceMemory: resource.MustParse("200Mi"),
   395  									corev1.ResourceCPU:    resource.MustParse("200m"),
   396  								},
   397  								Limits: corev1.ResourceList{
   398  									corev1.ResourceMemory: resource.MustParse("200Mi"),
   399  									corev1.ResourceCPU:    resource.MustParse("200m"),
   400  								},
   401  							},
   402  						},
   403  					},
   404  				},
   405  			},
   406  		},
   407  	}
   408  
   409  	if err := h.KubeRest().Create(context.Background(), Deployment); err != nil {
   410  		return nil, err
   411  	}
   412  
   413  	return Deployment, nil
   414  }