github.com/imran-kn/cilium-fork@v1.6.9/daemon/endpoint_test.go (about)

     1  // Copyright 2018 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  	"context"
    21  	"net"
    22  	"runtime"
    23  	"time"
    24  
    25  	"github.com/cilium/cilium/api/v1/models"
    26  	apiEndpoint "github.com/cilium/cilium/api/v1/server/restapi/endpoint"
    27  	"github.com/cilium/cilium/pkg/checker"
    28  	"github.com/cilium/cilium/pkg/endpoint"
    29  	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
    30  	"github.com/cilium/cilium/pkg/endpointmanager"
    31  	"github.com/cilium/cilium/pkg/eventqueue"
    32  	"github.com/cilium/cilium/pkg/identity"
    33  	"github.com/cilium/cilium/pkg/labels"
    34  	"github.com/cilium/cilium/pkg/metrics"
    35  	"github.com/cilium/cilium/pkg/option"
    36  
    37  	. "gopkg.in/check.v1"
    38  )
    39  
    40  func getEPTemplate(c *C, d *Daemon) *models.EndpointChangeRequest {
    41  	ip4, ip6, err := d.ipam.AllocateNext("", "test")
    42  	c.Assert(err, Equals, nil)
    43  	c.Assert(ip4, Not(IsNil))
    44  	c.Assert(ip6, Not(IsNil))
    45  
    46  	return &models.EndpointChangeRequest{
    47  		ContainerName: "foo",
    48  		State:         models.EndpointStateWaitingForIdentity,
    49  		Addressing: &models.AddressPair{
    50  			IPV6: ip6.IP.String(),
    51  			IPV4: ip4.IP.String(),
    52  		},
    53  	}
    54  }
    55  
    56  func (ds *DaemonSuite) TestEndpointAddReservedLabel(c *C) {
    57  	assertOnMetric(c, string(models.EndpointStateWaitingForIdentity), 0)
    58  
    59  	epTemplate := getEPTemplate(c, ds.d)
    60  	epTemplate.Labels = []string{"reserved:world"}
    61  	_, code, err := ds.d.createEndpoint(context.TODO(), epTemplate)
    62  	c.Assert(err, Not(IsNil))
    63  	c.Assert(code, Equals, apiEndpoint.PutEndpointIDInvalidCode)
    64  
    65  	// Endpoint was created with invalid data; should transition from
    66  	// WaitForIdentity -> Invalid.
    67  	assertOnMetric(c, string(models.EndpointStateWaitingForIdentity), 0)
    68  	assertOnMetric(c, string(models.EndpointStateInvalid), 0)
    69  
    70  	// Endpoint is created with inital label as well as disallowed
    71  	// reserved:world label.
    72  	epTemplate.Labels = append(epTemplate.Labels, "reserved:init")
    73  	_, code, err = ds.d.createEndpoint(context.TODO(), epTemplate)
    74  	c.Assert(err, ErrorMatches, "not allowed to add reserved labels:.+")
    75  	c.Assert(code, Equals, apiEndpoint.PutEndpointIDInvalidCode)
    76  
    77  	// Endpoint was created with invalid data; should transition from
    78  	// WaitForIdentity -> Invalid.
    79  	assertOnMetric(c, string(models.EndpointStateWaitingForIdentity), 0)
    80  	assertOnMetric(c, string(models.EndpointStateInvalid), 0)
    81  }
    82  
    83  func (ds *DaemonSuite) TestEndpointAddInvalidLabel(c *C) {
    84  	assertOnMetric(c, string(models.EndpointStateWaitingForIdentity), 0)
    85  
    86  	epTemplate := getEPTemplate(c, ds.d)
    87  	epTemplate.Labels = []string{"reserved:foo"}
    88  	_, code, err := ds.d.createEndpoint(context.TODO(), epTemplate)
    89  	c.Assert(err, Not(IsNil))
    90  	c.Assert(code, Equals, apiEndpoint.PutEndpointIDInvalidCode)
    91  
    92  	// Endpoint was created with invalid data; should transition from
    93  	// WaitForIdentity -> Invalid.
    94  	assertOnMetric(c, string(models.EndpointStateWaitingForIdentity), 0)
    95  	assertOnMetric(c, string(models.EndpointStateInvalid), 0)
    96  }
    97  
    98  func (ds *DaemonSuite) TestEndpointAddNoLabels(c *C) {
    99  	assertOnMetric(c, string(models.EndpointStateWaitingForIdentity), 0)
   100  
   101  	// Create the endpoint without any labels.
   102  	epTemplate := getEPTemplate(c, ds.d)
   103  	_, _, err := ds.d.createEndpoint(context.TODO(), epTemplate)
   104  	c.Assert(err, IsNil)
   105  
   106  	// Endpoint enters WaitingToRegenerate as it has its labels updated during
   107  	// creation.
   108  	assertOnMetric(c, string(models.EndpointStateWaitingToRegenerate), 1)
   109  
   110  	expectedLabels := labels.Labels{
   111  		labels.IDNameInit: labels.NewLabel(labels.IDNameInit, "", labels.LabelSourceReserved),
   112  	}
   113  	// Check that the endpoint has the reserved:init label.
   114  	ep, err := endpointmanager.Lookup(endpointid.NewIPPrefixID(net.ParseIP(epTemplate.Addressing.IPV4)))
   115  	c.Assert(err, IsNil)
   116  	c.Assert(ep.OpLabels.IdentityLabels(), checker.DeepEquals, expectedLabels)
   117  
   118  	// Check that the endpoint received the reserved identity for the
   119  	// reserved:init entities.
   120  	timeout := time.NewTimer(3 * time.Second)
   121  	defer timeout.Stop()
   122  	tick := time.NewTicker(200 * time.Millisecond)
   123  	defer tick.Stop()
   124  	var secID *identity.Identity
   125  Loop:
   126  	for {
   127  		select {
   128  		case <-timeout.C:
   129  			break Loop
   130  		case <-tick.C:
   131  			ep.UnconditionalRLock()
   132  			secID = ep.SecurityIdentity
   133  			ep.RUnlock()
   134  			if secID != nil {
   135  				break Loop
   136  			}
   137  		}
   138  	}
   139  	c.Assert(secID, Not(IsNil))
   140  	c.Assert(secID.ID, Equals, identity.ReservedIdentityInit)
   141  
   142  	// Endpoint should transition from WaitingToRegenerate -> Ready.
   143  	assertOnMetric(c, string(models.EndpointStateWaitingToRegenerate), 0)
   144  	assertOnMetric(c, string(models.EndpointStateReady), 1)
   145  }
   146  
   147  func (ds *DaemonSuite) TestUpdateSecLabels(c *C) {
   148  	lbls := labels.NewLabelsFromModel([]string{"reserved:world"})
   149  	code, err := ds.d.modifyEndpointIdentityLabelsFromAPI("1", lbls, nil)
   150  	c.Assert(err, Not(IsNil))
   151  	c.Assert(code, Equals, apiEndpoint.PatchEndpointIDLabelsUpdateFailedCode)
   152  }
   153  
   154  func (ds *DaemonSuite) TestUpdateLabelsFailed(c *C) {
   155  	cancelledContext, cancelFunc := context.WithTimeout(context.Background(), 1*time.Second)
   156  	cancelFunc() // Cancel immediatly to trigger the codepath to test.
   157  
   158  	// Create the endpoint without any labels.
   159  	epTemplate := getEPTemplate(c, ds.d)
   160  	_, _, err := ds.d.createEndpoint(cancelledContext, epTemplate)
   161  	c.Assert(err, ErrorMatches, "request cancelled while resolving identity")
   162  
   163  	assertOnMetric(c, string(models.EndpointStateReady), 0)
   164  }
   165  
   166  func getMetricValue(state string) int64 {
   167  	return int64(metrics.GetGaugeValue(metrics.EndpointStateCount.WithLabelValues(state)))
   168  }
   169  
   170  func assertOnMetric(c *C, state string, expected int64) {
   171  	obtained := getMetricValue(state)
   172  	if obtained != expected {
   173  		_, _, line, _ := runtime.Caller(1)
   174  		c.Errorf("Metrics assertion failed on line %d for Endpoint state %s: obtained %d, expected %d",
   175  			line, state, obtained, expected)
   176  	}
   177  }
   178  
   179  type EndpointDeadlockEvent struct {
   180  	ep           *endpoint.Endpoint
   181  	deadlockChan chan struct{}
   182  }
   183  
   184  var (
   185  	deadlockTimeout     = 2 * time.Second
   186  	deadlockTestTimeout = 3*deadlockTimeout + 1*time.Second
   187  )
   188  
   189  func (n *EndpointDeadlockEvent) Handle(ifc chan interface{}) {
   190  	// We need to sleep here so that we are consuming an event off the queue,
   191  	// but not acquiring the lock yet.
   192  	// There isn't much of a better way to ensure that an Event is being
   193  	// processed off of the EventQueue, but hasn't acquired the Endpoint's
   194  	// lock *before* we call deleteEndpointQuiet (see below test).
   195  	close(n.deadlockChan)
   196  	time.Sleep(deadlockTimeout)
   197  	n.ep.UnconditionalLock()
   198  	n.ep.Unlock()
   199  }
   200  
   201  // This unit test is a bit weird - see
   202  // https://github.com/cilium/cilium/pull/8687 .
   203  func (ds *DaemonSuite) TestEndpointEventQueueDeadlockUponDeletion(c *C) {
   204  	// Need to modify global configuration (hooray!), change back when test is
   205  	// done.
   206  	oldQueueSize := option.Config.EndpointQueueSize
   207  	option.Config.EndpointQueueSize = 1
   208  	defer func() {
   209  		option.Config.EndpointQueueSize = oldQueueSize
   210  	}()
   211  
   212  	// Create the endpoint without any labels.
   213  	epTemplate := getEPTemplate(c, ds.d)
   214  	ep, _, err := ds.d.createEndpoint(context.TODO(), epTemplate)
   215  	c.Assert(err, IsNil)
   216  	c.Assert(ep, Not(IsNil))
   217  
   218  	// In case deadlock occurs, provide a timeout of 3 (number of events) *
   219  	// deadlockTimeout + 1 seconds to ensure that we are actually testing for
   220  	// deadlock, and not prematurely exiting, and also so the test suite doesn't
   221  	// hang forever.
   222  	ctx, cancel := context.WithTimeout(context.Background(), deadlockTestTimeout)
   223  	defer cancel()
   224  
   225  	// Create three events that go on the endpoint's EventQueue. We need three
   226  	// events because the first event enqueued immediately is consumed off of
   227  	// the queue; the second event is put onto the queue (which has length of
   228  	// one), and the third queue is waiting for the queue's buffer to not be
   229  	// full (e.g., the first event is finished processing). If the first event
   230  	// gets stuck processing forever due to deadlock, then the third event
   231  	// will never be consumed, and the endpoint's EventQueue will never be
   232  	// closed because Enqueue gets stuck.
   233  	ev1Ch := make(chan struct{})
   234  	ev2Ch := make(chan struct{})
   235  	ev3Ch := make(chan struct{})
   236  
   237  	ev := eventqueue.NewEvent(&EndpointDeadlockEvent{
   238  		ep:           ep,
   239  		deadlockChan: ev1Ch,
   240  	})
   241  
   242  	ev2 := eventqueue.NewEvent(&EndpointDeadlockEvent{
   243  		ep:           ep,
   244  		deadlockChan: ev2Ch,
   245  	})
   246  
   247  	ev3 := eventqueue.NewEvent(&EndpointDeadlockEvent{
   248  		ep:           ep,
   249  		deadlockChan: ev3Ch,
   250  	})
   251  
   252  	ev2EnqueueCh := make(chan struct{})
   253  
   254  	go func() {
   255  		_, err := ep.EventQueue.Enqueue(ev)
   256  		c.Assert(err, IsNil)
   257  		_, err = ep.EventQueue.Enqueue(ev2)
   258  		c.Assert(err, IsNil)
   259  		close(ev2EnqueueCh)
   260  		_, err = ep.EventQueue.Enqueue(ev3)
   261  		c.Assert(err, IsNil)
   262  	}()
   263  
   264  	// Ensure that the second event is enqueued before proceeding further, as
   265  	// we need to assume that at least one event is being processed, and another
   266  	// one is pushed onto the endpoint's EventQueue.
   267  	<-ev2EnqueueCh
   268  	epDelComplete := make(chan struct{})
   269  
   270  	// Launch endpoint deletion async so that we do not deadlock (which is what
   271  	// this unit test is designed to test).
   272  	go func(ch chan struct{}) {
   273  		errors := ds.d.deleteEndpointQuiet(ep, endpoint.DeleteConfig{})
   274  		c.Assert(errors, Not(IsNil))
   275  		epDelComplete <- struct{}{}
   276  	}(epDelComplete)
   277  
   278  	select {
   279  	case <-ctx.Done():
   280  		c.Log("endpoint deletion did not complete in time")
   281  		c.Fail()
   282  	case <-epDelComplete:
   283  		// Success, do nothing.
   284  	}
   285  }