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 }