github.com/imran-kn/cilium-fork@v1.6.9/pkg/k8s/cnp_test.go (about)

     1  // Copyright 2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !privileged_tests
    16  
    17  package k8s
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"k8s.io/client-go/kubernetes"
    24  	"os"
    25  	go_runtime "runtime"
    26  	"strconv"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/cilium/cilium/pkg/checker"
    31  	"github.com/cilium/cilium/pkg/defaults"
    32  	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    33  	clientset "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned"
    34  	"github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/fake"
    35  	informer "github.com/cilium/cilium/pkg/k8s/client/informers/externalversions"
    36  	"github.com/cilium/cilium/pkg/k8s/types"
    37  	k8sversion "github.com/cilium/cilium/pkg/k8s/version"
    38  	"github.com/cilium/cilium/pkg/logging"
    39  	"github.com/cilium/cilium/pkg/logging/logfields"
    40  	"github.com/cilium/cilium/pkg/policy/api"
    41  
    42  	"github.com/sirupsen/logrus"
    43  	. "gopkg.in/check.v1"
    44  	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    45  	"k8s.io/apimachinery/pkg/api/errors"
    46  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    47  	"k8s.io/apimachinery/pkg/runtime"
    48  	"k8s.io/apimachinery/pkg/util/wait"
    49  	k8sTesting "k8s.io/client-go/testing"
    50  	"k8s.io/client-go/tools/cache"
    51  )
    52  
    53  type K8sIntegrationSuite struct{}
    54  
    55  var _ = Suite(&K8sIntegrationSuite{})
    56  
    57  func (k *K8sIntegrationSuite) SetUpSuite(c *C) {
    58  	if true {
    59  		logging.DefaultLogger.SetLevel(logrus.PanicLevel)
    60  		log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s)
    61  	}
    62  	if os.Getenv("INTEGRATION") != "" {
    63  		if k8sConfigPath := os.Getenv("KUBECONFIG"); k8sConfigPath == "" {
    64  			Configure("", "/var/lib/cilium/cilium.kubeconfig", defaults.K8sClientQPSLimit, defaults.K8sClientBurst)
    65  		} else {
    66  			Configure("", k8sConfigPath, defaults.K8sClientQPSLimit, defaults.K8sClientBurst)
    67  		}
    68  		restConfig, err := CreateConfig()
    69  		c.Assert(err, IsNil)
    70  		apiextensionsclientset, err := apiextensionsclient.NewForConfig(restConfig)
    71  		c.Assert(err, IsNil)
    72  		err = v2.CreateCustomResourceDefinitions(apiextensionsclientset)
    73  		c.Assert(err, IsNil)
    74  
    75  		client, err := clientset.NewForConfig(restConfig)
    76  		c.Assert(err, IsNil)
    77  		client.CiliumV2().CiliumNetworkPolicies("default").Delete("testing-policy", &metav1.DeleteOptions{})
    78  	}
    79  }
    80  
    81  func testUpdateCNPNodeStatusK8s(integrationTest bool, k8sVersion string, c *C) {
    82  	// For k8s <v1.13
    83  	// the unit tests will perform 3 actions, A, B and C where:
    84  	// A-1.10) update k8s1 node status
    85  	//    this will make 1 attempt as it is the first node populating status
    86  	// B-1.10) update k8s2 node status
    87  	//    this will make 3 attempts
    88  	// C-1.10) update k8s1 node status with revision=2 and enforcing=false
    89  	//    this will make 3 attempts
    90  	// the code paths for A-1.10, B-1.10 and C-1.10 can be found in the comments
    91  
    92  	// For k8s >=v1.13
    93  	// the unit tests will perform 3 actions, A, B and C where:
    94  	// A-1.13) update k8s1 node status
    95  	//         this will make 1 attempt as it is the first node populating status
    96  	// B-1.13) update k8s2 node status
    97  	//         this will make 2 attempts
    98  	// C-1.13) update k8s1 node status with revision=2 and enforcing=false
    99  	//         this will make 2 attempts
   100  	// the code paths for A-1.13, B-1.13 and C-1.13 can be found in the comments
   101  
   102  	err := k8sversion.Force(k8sVersion)
   103  	c.Assert(err, IsNil)
   104  
   105  	cnp := &types.SlimCNP{
   106  		CiliumNetworkPolicy: &v2.CiliumNetworkPolicy{
   107  			TypeMeta: metav1.TypeMeta{
   108  				Kind:       "CiliumNetworkPolicy",
   109  				APIVersion: "cilium.io/v2",
   110  			},
   111  			ObjectMeta: metav1.ObjectMeta{
   112  				Name:      "testing-policy",
   113  				Namespace: "default",
   114  			},
   115  			Spec: &api.Rule{
   116  				EndpointSelector: api.EndpointSelector{
   117  					LabelSelector: &metav1.LabelSelector{
   118  						MatchLabels: map[string]string{
   119  							"foo": "bar",
   120  						},
   121  					},
   122  				},
   123  			},
   124  		},
   125  	}
   126  
   127  	wantedCNP := cnp.DeepCopy()
   128  
   129  	wantedCNPS := v2.CiliumNetworkPolicyStatus{
   130  		Nodes: map[string]v2.CiliumNetworkPolicyNodeStatus{
   131  			"k8s1": {
   132  				Enforcing:   true,
   133  				Revision:    1,
   134  				OK:          true,
   135  				LastUpdated: v2.Timestamp{},
   136  				Annotations: map[string]string{
   137  					"foo":                            "bar",
   138  					"i-will-disappear-in-2nd-update": "bar",
   139  				},
   140  			},
   141  			"k8s2": {
   142  				Enforcing:   true,
   143  				Revision:    2,
   144  				OK:          true,
   145  				LastUpdated: v2.Timestamp{},
   146  			},
   147  		},
   148  	}
   149  
   150  	wantedCNP.Status = wantedCNPS
   151  
   152  	var ciliumNPClient clientset.Interface
   153  	if integrationTest {
   154  		restConfig, err := CreateConfig()
   155  		c.Assert(err, IsNil)
   156  		ciliumNPClient, err = clientset.NewForConfig(restConfig)
   157  		c.Assert(err, IsNil)
   158  		cnp.CiliumNetworkPolicy, err = ciliumNPClient.CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Create(cnp.CiliumNetworkPolicy)
   159  		c.Assert(err, IsNil)
   160  		defer func() {
   161  			err = ciliumNPClient.CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Delete(cnp.GetName(), &metav1.DeleteOptions{})
   162  			c.Assert(err, IsNil)
   163  		}()
   164  	} else {
   165  		ciliumNPClientFake := &fake.Clientset{}
   166  		ciliumNPClientFake.AddReactor("patch", "ciliumnetworkpolicies",
   167  			func(action k8sTesting.Action) (bool, runtime.Object, error) {
   168  				pa := action.(k8sTesting.PatchAction)
   169  				time.Sleep(1 * time.Millisecond)
   170  				var receivedJsonPatch []JSONPatch
   171  				err := json.Unmarshal(pa.GetPatch(), &receivedJsonPatch)
   172  				c.Assert(err, IsNil)
   173  
   174  				switch {
   175  				case receivedJsonPatch[0].OP == "test" && receivedJsonPatch[0].Path == "/status":
   176  					switch {
   177  					case receivedJsonPatch[0].Value == nil:
   178  						cnpns := receivedJsonPatch[1].Value.(map[string]interface{})
   179  						nodes := cnpns["nodes"].(map[string]interface{})
   180  						if nodes["k8s1"] == nil {
   181  							// codepath B-1.10) and B-1.13) 1st attempt
   182  							// This is an attempt from k8s2 so we need
   183  							// to return an error because `/status` is not nil as
   184  							// it was previously set by k8s1
   185  							return true, nil, &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonInvalid}}
   186  						}
   187  						// codepath A-1.10), C-1.10), A-1.13) and C-1.13)
   188  						n := nodes["k8s1"].(map[string]interface{})
   189  
   190  						if n["localPolicyRevision"].(float64) == 2 {
   191  							// codepath C-1.10) and C-1.13) 1st attempt
   192  							// This is an attempt from k8s1 to update its status
   193  							// again, return an error because `/status` is not nil
   194  							// as it was previously set by k8s1
   195  							return true, nil, &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonInvalid}}
   196  						}
   197  						// codepath A-1.10) and A-1.13)
   198  
   199  						// Ignore lastUpdated timestamp as it will mess up with
   200  						// the deepequals
   201  						n["lastUpdated"] = "0001-01-01T00:00:00Z"
   202  
   203  						// Remove k8s2 from the nodes status.
   204  						cnpsK8s1 := wantedCNPS.DeepCopy()
   205  						delete(cnpsK8s1.Nodes, "k8s2")
   206  						createStatusAndNodePatch := []JSONPatch{
   207  							{
   208  								OP:    "test",
   209  								Path:  "/status",
   210  								Value: nil,
   211  							},
   212  							{
   213  								OP:    "add",
   214  								Path:  "/status",
   215  								Value: cnpsK8s1,
   216  							},
   217  						}
   218  						expectedJSONPatchBytes, err := json.Marshal(createStatusAndNodePatch)
   219  						c.Assert(err, IsNil)
   220  						var expectedJSONPatch []JSONPatch
   221  						err = json.Unmarshal(expectedJSONPatchBytes, &expectedJSONPatch)
   222  						c.Assert(err, IsNil)
   223  
   224  						c.Assert(receivedJsonPatch, checker.DeepEquals, expectedJSONPatch)
   225  
   226  						// Copy the status the the cnp so we can compare it at
   227  						// the end of this test to make sure everything is alright.
   228  						cnp.Status = *cnpsK8s1
   229  						return true, cnp.CiliumNetworkPolicy, nil
   230  
   231  					case receivedJsonPatch[0].Value != nil:
   232  						// codepath B-1.10) and C-1.10) 2nd attempt
   233  						// k8s1 and k8s2 knows that `/status` exists and was created
   234  						// by a different node so he just needs to add itself to
   235  						// the list of nodes.
   236  						// "Unfortunately" the list of node is not-empty so
   237  						// the test value of `/status` needs to fail
   238  						c.Assert(cnp.Status.Nodes, Not(Equals), 0)
   239  						return true, nil, &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonInvalid}}
   240  					}
   241  				case receivedJsonPatch[0].OP == "replace":
   242  					// codepath B-1.13) and C-1.13) 2nd attempt
   243  					fallthrough
   244  				case receivedJsonPatch[0].OP == "add":
   245  					cnpns := receivedJsonPatch[0].Value.(map[string]interface{})
   246  					// codepath B-1.10) and C-1.10) 3rd attempt
   247  					// k8s2 knows that `/status` exists and was created
   248  					// by a different node so he just needs to add itself to
   249  					// the list of nodes.
   250  					if len(cnp.Status.Nodes) == 1 {
   251  						// codepath B-1.10) 3rd attempt
   252  						// k8s1 knows that `/status` exists and was populated
   253  						// by a different node so he just needs to add (update)
   254  						// itself to the list of nodes.
   255  						// Ignore lastUpdated timestamp as it will mess up with
   256  						// the deepequals
   257  						cnpns["lastUpdated"] = "0001-01-01T00:00:00Z"
   258  
   259  						// Remove k8s1 from the nodes status.
   260  						cnpsK8s2 := wantedCNPS.DeepCopy()
   261  						delete(cnpsK8s2.Nodes, "k8s1")
   262  
   263  						createStatusAndNodePatch := []JSONPatch{
   264  							{
   265  								OP:    receivedJsonPatch[0].OP,
   266  								Path:  "/status/nodes/k8s2",
   267  								Value: cnpsK8s2.Nodes["k8s2"],
   268  							},
   269  						}
   270  						expectedJSONPatchBytes, err := json.Marshal(createStatusAndNodePatch)
   271  						c.Assert(err, IsNil)
   272  						var expectedJSONPatch []JSONPatch
   273  						err = json.Unmarshal(expectedJSONPatchBytes, &expectedJSONPatch)
   274  						c.Assert(err, IsNil)
   275  
   276  						c.Assert(receivedJsonPatch, checker.DeepEquals, expectedJSONPatch)
   277  
   278  						cnp.Status.Nodes["k8s2"] = cnpsK8s2.Nodes["k8s2"]
   279  						return true, cnp.CiliumNetworkPolicy, nil
   280  					}
   281  					// codepath C-1.10) 3rd attempt
   282  					cnpns["lastUpdated"] = "0001-01-01T00:00:00Z"
   283  
   284  					// Remove k8s2 from the nodes status.
   285  					cnpsK8s1 := wantedCNPS.DeepCopy()
   286  					delete(cnpsK8s1.Nodes, "k8s2")
   287  					// This update from k8s1 should have enforcing=false and
   288  					// revision=2
   289  					nWanted := cnpsK8s1.Nodes["k8s1"]
   290  					nWanted.Revision = 2
   291  					nWanted.Enforcing = false
   292  					cnpsK8s1.Nodes["k8s1"] = nWanted
   293  
   294  					createStatusAndNodePatch := []JSONPatch{
   295  						{
   296  							OP:    receivedJsonPatch[0].OP,
   297  							Path:  "/status/nodes/k8s1",
   298  							Value: nWanted,
   299  						},
   300  					}
   301  					expectedJSONPatchBytes, err := json.Marshal(createStatusAndNodePatch)
   302  					c.Assert(err, IsNil)
   303  					var expectedJSONPatch []JSONPatch
   304  					err = json.Unmarshal(expectedJSONPatchBytes, &expectedJSONPatch)
   305  					c.Assert(err, IsNil)
   306  
   307  					c.Assert(receivedJsonPatch, checker.DeepEquals, expectedJSONPatch)
   308  
   309  					cnp.Status.Nodes["k8s1"] = cnpsK8s1.Nodes["k8s1"]
   310  					return true, cnp.CiliumNetworkPolicy, nil
   311  				}
   312  				// should never reach this point
   313  				c.FailNow()
   314  				return true, nil, fmt.Errorf("should not been called")
   315  			})
   316  		ciliumNPClient = ciliumNPClientFake
   317  	}
   318  
   319  	updateContext := &CNPStatusUpdateContext{
   320  		CiliumNPClient: ciliumNPClient,
   321  		NodeName:       "k8s1",
   322  	}
   323  
   324  	cnpns := wantedCNPS.Nodes["k8s1"]
   325  	err = updateContext.update(cnp, cnpns.Enforcing, cnpns.OK, err, cnpns.Revision, cnpns.Annotations)
   326  	c.Assert(err, IsNil)
   327  
   328  	if integrationTest {
   329  		cnp.CiliumNetworkPolicy, err = ciliumNPClient.CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Get(cnp.GetName(), metav1.GetOptions{})
   330  		c.Assert(err, IsNil)
   331  	}
   332  
   333  	updateContext = &CNPStatusUpdateContext{
   334  		CiliumNPClient: ciliumNPClient,
   335  		NodeName:       "k8s2",
   336  	}
   337  
   338  	cnpns = wantedCNPS.Nodes["k8s2"]
   339  	err = updateContext.update(cnp, cnpns.Enforcing, cnpns.OK, err, cnpns.Revision, cnpns.Annotations)
   340  	c.Assert(err, IsNil)
   341  
   342  	if integrationTest {
   343  		cnp.CiliumNetworkPolicy, err = ciliumNPClient.CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Get(cnp.GetName(), metav1.GetOptions{})
   344  		c.Assert(err, IsNil)
   345  
   346  		// Ignore timestamps
   347  		n := cnp.Status.Nodes["k8s1"]
   348  		n.LastUpdated = v2.Timestamp{}
   349  		cnp.Status.Nodes["k8s1"] = n
   350  		n = cnp.Status.Nodes["k8s2"]
   351  		n.LastUpdated = v2.Timestamp{}
   352  		cnp.Status.Nodes["k8s2"] = n
   353  
   354  		c.Assert(cnp.Status, checker.DeepEquals, wantedCNP.Status)
   355  	} else {
   356  		c.Assert(cnp.Status, checker.DeepEquals, wantedCNP.Status)
   357  	}
   358  
   359  	n := wantedCNP.Status.Nodes["k8s1"]
   360  	n.Revision = 2
   361  	n.Enforcing = false
   362  	n.Annotations = map[string]string{
   363  		"foo": "bar",
   364  	}
   365  	wantedCNP.Status.Nodes["k8s1"] = n
   366  
   367  	updateContext = &CNPStatusUpdateContext{
   368  		CiliumNPClient: ciliumNPClient,
   369  		NodeName:       "k8s1",
   370  	}
   371  
   372  	cnpns = wantedCNPS.Nodes["k8s1"]
   373  	err = updateContext.update(cnp, cnpns.Enforcing, cnpns.OK, err, cnpns.Revision, cnpns.Annotations)
   374  	c.Assert(err, IsNil)
   375  
   376  	if integrationTest {
   377  		cnp.CiliumNetworkPolicy, err = ciliumNPClient.CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Get(cnp.GetName(), metav1.GetOptions{})
   378  		c.Assert(err, IsNil)
   379  
   380  		// Ignore timestamps
   381  		n := cnp.Status.Nodes["k8s1"]
   382  		n.LastUpdated = v2.Timestamp{}
   383  		cnp.Status.Nodes["k8s1"] = n
   384  		n = cnp.Status.Nodes["k8s2"]
   385  		n.LastUpdated = v2.Timestamp{}
   386  		cnp.Status.Nodes["k8s2"] = n
   387  
   388  		c.Assert(cnp.Status, checker.DeepEquals, wantedCNP.Status)
   389  	} else {
   390  		c.Assert(cnp.Status, checker.DeepEquals, wantedCNP.Status)
   391  	}
   392  }
   393  
   394  func (k *K8sIntegrationSuite) Test_updateCNPNodeStatus_1_10(c *C) {
   395  	c.Skip("Test not available as implementation is not made")
   396  	testUpdateCNPNodeStatusK8s(os.Getenv("INTEGRATION") != "", "1.10", c)
   397  }
   398  
   399  func (k *K8sIntegrationSuite) Test_updateCNPNodeStatus_1_13(c *C) {
   400  	testUpdateCNPNodeStatusK8s(os.Getenv("INTEGRATION") != "", "1.13", c)
   401  }
   402  
   403  func benchmarkCNPNodeStatusController(integrationTest bool, nNodes int, nParallelClients int, k8sVersion string, c *C) {
   404  	if !integrationTest {
   405  		c.Skip("Unit test only available with INTEGRATION=1")
   406  	}
   407  
   408  	err := k8sversion.Force(k8sVersion)
   409  	c.Assert(err, IsNil)
   410  
   411  	cnp := &types.SlimCNP{
   412  		CiliumNetworkPolicy: &v2.CiliumNetworkPolicy{
   413  			TypeMeta: metav1.TypeMeta{
   414  				Kind:       "CiliumNetworkPolicy",
   415  				APIVersion: "cilium.io/v2",
   416  			},
   417  			ObjectMeta: metav1.ObjectMeta{
   418  				Name:      "testing-policy",
   419  				Namespace: "default",
   420  			},
   421  			Spec: &api.Rule{
   422  				EndpointSelector: api.EndpointSelector{
   423  					LabelSelector: &metav1.LabelSelector{
   424  						MatchLabels: map[string]string{"foo": "bar"},
   425  					},
   426  				},
   427  			},
   428  		},
   429  	}
   430  
   431  	restConfig, err := CreateConfig()
   432  	c.Assert(err, IsNil)
   433  	err = Init()
   434  	c.Assert(err, IsNil)
   435  
   436  	// One client per node
   437  	ciliumNPClients := make([]clientset.Interface, nNodes)
   438  	for i := range ciliumNPClients {
   439  		ciliumNPClients[i], err = clientset.NewForConfig(restConfig)
   440  		c.Assert(err, IsNil)
   441  	}
   442  
   443  	cnp.CiliumNetworkPolicy, err = ciliumNPClients[0].CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Create(cnp.CiliumNetworkPolicy)
   444  	c.Assert(err, IsNil)
   445  	defer func() {
   446  		err = ciliumNPClients[0].CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Delete(cnp.GetName(), &metav1.DeleteOptions{})
   447  		c.Assert(err, IsNil)
   448  	}()
   449  
   450  	var cnpStore cache.Store
   451  	switch {
   452  	case k8sversion.Capabilities().UpdateStatus:
   453  		// k8s >= 1.13 does not require a store
   454  	default:
   455  		// TODO create a cache.Store per node
   456  		si := informer.NewSharedInformerFactory(ciliumNPClients[0], 5*time.Minute)
   457  		ciliumV2Controller := si.Cilium().V2().CiliumNetworkPolicies().Informer()
   458  		cnpStore = ciliumV2Controller.GetStore()
   459  		si.Start(wait.NeverStop)
   460  		var exists bool
   461  		// wait for the cnp created to be in the store
   462  		for !exists {
   463  			_, exists, err = cnpStore.Get(cnp)
   464  			time.Sleep(100 * time.Millisecond)
   465  		}
   466  	}
   467  
   468  	wg := sync.WaitGroup{}
   469  	wg.Add(nNodes)
   470  	r := make(chan int, nNodes)
   471  	for i := 0; i < nParallelClients; i++ {
   472  		go func() {
   473  			for i := range r {
   474  				updateContext := &CNPStatusUpdateContext{
   475  					CiliumNPClient: ciliumNPClients[i],
   476  					NodeName:       "k8s" + strconv.Itoa(i),
   477  					CiliumV2Store:  cnpStore,
   478  					WaitForEndpointsAtPolicyRev: func(ctx context.Context, rev uint64) error {
   479  						return nil
   480  					},
   481  				}
   482  				err := updateContext.UpdateStatus(context.Background(), cnp, uint64(i), nil)
   483  				c.Assert(err, IsNil)
   484  				wg.Done()
   485  			}
   486  		}()
   487  	}
   488  
   489  	start := time.Now()
   490  	c.ResetTimer()
   491  	for i := 0; i < nNodes; i++ {
   492  		r <- i
   493  	}
   494  	wg.Wait()
   495  	c.StopTimer()
   496  	c.Logf("Test took: %s", time.Since(start))
   497  }
   498  
   499  func (k *K8sIntegrationSuite) Benchmark_CNPNodeStatusController_1_10(c *C) {
   500  	nNodes, err := strconv.Atoi(os.Getenv("NODES"))
   501  	c.Assert(err, IsNil)
   502  
   503  	// create nTh parallel clients. We achieve better results if the number
   504  	// of clients are not the same as number of NODES. We can simulate 1000 Nodes
   505  	// but we can simulate 1000 clients with a 8 CPU machine.
   506  	nClients := go_runtime.NumCPU()
   507  	if nClientsStr := os.Getenv("PARALLEL_CLIENTS"); nClientsStr != "" {
   508  		nClients, err = strconv.Atoi(nClientsStr)
   509  		c.Assert(err, IsNil)
   510  	}
   511  	c.Logf("Running with %d parallel clients and %d nodes", nClients, nNodes)
   512  	benchmarkCNPNodeStatusController(os.Getenv("INTEGRATION") != "", nNodes, nClients, "1.10", c)
   513  }
   514  
   515  func (k *K8sIntegrationSuite) Benchmark_CNPNodeStatusController_1_13(c *C) {
   516  	nNodes, err := strconv.Atoi(os.Getenv("NODES"))
   517  	c.Assert(err, IsNil)
   518  
   519  	// create nTh parallel clients. We achieve better results if the number
   520  	// of clients are not the same as number of NODES. We can simulate 1000 Nodes
   521  	// but we can simulate 1000 clients with a 8 CPU machine.
   522  	nClients := go_runtime.NumCPU()
   523  	if nClientsStr := os.Getenv("PARALLEL_CLIENTS"); nClientsStr != "" {
   524  		nClients, err = strconv.Atoi(nClientsStr)
   525  		c.Assert(err, IsNil)
   526  	}
   527  	c.Logf("Running with %d parallel clients and %d nodes", nClients, nNodes)
   528  	benchmarkCNPNodeStatusController(os.Getenv("INTEGRATION") != "", nNodes, nClients, "1.13", c)
   529  }
   530  
   531  func (k *K8sIntegrationSuite) benchmarkUpdateCNPNodeStatus(integrationTest bool, nNodes int, nParallelClients int, k8sVersion string, c *C) {
   532  	err := k8sversion.Force(k8sVersion)
   533  	c.Assert(err, IsNil)
   534  	cnp := &types.SlimCNP{
   535  		CiliumNetworkPolicy: &v2.CiliumNetworkPolicy{
   536  			TypeMeta: metav1.TypeMeta{
   537  				Kind:       "CiliumNetworkPolicy",
   538  				APIVersion: "cilium.io/v2",
   539  			},
   540  			ObjectMeta: metav1.ObjectMeta{
   541  				Name:      "testing-policy",
   542  				Namespace: "default",
   543  			},
   544  			Spec: &api.Rule{
   545  				EndpointSelector: api.EndpointSelector{
   546  					LabelSelector: &metav1.LabelSelector{
   547  						MatchLabels: map[string]string{"foo": "bar"},
   548  					},
   549  				},
   550  			},
   551  		},
   552  	}
   553  
   554  	// One client per node
   555  	ciliumNPClients := make([]clientset.Interface, nNodes)
   556  	if integrationTest {
   557  		restConfig, err := CreateConfig()
   558  		c.Assert(err, IsNil)
   559  		for i := range ciliumNPClients {
   560  			ciliumNPClients[i], err = clientset.NewForConfig(restConfig)
   561  			c.Assert(err, IsNil)
   562  		}
   563  		cnp.CiliumNetworkPolicy, err = ciliumNPClients[0].CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Create(cnp.CiliumNetworkPolicy)
   564  		c.Assert(err, IsNil)
   565  		defer func() {
   566  			err = ciliumNPClients[0].CiliumV2().CiliumNetworkPolicies(cnp.GetNamespace()).Delete(cnp.GetName(), &metav1.DeleteOptions{})
   567  			c.Assert(err, IsNil)
   568  		}()
   569  	} else {
   570  		ciliumNPClientFake := &fake.Clientset{}
   571  		ciliumNPClientFake.AddReactor("patch", "ciliumnetworkpolicies",
   572  			func(action k8sTesting.Action) (bool, runtime.Object, error) {
   573  				time.Sleep(1 * time.Millisecond)
   574  				return true, cnp.CiliumNetworkPolicy, nil
   575  			})
   576  		ciliumNPClientFake.AddReactor("get", "ciliumnetworkpolicies",
   577  			func(action k8sTesting.Action) (bool, runtime.Object, error) {
   578  				time.Sleep(1 * time.Millisecond)
   579  				return true, cnp.CiliumNetworkPolicy, nil
   580  			})
   581  		ciliumNPClientFake.AddReactor("update", "ciliumnetworkpolicies",
   582  			func(action k8sTesting.Action) (bool, runtime.Object, error) {
   583  				ua := action.(k8sTesting.UpdateAction)
   584  				cnp := ua.GetObject().(*v2.CiliumNetworkPolicy)
   585  				time.Sleep(1 * time.Millisecond)
   586  				return true, cnp, nil
   587  			})
   588  
   589  		for i := range ciliumNPClients {
   590  			ciliumNPClients[i] = ciliumNPClientFake
   591  		}
   592  	}
   593  	wg := sync.WaitGroup{}
   594  	wg.Add(nNodes)
   595  	r := make(chan int, nNodes)
   596  	for i := 0; i < nParallelClients; i++ {
   597  		go func() {
   598  			for i := range r {
   599  				updateContext := &CNPStatusUpdateContext{
   600  					CiliumNPClient: ciliumNPClients[i],
   601  					NodeName:       "k8s" + strconv.Itoa(i),
   602  				}
   603  				err := updateContext.update(cnp, true, true, nil, uint64(i), nil)
   604  				c.Assert(err, IsNil)
   605  				wg.Done()
   606  			}
   607  		}()
   608  	}
   609  
   610  	start := time.Now()
   611  	c.ResetTimer()
   612  	for i := 0; i < nNodes; i++ {
   613  		r <- i
   614  	}
   615  	wg.Wait()
   616  	c.StopTimer()
   617  	c.Logf("Test took: %s", time.Since(start))
   618  }
   619  
   620  func (k *K8sIntegrationSuite) Benchmark_UpdateCNPNodeStatus_1_10(c *C) {
   621  	nNodes, err := strconv.Atoi(os.Getenv("NODES"))
   622  	c.Assert(err, IsNil)
   623  
   624  	// create nTh parallel clients. We achieve better results if the number
   625  	// of clients are not the same as number of NODES. We can simulate 1000 Nodes
   626  	// but we can simulate 1000 clients with a 8 CPU machine.
   627  	nClients := go_runtime.NumCPU()
   628  	if nClientsStr := os.Getenv("PARALLEL_CLIENTS"); nClientsStr != "" {
   629  		nClients, err = strconv.Atoi(nClientsStr)
   630  		c.Assert(err, IsNil)
   631  	}
   632  	c.Logf("Running with %d parallel clients and %d nodes", nClients, nNodes)
   633  	k.benchmarkUpdateCNPNodeStatus(os.Getenv("INTEGRATION") != "", nNodes, nClients, "1.10", c)
   634  }
   635  
   636  func (k *K8sIntegrationSuite) Benchmark_UpdateCNPNodeStatus_1_13(c *C) {
   637  	nNodes, err := strconv.Atoi(os.Getenv("NODES"))
   638  	c.Assert(err, IsNil)
   639  
   640  	// create nTh parallel clients. We achieve better results if the number
   641  	// of clients are not the same as number of NODES. We can simulate 1000 Nodes
   642  	// but we can simulate 1000 clients with a 8 CPU machine.
   643  	nClients := go_runtime.NumCPU()
   644  	if nClientsStr := os.Getenv("PARALLEL_CLIENTS"); nClientsStr != "" {
   645  		nClients, err = strconv.Atoi(nClientsStr)
   646  		c.Assert(err, IsNil)
   647  	}
   648  	c.Logf("Running with %d parallel clients and %d nodes", nClients, nNodes)
   649  	k.benchmarkUpdateCNPNodeStatus(os.Getenv("INTEGRATION") != "", nNodes, nClients, "1.13", c)
   650  }
   651  
   652  func (k *K8sIntegrationSuite) benchmarkGetNodes(integrationTest bool, nCycles int, nParallelClients int, protobuf bool, c *C) {
   653  
   654  	// One client per node
   655  	k8sClients := make([]kubernetes.Interface, nParallelClients)
   656  	if integrationTest {
   657  		restConfig, err := CreateConfig()
   658  		c.Assert(err, IsNil)
   659  		if protobuf {
   660  			restConfig.ContentConfig.ContentType = `application/vnd.kubernetes.protobuf`
   661  		}
   662  		for i := range k8sClients {
   663  			k8sClients[i], err = kubernetes.NewForConfig(restConfig)
   664  			c.Assert(err, IsNil)
   665  		}
   666  	}
   667  	wg := sync.WaitGroup{}
   668  	wg.Add(nCycles)
   669  	r := make(chan int, nCycles)
   670  	for i := 0; i < nParallelClients; i++ {
   671  		go func(clientID int) {
   672  			for range r {
   673  				_, err := k8sClients[clientID].CoreV1().Nodes().Get("k8s1", metav1.GetOptions{})
   674  				c.Assert(err, IsNil)
   675  				wg.Done()
   676  			}
   677  		}(i)
   678  	}
   679  
   680  	start := time.Now()
   681  	c.ResetTimer()
   682  	for i := 0; i < nCycles; i++ {
   683  		r <- i
   684  	}
   685  	wg.Wait()
   686  	c.StopTimer()
   687  	c.Logf("Test took: %s", time.Since(start))
   688  }
   689  
   690  func (k *K8sIntegrationSuite) Benchmark_GetNodesProto(c *C) {
   691  	nCycles, err := strconv.Atoi(os.Getenv("CYCLES"))
   692  	if err != nil {
   693  		nCycles = c.N
   694  	}
   695  
   696  	// create nTh parallel clients. We achieve better results if the number
   697  	// of clients are not the same as number of CYCLES. We can simulate 1000 Nodes
   698  	// but we can simulate 1000 clients with a 8 CPU machine.
   699  	nClients := go_runtime.NumCPU()
   700  	if nClientsStr := os.Getenv("PARALLEL_CLIENTS"); nClientsStr != "" {
   701  		nClients, err = strconv.Atoi(nClientsStr)
   702  		c.Assert(err, IsNil)
   703  	}
   704  	c.Logf("Running with %d parallel clients and %d nodes", nClients, nCycles)
   705  	k.benchmarkGetNodes(os.Getenv("INTEGRATION") != "", nCycles, nClients, true, c)
   706  }
   707  
   708  func (k *K8sIntegrationSuite) Benchmark_GetNodesJSON(c *C) {
   709  	nCycles, err := strconv.Atoi(os.Getenv("CYCLES"))
   710  	if err != nil {
   711  		nCycles = c.N
   712  	}
   713  
   714  	// create nTh parallel clients. We achieve better results if the number
   715  	// of clients are not the same as number of CYCLES. We can simulate 1000 Nodes
   716  	// but we can simulate 1000 clients with a 8 CPU machine.
   717  	nClients := go_runtime.NumCPU()
   718  	if nClientsStr := os.Getenv("PARALLEL_CLIENTS"); nClientsStr != "" {
   719  		nClients, err = strconv.Atoi(nClientsStr)
   720  		c.Assert(err, IsNil)
   721  	}
   722  	c.Logf("Running with %d parallel clients and %d nodes", nClients, nCycles)
   723  	k.benchmarkGetNodes(os.Getenv("INTEGRATION") != "", nCycles, nClients, false, c)
   724  }