github.com/datadog/cilium@v1.6.12/daemon/status_test.go (about)

     1  // Copyright 2016-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 main
    18  
    19  import (
    20  	"math/rand"
    21  	"time"
    22  
    23  	"github.com/cilium/cilium/api/v1/models"
    24  	. "github.com/cilium/cilium/api/v1/server/restapi/daemon"
    25  	"github.com/cilium/cilium/pkg/checker"
    26  	"github.com/cilium/cilium/pkg/datapath/fake"
    27  	"github.com/cilium/cilium/pkg/mtu"
    28  	"github.com/cilium/cilium/pkg/node/manager"
    29  	"github.com/cilium/cilium/pkg/nodediscovery"
    30  	"github.com/cilium/cilium/pkg/option"
    31  
    32  	"github.com/go-openapi/runtime/middleware"
    33  	. "gopkg.in/check.v1"
    34  )
    35  
    36  type GetNodesSuite struct {
    37  }
    38  
    39  var _ = Suite(&GetNodesSuite{})
    40  
    41  var nm *manager.Manager
    42  
    43  func (g *GetNodesSuite) SetUpTest(c *C) {
    44  	option.Config.IPv4ServiceRange = AutoCIDR
    45  	option.Config.IPv6ServiceRange = AutoCIDR
    46  }
    47  
    48  func (g *GetNodesSuite) SetUpSuite(c *C) {
    49  	var err error
    50  	nm, err = manager.NewManager("", fake.NewNodeHandler())
    51  	c.Assert(err, IsNil)
    52  }
    53  
    54  func (g *GetNodesSuite) Test_getNodesHandle(c *C) {
    55  	// Set seed so we can have the same pseudorandom client IDs.
    56  	// The seed is set to 0 for each unit test.
    57  	rand.Seed(0)
    58  	const numberOfClients = 10
    59  
    60  	clientIDs := make([]int64, 0, numberOfClients)
    61  	for i := 0; i < numberOfClients; i++ {
    62  		clientIDs = append(clientIDs, rand.Int63())
    63  	}
    64  
    65  	var zero int64
    66  	type args struct {
    67  		params  GetClusterNodesParams
    68  		clients map[int64]*clusterNodesClient
    69  		daemon  *Daemon
    70  	}
    71  	type want struct {
    72  		clients   map[int64]*clusterNodesClient
    73  		responder *GetClusterNodesOK
    74  	}
    75  	tests := []struct {
    76  		name        string
    77  		setupArgs   func() args
    78  		setupWanted func() want
    79  	}{
    80  		{
    81  			name: "create a client ID and store it locally",
    82  			setupArgs: func() args {
    83  				nodeDiscovery := nodediscovery.NewNodeDiscovery(nm, mtu.NewConfiguration(0, false, false, 0))
    84  				nodeDiscovery.LocalNode.Name = "foo"
    85  				return args{
    86  					params: GetClusterNodesParams{
    87  						ClientID: &zero,
    88  					},
    89  					daemon: &Daemon{
    90  						nodeDiscovery: nodeDiscovery,
    91  					},
    92  					clients: map[int64]*clusterNodesClient{},
    93  				}
    94  			},
    95  			setupWanted: func() want {
    96  				m := &models.ClusterNodeStatus{
    97  					ClientID: clientIDs[0],
    98  					Self:     "foo",
    99  				}
   100  				return want{
   101  					clients: map[int64]*clusterNodesClient{
   102  						clientIDs[0]: {
   103  							ClusterNodeStatus: m,
   104  						},
   105  					},
   106  					responder: &GetClusterNodesOK{
   107  						Payload: m,
   108  					},
   109  				}
   110  			},
   111  		},
   112  		{
   113  			name: "retrieve nodes diff from a client that was already present",
   114  			setupArgs: func() args {
   115  				nodeDiscovery := nodediscovery.NewNodeDiscovery(nm, mtu.NewConfiguration(0, false, false, 0))
   116  				nodeDiscovery.LocalNode.Name = "foo"
   117  				return args{
   118  					params: GetClusterNodesParams{
   119  						ClientID: &clientIDs[0],
   120  					},
   121  					daemon: &Daemon{
   122  						nodeDiscovery: nodeDiscovery,
   123  					},
   124  					clients: map[int64]*clusterNodesClient{
   125  						clientIDs[0]: {
   126  							ClusterNodeStatus: &models.ClusterNodeStatus{
   127  								ClientID: clientIDs[0],
   128  								Self:     "foo",
   129  								NodesAdded: []*models.NodeElement{
   130  									{
   131  										Name: "random-node-added",
   132  									},
   133  								},
   134  							},
   135  						},
   136  					},
   137  				}
   138  			},
   139  			setupWanted: func() want {
   140  				return want{
   141  					// let's not forget once the server sends the diff to the
   142  					// client, the slice of nodes added and removed gets cleaned
   143  					// up
   144  					clients: map[int64]*clusterNodesClient{
   145  						clientIDs[0]: {
   146  							ClusterNodeStatus: &models.ClusterNodeStatus{
   147  								ClientID: clientIDs[0],
   148  								Self:     "foo",
   149  							},
   150  						},
   151  					},
   152  					responder: &GetClusterNodesOK{
   153  						Payload: &models.ClusterNodeStatus{
   154  							ClientID: clientIDs[0],
   155  							Self:     "foo",
   156  							NodesAdded: []*models.NodeElement{
   157  								{
   158  									Name: "random-node-added",
   159  								},
   160  							},
   161  						},
   162  					},
   163  				}
   164  			},
   165  		},
   166  		{
   167  			name: "retrieve nodes from an expired client, it should be ok because the clean up only happens when on insertion",
   168  			setupArgs: func() args {
   169  				nodeDiscovery := nodediscovery.NewNodeDiscovery(nm, mtu.NewConfiguration(0, false, false, 0))
   170  				nodeDiscovery.LocalNode.Name = "foo"
   171  				return args{
   172  					params: GetClusterNodesParams{
   173  						ClientID: &clientIDs[0],
   174  					},
   175  					daemon: &Daemon{
   176  						nodeDiscovery: nodeDiscovery,
   177  					},
   178  					clients: map[int64]*clusterNodesClient{
   179  						clientIDs[0]: {
   180  							lastSync: time.Now().Add(-clientGCTimeout),
   181  							ClusterNodeStatus: &models.ClusterNodeStatus{
   182  								ClientID: clientIDs[0],
   183  								Self:     "foo",
   184  								NodesAdded: []*models.NodeElement{
   185  									{
   186  										Name: "random-node-added",
   187  									},
   188  								},
   189  							},
   190  						},
   191  					},
   192  				}
   193  			},
   194  			setupWanted: func() want {
   195  				return want{
   196  					// let's not forget once the server sends the diff to the
   197  					// client, the slice of nodes added and removed gets cleaned
   198  					// up
   199  					clients: map[int64]*clusterNodesClient{
   200  						clientIDs[0]: {
   201  							ClusterNodeStatus: &models.ClusterNodeStatus{
   202  								ClientID: clientIDs[0],
   203  								Self:     "foo",
   204  							},
   205  						},
   206  					},
   207  					responder: &GetClusterNodesOK{
   208  						Payload: &models.ClusterNodeStatus{
   209  							ClientID: clientIDs[0],
   210  							Self:     "foo",
   211  							NodesAdded: []*models.NodeElement{
   212  								{
   213  									Name: "random-node-added",
   214  								},
   215  							},
   216  						},
   217  					},
   218  				}
   219  			},
   220  		},
   221  		{
   222  			name: "retrieve nodes for a new client, the expired client should be deleted",
   223  			setupArgs: func() args {
   224  				nodeDiscovery := nodediscovery.NewNodeDiscovery(nm, mtu.NewConfiguration(0, false, false, 0))
   225  				nodeDiscovery.LocalNode.Name = "foo"
   226  				return args{
   227  					params: GetClusterNodesParams{
   228  						ClientID: &zero,
   229  					},
   230  					daemon: &Daemon{
   231  						nodeDiscovery: nodeDiscovery,
   232  					},
   233  					clients: map[int64]*clusterNodesClient{
   234  						clientIDs[numberOfClients-1]: {
   235  							lastSync: time.Now().Add(-clientGCTimeout),
   236  							ClusterNodeStatus: &models.ClusterNodeStatus{
   237  								ClientID: clientIDs[numberOfClients-1],
   238  								Self:     "foo",
   239  								NodesAdded: []*models.NodeElement{
   240  									{
   241  										Name: "random-node-added",
   242  									},
   243  								},
   244  							},
   245  						},
   246  					},
   247  				}
   248  			},
   249  			setupWanted: func() want {
   250  				return want{
   251  					// let's not forget once the server sends the diff to the
   252  					// client, the slice of nodes added and removed gets cleaned
   253  					// up
   254  					clients: map[int64]*clusterNodesClient{
   255  						clientIDs[0]: {
   256  							ClusterNodeStatus: &models.ClusterNodeStatus{
   257  								ClientID: clientIDs[0],
   258  								Self:     "foo",
   259  							},
   260  						},
   261  					},
   262  					responder: &GetClusterNodesOK{
   263  						Payload: &models.ClusterNodeStatus{
   264  							ClientID: clientIDs[0],
   265  							Self:     "foo",
   266  						},
   267  					},
   268  				}
   269  			},
   270  		},
   271  		{
   272  			name: "retrieve nodes for a new client, however the randomizer allocated an existing clientID, so we should return a empty clientID",
   273  			setupArgs: func() args {
   274  				nodeDiscovery := nodediscovery.NewNodeDiscovery(nm, mtu.NewConfiguration(0, false, false, 0))
   275  				nodeDiscovery.LocalNode.Name = "foo"
   276  				return args{
   277  					params: GetClusterNodesParams{
   278  						ClientID: &zero,
   279  					},
   280  					daemon: &Daemon{
   281  						nodeDiscovery: nodeDiscovery,
   282  					},
   283  					clients: map[int64]*clusterNodesClient{
   284  						clientIDs[0]: {
   285  							ClusterNodeStatus: &models.ClusterNodeStatus{
   286  								ClientID: clientIDs[0],
   287  								Self:     "foo",
   288  								NodesAdded: []*models.NodeElement{
   289  									{
   290  										Name: "random-node-added",
   291  									},
   292  								},
   293  							},
   294  						},
   295  					},
   296  				}
   297  			},
   298  			setupWanted: func() want {
   299  				return want{
   300  					clients: map[int64]*clusterNodesClient{
   301  						clientIDs[0]: {
   302  							ClusterNodeStatus: &models.ClusterNodeStatus{
   303  								ClientID: clientIDs[0],
   304  								Self:     "foo",
   305  								NodesAdded: []*models.NodeElement{
   306  									{
   307  										Name: "random-node-added",
   308  									},
   309  								},
   310  							},
   311  						},
   312  					},
   313  					responder: &GetClusterNodesOK{
   314  						Payload: &models.ClusterNodeStatus{
   315  							Self: "foo",
   316  						},
   317  					},
   318  				}
   319  			},
   320  		},
   321  		{
   322  			name: "retrieve nodes for a client that does not want to have diffs, leave all other stored clients alone",
   323  			setupArgs: func() args {
   324  				nodeDiscovery := nodediscovery.NewNodeDiscovery(nm, mtu.NewConfiguration(0, false, false, 0))
   325  				nodeDiscovery.LocalNode.Name = "foo"
   326  				return args{
   327  					params: GetClusterNodesParams{},
   328  					daemon: &Daemon{
   329  						nodeDiscovery: nodeDiscovery,
   330  					},
   331  					clients: map[int64]*clusterNodesClient{
   332  						clientIDs[0]: {
   333  							ClusterNodeStatus: &models.ClusterNodeStatus{
   334  								ClientID: clientIDs[0],
   335  								Self:     "foo",
   336  								NodesAdded: []*models.NodeElement{
   337  									{
   338  										Name: "random-node-added",
   339  									},
   340  								},
   341  							},
   342  						},
   343  					},
   344  				}
   345  			},
   346  			setupWanted: func() want {
   347  				return want{
   348  					clients: map[int64]*clusterNodesClient{
   349  						clientIDs[0]: {
   350  							ClusterNodeStatus: &models.ClusterNodeStatus{
   351  								ClientID: clientIDs[0],
   352  								Self:     "foo",
   353  								NodesAdded: []*models.NodeElement{
   354  									{
   355  										Name: "random-node-added",
   356  									},
   357  								},
   358  							},
   359  						},
   360  					},
   361  					responder: &GetClusterNodesOK{
   362  						Payload: &models.ClusterNodeStatus{
   363  							Self: "foo",
   364  						},
   365  					},
   366  				}
   367  			},
   368  		},
   369  	}
   370  
   371  	for _, tt := range tests {
   372  		c.Log(tt.name)
   373  		rand.Seed(0)
   374  		args := tt.setupArgs()
   375  		want := tt.setupWanted()
   376  		h := &getNodes{
   377  			clients: args.clients,
   378  			d:       args.daemon,
   379  		}
   380  		responder := h.Handle(args.params)
   381  		c.Assert(len(h.clients), checker.DeepEquals, len(want.clients))
   382  		for k, v := range h.clients {
   383  			wantClient, ok := want.clients[k]
   384  			c.Assert(ok, Equals, true)
   385  			c.Assert(v.ClusterNodeStatus, checker.DeepEquals, wantClient.ClusterNodeStatus)
   386  		}
   387  		c.Assert(responder, checker.DeepEquals, middleware.Responder(want.responder))
   388  	}
   389  }
   390  
   391  func (g *GetNodesSuite) Test_cleanupClients(c *C) {
   392  	now := time.Now()
   393  	type args struct {
   394  		clients map[int64]*clusterNodesClient
   395  	}
   396  	tests := []struct {
   397  		name        string
   398  		setupArgs   func() args
   399  		setupWanted func() args
   400  	}{
   401  		{
   402  			name: "delete expired clients",
   403  			setupArgs: func() args {
   404  				return args{
   405  					clients: map[int64]*clusterNodesClient{
   406  						0: {
   407  							lastSync: now.Add(-clientGCTimeout),
   408  						},
   409  						1: {
   410  							lastSync: now,
   411  						},
   412  					},
   413  				}
   414  			},
   415  			setupWanted: func() args {
   416  				return args{
   417  					clients: map[int64]*clusterNodesClient{
   418  						1: {
   419  							lastSync: now,
   420  						},
   421  					},
   422  				}
   423  			},
   424  		},
   425  	}
   426  
   427  	for _, tt := range tests {
   428  		c.Log(tt.name)
   429  		args := tt.setupArgs()
   430  		want := tt.setupWanted()
   431  		h := &getNodes{
   432  			clients: args.clients,
   433  			d: &Daemon{
   434  				nodeDiscovery: nodediscovery.NewNodeDiscovery(nm, mtu.NewConfiguration(0, false, false, 0)),
   435  			},
   436  		}
   437  		h.cleanupClients()
   438  		c.Assert(h.clients, checker.DeepEquals, want.clients)
   439  	}
   440  }