k8s.io/kubernetes@v1.29.3/test/integration/ipamperf/ipam_test.go (about)

     1  /*
     2  Copyright 2018 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 ipamperf
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net"
    24  	"os"
    25  	"testing"
    26  	"time"
    27  
    28  	"k8s.io/klog/v2"
    29  	"k8s.io/klog/v2/ktesting"
    30  	netutils "k8s.io/utils/net"
    31  
    32  	"k8s.io/client-go/informers"
    33  	clientset "k8s.io/client-go/kubernetes"
    34  	restclient "k8s.io/client-go/rest"
    35  	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
    36  	"k8s.io/kubernetes/pkg/controller/nodeipam"
    37  	"k8s.io/kubernetes/pkg/controller/nodeipam/ipam"
    38  	"k8s.io/kubernetes/test/integration/framework"
    39  	"k8s.io/kubernetes/test/integration/util"
    40  )
    41  
    42  func setupAllocator(ctx context.Context, kubeConfig *restclient.Config, config *Config, clusterCIDR, serviceCIDR *net.IPNet, subnetMaskSize int) (*clientset.Clientset, util.ShutdownFunc, error) {
    43  	controllerStopChan := make(chan struct{})
    44  	shutdownFunc := func() {
    45  		close(controllerStopChan)
    46  	}
    47  
    48  	clientConfig := restclient.CopyConfig(kubeConfig)
    49  	clientConfig.QPS = float32(config.KubeQPS)
    50  	clientConfig.Burst = config.KubeQPS
    51  	clientSet := clientset.NewForConfigOrDie(clientConfig)
    52  
    53  	sharedInformer := informers.NewSharedInformerFactory(clientSet, 1*time.Hour)
    54  	ipamController, err := nodeipam.NewNodeIpamController(
    55  		ctx,
    56  		sharedInformer.Core().V1().Nodes(),
    57  		config.Cloud, clientSet, []*net.IPNet{clusterCIDR}, serviceCIDR, nil,
    58  		[]int{subnetMaskSize}, config.AllocatorType,
    59  	)
    60  	if err != nil {
    61  		return nil, shutdownFunc, err
    62  	}
    63  	go ipamController.Run(ctx)
    64  	sharedInformer.Start(controllerStopChan)
    65  
    66  	return clientSet, shutdownFunc, nil
    67  }
    68  
    69  func runTest(t *testing.T, kubeConfig *restclient.Config, config *Config, clusterCIDR, serviceCIDR *net.IPNet, subnetMaskSize int) (*Results, error) {
    70  	t.Helper()
    71  	klog.Infof("Running test %s", t.Name())
    72  
    73  	nodeClientConfig := restclient.CopyConfig(kubeConfig)
    74  	nodeClientConfig.QPS = float32(config.CreateQPS)
    75  	nodeClientConfig.Burst = config.CreateQPS
    76  	nodeClient := clientset.NewForConfigOrDie(nodeClientConfig)
    77  
    78  	defer deleteNodes(nodeClient) // cleanup nodes on after controller shutdown
    79  	_, ctx := ktesting.NewTestContext(t)
    80  	clientSet, shutdownFunc, err := setupAllocator(ctx, kubeConfig, config, clusterCIDR, serviceCIDR, subnetMaskSize)
    81  	if err != nil {
    82  		t.Fatalf("Error starting IPAM allocator: %v", err)
    83  	}
    84  	defer shutdownFunc()
    85  
    86  	o := NewObserver(clientSet, config.NumNodes)
    87  	if err := o.StartObserving(); err != nil {
    88  		t.Fatalf("Could not start test observer: %v", err)
    89  	}
    90  
    91  	if err := createNodes(nodeClient, config); err != nil {
    92  		t.Fatalf("Could not create nodes: %v", err)
    93  	}
    94  
    95  	results := o.Results(t.Name(), config)
    96  	klog.Infof("Results: %s", results)
    97  	if !results.Succeeded {
    98  		t.Errorf("%s: Not allocations succeeded", t.Name())
    99  	}
   100  	return results, nil
   101  }
   102  
   103  func logResults(allResults []*Results) {
   104  	jStr, err := json.MarshalIndent(allResults, "", "  ")
   105  	if err != nil {
   106  		klog.Errorf("Error formatting results: %v", err)
   107  		return
   108  	}
   109  	if resultsLogFile != "" {
   110  		klog.Infof("Logging results to %s", resultsLogFile)
   111  		if err := os.WriteFile(resultsLogFile, jStr, os.FileMode(0644)); err != nil {
   112  			klog.Errorf("Error logging results to %s: %v", resultsLogFile, err)
   113  		}
   114  	}
   115  	klog.Infof("AllResults:\n%s", string(jStr))
   116  }
   117  
   118  func TestPerformance(t *testing.T) {
   119  	// TODO (#93112) skip test until appropriate timeout established
   120  	if testing.Short() || true {
   121  		// TODO (#61854) find why flakiness is caused by etcd connectivity before enabling always
   122  		t.Skip("Skipping because we want to run short tests")
   123  	}
   124  
   125  	_, ctx := ktesting.NewTestContext(t)
   126  	ctx, cancel := context.WithCancel(ctx)
   127  	defer cancel()
   128  
   129  	_, kubeConfig, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
   130  		ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
   131  			// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
   132  			opts.Admission.GenericAdmission.DisablePlugins = []string{"ServiceAccount", "TaintNodesByCondition"}
   133  		},
   134  	})
   135  	defer tearDownFn()
   136  
   137  	_, clusterCIDR, _ := netutils.ParseCIDRSloppy("10.96.0.0/11") // allows up to 8K nodes
   138  	_, serviceCIDR, _ := netutils.ParseCIDRSloppy("10.94.0.0/24") // does not matter for test - pick upto  250 services
   139  	subnetMaskSize := 24
   140  
   141  	var (
   142  		allResults []*Results
   143  		tests      []*Config
   144  	)
   145  
   146  	if isCustom {
   147  		tests = append(tests, customConfig)
   148  	} else {
   149  		for _, numNodes := range []int{10, 100} {
   150  			for _, alloc := range []ipam.CIDRAllocatorType{ipam.RangeAllocatorType, ipam.CloudAllocatorType, ipam.IPAMFromClusterAllocatorType, ipam.IPAMFromCloudAllocatorType} {
   151  				tests = append(tests, &Config{AllocatorType: alloc, NumNodes: numNodes, CreateQPS: numNodes, KubeQPS: 10, CloudQPS: 10})
   152  			}
   153  		}
   154  	}
   155  
   156  	for _, test := range tests {
   157  		testName := fmt.Sprintf("%s-KubeQPS%d-Nodes%d", test.AllocatorType, test.KubeQPS, test.NumNodes)
   158  		t.Run(testName, func(t *testing.T) {
   159  			allocateCIDR := false
   160  			if test.AllocatorType == ipam.IPAMFromCloudAllocatorType || test.AllocatorType == ipam.CloudAllocatorType {
   161  				allocateCIDR = true
   162  			}
   163  			bil := newBaseInstanceList(allocateCIDR, clusterCIDR, subnetMaskSize)
   164  			cloud, err := util.NewMockGCECloud(bil.newMockCloud())
   165  			if err != nil {
   166  				t.Fatalf("Unable to create mock cloud: %v", err)
   167  			}
   168  			test.Cloud = cloud
   169  			if results, err := runTest(t, kubeConfig, test, clusterCIDR, serviceCIDR, subnetMaskSize); err == nil {
   170  				allResults = append(allResults, results)
   171  			}
   172  		})
   173  	}
   174  
   175  	logResults(allResults)
   176  }