mirror of
https://github.com/cloudnativelabs/kube-router.git
synced 2025-10-04 14:31:03 +02:00
* Makefile: Add lint using golangci-lint * build/travis-test.sh: Run lint step * metrics_controller: Lint pkg/metrics/metrics_controller.go:150:2: `mu` is unused (structcheck) mu sync.Mutex ^ pkg/metrics/metrics_controller.go:151:2: `nodeIP` is unused (structcheck) nodeIP net.IP ^ * network_service_graceful: Lint pkg/controllers/proxy/network_service_graceful.go:21:6: `gracefulQueueItem` is unused (deadcode) type gracefulQueueItem struct { ^ pkg/controllers/proxy/network_service_graceful.go:22:2: `added` is unused (structcheck) added time.Time ^ pkg/controllers/proxy/network_service_graceful.go:23:2: `service` is unused (structcheck) service *ipvs.Service ^ * network_services_controller_test: Lint pkg/controllers/proxy/network_services_controller_test.go:80:6: func `logf` is unused (unused) * ecmp_vip: Lint pkg/controllers/routing/ecmp_vip.go:208:4: S1023: redundant `return` statement (gosimple) return ^ * bgp_peers: Lint pkg/controllers/routing/bgp_peers.go:331:4: S1023: redundant `return` statement (gosimple) return ^ * bgp_policies: Lint pkg/controllers/routing/bgp_policies.go:80:3: S1011: should replace loop with `externalBgpPeers = append(externalBgpPeers, nrc.nodePeerRouters...)` (gosimple) for _, peer := range nrc.nodePeerRouters { ^ pkg/controllers/routing/bgp_policies.go:23:20: ineffectual assignment to `err` (ineffassign) podCidrPrefixSet, err := table.NewPrefixSet(config.PrefixSet{ ^ pkg/controllers/routing/bgp_policies.go:42:22: ineffectual assignment to `err` (ineffassign) clusterIPPrefixSet, err := table.NewPrefixSet(config.PrefixSet{ ^ pkg/controllers/routing/bgp_policies.go:33:30: Error return value of `nrc.bgpServer.AddDefinedSet` is not checked (errcheck) nrc.bgpServer.AddDefinedSet(podCidrPrefixSet) ^ pkg/controllers/routing/bgp_policies.go:48:30: Error return value of `nrc.bgpServer.AddDefinedSet` is not checked (errcheck) nrc.bgpServer.AddDefinedSet(clusterIPPrefixSet) ^ pkg/controllers/routing/bgp_policies.go:69:31: Error return value of `nrc.bgpServer.AddDefinedSet` is not checked (errcheck) nrc.bgpServer.AddDefinedSet(iBGPPeerNS) ^ pkg/controllers/routing/bgp_policies.go:108:31: Error return value of `nrc.bgpServer.AddDefinedSet` is not checked (errcheck) nrc.bgpServer.AddDefinedSet(ns) ^ pkg/controllers/routing/bgp_policies.go:120:30: Error return value of `nrc.bgpServer.AddDefinedSet` is not checked (errcheck) nrc.bgpServer.AddDefinedSet(ns) ^ ^ * network_policy_controller: Lint pkg/controllers/netpol/network_policy_controller.go:35:2: `networkPolicyAnnotation` is unused (deadcode) networkPolicyAnnotation = "net.beta.kubernetes.io/network-policy" ^ pkg/controllers/netpol/network_policy_controller.go:1047:4: SA9003: empty branch (staticcheck) if err != nil { ^ pkg/controllers/netpol/network_policy_controller.go:969:10: SA4006: this value of `err` is never used (staticcheck) chains, err := iptablesCmdHandler.ListChains("filter") ^ pkg/controllers/netpol/network_policy_controller.go:1568:4: SA4006: this value of `err` is never used (staticcheck) err = iptablesCmdHandler.Delete("filter", "FORWARD", strconv.Itoa(i-realRuleNo)) ^ pkg/controllers/netpol/network_policy_controller.go:1584:4: SA4006: this value of `err` is never used (staticcheck) err = iptablesCmdHandler.Delete("filter", "OUTPUT", strconv.Itoa(i-realRuleNo)) ^ * network_services_controller: Lint pkg/controllers/proxy/network_services_controller.go:66:2: `h` is unused (deadcode) h *ipvs.Handle ^ pkg/controllers/proxy/network_services_controller.go:879:23: SA1019: client.NewEnvClient is deprecated: use NewClientWithOpts(FromEnv) (staticcheck) dockerClient, err := client.NewEnvClient() ^ pkg/controllers/proxy/network_services_controller.go:944:5: unreachable: unreachable code (govet) glog.V(3).Infof("Waiting for tunnel interface %s to come up in the pod, retrying", KUBE_TUNNEL_IF) ^ pkg/controllers/proxy/network_services_controller.go:1289:5: S1002: should omit comparison to bool constant, can be simplified to `!hasHairpinChain` (gosimple) if hasHairpinChain != true { ^ pkg/controllers/proxy/network_services_controller.go:1237:43: S1019: should use make(map[string][]string) instead (gosimple) rulesNeeded := make(map[string][]string, 0) ^ pkg/controllers/proxy/network_services_controller.go:1111:4: S1023: redundant break statement (gosimple) break ^ pkg/controllers/proxy/network_services_controller.go:1114:4: S1023: redundant break statement (gosimple) break ^ pkg/controllers/proxy/network_services_controller.go:1117:4: S1023: redundant break statement (gosimple) break ^ pkg/controllers/proxy/network_services_controller.go:445:21: Error return value of `nsc.publishMetrics` is not checked (errcheck) nsc.publishMetrics(nsc.serviceMap) ^ pkg/controllers/proxy/network_services_controller.go:1609:9: Error return value of `h.Write` is not checked (errcheck) h.Write([]byte(ip + "-" + protocol + "-" + port)) ^ pkg/controllers/proxy/network_services_controller.go:912:13: Error return value of `netns.Set` is not checked (errcheck) netns.Set(hostNetworkNamespaceHandle) ^ pkg/controllers/proxy/network_services_controller.go:926:13: Error return value of `netns.Set` is not checked (errcheck) netns.Set(hostNetworkNamespaceHandle) ^ pkg/controllers/proxy/network_services_controller.go:950:13: Error return value of `netns.Set` is not checked (errcheck) netns.Set(hostNetworkNamespaceHandle) ^ pkg/controllers/proxy/network_services_controller.go:641:9: SA4006: this value of `err` is never used (staticcheck) addrs, err := getAllLocalIPs() ^ * network_routes_controller: Lint pkg/controllers/routing/network_routes_controller.go:340:2: S1000: should use for range instead of for { select {} } (gosimple) for { ^ pkg/controllers/routing/network_routes_controller.go:757:22: Error return value of `nrc.bgpServer.Stop` is not checked (errcheck) nrc.bgpServer.Stop() ^ pkg/controllers/routing/network_routes_controller.go:770:22: Error return value of `nrc.bgpServer.Stop` is not checked (errcheck) nrc.bgpServer.Stop() ^ pkg/controllers/routing/network_routes_controller.go:782:23: Error return value of `nrc.bgpServer.Stop` is not checked (errcheck) nrc.bgpServer.Stop() ^ pkg/controllers/routing/network_routes_controller.go:717:12: Error return value of `g.Serve` is not checked (errcheck) go g.Serve() * ipset: Lint pkg/utils/ipset.go:243:23: Error return value of `entry.Set.Parent.Save` is not checked (errcheck) entry.Set.Parent.Save() ^ * pkg/cmd/kube-router: Lint pkg/cmd/kube-router.go:214:26: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) fmt.Fprintf(os.Stderr, output) ^ pkg/cmd/kube-router.go:184:15: SA1017: the channel used with signal.Notify should be buffered (staticcheck) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) ^ pkg/cmd/kube-router.go:94:17: Error return value of `hc.RunServer` is not checked (errcheck) go hc.RunServer(stopCh, &wg) ^ pkg/cmd/kube-router.go:112:16: Error return value of `hc.RunCheck` is not checked (errcheck) go hc.RunCheck(healthChan, stopCh, &wg) ^ pkg/cmd/kube-router.go:121:12: Error return value of `mc.Run` is not checked (errcheck) go mc.Run(healthChan, stopCh, &wg) ^ * cmd/kube-router/kube-router: Lint cmd/kube-router/kube-router.go:31:24: Error return value of `flag.CommandLine.Parse` is not checked (errcheck) flag.CommandLine.Parse([]string{}) ^ cmd/kube-router/kube-router.go:33:10: Error return value of `flag.Set` is not checked (errcheck) flag.Set("logtostderr", "true") ^ cmd/kube-router/kube-router.go:34:10: Error return value of `flag.Set` is not checked (errcheck) flag.Set("v", config.VLevel) ^ cmd/kube-router/kube-router.go:62:27: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) fmt.Fprintf(os.Stdout, http.ListenAndServe("0.0.0.0:6060", nil).Error()) ^ * kube-router_test: Lint cmd/kube-router/kube-router_test.go:21:10: Error return value of `io.Copy` is not checked (errcheck) io.Copy(stderrBuf, stderrR) ^ cmd/kube-router/kube-router_test.go:40:17: Error return value of `docBuf.ReadFrom` is not checked (errcheck) docBuf.ReadFrom(docF) ^ * service_endpoints_sync: Lint pkg/controllers/proxy/service_endpoints_sync.go:460:2: ineffectual assignment to `ipvsSvcs` (ineffassign) ipvsSvcs, err := nsc.ln.ipvsGetServices() ^ pkg/controllers/proxy/service_endpoints_sync.go:311:5: SA4006: this value of `err` is never used (staticcheck) err = nsc.ln.ipAddrDel(dummyVipInterface, externalIP) ^ * node: Lint pkg/utils/node.go:19:16: SA1019: clientset.Core is deprecated: please explicitly pick a version if possible. (staticcheck) node, err := clientset.Core().Nodes().Get(nodeName, metav1.GetOptions{}) ^ pkg/utils/node.go:27:15: SA1019: clientset.Core is deprecated: please explicitly pick a version if possible. (staticcheck) node, err := clientset.Core().Nodes().Get(hostName, metav1.GetOptions{}) ^ pkg/utils/node.go:34:15: SA1019: clientset.Core is deprecated: please explicitly pick a version if possible. (staticcheck) node, err = clientset.Core().Nodes().Get(hostnameOverride, metav1.GetOptions{}) ^ * aws: Lint pkg/controllers/routing/aws.go:31:8: SA4006: this value of `err` is never used (staticcheck) URL, err := url.Parse(providerID) ^ * health_controller: Lint pkg/healthcheck/health_controller.go:54:10: Error return value of `w.Write` is not checked (errcheck) w.Write([]byte("OK\n")) ^ pkg/healthcheck/health_controller.go:68:10: Error return value of `w.Write` is not checked (errcheck) w.Write([]byte("Unhealthy")) ^ pkg/healthcheck/health_controller.go:159:2: S1000: should use a simple channel send/receive instead of `select` with a single case (gosimple) select { ^ * network_routes_controller_test: Lint pkg/controllers/routing/network_routes_controller_test.go:1113:37: Error return value of `testcase.nrc.bgpServer.Stop` is not checked (errcheck) defer testcase.nrc.bgpServer.Stop() ^ pkg/controllers/routing/network_routes_controller_test.go:1314:37: Error return value of `testcase.nrc.bgpServer.Stop` is not checked (errcheck) defer testcase.nrc.bgpServer.Stop() ^ pkg/controllers/routing/network_routes_controller_test.go:2327:37: Error return value of `testcase.nrc.bgpServer.Stop` is not checked (errcheck) defer testcase.nrc.bgpServer.Stop() ^ * .golangci.yml: Increase timeout Default is 1m, increase to 5m otherwise travis might fail * Makefile: Update golangci-lint to 1.27.0 * kube-router_test.go: defer waitgroup Co-authored-by: Aaron U'Ren <aauren@users.noreply.github.com> * network_routes_controller: Incorporate review * bgp_policies: Incorporate review * network_routes_controller: Incorporate review * bgp_policies: Log error instead * network_services_controller: Incorporate review Co-authored-by: Aaron U'Ren <aauren@users.noreply.github.com>
452 lines
13 KiB
Go
452 lines
13 KiB
Go
package routing
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
|
|
"strings"
|
|
|
|
"github.com/golang/glog"
|
|
"github.com/osrg/gobgp/packet/bgp"
|
|
"github.com/osrg/gobgp/table"
|
|
v1core "k8s.io/api/core/v1"
|
|
"k8s.io/client-go/tools/cache"
|
|
)
|
|
|
|
// bgpAdvertiseVIP advertises the service vip (cluster ip or load balancer ip or external IP) the configured peers
|
|
func (nrc *NetworkRoutingController) bgpAdvertiseVIP(vip string) error {
|
|
|
|
attrs := []bgp.PathAttributeInterface{
|
|
bgp.NewPathAttributeOrigin(0),
|
|
bgp.NewPathAttributeNextHop(nrc.nodeIP.String()),
|
|
}
|
|
|
|
glog.V(2).Infof("Advertising route: '%s/%s via %s' to peers", vip, strconv.Itoa(32), nrc.nodeIP.String())
|
|
|
|
_, err := nrc.bgpServer.AddPath("", []*table.Path{table.NewPath(nil, bgp.NewIPAddrPrefix(uint8(32),
|
|
vip), false, attrs, time.Now(), false)})
|
|
|
|
return err
|
|
}
|
|
|
|
// bgpWithdrawVIP unadvertises the service vip
|
|
func (nrc *NetworkRoutingController) bgpWithdrawVIP(vip string) error {
|
|
glog.V(2).Infof("Withdrawing route: '%s/%s via %s' to peers", vip, strconv.Itoa(32), nrc.nodeIP.String())
|
|
|
|
pathList := []*table.Path{table.NewPath(nil, bgp.NewIPAddrPrefix(uint8(32),
|
|
vip), true, nil, time.Now(), false)}
|
|
|
|
err := nrc.bgpServer.DeletePath([]byte(nil), 0, "", pathList)
|
|
|
|
return err
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) advertiseVIPs(vips []string) {
|
|
for _, vip := range vips {
|
|
err := nrc.bgpAdvertiseVIP(vip)
|
|
if err != nil {
|
|
glog.Errorf("error advertising IP: %q, error: %v", vip, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) withdrawVIPs(vips []string) {
|
|
for _, vip := range vips {
|
|
err := nrc.bgpWithdrawVIP(vip)
|
|
if err != nil {
|
|
glog.Errorf("error withdrawing IP: %q, error: %v", vip, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) newServiceEventHandler() cache.ResourceEventHandler {
|
|
return cache.ResourceEventHandlerFuncs{
|
|
AddFunc: func(obj interface{}) {
|
|
nrc.OnServiceCreate(obj)
|
|
},
|
|
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
nrc.OnServiceUpdate(newObj, oldObj)
|
|
},
|
|
DeleteFunc: func(obj interface{}) {
|
|
nrc.OnServiceDelete(obj)
|
|
},
|
|
}
|
|
}
|
|
|
|
func getServiceObject(obj interface{}) (svc *v1core.Service) {
|
|
if svc, _ = obj.(*v1core.Service); svc == nil {
|
|
glog.Errorf("cache indexer returned obj that is not type *v1.Service")
|
|
}
|
|
return
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) handleServiceUpdate(svc *v1core.Service) {
|
|
if !nrc.bgpServerStarted {
|
|
glog.V(3).Infof("Skipping update to service: %s/%s, controller still performing bootup full-sync", svc.Namespace, svc.Name)
|
|
return
|
|
}
|
|
|
|
toAdvertise, toWithdraw, err := nrc.getVIPsForService(svc, true)
|
|
if err != nil {
|
|
glog.Errorf("error getting routes for service: %s, err: %s", svc.Name, err)
|
|
return
|
|
}
|
|
|
|
// update export policies so that new VIP's gets addedd to clusteripprefixsit and vip gets advertised to peers
|
|
err = nrc.AddPolicies()
|
|
if err != nil {
|
|
glog.Errorf("Error adding BGP policies: %s", err.Error())
|
|
}
|
|
|
|
nrc.advertiseVIPs(toAdvertise)
|
|
nrc.withdrawVIPs(toWithdraw)
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) handleServiceDelete(svc *v1core.Service) {
|
|
|
|
if !nrc.bgpServerStarted {
|
|
glog.V(3).Infof("Skipping update to service: %s/%s, controller still performing bootup full-sync", svc.Namespace, svc.Name)
|
|
return
|
|
}
|
|
|
|
err := nrc.AddPolicies()
|
|
if err != nil {
|
|
glog.Errorf("Error adding BGP policies: %s", err.Error())
|
|
}
|
|
|
|
activeVIPs, _, err := nrc.getActiveVIPs()
|
|
if err != nil {
|
|
glog.Errorf("Failed to get active VIP's on service delete event due to: %s", err.Error())
|
|
return
|
|
}
|
|
activeVIPsMap := make(map[string]bool)
|
|
for _, activeVIP := range activeVIPs {
|
|
activeVIPsMap[activeVIP] = true
|
|
}
|
|
serviceVIPs := nrc.getAllVIPsForService(svc)
|
|
withdrawVIPs := make([]string, 0)
|
|
for _, serviceVIP := range serviceVIPs {
|
|
// withdraw VIP only if deleted service is the last service using the VIP
|
|
if !activeVIPsMap[serviceVIP] {
|
|
withdrawVIPs = append(withdrawVIPs, serviceVIP)
|
|
}
|
|
}
|
|
nrc.withdrawVIPs(withdrawVIPs)
|
|
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) tryHandleServiceUpdate(obj interface{}, logMsgFormat string) {
|
|
if svc := getServiceObject(obj); svc != nil {
|
|
glog.V(1).Infof(logMsgFormat, svc.Namespace, svc.Name)
|
|
nrc.handleServiceUpdate(svc)
|
|
}
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) tryHandleServiceDelete(obj interface{}, logMsgFormat string) {
|
|
svc, ok := obj.(*v1core.Service)
|
|
if !ok {
|
|
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
|
|
if !ok {
|
|
glog.Errorf("unexpected object type: %v", obj)
|
|
return
|
|
}
|
|
if svc, ok = tombstone.Obj.(*v1core.Service); !ok {
|
|
glog.Errorf("unexpected object type: %v", obj)
|
|
return
|
|
}
|
|
}
|
|
nrc.handleServiceDelete(svc)
|
|
}
|
|
|
|
// OnServiceCreate handles new service create event from the kubernetes API server
|
|
func (nrc *NetworkRoutingController) OnServiceCreate(obj interface{}) {
|
|
nrc.tryHandleServiceUpdate(obj, "Received new service: %s/%s from watch API")
|
|
}
|
|
|
|
// OnServiceUpdate handles the service relates updates from the kubernetes API server
|
|
func (nrc *NetworkRoutingController) OnServiceUpdate(objNew interface{}, objOld interface{}) {
|
|
nrc.tryHandleServiceUpdate(objNew, "Received update on service: %s/%s from watch API")
|
|
|
|
nrc.withdrawVIPs(nrc.getWithdraw(getServiceObject(objOld), getServiceObject(objNew)))
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getWithdraw(svcOld, svcNew *v1core.Service) (out []string) {
|
|
if svcOld != nil && svcNew != nil {
|
|
out = getMissingPrevGen(nrc.getExternalIps(svcOld), nrc.getExternalIps(svcNew))
|
|
}
|
|
return
|
|
}
|
|
|
|
func getMissingPrevGen(old, new []string) (withdrawIPs []string) {
|
|
lookIn := " " + strings.Join(new, " ") + " "
|
|
for _, s := range old {
|
|
if !strings.Contains(lookIn, " "+s+" ") {
|
|
withdrawIPs = append(withdrawIPs, s)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// OnServiceDelete handles the service delete updates from the kubernetes API server
|
|
func (nrc *NetworkRoutingController) OnServiceDelete(obj interface{}) {
|
|
nrc.tryHandleServiceDelete(obj, "Received event to delete service: %s/%s from watch API")
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) newEndpointsEventHandler() cache.ResourceEventHandler {
|
|
return cache.ResourceEventHandlerFuncs{
|
|
AddFunc: func(obj interface{}) {
|
|
nrc.OnEndpointsAdd(obj)
|
|
},
|
|
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
nrc.OnEndpointsUpdate(newObj)
|
|
},
|
|
DeleteFunc: func(obj interface{}) {
|
|
// don't do anything if an endpoints resource is deleted since
|
|
// the service delete event handles route withdrawals
|
|
},
|
|
}
|
|
}
|
|
|
|
// OnEndpointsAdd handles endpoint add events from apiserver
|
|
// This method calls OnEndpointsUpdate with the addition of updating BGP export policies
|
|
// Calling AddPolicies here covers the edge case where AddPolicies fails in
|
|
// OnServiceUpdate because the corresponding Endpoint resource for the
|
|
// Service was not created yet.
|
|
func (nrc *NetworkRoutingController) OnEndpointsAdd(obj interface{}) {
|
|
if !nrc.bgpServerStarted {
|
|
glog.V(3).Info("Skipping OnAdd event to endpoint, controller still performing bootup full-sync")
|
|
return
|
|
}
|
|
|
|
err := nrc.AddPolicies()
|
|
if err != nil {
|
|
glog.Errorf("error adding BGP policies: %s", err)
|
|
}
|
|
|
|
nrc.OnEndpointsUpdate(obj)
|
|
}
|
|
|
|
// OnEndpointsUpdate handles the endpoint updates from the kubernetes API server
|
|
func (nrc *NetworkRoutingController) OnEndpointsUpdate(obj interface{}) {
|
|
ep, ok := obj.(*v1core.Endpoints)
|
|
if !ok {
|
|
glog.Errorf("cache indexer returned obj that is not type *v1.Endpoints")
|
|
return
|
|
}
|
|
|
|
if isEndpointsForLeaderElection(ep) {
|
|
return
|
|
}
|
|
|
|
glog.V(1).Infof("Received update to endpoint: %s/%s from watch API", ep.Namespace, ep.Name)
|
|
if !nrc.bgpServerStarted {
|
|
glog.V(3).Infof("Skipping update to endpoint: %s/%s, controller still performing bootup full-sync", ep.Namespace, ep.Name)
|
|
return
|
|
}
|
|
|
|
svc, err := nrc.serviceForEndpoints(ep)
|
|
if err != nil {
|
|
glog.Errorf("failed to convert endpoints resource to service: %s", err)
|
|
return
|
|
}
|
|
|
|
nrc.tryHandleServiceUpdate(svc, "Updating service %s/%s triggered by endpoint update event")
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) serviceForEndpoints(ep *v1core.Endpoints) (interface{}, error) {
|
|
key, err := cache.MetaNamespaceKeyFunc(ep)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
item, exists, err := nrc.svcLister.GetByKey(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !exists {
|
|
return nil, fmt.Errorf("service resource doesn't exist for endpoints: %q", ep.Name)
|
|
}
|
|
|
|
return item, nil
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getClusterIp(svc *v1core.Service) string {
|
|
clusterIp := ""
|
|
if svc.Spec.Type == "ClusterIP" || svc.Spec.Type == "NodePort" || svc.Spec.Type == "LoadBalancer" {
|
|
|
|
// skip headless services
|
|
if svc.Spec.ClusterIP != "None" && svc.Spec.ClusterIP != "" {
|
|
clusterIp = svc.Spec.ClusterIP
|
|
}
|
|
}
|
|
return clusterIp
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getExternalIps(svc *v1core.Service) []string {
|
|
externalIpList := make([]string, 0)
|
|
if svc.Spec.Type == "ClusterIP" || svc.Spec.Type == "NodePort" {
|
|
|
|
// skip headless services
|
|
if svc.Spec.ClusterIP != "None" && svc.Spec.ClusterIP != "" {
|
|
externalIpList = append(externalIpList, svc.Spec.ExternalIPs...)
|
|
}
|
|
}
|
|
return externalIpList
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getLoadBalancerIps(svc *v1core.Service) []string {
|
|
loadBalancerIpList := make([]string, 0)
|
|
if svc.Spec.Type == "LoadBalancer" {
|
|
// skip headless services
|
|
if svc.Spec.ClusterIP != "None" && svc.Spec.ClusterIP != "" {
|
|
for _, lbIngress := range svc.Status.LoadBalancer.Ingress {
|
|
if len(lbIngress.IP) > 0 {
|
|
loadBalancerIpList = append(loadBalancerIpList, lbIngress.IP)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return loadBalancerIpList
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getAllVIPs() ([]string, []string, error) {
|
|
return nrc.getVIPs(false)
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getActiveVIPs() ([]string, []string, error) {
|
|
return nrc.getVIPs(true)
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getVIPs(onlyActiveEndpoints bool) ([]string, []string, error) {
|
|
toAdvertiseList := make([]string, 0)
|
|
toWithdrawList := make([]string, 0)
|
|
|
|
for _, obj := range nrc.svcLister.List() {
|
|
svc := obj.(*v1core.Service)
|
|
|
|
toAdvertise, toWithdraw, err := nrc.getVIPsForService(svc, onlyActiveEndpoints)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if len(toAdvertise) > 0 {
|
|
toAdvertiseList = append(toAdvertiseList, toAdvertise...)
|
|
}
|
|
|
|
if len(toWithdraw) > 0 {
|
|
toWithdrawList = append(toWithdrawList, toWithdraw...)
|
|
}
|
|
}
|
|
|
|
return toAdvertiseList, toWithdrawList, nil
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) shouldAdvertiseService(svc *v1core.Service, annotation string, defaultValue bool) bool {
|
|
returnValue := defaultValue
|
|
stringValue, exists := svc.Annotations[annotation]
|
|
if exists {
|
|
// Service annotations overrides defaults.
|
|
returnValue, _ = strconv.ParseBool(stringValue)
|
|
}
|
|
return returnValue
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getVIPsForService(svc *v1core.Service, onlyActiveEndpoints bool) ([]string, []string, error) {
|
|
|
|
advertise := true
|
|
|
|
_, hasLocalAnnotation := svc.Annotations[svcLocalAnnotation]
|
|
hasLocalTrafficPolicy := svc.Spec.ExternalTrafficPolicy == v1core.ServiceExternalTrafficPolicyTypeLocal
|
|
isLocal := hasLocalAnnotation || hasLocalTrafficPolicy
|
|
|
|
if onlyActiveEndpoints && isLocal {
|
|
var err error
|
|
advertise, err = nrc.nodeHasEndpointsForService(svc)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
|
|
ipList := nrc.getAllVIPsForService(svc)
|
|
|
|
if !advertise {
|
|
return nil, ipList, nil
|
|
}
|
|
|
|
return ipList, nil, nil
|
|
}
|
|
|
|
func (nrc *NetworkRoutingController) getAllVIPsForService(svc *v1core.Service) []string {
|
|
|
|
ipList := make([]string, 0)
|
|
|
|
if nrc.shouldAdvertiseService(svc, svcAdvertiseClusterAnnotation, nrc.advertiseClusterIP) {
|
|
clusterIP := nrc.getClusterIp(svc)
|
|
if clusterIP != "" {
|
|
ipList = append(ipList, clusterIP)
|
|
}
|
|
}
|
|
|
|
if nrc.shouldAdvertiseService(svc, svcAdvertiseExternalAnnotation, nrc.advertiseExternalIP) {
|
|
ipList = append(ipList, nrc.getExternalIps(svc)...)
|
|
}
|
|
|
|
// Deprecated: Use service.advertise.loadbalancer=false instead of service.skiplbips.
|
|
_, skiplbips := svc.Annotations[svcSkipLbIpsAnnotation]
|
|
advertiseLoadBalancer := nrc.shouldAdvertiseService(svc, svcAdvertiseLoadBalancerAnnotation, nrc.advertiseLoadBalancerIP)
|
|
if advertiseLoadBalancer && !skiplbips {
|
|
ipList = append(ipList, nrc.getLoadBalancerIps(svc)...)
|
|
}
|
|
|
|
return ipList
|
|
|
|
}
|
|
|
|
func isEndpointsForLeaderElection(ep *v1core.Endpoints) bool {
|
|
_, isLeaderElection := ep.Annotations[LeaderElectionRecordAnnotationKey]
|
|
return isLeaderElection
|
|
}
|
|
|
|
// nodeHasEndpointsForService will get the corresponding Endpoints resource for a given Service
|
|
// return true if any endpoint addresses has NodeName matching the node name of the route controller
|
|
func (nrc *NetworkRoutingController) nodeHasEndpointsForService(svc *v1core.Service) (bool, error) {
|
|
// listers for endpoints and services should use the same keys since
|
|
// endpoint and service resources share the same object name and namespace
|
|
key, err := cache.MetaNamespaceKeyFunc(svc)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
item, exists, err := nrc.epLister.GetByKey(key)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if !exists {
|
|
return false, fmt.Errorf("endpoint resource doesn't exist for service: %q", svc.Name)
|
|
}
|
|
|
|
ep, ok := item.(*v1core.Endpoints)
|
|
if !ok {
|
|
return false, errors.New("failed to convert cache item to Endpoints type")
|
|
}
|
|
|
|
for _, subset := range ep.Subsets {
|
|
for _, address := range subset.Addresses {
|
|
if address.NodeName != nil {
|
|
if *address.NodeName == nrc.nodeName {
|
|
return true, nil
|
|
}
|
|
} else {
|
|
if address.IP == nrc.nodeIP.String() {
|
|
return true, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|