Adds golangci-lint to check for code errors + clean up resulting findings after running.

Signed-off-by: Steve Sloka <slokas@vmware.com>
This commit is contained in:
Steve Sloka
2021-10-19 11:15:09 -04:00
parent 70f14a5bfd
commit 2e9c2244c1
38 changed files with 192 additions and 276 deletions

13
.golangci.yml Normal file
View File

@@ -0,0 +1,13 @@
run:
timeout: 10m
linters:
enable:
- bodyclose
- gofmt
- goimports
- revive
- gosec
- misspell
- unconvert
- unparam

View File

@@ -87,7 +87,7 @@ check:
go mod tidy go mod tidy
test -z "$(git status --porcelain)" test -z "$(git status --porcelain)"
test -z $(shell gofmt -l main.go | tee /dev/stderr) || echo "[WARN] Fix formatting issues with 'make fmt'" test -z $(shell gofmt -l main.go | tee /dev/stderr) || echo "[WARN] Fix formatting issues with 'make fmt'"
golint ./... golangci-lint run
go vet ./... go vet ./...
run: install run: install

View File

@@ -24,13 +24,13 @@ func init() {
kubeVipSampleConfig.Flags().BoolVar(&cliConfig.StartAsLeader, "startAsLeader", false, "Start this instance as the cluster leader") kubeVipSampleConfig.Flags().BoolVar(&cliConfig.StartAsLeader, "startAsLeader", false, "Start this instance as the cluster leader")
kubeVipSampleConfig.Flags().BoolVar(&cliConfig.EnableARP, "arp", true, "Use ARP broadcasts to improve VIP re-allocations") kubeVipSampleConfig.Flags().BoolVar(&cliConfig.EnableARP, "arp", true, "Use ARP broadcasts to improve VIP re-allocations")
kubeVipSampleConfig.Flags().StringVar(&cliLocalPeer, "localPeer", "server1:192.168.0.1:10000", "Settings for this peer, format: id:address:port") kubeVipSampleConfig.Flags().StringVar(&cliLocalPeer, "localPeer", "server1:192.168.0.1:10000", "Settings for this peer, format: id:address:port")
kubeVipSampleConfig.Flags().StringSliceVar(&cliRemotePeers, "remotePeers", []string{"server2:192.168.0.2:10000", "server3:192.168.0.3:10000"}, "Comma seperated remotePeers, format: id:address:port") kubeVipSampleConfig.Flags().StringSliceVar(&cliRemotePeers, "remotePeers", []string{"server2:192.168.0.2:10000", "server3:192.168.0.3:10000"}, "Comma separated remotePeers, format: id:address:port")
// Load Balancer flags // Load Balancer flags
kubeVipSampleConfig.Flags().BoolVar(&cliConfigLB.BindToVip, "lbBindToVip", false, "Bind example load balancer to VIP") kubeVipSampleConfig.Flags().BoolVar(&cliConfigLB.BindToVip, "lbBindToVip", false, "Bind example load balancer to VIP")
kubeVipSampleConfig.Flags().StringVar(&cliConfigLB.Type, "lbType", "tcp", "Type of load balancer instance (TCP/HTTP)") kubeVipSampleConfig.Flags().StringVar(&cliConfigLB.Type, "lbType", "tcp", "Type of load balancer instance (TCP/HTTP)")
kubeVipSampleConfig.Flags().StringVar(&cliConfigLB.Name, "lbName", "Example Load Balancer", "The name of a load balancer instance") kubeVipSampleConfig.Flags().StringVar(&cliConfigLB.Name, "lbName", "Example Load Balancer", "The name of a load balancer instance")
kubeVipSampleConfig.Flags().IntVar(&cliConfigLB.Port, "lbPort", 8080, "Port that load balancer will expose on") kubeVipSampleConfig.Flags().IntVar(&cliConfigLB.Port, "lbPort", 8080, "Port that load balancer will expose on")
kubeVipSampleConfig.Flags().StringSliceVar(&cliBackends, "lbBackends", []string{"192.168.0.1:8080", "192.168.0.2:8080"}, "Comma seperated backends, format: address:port") kubeVipSampleConfig.Flags().StringSliceVar(&cliBackends, "lbBackends", []string{"192.168.0.1:8080", "192.168.0.2:8080"}, "Comma separated backends, format: address:port")
} }
var kubeVipSampleConfig = &cobra.Command{ var kubeVipSampleConfig = &cobra.Command{
@@ -72,13 +72,13 @@ var kubeVipSampleConfig = &cobra.Command{
err := cliConfig.ParseFlags(cliLocalPeer, cliRemotePeers, cliBackends) err := cliConfig.ParseFlags(cliLocalPeer, cliRemotePeers, cliBackends)
if err != nil { if err != nil {
cmd.Help() _ = cmd.Help()
log.Fatalln(err) log.Fatalln(err)
} }
err = kubevip.ParseEnvironment(&cliConfig) err = kubevip.ParseEnvironment(&cliConfig)
if err != nil { if err != nil {
cmd.Help() _ = cmd.Help()
log.Fatalln(err) log.Fatalln(err)
} }

View File

@@ -29,7 +29,7 @@ var kubeKubeadm = &cobra.Command{
Use: "kubeadm", Use: "kubeadm",
Short: "Kubeadm functions", Short: "Kubeadm functions",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd.Help() _ = cmd.Help()
// TODO - A load of text detailing what's actually happening // TODO - A load of text detailing what's actually happening
}, },
} }
@@ -43,15 +43,19 @@ var kubeKubeadmInit = &cobra.Command{
log.SetLevel(log.Level(logLevel)) log.SetLevel(log.Level(logLevel))
initConfig.LoadBalancers = append(initConfig.LoadBalancers, initLoadBalancer) initConfig.LoadBalancers = append(initConfig.LoadBalancers, initLoadBalancer)
// TODO - A load of text detailing what's actually happening // TODO - A load of text detailing what's actually happening
kubevip.ParseEnvironment(&initConfig) err := kubevip.ParseEnvironment(&initConfig)
if err != nil {
log.Fatalf("Error parsing environment from config: %v", err)
}
// TODO - check for certain things VIP/interfaces // TODO - check for certain things VIP/interfaces
if initConfig.Interface == "" { if initConfig.Interface == "" {
cmd.Help() _ = cmd.Help()
log.Fatalln("No interface is specified for kube-vip to bind to") log.Fatalln("No interface is specified for kube-vip to bind to")
} }
if initConfig.VIP == "" && initConfig.Address == "" { if initConfig.VIP == "" && initConfig.Address == "" {
cmd.Help() _ = cmd.Help()
log.Fatalln("No address is specified for kube-vip to expose services on") log.Fatalln("No address is specified for kube-vip to expose services on")
} }
cfg := kubevip.GeneratePodManifestFromConfig(&initConfig, Release.Version, inCluster) cfg := kubevip.GeneratePodManifestFromConfig(&initConfig, Release.Version, inCluster)
@@ -69,15 +73,19 @@ var kubeKubeadmJoin = &cobra.Command{
initConfig.LoadBalancers = append(initConfig.LoadBalancers, initLoadBalancer) initConfig.LoadBalancers = append(initConfig.LoadBalancers, initLoadBalancer)
// TODO - A load of text detailing what's actually happening // TODO - A load of text detailing what's actually happening
kubevip.ParseEnvironment(&initConfig) err := kubevip.ParseEnvironment(&initConfig)
if err != nil {
log.Fatalf("Error parsing environment from config: %v", err)
}
// TODO - check for certain things VIP/interfaces // TODO - check for certain things VIP/interfaces
if initConfig.Interface == "" { if initConfig.Interface == "" {
cmd.Help() _ = cmd.Help()
log.Fatalln("No interface is specified for kube-vip to bind to") log.Fatalln("No interface is specified for kube-vip to bind to")
} }
if initConfig.VIP == "" && initConfig.Address == "" { if initConfig.VIP == "" && initConfig.Address == "" {
cmd.Help() _ = cmd.Help()
log.Fatalln("No address is specified for kube-vip to expose services on") log.Fatalln("No address is specified for kube-vip to expose services on")
} }

View File

@@ -28,7 +28,7 @@ var kubeManifest = &cobra.Command{
Use: "manifest", Use: "manifest",
Short: "Manifest functions", Short: "Manifest functions",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd.Help() _ = cmd.Help()
// TODO - A load of text detailing what's actually happening // TODO - A load of text detailing what's actually happening
}, },
} }
@@ -41,11 +41,13 @@ var kubeManifestPod = &cobra.Command{
log.SetLevel(log.Level(logLevel)) log.SetLevel(log.Level(logLevel))
initConfig.LoadBalancers = append(initConfig.LoadBalancers, initLoadBalancer) initConfig.LoadBalancers = append(initConfig.LoadBalancers, initLoadBalancer)
// TODO - A load of text detailing what's actually happening // TODO - A load of text detailing what's actually happening
kubevip.ParseEnvironment(&initConfig) if err := kubevip.ParseEnvironment(&initConfig); err != nil {
log.Fatalf("Error parsing environment from config: %v", err)
}
// The control plane has a requirement for a VIP being specified // The control plane has a requirement for a VIP being specified
if initConfig.EnableControlPane && (initConfig.VIP == "" && initConfig.Address == "" && !initConfig.DDNS) { if initConfig.EnableControlPane && (initConfig.VIP == "" && initConfig.Address == "" && !initConfig.DDNS) {
cmd.Help() _ = cmd.Help()
log.Fatalln("No address is specified for kube-vip to expose services on") log.Fatalln("No address is specified for kube-vip to expose services on")
} }
cfg := kubevip.GeneratePodManifestFromConfig(&initConfig, Release.Version, inCluster) cfg := kubevip.GeneratePodManifestFromConfig(&initConfig, Release.Version, inCluster)
@@ -62,12 +64,15 @@ var kubeManifestDaemon = &cobra.Command{
log.SetLevel(log.Level(logLevel)) log.SetLevel(log.Level(logLevel))
initConfig.LoadBalancers = append(initConfig.LoadBalancers, initLoadBalancer) initConfig.LoadBalancers = append(initConfig.LoadBalancers, initLoadBalancer)
// TODO - A load of text detailing what's actually happening // TODO - A load of text detailing what's actually happening
kubevip.ParseEnvironment(&initConfig) if err := kubevip.ParseEnvironment(&initConfig); err != nil {
log.Fatalf("error parsing environment config: %v", err)
}
// TODO - check for certain things VIP/interfaces // TODO - check for certain things VIP/interfaces
// The control plane has a requirement for a VIP being specified // The control plane has a requirement for a VIP being specified
if initConfig.EnableControlPane && (initConfig.VIP == "" && initConfig.Address == "" && !initConfig.DDNS) { if initConfig.EnableControlPane && (initConfig.VIP == "" && initConfig.Address == "" && !initConfig.DDNS) {
cmd.Help() _ = cmd.Help()
log.Fatalln("No address is specified for kube-vip to expose services on") log.Fatalln("No address is specified for kube-vip to expose services on")
} }
cfg := kubevip.GenerateDeamonsetManifestFromConfig(&initConfig, Release.Version, inCluster, taint) cfg := kubevip.GenerateDeamonsetManifestFromConfig(&initConfig, Release.Version, inCluster, taint)

View File

@@ -33,14 +33,14 @@ func init() {
kubeVipStart.Flags().BoolVar(&startConfig.StartAsLeader, "startAsLeader", false, "Start this instance as the cluster leader") kubeVipStart.Flags().BoolVar(&startConfig.StartAsLeader, "startAsLeader", false, "Start this instance as the cluster leader")
kubeVipStart.Flags().BoolVar(&startConfig.EnableARP, "arp", false, "Use ARP broadcasts to improve VIP re-allocations") kubeVipStart.Flags().BoolVar(&startConfig.EnableARP, "arp", false, "Use ARP broadcasts to improve VIP re-allocations")
kubeVipStart.Flags().StringVar(&startLocalPeer, "localPeer", "server1:192.168.0.1:10000", "Settings for this peer, format: id:address:port") kubeVipStart.Flags().StringVar(&startLocalPeer, "localPeer", "server1:192.168.0.1:10000", "Settings for this peer, format: id:address:port")
kubeVipStart.Flags().StringSliceVar(&startRemotePeers, "remotePeers", []string{"server2:192.168.0.2:10000", "server3:192.168.0.3:10000"}, "Comma seperated remotePeers, format: id:address:port") kubeVipStart.Flags().StringSliceVar(&startRemotePeers, "remotePeers", []string{"server2:192.168.0.2:10000", "server3:192.168.0.3:10000"}, "Comma separated remotePeers, format: id:address:port")
// Load Balancer flags // Load Balancer flags
kubeVipStart.Flags().BoolVar(&startConfigLB.BindToVip, "lbBindToVip", false, "Bind example load balancer to VIP") kubeVipStart.Flags().BoolVar(&startConfigLB.BindToVip, "lbBindToVip", false, "Bind example load balancer to VIP")
kubeVipStart.Flags().StringVar(&startConfigLB.Type, "lbType", "tcp", "Type of load balancer instance (TCP/HTTP)") kubeVipStart.Flags().StringVar(&startConfigLB.Type, "lbType", "tcp", "Type of load balancer instance (TCP/HTTP)")
kubeVipStart.Flags().StringVar(&startConfigLB.Name, "lbName", "Example Load Balancer", "The name of a load balancer instance") kubeVipStart.Flags().StringVar(&startConfigLB.Name, "lbName", "Example Load Balancer", "The name of a load balancer instance")
kubeVipStart.Flags().IntVar(&startConfigLB.Port, "lbPort", 8080, "Port that load balancer will expose on") kubeVipStart.Flags().IntVar(&startConfigLB.Port, "lbPort", 8080, "Port that load balancer will expose on")
kubeVipStart.Flags().IntVar(&startConfigLB.BackendPort, "lbBackEndPort", 6443, "A port that all backends may be using (optional)") kubeVipStart.Flags().IntVar(&startConfigLB.BackendPort, "lbBackEndPort", 6443, "A port that all backends may be using (optional)")
kubeVipStart.Flags().StringSliceVar(&startBackends, "lbBackends", []string{"192.168.0.1:8080", "192.168.0.2:8080"}, "Comma seperated backends, format: address:port") kubeVipStart.Flags().StringSliceVar(&startBackends, "lbBackends", []string{"192.168.0.1:8080", "192.168.0.2:8080"}, "Comma separated backends, format: address:port")
// Cluster configuration // Cluster configuration
kubeVipStart.Flags().StringVar(&startKubeConfigPath, "kubeConfig", "/etc/kubernetes/admin.conf", "The path of a kubernetes configuration file") kubeVipStart.Flags().StringVar(&startKubeConfigPath, "kubeConfig", "/etc/kubernetes/admin.conf", "The path of a kubernetes configuration file")
@@ -83,7 +83,9 @@ var kubeVipStart = &cobra.Command{
if startConfig.SingleNode { if startConfig.SingleNode {
// If the Virtual IP isn't disabled then create the netlink configuration // If the Virtual IP isn't disabled then create the netlink configuration
// Start a single node cluster // Start a single node cluster
newCluster.StartSingleNode(&startConfig, disableVIP) if err := newCluster.StartSingleNode(&startConfig, disableVIP); err != nil {
log.Errorf("error starting single node: %v", err)
}
} else { } else {
if disableVIP { if disableVIP {
log.Fatalln("Cluster mode requires the Virtual IP to be enabled, use single node with no VIP") log.Fatalln("Cluster mode requires the Virtual IP to be enabled, use single node with no VIP")

View File

@@ -21,16 +21,16 @@ import (
var configPath string var configPath string
// Path to the configuration file // Path to the configuration file
var namespace string // var namespace string
// Disable the Virtual IP (bind to the existing network stack) // Disable the Virtual IP (bind to the existing network stack)
var disableVIP bool var disableVIP bool
// Disable the Virtual IP (bind to the existing network stack) // Disable the Virtual IP (bind to the existing network stack)
var controlPlane bool // var controlPlane bool
// Run as a load balancer service (within a pod / kubernetes) // Run as a load balancer service (within a pod / kubernetes)
var serviceArp bool // var serviceArp bool
// ConfigMap name within a Kubernetes cluster // ConfigMap name within a Kubernetes cluster
var configMap string var configMap string
@@ -114,7 +114,7 @@ func init() {
kubeVipCmd.PersistentFlags().Uint32Var(&initConfig.BGPPeerConfig.AS, "peerAS", 65000, "The AS number for a BGP peer") kubeVipCmd.PersistentFlags().Uint32Var(&initConfig.BGPPeerConfig.AS, "peerAS", 65000, "The AS number for a BGP peer")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPPeerConfig.Password, "peerPass", "", "The md5 password for a BGP peer") kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPPeerConfig.Password, "peerPass", "", "The md5 password for a BGP peer")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.BGPPeerConfig.MultiHop, "multihop", false, "This will enable BGP multihop support") kubeVipCmd.PersistentFlags().BoolVar(&initConfig.BGPPeerConfig.MultiHop, "multihop", false, "This will enable BGP multihop support")
kubeVipCmd.PersistentFlags().StringSliceVar(&initConfig.BGPPeers, "bgppeers", []string{}, "Comma seperated BGP Peer, format: address:as:password:multihop") kubeVipCmd.PersistentFlags().StringSliceVar(&initConfig.BGPPeers, "bgppeers", []string{}, "Comma separated BGP Peer, format: address:as:password:multihop")
// Control plane specific flags // Control plane specific flags
kubeVipCmd.PersistentFlags().StringVarP(&initConfig.Namespace, "namespace", "n", "kube-system", "The configuration map defined within the cluster") kubeVipCmd.PersistentFlags().StringVarP(&initConfig.Namespace, "namespace", "n", "kube-system", "The configuration map defined within the cluster")
@@ -129,7 +129,7 @@ func init() {
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableServices, "services", false, "Enable Kubernetes services, hybrid mode") kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableServices, "services", false, "Enable Kubernetes services, hybrid mode")
// Prometheus HTTP Server // Prometheus HTTP Server
kubeVipCmd.PersistentFlags().StringVar(&initConfig.PrometheusHTTPServer, "promethuesHTTPServer", ":2112", "Host and port used to expose Promethues metrics via an HTTP server") kubeVipCmd.PersistentFlags().StringVar(&initConfig.PrometheusHTTPServer, "promethuesHTTPServer", ":2112", "Host and port used to expose Prometheus metrics via an HTTP server")
kubeVipCmd.AddCommand(kubeKubeadm) kubeVipCmd.AddCommand(kubeKubeadm)
kubeVipCmd.AddCommand(kubeManifest) kubeVipCmd.AddCommand(kubeManifest)
@@ -167,7 +167,7 @@ var kubeVipSample = &cobra.Command{
Use: "sample", Use: "sample",
Short: "Generate a Sample configuration", Short: "Generate a Sample configuration",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd.Help() _ = cmd.Help()
}, },
} }
@@ -215,7 +215,7 @@ var kubeVipManager = &cobra.Command{
log.Infof("No interface is specified for VIP in config, auto-detecting default Interface") log.Infof("No interface is specified for VIP in config, auto-detecting default Interface")
defaultIF, err := vip.GetDefaultGatewayInterface() defaultIF, err := vip.GetDefaultGatewayInterface()
if err != nil { if err != nil {
cmd.Help() _ = cmd.Help()
log.Fatalf("unable to detect default interface -> [%v]", err) log.Fatalf("unable to detect default interface -> [%v]", err)
} }
initConfig.Interface = defaultIF.Name initConfig.Interface = defaultIF.Name
@@ -266,6 +266,7 @@ var kubeVipManager = &cobra.Command{
}, },
} }
// PrometheusHTTPServerConfig defines the Prometheus server configuration.
type PrometheusHTTPServerConfig struct { type PrometheusHTTPServerConfig struct {
// Addr sets the http server address used to expose the metric endpoint // Addr sets the http server address used to expose the metric endpoint
Addr string Addr string

View File

@@ -7,12 +7,12 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes" //nolint
"github.com/golang/protobuf/ptypes/any" "github.com/golang/protobuf/ptypes/any"
api "github.com/osrg/gobgp/api" api "github.com/osrg/gobgp/api"
) )
//AddPeer will add peers to the BGP configuration // AddPeer will add peers to the BGP configuration
func (b *Server) AddPeer(peer Peer) (err error) { func (b *Server) AddPeer(peer Peer) (err error) {
port := 179 port := 179
@@ -20,7 +20,7 @@ func (b *Server) AddPeer(peer Peer) (err error) {
peer.Address = t[0] peer.Address = t[0]
if port, err = strconv.Atoi(t[1]); err != nil { if port, err = strconv.Atoi(t[1]); err != nil {
return fmt.Errorf("Unable to parse port '%s' as int: %s", t[1], err) return fmt.Errorf("unable to parse port '%s' as int: %s", t[1], err)
} }
} }
@@ -73,11 +73,13 @@ func (b *Server) getPath(ip net.IP) *api.Path {
pfxLen = 128 pfxLen = 128
} }
//nolint
nlri, _ := ptypes.MarshalAny(&api.IPAddressPrefix{ nlri, _ := ptypes.MarshalAny(&api.IPAddressPrefix{
Prefix: ip.String(), Prefix: ip.String(),
PrefixLen: pfxLen, PrefixLen: pfxLen,
}) })
//nolint
a1, _ := ptypes.MarshalAny(&api.OriginAttribute{ a1, _ := ptypes.MarshalAny(&api.OriginAttribute{
Origin: 0, Origin: 0,
}) })
@@ -91,6 +93,7 @@ func (b *Server) getPath(ip net.IP) *api.Path {
nh = b.c.RouterID nh = b.c.RouterID
} }
//nolint
a2, _ := ptypes.MarshalAny(&api.NextHopAttribute{ a2, _ := ptypes.MarshalAny(&api.NextHopAttribute{
NextHop: nh, NextHop: nh,
}) })

