github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/daemon/endpoint.go (about) 1 // Copyright 2016-2020 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 package main 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "net" 22 "sync" 23 "time" 24 25 "github.com/cilium/cilium/api/v1/models" 26 . "github.com/cilium/cilium/api/v1/server/restapi/endpoint" 27 "github.com/cilium/cilium/pkg/api" 28 "github.com/cilium/cilium/pkg/completion" 29 "github.com/cilium/cilium/pkg/endpoint" 30 endpointid "github.com/cilium/cilium/pkg/endpoint/id" 31 "github.com/cilium/cilium/pkg/endpoint/regeneration" 32 "github.com/cilium/cilium/pkg/endpointmanager" 33 "github.com/cilium/cilium/pkg/k8s" 34 k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 35 "github.com/cilium/cilium/pkg/labels" 36 "github.com/cilium/cilium/pkg/logging/logfields" 37 "github.com/cilium/cilium/pkg/maps/lxcmap" 38 monitorAPI "github.com/cilium/cilium/pkg/monitor/api" 39 "github.com/cilium/cilium/pkg/option" 40 "github.com/cilium/cilium/pkg/workloads" 41 42 "github.com/go-openapi/runtime/middleware" 43 ) 44 45 type getEndpoint struct { 46 d *Daemon 47 } 48 49 func NewGetEndpointHandler(d *Daemon) GetEndpointHandler { 50 return &getEndpoint{d: d} 51 } 52 53 func (h *getEndpoint) Handle(params GetEndpointParams) middleware.Responder { 54 log.WithField(logfields.Params, logfields.Repr(params)).Debug("GET /endpoint request") 55 resEPs := getEndpointList(params) 56 57 if params.Labels != nil && len(resEPs) == 0 { 58 return NewGetEndpointNotFound() 59 } 60 61 return NewGetEndpointOK().WithPayload(resEPs) 62 } 63 64 func getEndpointList(params GetEndpointParams) []*models.Endpoint { 65 var ( 66 epModelsWg, epsAppendWg sync.WaitGroup 67 convertedLabels labels.Labels 68 resEPs []*models.Endpoint 69 ) 70 71 if params.Labels != nil { 72 // Convert params.Labels to model that we can compare with the endpoint's labels. 73 convertedLabels = labels.NewLabelsFromModel(params.Labels) 74 } 75 76 eps := endpointmanager.GetEndpoints() 77 epModelsCh := make(chan *models.Endpoint, len(eps)) 78 79 epModelsWg.Add(len(eps)) 80 for _, ep := range eps { 81 go func(wg *sync.WaitGroup, epChan chan<- *models.Endpoint, ep *endpoint.Endpoint) { 82 if ep.HasLabels(convertedLabels) { 83 epChan <- ep.GetModel() 84 } 85 wg.Done() 86 }(&epModelsWg, epModelsCh, ep) 87 } 88 89 epsAppendWg.Add(1) 90 // This needs to be done over channels since we might not receive all 91 // the existing endpoints since not all endpoints contain the list of 92 // labels that we will use to filter in `ep.HasLabels(convertedLabels)` 93 go func(epsAppended *sync.WaitGroup) { 94 for ep := range epModelsCh { 95 resEPs = append(resEPs, ep) 96 } 97 epsAppended.Done() 98 }(&epsAppendWg) 99 100 epModelsWg.Wait() 101 close(epModelsCh) 102 epsAppendWg.Wait() 103 104 return resEPs 105 } 106 107 type getEndpointID struct { 108 d *Daemon 109 } 110 111 func NewGetEndpointIDHandler(d *Daemon) GetEndpointIDHandler { 112 return &getEndpointID{d: d} 113 } 114 115 func (h *getEndpointID) Handle(params GetEndpointIDParams) middleware.Responder { 116 log.WithField(logfields.EndpointID, params.ID).Debug("GET /endpoint/{id} request") 117 118 ep, err := endpointmanager.Lookup(params.ID) 119 120 if err != nil { 121 return api.Error(GetEndpointIDInvalidCode, err) 122 } else if ep == nil { 123 return NewGetEndpointIDNotFound() 124 } else { 125 return NewGetEndpointIDOK().WithPayload(ep.GetModel()) 126 } 127 } 128 129 type putEndpointID struct { 130 d *Daemon 131 } 132 133 func NewPutEndpointIDHandler(d *Daemon) PutEndpointIDHandler { 134 return &putEndpointID{d: d} 135 } 136 137 // fetchK8sLabels wraps the k8s package to fetch and provide 138 // endpoint metadata. It implements endpoint.MetadataResolverCB. 139 func fetchK8sLabels(ep *endpoint.Endpoint) (labels.Labels, labels.Labels, error) { 140 lbls, err := k8s.GetPodLabels(ep.GetK8sNamespace(), ep.GetK8sPodName()) 141 if err != nil { 142 return nil, nil, err 143 } 144 145 k8sLbls := labels.Map2Labels(lbls, labels.LabelSourceK8s) 146 identityLabels, infoLabels := labels.FilterLabels(k8sLbls) 147 return identityLabels, infoLabels, nil 148 } 149 150 func invalidDataError(ep *endpoint.Endpoint, err error) (*endpoint.Endpoint, int, error) { 151 ep.Logger(daemonSubsys).WithError(err).Warning("Creation of endpoint failed due to invalid data") 152 ep.SetState(endpoint.StateInvalid, "Invalid endpoint") 153 return nil, PutEndpointIDInvalidCode, err 154 } 155 156 func (d *Daemon) errorDuringCreation(ep *endpoint.Endpoint, err error) (*endpoint.Endpoint, int, error) { 157 d.deleteEndpointQuiet(ep, endpoint.DeleteConfig{ 158 // The IP has been provided by the caller and must be released 159 // by the caller 160 NoIPRelease: true, 161 }) 162 ep.Logger(daemonSubsys).WithError(err).Warning("Creation of endpoint failed") 163 return nil, PutEndpointIDFailedCode, err 164 } 165 166 // createEndpoint attempts to create the endpoint corresponding to the change 167 // request that was specified. 168 func (d *Daemon) createEndpoint(ctx context.Context, epTemplate *models.EndpointChangeRequest) (*endpoint.Endpoint, int, error) { 169 if option.Config.EnableEndpointRoutes { 170 if epTemplate.DatapathConfiguration == nil { 171 epTemplate.DatapathConfiguration = &models.EndpointDatapathConfiguration{} 172 } 173 174 // Indicate to insert a per endpoint route instead of routing 175 // via cilium_host interface 176 epTemplate.DatapathConfiguration.InstallEndpointRoute = true 177 178 // Since routing occurs via endpoint interface directly, BPF 179 // program is needed on that device at egress as BPF program on 180 // cilium_host interface is bypassed 181 epTemplate.DatapathConfiguration.RequireEgressProg = true 182 183 // Delegate routing to the Linux stack rather than tail-calling 184 // between BPF programs. 185 disabled := false 186 epTemplate.DatapathConfiguration.RequireRouting = &disabled 187 } 188 189 ep, err := endpoint.NewEndpointFromChangeModel(d, epTemplate) 190 if err != nil { 191 return invalidDataError(ep, fmt.Errorf("unable to parse endpoint parameters: %s", err)) 192 } 193 194 oldEp := endpointmanager.LookupCiliumID(ep.ID) 195 if oldEp != nil { 196 return invalidDataError(ep, fmt.Errorf("endpoint ID %d already exists", ep.ID)) 197 } 198 199 oldEp = endpointmanager.LookupContainerID(ep.ContainerID) 200 if oldEp != nil { 201 return invalidDataError(ep, fmt.Errorf("endpoint for container %s already exists", ep.ContainerID)) 202 } 203 204 var checkIDs []string 205 206 if ep.IPv4.IsSet() { 207 checkIDs = append(checkIDs, endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String())) 208 } 209 210 if ep.IPv6.IsSet() { 211 checkIDs = append(checkIDs, endpointid.NewID(endpointid.IPv6Prefix, ep.IPv6.String())) 212 } 213 214 for _, id := range checkIDs { 215 oldEp, err := endpointmanager.Lookup(id) 216 if err != nil { 217 return invalidDataError(ep, err) 218 } else if oldEp != nil { 219 return invalidDataError(ep, fmt.Errorf("IP %s is already in use", id)) 220 } 221 } 222 223 if err = endpoint.APICanModify(ep); err != nil { 224 return invalidDataError(ep, err) 225 } 226 227 addLabels := labels.NewLabelsFromModel(epTemplate.Labels) 228 infoLabels := labels.NewLabelsFromModel([]string{}) 229 230 if len(addLabels) > 0 { 231 if lbls := addLabels.FindReserved(); lbls != nil { 232 return invalidDataError(ep, fmt.Errorf("not allowed to add reserved labels: %s", lbls)) 233 } 234 235 addLabels, _, _ = checkLabels(addLabels, nil) 236 if len(addLabels) == 0 { 237 return invalidDataError(ep, fmt.Errorf("no valid labels provided")) 238 } 239 } 240 241 if ep.K8sNamespaceAndPodNameIsSet() && k8s.IsEnabled() { 242 identityLabels, info, err := fetchK8sLabels(ep) 243 if err != nil { 244 ep.Logger("api").WithError(err).Warning("Unable to fetch kubernetes labels") 245 } else { 246 addLabels.MergeLabels(identityLabels) 247 infoLabels.MergeLabels(info) 248 } 249 } 250 251 if len(addLabels) == 0 { 252 // If the endpoint has no labels, give the endpoint a special identity with 253 // label reserved:init so we can generate a custom policy for it until we 254 // get its actual identity. 255 addLabels = labels.Labels{ 256 labels.IDNameInit: labels.NewLabel(labels.IDNameInit, "", labels.LabelSourceReserved), 257 } 258 } 259 260 // Static pods (mirror pods) might be configured before the apiserver 261 // is available or has received the notification that includes the 262 // static pod's labels. In this case, start a controller to attempt to 263 // resolve the labels. 264 if ep.K8sNamespaceAndPodNameIsSet() && k8s.IsEnabled() { 265 // If there are labels, but no pod namespace, then it's 266 // likely that there are no k8s labels at all. Resolve. 267 if _, k8sLabelsConfigured := addLabels[k8sConst.PodNamespaceLabel]; !k8sLabelsConfigured { 268 ep.RunMetadataResolver(fetchK8sLabels) 269 } 270 } 271 272 err = endpointmanager.AddEndpoint(d, ep, "Create endpoint from API PUT") 273 logger := ep.Logger(daemonSubsys) 274 if err != nil { 275 return d.errorDuringCreation(ep, fmt.Errorf("unable to insert endpoint into manager: %s", err)) 276 } 277 278 ep.UpdateLabels(ctx, addLabels, infoLabels, true) 279 280 select { 281 case <-ctx.Done(): 282 return d.errorDuringCreation(ep, fmt.Errorf("request cancelled while resolving identity")) 283 default: 284 } 285 286 if err := ep.LockAlive(); err != nil { 287 return d.errorDuringCreation(ep, fmt.Errorf("endpoint was deleted while processing the request")) 288 } 289 290 // Now that we have ep.ID we can pin the map from this point. This 291 // also has to happen before the first build took place. 292 if err = ep.PinDatapathMap(); err != nil { 293 ep.Unlock() 294 return d.errorDuringCreation(ep, fmt.Errorf("unable to pin datapath maps: %s", err)) 295 } 296 297 build := ep.GetStateLocked() == endpoint.StateReady 298 if build { 299 ep.SetStateLocked(endpoint.StateWaitingToRegenerate, "Identity is known at endpoint creation time") 300 } 301 ep.Unlock() 302 303 if build { 304 // Do not synchronously regenerate the endpoint when first creating it. 305 // We have custom logic later for waiting for specific checkpoints to be 306 // reached upon regeneration later (checking for when BPF programs have 307 // been compiled), as opposed to waiting for the entire regeneration to 308 // be complete (including proxies being configured). This is done to 309 // avoid a chicken-and-egg problem with L7 policies are imported which 310 // select the endpoint being generated, as when such policies are 311 // imported, regeneration blocks on waiting for proxies to be 312 // configured. When Cilium is used with Istio, though, the proxy is 313 // started as a sidecar, and is not launched yet when this specific code 314 // is executed; if we waited for regeneration to be complete, including 315 // proxy configuration, this code would effectively deadlock addition 316 // of endpoints. 317 ep.Regenerate(®eneration.ExternalRegenerationMetadata{ 318 Reason: "Initial build on endpoint creation", 319 ParentContext: ctx, 320 }) 321 } 322 323 // Only used for CRI-O since it does not support events. 324 if d.workloadsEventsCh != nil && ep.GetContainerID() != "" { 325 d.workloadsEventsCh <- &workloads.EventMessage{ 326 WorkloadID: ep.GetContainerID(), 327 EventType: workloads.EventTypeStart, 328 } 329 } 330 331 // Wait for endpoint to be in "ready" state if specified in API call. 332 if !epTemplate.SyncBuildEndpoint { 333 return ep, 0, nil 334 } 335 336 logger.Info("Waiting for endpoint to be generated") 337 338 // Default timeout for PUT /endpoint/{id} is 60 seconds, so put timeout 339 // in this function a bit below that timeout. If the timeout for clients 340 // in API is below this value, they will get a message containing 341 // "context deadline exceeded" if the operation takes longer than the 342 // client's configured timeout value. 343 ctx, cancel := context.WithTimeout(ctx, endpoint.EndpointGenerationTimeout) 344 345 // Check the endpoint's state and labels periodically. 346 ticker := time.NewTicker(1 * time.Second) 347 defer func() { 348 cancel() 349 ticker.Stop() 350 }() 351 352 // Wait for any successful BPF regeneration, which is indicated by any 353 // positive policy revision (>0). As long as at least one BPF 354 // regeneration is successful, the endpoint has network connectivity 355 // so we can return from the creation API call. 356 revCh := ep.WaitForPolicyRevision(ctx, 1, nil) 357 358 waitForSuccessfulBuild: 359 for { 360 select { 361 case <-revCh: 362 if ctx.Err() == nil { 363 // At least one BPF regeneration has successfully completed. 364 break waitForSuccessfulBuild 365 } 366 367 case <-ctx.Done(): 368 case <-ticker.C: 369 if err := ep.RLockAlive(); err != nil { 370 return d.errorDuringCreation(ep, fmt.Errorf("endpoint was deleted while waiting for initial endpoint generation to complete")) 371 } 372 hasSidecarProxy := ep.HasSidecarProxy() 373 ep.RUnlock() 374 if hasSidecarProxy && ep.HasBPFProgram() { 375 // If the endpoint is determined to have a sidecar proxy, 376 // return immediately to let the sidecar container start, 377 // in case it is required to enforce L7 rules. 378 logger.Info("Endpoint has sidecar proxy, returning from synchronous creation request before regeneration has succeeded") 379 break waitForSuccessfulBuild 380 } 381 } 382 383 if ctx.Err() != nil { 384 return d.errorDuringCreation(ep, fmt.Errorf("timeout while waiting for initial endpoint generation to complete")) 385 } 386 } 387 388 // The endpoint has been successfully created, stop the expiration 389 // timers of all attached IPs 390 if addressing := epTemplate.Addressing; addressing != nil { 391 if uuid := addressing.IPV4ExpirationUUID; uuid != "" { 392 if ip := net.ParseIP(addressing.IPV4); ip != nil { 393 if err := d.ipam.StopExpirationTimer(ip, uuid); err != nil { 394 return d.errorDuringCreation(ep, err) 395 } 396 } 397 } 398 if uuid := addressing.IPV6ExpirationUUID; uuid != "" { 399 if ip := net.ParseIP(addressing.IPV6); ip != nil { 400 if err := d.ipam.StopExpirationTimer(ip, uuid); err != nil { 401 return d.errorDuringCreation(ep, err) 402 } 403 } 404 } 405 } 406 407 return ep, 0, nil 408 } 409 410 func (h *putEndpointID) Handle(params PutEndpointIDParams) middleware.Responder { 411 log.WithField(logfields.Params, logfields.Repr(params)).Debug("PUT /endpoint/{id} request") 412 epTemplate := params.Endpoint 413 414 ep, code, err := h.d.createEndpoint(params.HTTPRequest.Context(), epTemplate) 415 if err != nil { 416 return api.Error(code, err) 417 } 418 419 ep.Logger(daemonSubsys).Info("Successful endpoint creation") 420 421 return NewPutEndpointIDCreated() 422 } 423 424 type patchEndpointID struct { 425 d *Daemon 426 } 427 428 func NewPatchEndpointIDHandler(d *Daemon) PatchEndpointIDHandler { 429 return &patchEndpointID{d: d} 430 } 431 432 func validPatchTransitionState(state models.EndpointState) bool { 433 switch string(state) { 434 case "", endpoint.StateWaitingForIdentity, endpoint.StateReady: 435 return true 436 } 437 return false 438 } 439 440 func (h *patchEndpointID) Handle(params PatchEndpointIDParams) middleware.Responder { 441 scopedLog := log.WithField(logfields.Params, logfields.Repr(params)) 442 scopedLog.Debug("PATCH /endpoint/{id} request") 443 444 epTemplate := params.Endpoint 445 446 // Validate the template. Assignment afterwards is atomic. 447 // Note: newEp's labels are ignored. 448 newEp, err2 := endpoint.NewEndpointFromChangeModel(h.d, epTemplate) 449 if err2 != nil { 450 return api.Error(PutEndpointIDInvalidCode, err2) 451 } 452 453 // Log invalid state transitions, but do not error out for backwards 454 // compatibility. 455 if !validPatchTransitionState(epTemplate.State) { 456 scopedLog.Debugf("PATCH /endpoint/{id} to invalid state '%s'", epTemplate.State) 457 } 458 459 ep, err := endpointmanager.Lookup(params.ID) 460 if err != nil { 461 return api.Error(GetEndpointIDInvalidCode, err) 462 } 463 if ep == nil { 464 return NewPatchEndpointIDNotFound() 465 } 466 if err = endpoint.APICanModify(ep); err != nil { 467 return api.Error(PatchEndpointIDInvalidCode, err) 468 } 469 470 // FIXME: Support changing these? 471 // - container ID 472 // - docker network id 473 // - docker endpoint id 474 // 475 // Support arbitrary changes? Support only if unset? 476 477 if err := ep.LockAlive(); err != nil { 478 return NewPatchEndpointIDNotFound() 479 } 480 481 changed := false 482 483 if epTemplate.InterfaceIndex != 0 && ep.IfIndex != newEp.IfIndex { 484 ep.IfIndex = newEp.IfIndex 485 changed = true 486 } 487 488 if epTemplate.InterfaceName != "" && ep.IfName != newEp.IfName { 489 ep.IfName = newEp.IfName 490 changed = true 491 } 492 493 // Only support transition to waiting-for-identity state, also 494 // if the request is for ready state, as we will check the 495 // existence of the security label below. Other transitions 496 // are always internally managed, but we do not error out for 497 // backwards compatibility. 498 if epTemplate.State != "" && 499 validPatchTransitionState(epTemplate.State) && 500 ep.GetStateLocked() != endpoint.StateWaitingForIdentity { 501 // Will not change state if the current state does not allow the transition. 502 if ep.SetStateLocked(endpoint.StateWaitingForIdentity, "Update endpoint from API PATCH") { 503 changed = true 504 } 505 } 506 507 if epTemplate.Mac != "" && bytes.Compare(ep.LXCMAC, newEp.LXCMAC) != 0 { 508 ep.LXCMAC = newEp.LXCMAC 509 changed = true 510 } 511 512 if epTemplate.HostMac != "" && bytes.Compare(ep.GetNodeMAC(), newEp.NodeMAC) != 0 { 513 ep.SetNodeMACLocked(newEp.NodeMAC) 514 changed = true 515 } 516 517 if epTemplate.Addressing != nil { 518 if ip := epTemplate.Addressing.IPV6; ip != "" && bytes.Compare(ep.IPv6, newEp.IPv6) != 0 { 519 ep.IPv6 = newEp.IPv6 520 changed = true 521 } 522 523 if ip := epTemplate.Addressing.IPV4; ip != "" && bytes.Compare(ep.IPv4, newEp.IPv4) != 0 { 524 ep.IPv4 = newEp.IPv4 525 changed = true 526 } 527 } 528 529 // TODO: Do something with the labels? 530 // addLabels := labels.NewLabelsFromModel(params.Endpoint.Labels) 531 532 // If desired state is waiting-for-identity but identity is already 533 // known, bump it to ready state immediately to force re-generation 534 if ep.GetStateLocked() == endpoint.StateWaitingForIdentity && ep.SecurityIdentity != nil { 535 ep.SetStateLocked(endpoint.StateReady, "Preparing to force endpoint regeneration because identity is known while handling API PATCH") 536 changed = true 537 } 538 539 reason := "" 540 if changed { 541 // Force policy regeneration as endpoint's configuration was changed. 542 // Other endpoints need not be regenerated as no labels were changed. 543 // Note that we still need to (eventually) regenerate the endpoint for 544 // the changes to take effect. 545 ep.ForcePolicyCompute() 546 547 // Transition to waiting-to-regenerate if ready. 548 if ep.GetStateLocked() == endpoint.StateReady { 549 ep.SetStateLocked(endpoint.StateWaitingToRegenerate, "Forcing endpoint regeneration because identity is known while handling API PATCH") 550 } 551 552 switch ep.GetStateLocked() { 553 case endpoint.StateWaitingToRegenerate: 554 reason = "Waiting on endpoint regeneration because identity is known while handling API PATCH" 555 case endpoint.StateWaitingForIdentity: 556 reason = "Waiting on endpoint initial program regeneration while handling API PATCH" 557 } 558 } 559 560 ep.UpdateLogger(nil) 561 ep.Unlock() 562 563 if reason != "" { 564 if err := ep.RegenerateWait(reason); err != nil { 565 return api.Error(PatchEndpointIDFailedCode, err) 566 } 567 // FIXME: Special return code to indicate regeneration happened? 568 } 569 570 return NewPatchEndpointIDOK() 571 } 572 573 func (d *Daemon) deleteEndpoint(ep *endpoint.Endpoint) int { 574 scopedLog := log.WithField(logfields.EndpointID, ep.ID) 575 errs := d.deleteEndpointQuiet(ep, endpoint.DeleteConfig{ 576 // If the IP is managed by an external IPAM, it does not need to be released 577 NoIPRelease: ep.DatapathConfiguration.ExternalIPAM, 578 }) 579 for _, err := range errs { 580 scopedLog.WithError(err).Warn("Ignoring error while deleting endpoint") 581 } 582 return len(errs) 583 } 584 585 // deleteEndpointQuiet sets the endpoint into disconnecting state and removes 586 // it from Cilium, releasing all resources associated with it such as its 587 // visibility in the endpointmanager, its BPF programs and maps, (optional) IP, 588 // L7 policy configuration, directories and controllers. 589 // 590 // Specific users such as the cilium-health EP may choose not to release the IP 591 // when deleting the endpoint. Most users should pass true for releaseIP. 592 func (d *Daemon) deleteEndpointQuiet(ep *endpoint.Endpoint, conf endpoint.DeleteConfig) []error { 593 594 // Only used for CRI-O since it does not support events. 595 if d.workloadsEventsCh != nil && ep.GetContainerID() != "" { 596 d.workloadsEventsCh <- &workloads.EventMessage{ 597 WorkloadID: ep.GetContainerID(), 598 EventType: workloads.EventTypeDelete, 599 } 600 } 601 602 errs := []error{} 603 604 // Since the endpoint is being deleted, we no longer need to run events 605 // in its event queue. This is a no-op if the queue has already been 606 // closed elsewhere. 607 ep.EventQueue.Stop() 608 609 // Wait for the queue to be drained in case an event which is currently 610 // running for the endpoint tries to acquire the lock - we cannot be sure 611 // what types of events will be pushed onto the EventQueue for an endpoint 612 // and when they will happen. After this point, no events for the endpoint 613 // will be processed on its EventQueue, specifically regenerations. 614 ep.EventQueue.WaitToBeDrained() 615 616 // Wait for existing builds to complete and prevent further builds 617 ep.BuildMutex.Lock() 618 619 // Given that we are deleting the endpoint and that no more builds are 620 // going to occur for this endpoint, close the channel which signals whether 621 // the endpoint has its BPF program compiled or not to avoid it persisting 622 // if anything is blocking on it. If a delete request has already been 623 // enqueued for this endpoint, this is a no-op. 624 ep.CloseBPFProgramChannel() 625 626 // Lock out any other writers to the endpoint. In case multiple delete 627 // requests have been enqueued, have all of them except the first 628 // return here. Ignore the request if the endpoint is already 629 // disconnected. 630 if err := ep.LockAlive(); err != nil { 631 ep.BuildMutex.Unlock() 632 return []error{} 633 } 634 ep.SetStateLocked(endpoint.StateDisconnecting, "Deleting endpoint") 635 636 // Remove the endpoint before we clean up. This ensures it is no longer 637 // listed or queued for rebuilds. 638 endpointmanager.Remove(ep) 639 640 defer func() { 641 repr, err := monitorAPI.EndpointDeleteRepr(ep) 642 // Ignore endpoint deletion if EndpointDeleteRepr != nil 643 if err == nil { 644 d.SendNotification(monitorAPI.AgentNotifyEndpointDeleted, repr) 645 } 646 }() 647 648 // If dry mode is enabled, no changes to BPF maps are performed 649 if !option.Config.DryMode { 650 if errs2 := lxcmap.DeleteElement(ep); errs2 != nil { 651 errs = append(errs, errs2...) 652 } 653 654 if errs2 := ep.DeleteMapsLocked(); errs2 != nil { 655 errs = append(errs, errs2...) 656 } 657 } 658 659 if !conf.NoIPRelease { 660 if option.Config.EnableIPv4 { 661 if err := d.ipam.ReleaseIP(ep.IPv4.IP()); err != nil { 662 errs = append(errs, fmt.Errorf("unable to release ipv4 address: %s", err)) 663 } 664 } 665 if option.Config.EnableIPv6 { 666 if err := d.ipam.ReleaseIP(ep.IPv6.IP()); err != nil { 667 errs = append(errs, fmt.Errorf("unable to release ipv6 address: %s", err)) 668 } 669 } 670 } 671 672 completionCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 673 proxyWaitGroup := completion.NewWaitGroup(completionCtx) 674 675 errs = append(errs, ep.LeaveLocked(proxyWaitGroup, conf)...) 676 ep.Unlock() 677 678 err := ep.WaitForProxyCompletions(proxyWaitGroup) 679 if err != nil { 680 errs = append(errs, fmt.Errorf("unable to remove proxy redirects: %s", err)) 681 } 682 cancel() 683 684 if option.Config.IsFlannelMasterDeviceSet() && 685 option.Config.FlannelUninstallOnExit { 686 ep.DeleteBPFProgramLocked() 687 } 688 689 ep.BuildMutex.Unlock() 690 691 return errs 692 } 693 694 func (d *Daemon) DeleteEndpoint(id string) (int, error) { 695 if ep, err := endpointmanager.Lookup(id); err != nil { 696 return 0, api.Error(DeleteEndpointIDInvalidCode, err) 697 } else if ep == nil { 698 return 0, api.New(DeleteEndpointIDNotFoundCode, "endpoint not found") 699 } else if err = endpoint.APICanModify(ep); err != nil { 700 return 0, api.Error(DeleteEndpointIDInvalidCode, err) 701 } else { 702 return d.deleteEndpoint(ep), nil 703 } 704 } 705 706 type deleteEndpointID struct { 707 daemon *Daemon 708 } 709 710 func NewDeleteEndpointIDHandler(d *Daemon) DeleteEndpointIDHandler { 711 return &deleteEndpointID{daemon: d} 712 } 713 714 func (h *deleteEndpointID) Handle(params DeleteEndpointIDParams) middleware.Responder { 715 log.WithField(logfields.Params, logfields.Repr(params)).Debug("DELETE /endpoint/{id} request") 716 717 d := h.daemon 718 if nerr, err := d.DeleteEndpoint(params.ID); err != nil { 719 if apierr, ok := err.(*api.APIError); ok { 720 return apierr 721 } 722 return api.Error(DeleteEndpointIDErrorsCode, err) 723 } else if nerr > 0 { 724 return NewDeleteEndpointIDErrors().WithPayload(int64(nerr)) 725 } else { 726 return NewDeleteEndpointIDOK() 727 } 728 } 729 730 // EndpointUpdate updates the options of the given endpoint and regenerates the endpoint 731 func (d *Daemon) EndpointUpdate(id string, cfg *models.EndpointConfigurationSpec) error { 732 ep, err := endpointmanager.Lookup(id) 733 if err != nil { 734 return api.Error(PatchEndpointIDInvalidCode, err) 735 } else if ep == nil { 736 return api.New(PatchEndpointIDConfigNotFoundCode, "endpoint %s not found", id) 737 } else if err = endpoint.APICanModify(ep); err != nil { 738 return api.Error(PatchEndpointIDInvalidCode, err) 739 } 740 741 if err := ep.Update(cfg); err != nil { 742 switch err.(type) { 743 case endpoint.UpdateValidationError: 744 return api.Error(PatchEndpointIDConfigInvalidCode, err) 745 default: 746 return api.Error(PatchEndpointIDConfigFailedCode, err) 747 } 748 } 749 if err := ep.RLockAlive(); err != nil { 750 return api.Error(PatchEndpointIDNotFoundCode, err) 751 } 752 endpointmanager.UpdateReferences(ep) 753 ep.RUnlock() 754 755 return nil 756 } 757 758 type patchEndpointIDConfig struct { 759 daemon *Daemon 760 } 761 762 func NewPatchEndpointIDConfigHandler(d *Daemon) PatchEndpointIDConfigHandler { 763 return &patchEndpointIDConfig{daemon: d} 764 } 765 766 func (h *patchEndpointIDConfig) Handle(params PatchEndpointIDConfigParams) middleware.Responder { 767 log.WithField(logfields.Params, logfields.Repr(params)).Debug("PATCH /endpoint/{id}/config request") 768 769 d := h.daemon 770 if err := d.EndpointUpdate(params.ID, params.EndpointConfiguration); err != nil { 771 if apierr, ok := err.(*api.APIError); ok { 772 return apierr 773 } 774 return api.Error(PatchEndpointIDFailedCode, err) 775 } 776 777 return NewPatchEndpointIDConfigOK() 778 } 779 780 type getEndpointIDConfig struct { 781 daemon *Daemon 782 } 783 784 func NewGetEndpointIDConfigHandler(d *Daemon) GetEndpointIDConfigHandler { 785 return &getEndpointIDConfig{daemon: d} 786 } 787 788 func (h *getEndpointIDConfig) Handle(params GetEndpointIDConfigParams) middleware.Responder { 789 log.WithField(logfields.Params, logfields.Repr(params)).Debug("GET /endpoint/{id}/config") 790 791 ep, err := endpointmanager.Lookup(params.ID) 792 if err != nil { 793 return api.Error(GetEndpointIDInvalidCode, err) 794 } else if ep == nil { 795 return NewGetEndpointIDConfigNotFound() 796 } else { 797 cfgStatus := &models.EndpointConfigurationStatus{ 798 Realized: &models.EndpointConfigurationSpec{ 799 LabelConfiguration: &models.LabelConfigurationSpec{ 800 User: ep.OpLabels.Custom.GetModel(), 801 }, 802 Options: *ep.Options.GetMutableModel(), 803 }, 804 Immutable: *ep.Options.GetImmutableModel(), 805 } 806 807 return NewGetEndpointIDConfigOK().WithPayload(cfgStatus) 808 } 809 } 810 811 type getEndpointIDLabels struct { 812 daemon *Daemon 813 } 814 815 func NewGetEndpointIDLabelsHandler(d *Daemon) GetEndpointIDLabelsHandler { 816 return &getEndpointIDLabels{daemon: d} 817 } 818 819 func (h *getEndpointIDLabels) Handle(params GetEndpointIDLabelsParams) middleware.Responder { 820 log.WithField(logfields.Params, logfields.Repr(params)).Debug("GET /endpoint/{id}/labels") 821 822 ep, err := endpointmanager.Lookup(params.ID) 823 if err != nil { 824 return api.Error(GetEndpointIDInvalidCode, err) 825 } 826 if ep == nil { 827 return NewGetEndpointIDLabelsNotFound() 828 } 829 830 if err := ep.RLockAlive(); err != nil { 831 return api.Error(GetEndpointIDInvalidCode, err) 832 } 833 spec := &models.LabelConfigurationSpec{ 834 User: ep.OpLabels.Custom.GetModel(), 835 } 836 837 cfg := models.LabelConfiguration{ 838 Spec: spec, 839 Status: &models.LabelConfigurationStatus{ 840 Realized: spec, 841 SecurityRelevant: ep.OpLabels.OrchestrationIdentity.GetModel(), 842 Derived: ep.OpLabels.OrchestrationInfo.GetModel(), 843 Disabled: ep.OpLabels.Disabled.GetModel(), 844 }, 845 } 846 ep.RUnlock() 847 848 return NewGetEndpointIDLabelsOK().WithPayload(&cfg) 849 } 850 851 type getEndpointIDLog struct { 852 d *Daemon 853 } 854 855 func NewGetEndpointIDLogHandler(d *Daemon) GetEndpointIDLogHandler { 856 return &getEndpointIDLog{d: d} 857 } 858 859 func (h *getEndpointIDLog) Handle(params GetEndpointIDLogParams) middleware.Responder { 860 log.WithField(logfields.EndpointID, params.ID).Debug("GET /endpoint/{id}/log request") 861 862 ep, err := endpointmanager.Lookup(params.ID) 863 864 if err != nil { 865 return api.Error(GetEndpointIDLogInvalidCode, err) 866 } else if ep == nil { 867 return NewGetEndpointIDLogNotFound() 868 } else { 869 return NewGetEndpointIDLogOK().WithPayload(ep.Status.GetModel()) 870 } 871 } 872 873 type getEndpointIDHealthz struct { 874 d *Daemon 875 } 876 877 func NewGetEndpointIDHealthzHandler(d *Daemon) GetEndpointIDHealthzHandler { 878 return &getEndpointIDHealthz{d: d} 879 } 880 881 func (h *getEndpointIDHealthz) Handle(params GetEndpointIDHealthzParams) middleware.Responder { 882 log.WithField(logfields.EndpointID, params.ID).Debug("GET /endpoint/{id}/log request") 883 884 ep, err := endpointmanager.Lookup(params.ID) 885 886 if err != nil { 887 return api.Error(GetEndpointIDHealthzInvalidCode, err) 888 } else if ep == nil { 889 return NewGetEndpointIDHealthzNotFound() 890 } else { 891 return NewGetEndpointIDHealthzOK().WithPayload(ep.GetHealthModel()) 892 } 893 } 894 895 func checkLabels(add, del labels.Labels) (addLabels, delLabels labels.Labels, ok bool) { 896 addLabels, _ = labels.FilterLabels(add) 897 delLabels, _ = labels.FilterLabels(del) 898 899 if len(addLabels) == 0 && len(delLabels) == 0 { 900 return nil, nil, false 901 } 902 return addLabels, delLabels, true 903 } 904 905 // modifyEndpointIdentityLabelsFromAPI adds and deletes the given labels on given endpoint ID. 906 // Performs checks for whether the endpoint may be modified by an API call. 907 // The received `add` and `del` labels will be filtered with the valid label prefixes. 908 // The `add` labels take precedence over `del` labels, this means if the same 909 // label is set on both `add` and `del`, that specific label will exist in the 910 // endpoint's labels. 911 // Returns an HTTP response code and an error msg (or nil on success). 912 func (d *Daemon) modifyEndpointIdentityLabelsFromAPI(id string, add, del labels.Labels) (int, error) { 913 addLabels, delLabels, ok := checkLabels(add, del) 914 if !ok { 915 return 0, nil 916 } 917 if lbls := addLabels.FindReserved(); lbls != nil { 918 return PatchEndpointIDLabelsUpdateFailedCode, fmt.Errorf("Not allowed to add reserved labels: %s", lbls) 919 } else if lbls := delLabels.FindReserved(); lbls != nil { 920 return PatchEndpointIDLabelsUpdateFailedCode, fmt.Errorf("Not allowed to delete reserved labels: %s", lbls) 921 } 922 923 ep, err := endpointmanager.Lookup(id) 924 if err != nil { 925 return PatchEndpointIDInvalidCode, err 926 } 927 if ep == nil { 928 return PatchEndpointIDLabelsNotFoundCode, fmt.Errorf("Endpoint ID %s not found", id) 929 } 930 if err = endpoint.APICanModify(ep); err != nil { 931 return PatchEndpointIDInvalidCode, err 932 } 933 934 if err := ep.ModifyIdentityLabels(addLabels, delLabels); err != nil { 935 return PatchEndpointIDLabelsNotFoundCode, err 936 } 937 938 return PatchEndpointIDLabelsOKCode, nil 939 } 940 941 type putEndpointIDLabels struct { 942 daemon *Daemon 943 } 944 945 func NewPatchEndpointIDLabelsHandler(d *Daemon) PatchEndpointIDLabelsHandler { 946 return &putEndpointIDLabels{daemon: d} 947 } 948 949 func (h *putEndpointIDLabels) Handle(params PatchEndpointIDLabelsParams) middleware.Responder { 950 log.WithField(logfields.Params, logfields.Repr(params)).Debug("PATCH /endpoint/{id}/labels request") 951 952 d := h.daemon 953 mod := params.Configuration 954 lbls := labels.NewLabelsFromModel(mod.User) 955 956 ep, err := endpointmanager.Lookup(params.ID) 957 if err != nil { 958 return api.Error(PutEndpointIDInvalidCode, err) 959 } else if ep == nil { 960 return NewPatchEndpointIDLabelsNotFound() 961 } 962 963 if err := ep.RLockAlive(); err != nil { 964 return api.Error(PutEndpointIDInvalidCode, err) 965 } 966 967 add, del := ep.OpLabels.SplitUserLabelChanges(lbls) 968 ep.RUnlock() 969 970 code, err := d.modifyEndpointIdentityLabelsFromAPI(params.ID, add, del) 971 if err != nil { 972 return api.Error(code, err) 973 } 974 return NewPatchEndpointIDLabelsOK() 975 }