k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/measurement/common/dns/dns_performance-k8s-hostnames.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package dns
    18  
    19  import (
    20  	"context"
    21  	"embed"
    22  	"fmt"
    23  	"time"
    24  
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	clientset "k8s.io/client-go/kubernetes"
    27  	"k8s.io/klog/v2"
    28  	"k8s.io/perf-tests/clusterloader2/pkg/framework"
    29  	"k8s.io/perf-tests/clusterloader2/pkg/framework/client"
    30  	"k8s.io/perf-tests/clusterloader2/pkg/measurement"
    31  	"k8s.io/perf-tests/clusterloader2/pkg/util"
    32  )
    33  
    34  /*
    35  The DNS Performance test for K8s Hostnames creates the required permissions for
    36  DNS client pods to get K8s hostnames (from services and endpoints). Then DNS
    37  client deployment is created that uses dnsperfgo image
    38  (https://github.com/kubernetes/perf-tests/tree/master/dns/dnsperfgo) to query
    39  the hostnames within the cluster and generate Prometheus metrics for number of
    40  errors, timeouts and their latencies. Outside this measurement, the metrics are
    41  scraped and results verified to not cross the specified thresholds.
    42  */
    43  
    44  const (
    45  	dnsPerfK8sHostnamesMeasureName = "DNSPerformanceK8sHostnames"
    46  	dnsPerfTestNamespace           = "dns-perf-test"
    47  	dnsPerfTestPermissionsName     = "dns-test-client"
    48  	serviceAccountFilePath         = "manifests/serviceaccount.yaml"
    49  	clusterRoleFilePath            = "manifests/clusterrole.yaml"
    50  	clusterRoleBindingFilePath     = "manifests/clusterrolebinding.yaml"
    51  	clientDeploymentFilePath       = "manifests/dns-client.yaml"
    52  )
    53  
    54  //go:embed manifests
    55  var manifestsFS embed.FS
    56  
    57  func init() {
    58  	klog.Info("Registering measurement: DNS Performance for K8s Hostnames")
    59  	if err := measurement.Register(dnsPerfK8sHostnamesMeasureName, createDNSPerfK8sHostnamesMeasurement); err != nil {
    60  		klog.Fatalf("Cannot register %s: %v", dnsPerfK8sHostnamesMeasureName, err)
    61  	}
    62  }
    63  
    64  func createDNSPerfK8sHostnamesMeasurement() measurement.Measurement {
    65  	return &dnsPerfK8sHostnamesMeasurement{}
    66  }
    67  
    68  type dnsPerfK8sHostnamesMeasurement struct {
    69  	k8sClient clientset.Interface
    70  	framework *framework.Framework
    71  	// testClientNamespace is the new namespace where the dns clients are going to
    72  	// be deployed. It's cleaned up when the test finishes.
    73  	testClientNamespace string
    74  	// podReplicas is the number of DNS client pods to be deployed.
    75  	podReplicas int
    76  	// qpsPerClient is the number of DNS queries each DNS client should send per
    77  	// second.
    78  	qpsPerClient int
    79  	// testDurationMinutes is the duration in minutes for DNS client pods to run.
    80  	testDurationMinutes int
    81  }
    82  
    83  func (m *dnsPerfK8sHostnamesMeasurement) Execute(config *measurement.Config) ([]measurement.Summary, error) {
    84  	err := m.initializeMeasurement(config)
    85  	if err != nil {
    86  		return nil, fmt.Errorf("failed to initialize the measurement: %v", err)
    87  	}
    88  
    89  	if err = client.CreateNamespace(m.k8sClient, m.testClientNamespace); err != nil {
    90  		return nil, fmt.Errorf("error while creating namespace: %v", err)
    91  	}
    92  
    93  	if err = m.createDNSClientPermissions(); err != nil {
    94  		return nil, fmt.Errorf("failed to create dns client permission resources: %v", err)
    95  	}
    96  
    97  	if err = m.createDNSClientDeployment(); err != nil {
    98  		return nil, fmt.Errorf("failed to create DNS client deployment: %v", err)
    99  	}
   100  
   101  	// Keep running the dns clients for the specified duration.
   102  	runDuration := time.Duration(m.testDurationMinutes) * time.Minute
   103  	klog.Infof("DNS tests are going to run for %v", runDuration)
   104  	time.Sleep(runDuration)
   105  
   106  	return nil, m.cleanUp()
   107  }
   108  
   109  func (m *dnsPerfK8sHostnamesMeasurement) initializeMeasurement(config *measurement.Config) error {
   110  	if m.framework != nil {
   111  		klog.Warningf("The %q was already started, but running it again", dnsPerfK8sHostnamesMeasureName)
   112  	}
   113  
   114  	var err error
   115  	if m.testClientNamespace, err = util.GetStringOrDefault(config.Params, "testNamespace", dnsPerfTestNamespace); err != nil {
   116  		return err
   117  	}
   118  
   119  	if m.podReplicas, err = util.GetIntOrDefault(config.Params, "podReplicas", 10); err != nil {
   120  		return err
   121  	}
   122  
   123  	if m.qpsPerClient, err = util.GetIntOrDefault(config.Params, "qpsPerClient", 10); err != nil {
   124  		return err
   125  	}
   126  
   127  	if m.testDurationMinutes, err = util.GetIntOrDefault(config.Params, "testDurationMinutes", 10); err != nil {
   128  		return err
   129  	}
   130  
   131  	m.framework = config.ClusterFramework
   132  	m.k8sClient = config.ClusterFramework.GetClientSets().GetClient()
   133  
   134  	return nil
   135  }
   136  
   137  // createDNSClientPermissions creates ServiceAccount, ClusterRole and
   138  // ClusterRoleBinding for the test client pods to access Services and Endpoints.
   139  func (m *dnsPerfK8sHostnamesMeasurement) createDNSClientPermissions() error {
   140  	templateMap := map[string]interface{}{
   141  		"Name":      dnsPerfTestPermissionsName,
   142  		"Namespace": m.testClientNamespace,
   143  	}
   144  
   145  	if err := m.framework.ApplyTemplatedManifests(manifestsFS, serviceAccountFilePath, templateMap); err != nil {
   146  		return fmt.Errorf("error while creating serviceaccount: %v", err)
   147  	}
   148  
   149  	if err := m.framework.ApplyTemplatedManifests(manifestsFS, clusterRoleFilePath, templateMap); err != nil {
   150  		return fmt.Errorf("error while creating clusterrole: %v", err)
   151  	}
   152  
   153  	if err := m.framework.ApplyTemplatedManifests(manifestsFS, clusterRoleBindingFilePath, templateMap); err != nil {
   154  		return fmt.Errorf("error while creating clusterrolebinding: %v", err)
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  func (m *dnsPerfK8sHostnamesMeasurement) createDNSClientDeployment() error {
   161  	templateMap := map[string]interface{}{
   162  		"Namespace":          m.testClientNamespace,
   163  		"PodReplicas":        m.podReplicas,
   164  		"QPSPerClient":       m.qpsPerClient,
   165  		"ServiceAccountName": dnsPerfTestPermissionsName,
   166  	}
   167  
   168  	return m.framework.ApplyTemplatedManifests(manifestsFS, clientDeploymentFilePath, templateMap)
   169  }
   170  
   171  func (m *dnsPerfK8sHostnamesMeasurement) deleteDNSClientPermissions() error {
   172  	klog.Infof("Deleting DNS client permission resources for measurement %q", dnsPerfK8sHostnamesMeasureName)
   173  
   174  	if err := m.k8sClient.CoreV1().ServiceAccounts(m.testClientNamespace).Delete(context.TODO(), dnsPerfTestPermissionsName, metav1.DeleteOptions{}); err != nil {
   175  		return err
   176  	}
   177  
   178  	if err := m.k8sClient.RbacV1().ClusterRoles().Delete(context.TODO(), dnsPerfTestPermissionsName, metav1.DeleteOptions{}); err != nil {
   179  		return err
   180  	}
   181  
   182  	return m.k8sClient.RbacV1().ClusterRoleBindings().Delete(context.TODO(), dnsPerfTestPermissionsName, metav1.DeleteOptions{})
   183  }
   184  
   185  func (m *dnsPerfK8sHostnamesMeasurement) cleanUp() error {
   186  	if m.framework == nil {
   187  		klog.Warning("Cleanup skipped. The measurement is not running")
   188  		return nil
   189  	}
   190  
   191  	if err := m.deleteDNSClientPermissions(); err != nil {
   192  		return err
   193  	}
   194  
   195  	klog.Infof("Deleting namespace %q for measurement %q", m.testClientNamespace, dnsPerfK8sHostnamesMeasureName)
   196  	return m.k8sClient.CoreV1().Namespaces().Delete(context.TODO(), m.testClientNamespace, metav1.DeleteOptions{})
   197  }
   198  
   199  // String returns a string representation of the measurement.
   200  func (m *dnsPerfK8sHostnamesMeasurement) String() string {
   201  	return dnsPerfK8sHostnamesMeasureName
   202  }
   203  
   204  // Dispose cleans up after the measurement.
   205  func (m *dnsPerfK8sHostnamesMeasurement) Dispose() {
   206  	if err := m.cleanUp(); err != nil {
   207  		klog.Infof("Cleanup failed: %v", err)
   208  	}
   209  }