View File

@@ -126,6 +126,7 @@ func (cluster *Cluster) StartLeaderCluster(c *kubevip.Config, sm *Manager, bgpSe
signal.Notify(signalChan, syscall.SIGTERM) signal.Notify(signalChan, syscall.SIGTERM)
// Add Notification for SIGKILL (sent from Kubernetes) // Add Notification for SIGKILL (sent from Kubernetes)
//nolint
signal.Notify(signalChan, syscall.SIGKILL) signal.Notify(signalChan, syscall.SIGKILL)
go func() { go func() {
@@ -137,7 +138,10 @@ func (cluster *Cluster) StartLeaderCluster(c *kubevip.Config, sm *Manager, bgpSe
}() }()
// (attempt to) Remove the virtual IP, incase it already exists // (attempt to) Remove the virtual IP, incase it already exists
cluster.Network.DeleteIP() err = cluster.Network.DeleteIP()
if err != nil {
log.Errorf("could not delete virtualIP: %v", err)
}
// Managers for Vip load balancers and none-vip loadbalancers // Managers for Vip load balancers and none-vip loadbalancers
nonVipLB := loadbalancer.LBManager{} nonVipLB := loadbalancer.LBManager{}
@@ -150,7 +154,7 @@ func (cluster *Cluster) StartLeaderCluster(c *kubevip.Config, sm *Manager, bgpSe
} }
}() }()
// If Packet is enabled then we can begin our preperation work // If Packet is enabled then we can begin our preparation work
var packetClient *packngo.Client var packetClient *packngo.Client
if c.EnableMetal { if c.EnableMetal {
if c.ProviderConfig != "" { if c.ProviderConfig != "" {
@@ -194,7 +198,7 @@ func (cluster *Cluster) StartLeaderCluster(c *kubevip.Config, sm *Manager, bgpSe
if len(c.LoadBalancers) != 0 { if len(c.LoadBalancers) != 0 {
for x := range c.LoadBalancers { for x := range c.LoadBalancers {
// If the load balancer doesn't bind to the VIP // If the load balancer doesn't bind to the VIP
if c.LoadBalancers[x].BindToVip == false { if !c.LoadBalancers[x].BindToVip {
err = nonVipLB.Add("", &c.LoadBalancers[x]) err = nonVipLB.Add("", &c.LoadBalancers[x])
if err != nil { if err != nil {
log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err) log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err)
@@ -267,7 +271,7 @@ func (cluster *Cluster) StartLeaderCluster(c *kubevip.Config, sm *Manager, bgpSe
// Once we have the VIP running, start the load balancer(s) that bind to the VIP // Once we have the VIP running, start the load balancer(s) that bind to the VIP
for x := range c.LoadBalancers { for x := range c.LoadBalancers {
if c.LoadBalancers[x].BindToVip == true { if c.LoadBalancers[x].BindToVip {
err = VipLB.Add(cluster.Network.IP(), &c.LoadBalancers[x]) err = VipLB.Add(cluster.Network.IP(), &c.LoadBalancers[x])
if err != nil { if err != nil {
log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err) log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err)
@@ -287,7 +291,7 @@ func (cluster *Cluster) StartLeaderCluster(c *kubevip.Config, sm *Manager, bgpSe
} }
} }
if c.EnableARP == true { if c.EnableARP {
ctxArp, cancelArp = context.WithCancel(context.Background()) ctxArp, cancelArp = context.WithCancel(context.Background())
ipString := cluster.Network.IP() ipString := cluster.Network.IP()
@@ -375,10 +379,6 @@ func (cluster *Cluster) StartLeaderCluster(c *kubevip.Config, sm *Manager, bgpSe
OnNewLeader: func(identity string) { OnNewLeader: func(identity string) {
// we're notified when new leader elected // we're notified when new leader elected
log.Infof("Node [%s] is assuming leadership of the cluster", identity) log.Infof("Node [%s] is assuming leadership of the cluster", identity)
if identity == id {
// We have the lock
}
}, },
}, },
}) })

View File

@@ -51,7 +51,7 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
// If we want to start a node as leader then we will not add any remote peers, this will leave this as a cluster of one // If we want to start a node as leader then we will not add any remote peers, this will leave this as a cluster of one
// The remotePeers will add themselves to the cluster as they're added // The remotePeers will add themselves to the cluster as they're added
if c.StartAsLeader != true { if !c.StartAsLeader {
for x := range c.RemotePeers { for x := range c.RemotePeers {
// Make sure that we don't add in this server twice // Make sure that we don't add in this server twice
if c.LocalPeer.Address != c.RemotePeers[x].Address { if c.LocalPeer.Address != c.RemotePeers[x].Address {
@@ -87,7 +87,10 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
isLeader := c.StartAsLeader isLeader := c.StartAsLeader
// (attempt to) Remove the virtual IP, incase it already exists // (attempt to) Remove the virtual IP, incase it already exists
cluster.Network.DeleteIP() err = cluster.Network.DeleteIP()
if err != nil {
log.Errorf("error deleting virtual IP: %v", err)
}
// leader log broadcast - this counter is used to stop flooding STDOUT with leader log entries // leader log broadcast - this counter is used to stop flooding STDOUT with leader log entries
var leaderbroadcast int var leaderbroadcast int
@@ -98,7 +101,7 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
// Iterate through all Configurations // Iterate through all Configurations
for x := range c.LoadBalancers { for x := range c.LoadBalancers {
// If the load balancer doesn't bind to the VIP // If the load balancer doesn't bind to the VIP
if c.LoadBalancers[x].BindToVip == false { if !c.LoadBalancers[x].BindToVip {
err = nonVipLB.Add("", &c.LoadBalancers[x]) err = nonVipLB.Add("", &c.LoadBalancers[x])
if err != nil { if err != nil {
log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err) log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err)
@@ -112,15 +115,6 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
go func() { go func() {
for { for {
if c.AddPeersAsBackends == true {
// Get addresses and change backends
// c.LoadBalancers[0].Backends
// for x := range raftServer.GetConfiguration().Configuration().Servers {
// raftServer.GetConfiguration().Configuration().Servers[x].Address
// }
}
// Broadcast the current leader on this node if it's the correct time (every leaderLogcount * time.Second) // Broadcast the current leader on this node if it's the correct time (every leaderLogcount * time.Second)
if leaderbroadcast == leaderLogcount { if leaderbroadcast == leaderLogcount {
log.Infof("The Node [%s] is leading", raftServer.Leader()) log.Infof("The Node [%s] is leading", raftServer.Leader())
@@ -130,7 +124,7 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
// ensure that if this node is the leader, it is set as the leader // ensure that if this node is the leader, it is set as the leader
if localAddress == string(raftServer.Leader()) { if localAddress == string(raftServer.Leader()) {
// Re-broadcast arp to ensure network stays up to date // Re-broadcast arp to ensure network stays up to date
if c.EnableARP == true { if c.EnableARP {
// Gratuitous ARP, will broadcast to new MAC <-> IP // Gratuitous ARP, will broadcast to new MAC <-> IP
err = vip.ARPSendGratuitous(cluster.Network.IP(), c.Interface) err = vip.ARPSendGratuitous(cluster.Network.IP(), c.Interface)
if err != nil { if err != nil {
@@ -143,7 +137,10 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
} }
} else { } else {
// (attempt to) Remove the virtual IP, incase it already exists to keep nodes clean // (attempt to) Remove the virtual IP, incase it already exists to keep nodes clean
cluster.Network.DeleteIP() err := cluster.Network.DeleteIP()
if err != nil {
log.Errorf("could not delete virtualIP: %v", err)
}
isLeader = false isLeader = false
} }
@@ -166,7 +163,7 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
for x := range c.LoadBalancers { for x := range c.LoadBalancers {
if c.LoadBalancers[x].BindToVip == true { if c.LoadBalancers[x].BindToVip {
err = VipLB.Add(cluster.Network.IP(), &c.LoadBalancers[x]) err = VipLB.Add(cluster.Network.IP(), &c.LoadBalancers[x])
if err != nil { if err != nil {
log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err) log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err)
@@ -187,7 +184,7 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
} }
} }
if c.EnableARP == true { if c.EnableARP {
// Gratuitous ARP, will broadcast to new MAC <-> IP // Gratuitous ARP, will broadcast to new MAC <-> IP
err = vip.ARPSendGratuitous(cluster.Network.IP(), c.Interface) err = vip.ARPSendGratuitous(cluster.Network.IP(), c.Interface)
if err != nil { if err != nil {
@@ -220,7 +217,7 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
log.WithFields(log.Fields{"error": err, "ip": cluster.Network.IP(), "interface": cluster.Network.Interface()}).Error("Could not check ip") log.WithFields(log.Fields{"error": err, "ip": cluster.Network.IP(), "interface": cluster.Network.Interface()}).Error("Could not check ip")
} }
if result == false { if !result {
log.Error("This node is leader and is adopting the virtual IP") log.Error("This node is leader and is adopting the virtual IP")
err = cluster.Network.AddIP() err = cluster.Network.AddIP()
@@ -231,14 +228,14 @@ func (cluster *Cluster) StartRaftCluster(c *kubevip.Config) error {
for x := range c.LoadBalancers { for x := range c.LoadBalancers {
if c.LoadBalancers[x].BindToVip == true { if c.LoadBalancers[x].BindToVip {
err = VipLB.Add(cluster.Network.IP(), &c.LoadBalancers[x]) err = VipLB.Add(cluster.Network.IP(), &c.LoadBalancers[x])
if err != nil { if err != nil {
log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err) log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err)
} }
} }
} }
if c.EnableARP == true { if c.EnableARP {
// Gratuitous ARP, will broadcast to new MAC <-> IP // Gratuitous ARP, will broadcast to new MAC <-> IP
err = vip.ARPSendGratuitous(cluster.Network.IP(), c.Interface) err = vip.ARPSendGratuitous(cluster.Network.IP(), c.Interface)
if err != nil { if err != nil {

View File

@@ -17,7 +17,7 @@ import (
func (cluster *Cluster) StartSingleNode(c *kubevip.Config, disableVIP bool) error { func (cluster *Cluster) StartSingleNode(c *kubevip.Config, disableVIP bool) error {
// Start kube-vip as a single node server // Start kube-vip as a single node server
// TODO - Split all this code out as a seperate function // TODO - Split all this code out as a separate function
log.Infoln("Starting kube-vip as a single node cluster") log.Infoln("Starting kube-vip as a single node cluster")
log.Info("This node is assuming leadership of the cluster") log.Info("This node is assuming leadership of the cluster")
@@ -32,7 +32,7 @@ func (cluster *Cluster) StartSingleNode(c *kubevip.Config, disableVIP bool) erro
// Iterate through all Configurations // Iterate through all Configurations
for x := range c.LoadBalancers { for x := range c.LoadBalancers {
// If the load balancer doesn't bind to the VIP // If the load balancer doesn't bind to the VIP
if c.LoadBalancers[x].BindToVip == false { if !c.LoadBalancers[x].BindToVip {
err := nonVipLB.Add("", &c.LoadBalancers[x]) err := nonVipLB.Add("", &c.LoadBalancers[x])
if err != nil { if err != nil {
log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err) log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err)
@@ -55,7 +55,7 @@ func (cluster *Cluster) StartSingleNode(c *kubevip.Config, disableVIP bool) erro
// Once we have the VIP running, start the load balancer(s) that bind to the VIP // Once we have the VIP running, start the load balancer(s) that bind to the VIP
for x := range c.LoadBalancers { for x := range c.LoadBalancers {
if c.LoadBalancers[x].BindToVip == true { if c.LoadBalancers[x].BindToVip {
err = VipLB.Add(cluster.Network.IP(), &c.LoadBalancers[x]) err = VipLB.Add(cluster.Network.IP(), &c.LoadBalancers[x])
if err != nil { if err != nil {
log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err) log.Warnf("Error creating loadbalancer [%s] type [%s] -> error [%s]", c.LoadBalancers[x].Name, c.LoadBalancers[x].Type, err)
@@ -64,7 +64,7 @@ func (cluster *Cluster) StartSingleNode(c *kubevip.Config, disableVIP bool) erro
} }
} }
if c.EnableARP == true { if c.EnableARP {
// Gratuitous ARP, will broadcast to new MAC <-> IP // Gratuitous ARP, will broadcast to new MAC <-> IP
err := vip.ARPSendGratuitous(cluster.Network.IP(), c.Interface) err := vip.ARPSendGratuitous(cluster.Network.IP(), c.Interface)
if err != nil { if err != nil {
@@ -73,6 +73,7 @@ func (cluster *Cluster) StartSingleNode(c *kubevip.Config, disableVIP bool) erro
} }
go func() { go func() {
//nolint
for { for {
select { select {
case <-cluster.stop: case <-cluster.stop:
@@ -114,6 +115,7 @@ func (cluster *Cluster) StartLoadBalancerService(c *kubevip.Config, bgp *bgp.Ser
// use a Go context so we can tell the arp loop code when we // use a Go context so we can tell the arp loop code when we
// want to step down // want to step down
//nolint
ctxArp, cancelArp := context.WithCancel(context.Background()) ctxArp, cancelArp := context.WithCancel(context.Background())
defer cancelArp() defer cancelArp()
@@ -130,7 +132,7 @@ func (cluster *Cluster) StartLoadBalancerService(c *kubevip.Config, bgp *bgp.Ser
log.Warnf("%v", err) log.Warnf("%v", err)
} }
if c.EnableARP == true { if c.EnableARP {
ctxArp, cancelArp = context.WithCancel(context.Background()) ctxArp, cancelArp = context.WithCancel(context.Background())
ipString := cluster.Network.IP() ipString := cluster.Network.IP()
@@ -196,6 +198,7 @@ func (cluster *Cluster) StartLoadBalancerService(c *kubevip.Config, bgp *bgp.Ser
} }
go func() { go func() {
//nolint
for { for {
select { select {
case <-cluster.stop: case <-cluster.stop:

View File

@@ -6,16 +6,16 @@ const (
//vipArp - defines if the arp broadcast should be enabled //vipArp - defines if the arp broadcast should be enabled
vipArp = "vip_arp" vipArp = "vip_arp"
//vipLeaderElection - defines if the kubernetes algorithim should be used //vipLeaderElection - defines if the kubernetes algorithm should be used
vipLeaderElection = "vip_leaderelection" vipLeaderElection = "vip_leaderelection"
//vipLeaderElection - defines if the kubernetes algorithim should be used //vipLeaderElection - defines if the kubernetes algorithm should be used
vipLeaseDuration = "vip_leaseduration" vipLeaseDuration = "vip_leaseduration"
//vipLeaderElection - defines if the kubernetes algorithim should be used //vipLeaderElection - defines if the kubernetes algorithm should be used
vipRenewDeadline = "vip_renewdeadline" vipRenewDeadline = "vip_renewdeadline"
//vipLeaderElection - defines if the kubernetes algorithim should be used //vipLeaderElection - defines if the kubernetes algorithm should be used
vipRetryPeriod = "vip_retryperiod" vipRetryPeriod = "vip_retryperiod"
//vipLogLevel - defines the level of logging to produce (5 being the most verbose) //vipLogLevel - defines the level of logging to produce (5 being the most verbose)
@@ -66,7 +66,7 @@ const (
vipLocalPeer = "vip_localpeer" vipLocalPeer = "vip_localpeer"
//vipRemotePeers defines the configuration of the local raft peer //vipRemotePeers defines the configuration of the local raft peer
vipRemotePeers = "vip_remotepeers" // vipRemotePeers = "vip_remotepeers"
//vipAddPeersToLB defines that RAFT peers should be added to the load-balancer //vipAddPeersToLB defines that RAFT peers should be added to the load-balancer
vipAddPeersToLB = "vip_addpeerstolb" vipAddPeersToLB = "vip_addpeerstolb"
@@ -98,7 +98,7 @@ const (
//bgpPeerAS defines the AS for a BGP peer //bgpPeerAS defines the AS for a BGP peer
bgpPeerAS = "bgp_peeras" bgpPeerAS = "bgp_peeras"
//bgpPeerAS defines the AS for a BGP peer //bgpPeerAS defines the AS for a BGP peer
bgpPeerPassword = "bgp_peerpass" bgpPeerPassword = "bgp_peerpass" // nolint
//bgpMultiHop enables mulithop routing //bgpMultiHop enables mulithop routing
bgpMultiHop = "bgp_multihop" bgpMultiHop = "bgp_multihop"
//bgpSourceIF defines the source interface for BGP peering //bgpSourceIF defines the source interface for BGP peering
@@ -137,5 +137,5 @@ const (
lbBackends = "lb_backends" lbBackends = "lb_backends"
//vipConfigMap defines the configmap that kube-vip will watch for service definitions //vipConfigMap defines the configmap that kube-vip will watch for service definitions
vipConfigMap = "vip_configmap" // vipConfigMap = "vip_configmap"
) )

View File

@@ -56,7 +56,7 @@ func ParseEnvironment(c *Config) error {
c.EnableLeaderElection = b c.EnableLeaderElection = b
} }
// Attempt to find the Lease configuration from teh environment variables // Attempt to find the Lease configuration from the environment variables
env = os.Getenv(vipLeaseDuration) env = os.Getenv(vipLeaseDuration)
if env != "" { if env != "" {
i, err := strconv.ParseInt(env, 10, 32) i, err := strconv.ParseInt(env, 10, 32)
@@ -196,7 +196,7 @@ func ParseEnvironment(c *Config) error {
c.EnableARP = b c.EnableARP = b
} }
//Removal of seperate peer //Removal of separate peer
env = os.Getenv(vipLocalPeer) env = os.Getenv(vipLocalPeer)
if env != "" { if env != "" {
// Parse the string in format <id>:<address>:<port> // Parse the string in format <id>:<address>:<port>
@@ -213,10 +213,10 @@ func ParseEnvironment(c *Config) error {
// Remove existing peers // Remove existing peers
c.RemotePeers = []RaftPeer{} c.RemotePeers = []RaftPeer{}
// Parse the remote peers (comma seperated) // Parse the remote peers (comma separated)
s := strings.Split(env, ",") s := strings.Split(env, ",")
if len(s) == 0 { if len(s) == 0 {
return fmt.Errorf("The Remote Peer List [%s] is unable to be parsed, should be in comma seperated format <id>:<address>:<port>", env) return fmt.Errorf("The Remote Peer List [%s] is unable to be parsed, should be in comma separated format <id>:<address>:<port>", env)
} }
for x := range s { for x := range s {
// Parse the each remote peer string in format <id>:<address>:<port> // Parse the each remote peer string in format <id>:<address>:<port>
@@ -426,10 +426,10 @@ func parseEnvironmentLoadBalancer(c *Config) error {
// Remove existing backends // Remove existing backends
c.LoadBalancers[0].Backends = []BackEnd{} c.LoadBalancers[0].Backends = []BackEnd{}
// Parse the remote peers (comma seperated) // Parse the remote peers (comma separated)
s := strings.Split(env, ",") s := strings.Split(env, ",")
if len(s) == 0 { if len(s) == 0 {
return fmt.Errorf("The Backends List [%s] is unable to be parsed, should be in comma seperated format <address>:<port>", env) return fmt.Errorf("The Backends List [%s] is unable to be parsed, should be in comma separated format <address>:<port>", env)
} }
for x := range s { for x := range s {
// Parse the each remote peer string in format <address>:<port> // Parse the each remote peer string in format <address>:<port>
@@ -473,7 +473,7 @@ func generatePodSpec(c *Config, imageVersion string, inCluster bool) *corev1.Pod
newEnvironment = append(newEnvironment, iface...) newEnvironment = append(newEnvironment, iface...)
} }
// Detect if we should be using a seperate interface for sercices // Detect if we should be using a separate interface for sercices
if c.ServicesInterface != "" { if c.ServicesInterface != "" {
// build environment variables // build environment variables
svcInterface := []corev1.EnvVar{ svcInterface := []corev1.EnvVar{
@@ -743,7 +743,7 @@ func generatePodSpec(c *Config, imageVersion string, inCluster bool) *corev1.Pod
}) })
} }
// Parse peers into a comma seperated string // Parse peers into a comma separated string
if len(c.RemotePeers) != 0 { if len(c.RemotePeers) != 0 {
var peers string var peers string
for x := range c.RemotePeers { for x := range c.RemotePeers {

View File

@@ -55,7 +55,7 @@ func OpenConfig(path string) (*Config, error) {
return nil, err return nil, err
} }
// If data is read succesfully parse the yaml // If data is read successfully parse the yaml
var c Config var c Config
err = yaml.Unmarshal(configData, &c) err = yaml.Unmarshal(configData, &c)
if err != nil { if err != nil {

View File

@@ -99,7 +99,7 @@ type Config struct {
// LeaderElection defines all of the settings for Kubernetes LeaderElection // LeaderElection defines all of the settings for Kubernetes LeaderElection
type LeaderElection struct { type LeaderElection struct {
// EnableLeaderElection will use the Kubernetes leader election algorithim // EnableLeaderElection will use the Kubernetes leader election algorithm
EnableLeaderElection bool `yaml:"enableLeaderElection"` EnableLeaderElection bool `yaml:"enableLeaderElection"`
// Lease Duration - length of time a lease can be held for // Lease Duration - length of time a lease can be held for

View File

@@ -109,6 +109,7 @@ func NewLeaderElector(lec LeaderElectionConfig) (*LeaderElector, error) {
} }
// LeaderElectionConfig defines the configuration for the leaderElection // LeaderElectionConfig defines the configuration for the leaderElection
// nolint
type LeaderElectionConfig struct { type LeaderElectionConfig struct {
// Lock is the resource that will be used for locking // Lock is the resource that will be used for locking
Lock rl.Interface Lock rl.Interface
@@ -191,7 +192,7 @@ type LeaderElector struct {
metrics leaderMetricsAdapter metrics leaderMetricsAdapter
// name is the name of the resource lock for debugging // name is the name of the resource lock for debugging
name string // name string
} }
// Run starts the leader election loop // Run starts the leader election loop

View File

@@ -28,7 +28,7 @@ type leaderMetricsAdapter interface {
leaderOff(name string) leaderOff(name string)
} }
// GaugeMetric represents a single numerical value that can arbitrarily go up // SwitchMetric represents a single numerical value that can arbitrarily go up
// and down. // and down.
type SwitchMetric interface { type SwitchMetric interface {
On(name string) On(name string)
@@ -72,7 +72,8 @@ type MetricsProvider interface {
type noopMetricsProvider struct{} type noopMetricsProvider struct{}
func (_ noopMetricsProvider) NewLeaderMetric() SwitchMetric { // NewLeaderMetric returns a new SwitchMetric.
func (nmp noopMetricsProvider) NewLeaderMetric() SwitchMetric {
return noopMetric{} return noopMetric{}
} }

View File

@@ -62,7 +62,7 @@ func persistentConnection(frontendConnection net.Conn, lb *kubevip.LoadBalancer)
wg.Done() wg.Done()
}() }()
// go func() { // go func() {
// Begin copying recieving (endpoint -> back to frontend) // Begin copying receiving (endpoint -> back to frontend)
bytes, err := io.Copy(frontendConnection, endpoint) bytes, err := io.Copy(frontendConnection, endpoint)
log.Debugf("[%d] bytes of data sent to client", bytes) log.Debugf("[%d] bytes of data sent to client", bytes)
if err != nil { if err != nil {
@@ -116,7 +116,7 @@ func persistentUDPConnection(frontendConnection net.Conn, lb *kubevip.LoadBalanc
wg.Done() wg.Done()
}() }()
// go func() { // go func() {
// Begin copying recieving (endpoint -> back to frontend) // Begin copying receiving (endpoint -> back to frontend)
bytes, err := io.Copy(frontendConnection, endpoint) bytes, err := io.Copy(frontendConnection, endpoint)
log.Debugf("[%d] bytes of data sent to client", bytes) log.Debugf("[%d] bytes of data sent to client", bytes)
if err != nil { if err != nil {

View File

@@ -57,11 +57,10 @@ func (lb *LBInstance) startHTTP(bindAddress string) error {
server := &http.Server{Addr: frontEnd, Handler: mux} server := &http.Server{Addr: frontEnd, Handler: mux}
go func() error { go func() {
if err := server.ListenAndServe(); err != nil { if err := server.ListenAndServe(); err != nil {
return err log.Fatalf("error starting http server: %v", err)
} }
return nil
}() }()
// If the stop channel is closed then the server will be gracefully shut down // If the stop channel is closed then the server will be gracefully shut down
@@ -120,7 +119,9 @@ func StartHTTP(lb *kubevip.LoadBalancer, address string) error {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/", handler) mux.HandleFunc("/", handler)
log.Infof("Starting server listening [%s]", frontEnd) log.Infof("Starting server listening [%s]", frontEnd)
http.ListenAndServe(frontEnd, mux) if err := http.ListenAndServe(frontEnd, mux); err != nil {
log.Fatalf("error serving: %v", err)
}
// Should never get here // Should never get here
return nil return nil
} }

View File

@@ -1,13 +1,11 @@
package loadbalancer package loadbalancer
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"net" "net"
"time" "time"
"github.com/kube-vip/kube-vip/pkg/kubevip"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@@ -61,118 +59,3 @@ func (lb *LBInstance) startTCP(bindAddress string) error {
return nil return nil
} }
// startTCPDNU - Start TCP service Do not use
// This stops the service by closing the listener and then ignorning the error from Accept()
func (lb *LBInstance) startTCPDNU(bindAddress string) error {
fullAddress := fmt.Sprintf("%s:%d", bindAddress, lb.instance.Port)
log.Infof("Starting TCP Load Balancer for service [%s]", fullAddress)
//l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", bindAddress, lb.instance.Port))
laddr, err := net.ResolveTCPAddr("tcp", fullAddress)
if nil != err {
log.Errorln(err)
}
l, err := net.ListenTCP("tcp", laddr)
if nil != err {
return fmt.Errorf("Unable to bind [%s]", err.Error())
}
go func() {
<-lb.stop
log.Debugln("Closing listener")
// We've closed the stop channel
err = l.Close()
if err != nil {
return
}
// Close the stopped channel as the listener has been stopped
close(lb.stopped)
}()
go func() {
for {
fd, err := l.Accept()
if err != nil {
// Check it it's an accept timeout
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
continue
} else if err != io.EOF {
log.Errorf("TCP Accept error [%s]", err)
return
}
}
go persistentConnection(fd, lb.instance)
//go processRequests(lb.instance, fd)
}
// }
}()
log.Infof("Load Balancer [%s] started", lb.instance.Name)
return nil
}
// user -> [LB]
// [LB] (select end pot) -> [endpoint]
//
//
//
//
//
//
func processRequests(lb *kubevip.LoadBalancer, frontendConnection net.Conn) {
for {
// READ FROM client
buf := make([]byte, 1024*1024)
datalen, err := frontendConnection.Read(buf)
if err != nil {
log.Fatalf("%v", err)
}
log.Debugf("Sent [%d] bytes to the LB", datalen)
data := buf[0:datalen]
// Connect to Endpoint
ep, err := lb.ReturnEndpointAddr()
if err != nil {
log.Errorf("No Backends available")
}
log.Debugf("Attempting endpoint [%s]", ep)
endpoint, err := net.Dial("tcp", ep)
if err != nil {
fmt.Println("dial error:", err)
// return nil, err
}
log.Debugf("succesfully connected to [%s]", ep)
// Set a timeout
endpoint.SetReadDeadline(time.Now().Add(time.Second * 1))
b, err := endpointRequest(endpoint, ep, string(data))
_, err = frontendConnection.Write(b)
if err != nil {
log.Fatal("Write: ", err)
}
}
}
// endpointRequest will take an endpoint address and send the data and wait for the response
func endpointRequest(endpoint net.Conn, endpointAddr, request string) ([]byte, error) {
// defer conn.Close()
datalen, err := fmt.Fprintf(endpoint, request)
if err != nil {
fmt.Println("dial error:", err)
return nil, err
}
log.Debugf("Sent [%d] bytes to the endpoint", datalen)
var b bytes.Buffer
io.Copy(&b, endpoint)
log.Debugf("Recieved [%d] from the endpoint", b.Len())
return b.Bytes(), nil
}

View File

@@ -31,7 +31,7 @@ type Manager struct {
config *kubevip.Config config *kubevip.Config
// Manager services // Manager services
service bool // service bool
// Keeps track of all running instances // Keeps track of all running instances
serviceInstances []Instance serviceInstances []Instance
@@ -45,7 +45,7 @@ type Manager struct {
// This channel is used to signal a shutdown // This channel is used to signal a shutdown
signalChan chan os.Signal signalChan chan os.Signal
// This is a promethues counter used to count the number of events received // This is a prometheus counter used to count the number of events received
// from the service watcher // from the service watcher
countServiceWatchEvent *prometheus.CounterVec countServiceWatchEvent *prometheus.CounterVec
} }
@@ -148,6 +148,7 @@ func (sm *Manager) Start() error {
signal.Notify(sm.signalChan, syscall.SIGTERM) signal.Notify(sm.signalChan, syscall.SIGTERM)
// Add Notification for SIGKILL (sent from Kubernetes) // Add Notification for SIGKILL (sent from Kubernetes)
//nolint
signal.Notify(sm.signalChan, syscall.SIGKILL) signal.Notify(sm.signalChan, syscall.SIGKILL)
// If BGP is enabled then we start a server instance that will broadcast VIPs // If BGP is enabled then we start a server instance that will broadcast VIPs

View File

@@ -110,7 +110,7 @@ func (sm *Manager) startARP() error {
// Set the struct to nil so nothing should use it in future // Set the struct to nil so nothing should use it in future
sm.upnp = nil sm.upnp = nil
} else { } else {
log.Infof("Succesfully enabled UPNP, Gateway address [%s]", sm.upnp.GatewayOutsideIP) log.Infof("Successfully enabled UPNP, Gateway address [%s]", sm.upnp.GatewayOutsideIP)
} }
} }

View File

@@ -19,7 +19,7 @@ func (sm *Manager) startBGP() error {
//var ns string //var ns string
var err error var err error
// If Packet is enabled then we can begin our preperation work // If Packet is enabled then we can begin our preparation work
var packetClient *packngo.Client var packetClient *packngo.Client
if sm.config.EnableMetal { if sm.config.EnableMetal {
if sm.config.ProviderConfig != "" { if sm.config.ProviderConfig != "" {

View File

@@ -2,6 +2,7 @@ package manager
import "github.com/prometheus/client_golang/prometheus" import "github.com/prometheus/client_golang/prometheus"
// PrometheusCollector defines a service watch event counter.
func (sm *Manager) PrometheusCollector() []prometheus.Collector { func (sm *Manager) PrometheusCollector() []prometheus.Collector {
return []prometheus.Collector{sm.countServiceWatchEvent} return []prometheus.Collector{sm.countServiceWatchEvent}
} }

View File

@@ -44,14 +44,18 @@ func (sm *Manager) deleteService(uid string) error {
} else { } else {
// Flip the found when we match // Flip the found when we match
found = true found = true
if sm.serviceInstances[x].isDHCP == true { if sm.serviceInstances[x].isDHCP {
sm.serviceInstances[x].dhcpClient.Stop() sm.serviceInstances[x].dhcpClient.Stop()
macvlan, err := netlink.LinkByName(sm.serviceInstances[x].dhcpInterface) macvlan, err := netlink.LinkByName(sm.serviceInstances[x].dhcpInterface)
if err != nil { if err != nil {
return fmt.Errorf("Error finding VIP Interface, for deleting DHCP Link : %v", err) return fmt.Errorf("error finding VIP Interface: %v", err)
}
err = netlink.LinkDel(macvlan)
if err != nil {
return fmt.Errorf("error deleting DHCP Link : %v", err)
} }
netlink.LinkDel(macvlan)
} }
if sm.serviceInstances[x].vipConfig.EnableBGP { if sm.serviceInstances[x].vipConfig.EnableBGP {
cidrVip := fmt.Sprintf("%s/%s", sm.serviceInstances[x].vipConfig.VIP, sm.serviceInstances[x].vipConfig.VIPCIDR) cidrVip := fmt.Sprintf("%s/%s", sm.serviceInstances[x].vipConfig.VIP, sm.serviceInstances[x].vipConfig.VIPCIDR)
@@ -61,8 +65,8 @@ func (sm *Manager) deleteService(uid string) error {
} }
} }
// If we've been through all services and not found the correct one then error // If we've been through all services and not found the correct one then error
if found == false { if !found {
return fmt.Errorf("Unable to find/stop service [%s]", uid) return fmt.Errorf("unable to find/stop service [%s]", uid)
} }
// Update the service array // Update the service array

View File

@@ -58,7 +58,7 @@ func (sm *Manager) servicesWatcher(ctx context.Context) error {
if !ok { if !ok {
return fmt.Errorf("Unable to parse Kubernetes services from API watcher") return fmt.Errorf("Unable to parse Kubernetes services from API watcher")
} }
if svc.Spec.LoadBalancerIP == "" { if svc.Spec.LoadBalancerIP == "" {
log.Infof("Service [%s] has been addded/modified, it has no assigned external addresses", svc.Name) log.Infof("Service [%s] has been addded/modified, it has no assigned external addresses", svc.Name)
} else { } else {
@@ -183,7 +183,6 @@ func (sm *Manager) annotationsWatcher() error {
sm.config.BGPPeerConfig = bgpPeer sm.config.BGPPeerConfig = bgpPeer
rw.Stop() rw.Stop()
break
case watch.Deleted: case watch.Deleted:
node, ok := event.Object.(*v1.Node) node, ok := event.Object.(*v1.Node)
if !ok { if !ok {

View File

@@ -19,7 +19,7 @@ func AttachEIP(c *packngo.Client, k *kubevip.Config, hostname string) error {
// Fallback to attempting to find the project by name // Fallback to attempting to find the project by name
proj := findProject(k.MetalProject, c) proj := findProject(k.MetalProject, c)
if proj == nil { if proj == nil {
return fmt.Errorf("Unable to find Project [%s]", k.MetalProject) return fmt.Errorf("unable to find Project [%s]", k.MetalProject)
} }
projID = proj.ID projID = proj.ID
@@ -40,7 +40,10 @@ func AttachEIP(c *packngo.Client, k *kubevip.Config, hostname string) error {
// If attachements already exist then remove them // If attachements already exist then remove them
if len(ip.Assignments) != 0 { if len(ip.Assignments) != 0 {
hrefID := path.Base(ip.Assignments[0].Href) hrefID := path.Base(ip.Assignments[0].Href)
c.DeviceIPs.Unassign(hrefID) _, err := c.DeviceIPs.Unassign(hrefID)
if err != nil {
return fmt.Errorf("unable to unassign deviceIP %q: %v", hrefID, err)
}
} }
} }
} }
@@ -48,7 +51,7 @@ func AttachEIP(c *packngo.Client, k *kubevip.Config, hostname string) error {
// Lookup this server through the packet API // Lookup this server through the packet API
thisDevice := findSelf(c, projID) thisDevice := findSelf(c, projID)
if thisDevice == nil { if thisDevice == nil {
return fmt.Errorf("Unable to find local/this device in packet API") return fmt.Errorf("unable to find local/this device in packet API")
} }
// Assign the EIP to this device // Assign the EIP to this device

View File

@@ -45,7 +45,7 @@ type Manager struct {
// This channel is used to signal a shutdown // This channel is used to signal a shutdown
signalChan chan os.Signal signalChan chan os.Signal
// This is a promethues counter used to count the number of events received // This is a prometheus counter used to count the number of events received
// from the service watcher // from the service watcher
countServiceWatchEvent *prometheus.CounterVec countServiceWatchEvent *prometheus.CounterVec
} }
@@ -77,7 +77,7 @@ type Instance struct {
// NewManager will create a new managing object // NewManager will create a new managing object
func NewManager(configMap string, config *kubevip.Config) (*Manager, error) { func NewManager(configMap string, config *kubevip.Config) (*Manager, error) {
var clientset *kubernetes.Clientset var clientset *kubernetes.Clientset
if OutSideCluster == false { if !OutSideCluster {
// This will attempt to load the configuration when running within a POD // This will attempt to load the configuration when running within a POD
cfg, err := rest.InClusterConfig() cfg, err := rest.InClusterConfig()
if err != nil { if err != nil {

View File

@@ -38,7 +38,7 @@ func (sm *Manager) startARP() error {
// Set the struct to nil so nothing should use it in future // Set the struct to nil so nothing should use it in future
sm.upnp = nil sm.upnp = nil
} else { } else {
log.Infof("Succesfully enabled UPNP, Gateway address [%s]", sm.upnp.GatewayOutsideIP) log.Infof("Successfully enabled UPNP, Gateway address [%s]", sm.upnp.GatewayOutsideIP)
} }
} }
@@ -89,6 +89,7 @@ func (sm *Manager) startARP() error {
signal.Notify(signalChan, syscall.SIGTERM) signal.Notify(signalChan, syscall.SIGTERM)
// Add Notification for SIGKILL (sent from Kubernetes) // Add Notification for SIGKILL (sent from Kubernetes)
//nolint
signal.Notify(signalChan, syscall.SIGKILL) signal.Notify(signalChan, syscall.SIGKILL)
go func() { go func() {
<-signalChan <-signalChan
@@ -112,7 +113,9 @@ func (sm *Manager) startARP() error {
RetryPeriod: 1 * time.Second, RetryPeriod: 1 * time.Second,
Callbacks: leaderelection.LeaderCallbacks{ Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: func(ctx context.Context) { OnStartedLeading: func(ctx context.Context) {
sm.servicesWatcher(ctx) if err := sm.servicesWatcher(ctx); err != nil {
log.Fatalf("error starting services watcher: %v", err)
}
}, },
OnStoppedLeading: func() { OnStoppedLeading: func() {
// we can do cleanup here // we can do cleanup here

View File

@@ -15,7 +15,7 @@ import (
// Start will begin the Manager, which will start services and watch the configmap // Start will begin the Manager, which will start services and watch the configmap
func (sm *Manager) startBGP() error { func (sm *Manager) startBGP() error {
// If Packet is enabled then we can begin our preperation work // If Packet is enabled then we can begin our preparation work
var packetClient *packngo.Client var packetClient *packngo.Client
var err error var err error
if sm.config.EnableMetal { if sm.config.EnableMetal {
@@ -63,6 +63,7 @@ func (sm *Manager) startBGP() error {
signal.Notify(signalChan, syscall.SIGTERM) signal.Notify(signalChan, syscall.SIGTERM)
// Add Notification for SIGKILL (sent from Kubernetes) // Add Notification for SIGKILL (sent from Kubernetes)
//nolint
signal.Notify(signalChan, syscall.SIGKILL) signal.Notify(signalChan, syscall.SIGKILL)
go func() { go func() {
<-signalChan <-signalChan
@@ -71,7 +72,9 @@ func (sm *Manager) startBGP() error {
cancel() cancel()
}() }()
sm.servicesWatcher(ctx) if err := sm.servicesWatcher(ctx); err != nil {
log.Fatalf("error starting services watcher: %v", err)
}
log.Infof("Shutting down Kube-Vip") log.Infof("Shutting down Kube-Vip")

View File

@@ -26,8 +26,8 @@ func (sm *Manager) stopService(uid string) error {
sm.serviceInstances[x].cluster.Stop() sm.serviceInstances[x].cluster.Stop()
} }
} }
if found == false { if !found {
return fmt.Errorf("Unable to find/stop service [%s]", uid) return fmt.Errorf("unable to find/stop service [%s]", uid)
} }
return nil return nil
} }
@@ -42,12 +42,14 @@ func (sm *Manager) deleteService(uid string) error {
} else { } else {
// Flip the found when we match // Flip the found when we match
found = true found = true
if sm.serviceInstances[x].isDHCP == true { if sm.serviceInstances[x].isDHCP {
macvlan, err := netlink.LinkByName(sm.serviceInstances[x].dhcpInterface) macvlan, err := netlink.LinkByName(sm.serviceInstances[x].dhcpInterface)
if err != nil { if err != nil {
return fmt.Errorf("Error finding VIP Interface, for deleting DHCP Link : %v", err) return fmt.Errorf("error finding VIP Interface, for deleting DHCP Link : %v", err)
}
if err := netlink.LinkDel(macvlan); err != nil {
return fmt.Errorf("error deleing link: %v", err)
} }
netlink.LinkDel(macvlan)
} }
if sm.serviceInstances[x].vipConfig.EnableBGP { if sm.serviceInstances[x].vipConfig.EnableBGP {
cidrVip := fmt.Sprintf("%s/%s", sm.serviceInstances[x].vipConfig.VIP, sm.serviceInstances[x].vipConfig.VIPCIDR) cidrVip := fmt.Sprintf("%s/%s", sm.serviceInstances[x].vipConfig.VIP, sm.serviceInstances[x].vipConfig.VIPCIDR)
@@ -57,8 +59,8 @@ func (sm *Manager) deleteService(uid string) error {
} }
} }
// If we've been through all services and not found the correct one then error // If we've been through all services and not found the correct one then error
if found == false { if !found {
return fmt.Errorf("Unable to find/stop service [%s]", uid) return fmt.Errorf("unable to find/stop service [%s]", uid)
} }
// Update the service array // Update the service array
@@ -95,7 +97,7 @@ func (sm *Manager) syncServices(service *v1.Service) error {
} }
// This instance wasn't found, we need to add it to the manager // This instance wasn't found, we need to add it to the manager
if foundInstance == false { if !foundInstance {
// Create new service // Create new service
var newService Instance var newService Instance
newService.UID = newServiceUID newService.UID = newServiceUID

View File

@@ -12,41 +12,8 @@ import (
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
watchtools "k8s.io/client-go/tools/watch" watchtools "k8s.io/client-go/tools/watch"
"github.com/kube-vip/kube-vip/pkg/kubevip"
) )
func rebuildEndpoints(eps v1.Endpoints) []kubevip.BackEnd {
var addresses []string
var ports []int32
for x := range eps.Subsets {
// Loop over addresses
for y := range eps.Subsets[x].Addresses {
addresses = append(addresses, eps.Subsets[x].Addresses[y].IP)
}
for y := range eps.Subsets[x].Ports {
ports = append(ports, eps.Subsets[x].Ports[y].Port)
}
}
var newBackend []kubevip.BackEnd
// Build endpoints
for x := range addresses {
for y := range ports {
// Print out Backends if debug logging is enabled
if log.GetLevel() == log.DebugLevel {
fmt.Printf("-> Address: %s:%d \n", addresses[x], ports[y])
}
newBackend = append(newBackend, kubevip.BackEnd{
Address: addresses[x],
Port: int(ports[y]),
})
}
}
return newBackend
}
// This file handles the watching of a services endpoints and updates a load balancers endpoint configurations accordingly // This file handles the watching of a services endpoints and updates a load balancers endpoint configurations accordingly
func (sm *Manager) servicesWatcher(ctx context.Context) error { func (sm *Manager) servicesWatcher(ctx context.Context) error {
// Watch function // Watch function

View File

@@ -92,7 +92,7 @@ func NewConfig(address string, iface string, isDDNS bool) (Network, error) {
return result, err return result, err
} }
//AddIP - Add an IP address to the interface // AddIP - Add an IP address to the interface
func (configurator *network) AddIP() error { func (configurator *network) AddIP() error {
if err := netlink.AddrReplace(configurator.link, configurator.address); err != nil { if err := netlink.AddrReplace(configurator.link, configurator.address); err != nil {
return errors.Wrap(err, "could not add ip") return errors.Wrap(err, "could not add ip")
@@ -100,7 +100,7 @@ func (configurator *network) AddIP() error {
return nil return nil
} }
//DeleteIP - Remove an IP address from the interface // DeleteIP - Remove an IP address from the interface
func (configurator *network) DeleteIP() error { func (configurator *network) DeleteIP() error {
result, err := configurator.IsSet() result, err := configurator.IsSet()
if err != nil { if err != nil {

View File

@@ -1,6 +1,7 @@
//go:build linux
// +build linux // +build linux
// These syscalls are only supported on Linux, so this uses a build directive during compilation. Other OS's will use the arp_unsupported.go and recieve an error // These syscalls are only supported on Linux, so this uses a build directive during compilation. Other OS's will use the arp_unsupported.go and receive an error
package vip package vip

View File

@@ -1,6 +1,7 @@
// It"s DHCP client implementation that refers to https://www.rfc-editor.org/rfc/rfc2131.html
package vip package vip
// DHCP client implementation that refers to https://www.rfc-editor.org/rfc/rfc2131.html
import ( import (
"context" "context"
"fmt" "fmt"
@@ -16,7 +17,7 @@ import (
// Callback is a function called on certain events // Callback is a function called on certain events
type Callback func(*nclient4.Lease) type Callback func(*nclient4.Lease)
// Client is a DHCP client, responsible for maintaining ipv4 lease for one specified interface // DHCPClient is responsible for maintaining ipv4 lease for one specified interface
type DHCPClient struct { type DHCPClient struct {
iface *net.Interface iface *net.Interface
lease *nclient4.Lease lease *nclient4.Lease
@@ -26,6 +27,7 @@ type DHCPClient struct {
onBound Callback onBound Callback
} }
// NewDHCPClient returns a new DHCP Client.
func NewDHCPClient(iface *net.Interface, initRebootFlag bool, requestedIP string, onBound Callback) *DHCPClient { func NewDHCPClient(iface *net.Interface, initRebootFlag bool, requestedIP string, onBound Callback) *DHCPClient {
return &DHCPClient{ return &DHCPClient{
iface: iface, iface: iface,
@@ -163,10 +165,12 @@ func (c *DHCPClient) Start() {
func (c *DHCPClient) request() (*nclient4.Lease, error) { func (c *DHCPClient) request() (*nclient4.Lease, error) {
broadcast, err := nclient4.New(c.iface.Name) broadcast, err := nclient4.New(c.iface.Name)
defer broadcast.Close()
if err != nil { if err != nil {
return nil, fmt.Errorf("create a broadcast client for iface %s failed, error: %w", c.iface.Name, err) return nil, fmt.Errorf("create a broadcast client for iface %s failed, error: %w", c.iface.Name, err)
} }
defer broadcast.Close()
return broadcast.Request(context.TODO()) return broadcast.Request(context.TODO())
} }

View File

@@ -40,8 +40,13 @@ func (d *ipUpdater) Run(ctx context.Context) {
} }
log.Infof("setting %s as an IP", ip) log.Infof("setting %s as an IP", ip)
d.vip.SetIP(ip) if err := d.vip.SetIP(ip); err != nil {
d.vip.AddIP() log.Errorf("setting %s as an IP: %v", ip, err)
}
if err := d.vip.AddIP(); err != nil {
log.Errorf("error adding virtual IP: %v", err)
}
} }
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)

View File

@@ -9,12 +9,14 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// NdpResponder defines the parameters for the NDP connection.
type NdpResponder struct { type NdpResponder struct {
intf string intf string
hardwareAddr net.HardwareAddr hardwareAddr net.HardwareAddr
conn *ndp.Conn conn *ndp.Conn
} }
// NewNDPResponder takes an ifaceName and returns a new NDP responder and error if encountered.
func NewNDPResponder(ifaceName string) (*NdpResponder, error) { func NewNDPResponder(ifaceName string) (*NdpResponder, error) {
iface, err := net.InterfaceByName(ifaceName) iface, err := net.InterfaceByName(ifaceName)
if err != nil { if err != nil {
@@ -35,10 +37,12 @@ func NewNDPResponder(ifaceName string) (*NdpResponder, error) {
return ret, nil return ret, nil
} }
// Close closes the NDP responder connection.
func (n *NdpResponder) Close() error { func (n *NdpResponder) Close() error {
return n.conn.Close() return n.conn.Close()
} }
// SendGratuitous broadcasts an NDP update or returns error if encountered.
func (n *NdpResponder) SendGratuitous(address string) error { func (n *NdpResponder) SendGratuitous(address string) error {
ip := net.ParseIP(address) ip := net.ParseIP(address)
if ip == nil { if ip == nil {

View File

@@ -227,7 +227,7 @@ func assertControlPlaneIsRoutable(controlPlaneVIP string, transportTimeout, even
} }
transport := &http.Transport{ transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // nolint
} }
client := &http.Client{Transport: transport, Timeout: transportTimeout} client := &http.Client{Transport: transport, Timeout: transportTimeout}
Eventually(func() int { Eventually(func() int {
@@ -235,6 +235,7 @@ func assertControlPlaneIsRoutable(controlPlaneVIP string, transportTimeout, even
if resp == nil { if resp == nil {
return -1 return -1
} }
defer resp.Body.Close()
return resp.StatusCode return resp.StatusCode
}, eventuallyTimeout).Should(Equal(http.StatusOK), "Failed to connect to VIP") }, eventuallyTimeout).Should(Equal(http.StatusOK), "Failed to connect to VIP")
} }