mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/kube-vip/kube-vip.git
synced 2026-09-20 08:03:47 +08:00
support changing the VIP of services
- Support changing the VIP of services - Refactory the manager.syncServices to improve code readability Signed-off-by: yaocw2020 <yaocanwu@gmail.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1 @@
|
||||
./idea/
|
||||
.idea
|
||||
@@ -15,8 +15,6 @@ type Cluster struct {
|
||||
|
||||
// InitCluster - Will attempt to initialise all of the required settings for the cluster
|
||||
func InitCluster(c *kubevip.Config, disableVIP bool) (*Cluster, error) {
|
||||
|
||||
// TODO - Check for root (needed to netlink)
|
||||
var network vip.Network
|
||||
var err error
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ func (cluster *Cluster) vipService(ctxArp, ctxDNS context.Context, c *kubevip.Co
|
||||
}
|
||||
|
||||
// StartLoadBalancerService will start a VIP instance and leave it for kube-proxy to handle
|
||||
func (cluster *Cluster) StartLoadBalancerService(c *kubevip.Config, bgp *bgp.Server) error {
|
||||
func (cluster *Cluster) StartLoadBalancerService(c *kubevip.Config, bgp *bgp.Server) {
|
||||
// Start a kube-vip loadbalancer service
|
||||
log.Infof("Starting advertising address [%s] with kube-vip", c.VIP)
|
||||
|
||||
@@ -265,5 +265,4 @@ func (cluster *Cluster) StartLoadBalancerService(c *kubevip.Config, bgp *bgp.Ser
|
||||
}
|
||||
}()
|
||||
log.Infoln("Started Load Balancer and Virtual IP")
|
||||
return nil
|
||||
}
|
||||
|
||||
191
pkg/manager/instance.go
Normal file
191
pkg/manager/instance.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
|
||||
"github.com/kube-vip/kube-vip/pkg/kubevip"
|
||||
"github.com/kube-vip/kube-vip/pkg/vip"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/kube-vip/kube-vip/pkg/cluster"
|
||||
)
|
||||
|
||||
const dhcpTimeout = 10 * time.Second
|
||||
|
||||
// Instance defines an instance of everything needed to manage a vip
|
||||
type Instance struct {
|
||||
// Virtual IP / Load Balancer configuration
|
||||
vipConfig *kubevip.Config
|
||||
|
||||
// cluster instance
|
||||
cluster *cluster.Cluster
|
||||
|
||||
// Service uses DHCP
|
||||
isDHCP bool
|
||||
dhcpInterface string
|
||||
dhcpInterfaceHwaddr string
|
||||
dhcpInterfaceIP string
|
||||
dhcpClient *vip.DHCPClient
|
||||
|
||||
// Kubernetes service mapping
|
||||
Vip string
|
||||
Port int32
|
||||
UID string
|
||||
Type string
|
||||
|
||||
ServiceName string
|
||||
ServiceNamespace string
|
||||
}
|
||||
|
||||
func NewInstance(service *v1.Service, config *kubevip.Config) (*Instance, error) {
|
||||
instanceAddress := service.Spec.LoadBalancerIP
|
||||
instanceUID := string(service.UID)
|
||||
|
||||
// Detect if we're using a specific interface for services
|
||||
var serviceInterface string
|
||||
if config.ServicesInterface != "" {
|
||||
serviceInterface = config.ServicesInterface
|
||||
} else {
|
||||
serviceInterface = config.Interface
|
||||
}
|
||||
|
||||
// Generate new Virtual IP configuration
|
||||
newVip := &kubevip.Config{
|
||||
VIP: instanceAddress, //TODO support more than one vip?
|
||||
Interface: serviceInterface,
|
||||
SingleNode: true,
|
||||
EnableARP: config.EnableARP,
|
||||
EnableBGP: config.EnableBGP,
|
||||
VIPCIDR: config.VIPCIDR,
|
||||
}
|
||||
|
||||
// Create new service
|
||||
instance := &Instance{
|
||||
UID: instanceUID,
|
||||
Vip: instanceAddress,
|
||||
ServiceName: service.Name,
|
||||
ServiceNamespace: service.Namespace,
|
||||
}
|
||||
if len(service.Spec.Ports) > 0 {
|
||||
instance.Type = string(service.Spec.Ports[0].Protocol)
|
||||
instance.Port = service.Spec.Ports[0].Port
|
||||
}
|
||||
if service.Annotations != nil {
|
||||
instance.dhcpInterfaceHwaddr = service.Annotations[hwAddrKey]
|
||||
instance.dhcpInterfaceIP = service.Annotations[requestedIP]
|
||||
}
|
||||
|
||||
// Generate Load Balancer config
|
||||
newLB := kubevip.LoadBalancer{
|
||||
Name: fmt.Sprintf("%s-load-balancer", instance.ServiceName),
|
||||
Port: int(instance.Port),
|
||||
Type: instance.Type,
|
||||
BindToVip: true,
|
||||
}
|
||||
// Add Load Balancer Configuration
|
||||
newVip.LoadBalancers = append(newVip.LoadBalancers, newLB)
|
||||
// Create Add configuration to the new service
|
||||
instance.vipConfig = newVip
|
||||
|
||||
// If this was purposely created with the address 0.0.0.0,
|
||||
// we will create a macvlan on the main interface and a DHCP client
|
||||
if instanceAddress == "0.0.0.0" {
|
||||
ipChan, err := instance.startDHCP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
select {
|
||||
case <-time.After(dhcpTimeout):
|
||||
return nil, fmt.Errorf("timeout to request the IP from DHCP server for service %s/%s",
|
||||
instance.ServiceNamespace, instance.ServiceName)
|
||||
case ip := <-ipChan:
|
||||
instance.vipConfig.VIP = ip
|
||||
instance.dhcpInterfaceIP = ip
|
||||
}
|
||||
}
|
||||
|
||||
c, err := cluster.InitCluster(instance.vipConfig, false)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add Service %s/%s", instance.ServiceNamespace, instance.ServiceName)
|
||||
return nil, err
|
||||
}
|
||||
instance.cluster = c
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (i *Instance) startDHCP() (chan string, error) {
|
||||
parent, err := netlink.LinkByName(i.vipConfig.Interface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finding VIP Interface, for building DHCP Link : %v", err)
|
||||
}
|
||||
|
||||
// Generate name from UID
|
||||
interfaceName := fmt.Sprintf("vip-%s", i.UID[0:8])
|
||||
|
||||
// Check if the interface doesn't exist first
|
||||
iface, err := net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
log.Infof("Creating new macvlan interface for DHCP [%s]", interfaceName)
|
||||
|
||||
hwaddr, err := net.ParseMAC(i.dhcpInterfaceHwaddr)
|
||||
if i.dhcpInterfaceHwaddr != "" && err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mac := &netlink.Macvlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: interfaceName,
|
||||
ParentIndex: parent.Attrs().Index,
|
||||
HardwareAddr: hwaddr,
|
||||
},
|
||||
Mode: netlink.MACVLAN_MODE_DEFAULT,
|
||||
}
|
||||
|
||||
err = netlink.LinkAdd(mac)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not add %s: %v", interfaceName, err)
|
||||
}
|
||||
|
||||
err = netlink.LinkSetUp(mac)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not bring up interface [%s] : %v", interfaceName, err)
|
||||
}
|
||||
iface, err = net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finding new DHCP interface by name [%v]", err)
|
||||
}
|
||||
} else {
|
||||
log.Infof("Using existing macvlan interface for DHCP [%s]", interfaceName)
|
||||
}
|
||||
|
||||
var initRebootFlag bool
|
||||
if i.dhcpInterfaceHwaddr != "" {
|
||||
initRebootFlag = true
|
||||
}
|
||||
|
||||
ipChan := make(chan string)
|
||||
|
||||
client := vip.NewDHCPClient(iface, initRebootFlag, i.dhcpInterfaceIP, func(lease *nclient4.Lease) {
|
||||
ipChan <- lease.ACK.YourIPAddr.String()
|
||||
|
||||
log.Infof("DHCP VIP [%s] for [%s/%s] ", i.vipConfig.VIP, i.ServiceNamespace, i.ServiceName)
|
||||
})
|
||||
|
||||
go client.Start()
|
||||
|
||||
// Set that DHCP is enabled
|
||||
i.isDHCP = true
|
||||
// Set the name of the interface so that it can be removed on Service deletion
|
||||
i.dhcpInterface = interfaceName
|
||||
i.dhcpInterfaceHwaddr = iface.HardwareAddr.String()
|
||||
// Add the client so that we can call it to stop function
|
||||
i.dhcpClient = client
|
||||
|
||||
return ipChan, nil
|
||||
}
|
||||
@@ -13,9 +13,7 @@ import (
|
||||
|
||||
"github.com/kamhlos/upnp"
|
||||
"github.com/kube-vip/kube-vip/pkg/bgp"
|
||||
"github.com/kube-vip/kube-vip/pkg/cluster"
|
||||
"github.com/kube-vip/kube-vip/pkg/kubevip"
|
||||
"github.com/kube-vip/kube-vip/pkg/vip"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
@@ -33,7 +31,7 @@ type Manager struct {
|
||||
// service bool
|
||||
|
||||
// Keeps track of all running instances
|
||||
serviceInstances []Instance
|
||||
serviceInstances []*Instance
|
||||
|
||||
// Additional functionality
|
||||
upnp *upnp.Upnp
|
||||
@@ -49,30 +47,6 @@ type Manager struct {
|
||||
countServiceWatchEvent *prometheus.CounterVec
|
||||
}
|
||||
|
||||
// Instance defines an instance of everything needed to manage a vip
|
||||
type Instance struct {
|
||||
// Virtual IP / Load Balancer configuration
|
||||
vipConfig kubevip.Config
|
||||
|
||||
// cluster instance
|
||||
cluster cluster.Cluster
|
||||
|
||||
// Service uses DHCP
|
||||
isDHCP bool
|
||||
dhcpInterface string
|
||||
dhcpInterfaceHwaddr string
|
||||
dhcpInterfaceIP string
|
||||
dhcpClient *vip.DHCPClient
|
||||
|
||||
// Kubernetes service mapping
|
||||
Vip string
|
||||
Port int32
|
||||
UID string
|
||||
Type string
|
||||
|
||||
ServiceName string
|
||||
}
|
||||
|
||||
// New will create a new managing object
|
||||
func New(configMap string, config *kubevip.Config) (*Manager, error) {
|
||||
|
||||
|
||||
@@ -11,9 +11,6 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/util/retry"
|
||||
|
||||
"github.com/kube-vip/kube-vip/pkg/cluster"
|
||||
"github.com/kube-vip/kube-vip/pkg/kubevip"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -21,22 +18,8 @@ const (
|
||||
requestedIP = "kube-vip.io/requestedIP"
|
||||
)
|
||||
|
||||
func (sm *Manager) stopService(uid string) error {
|
||||
found := false
|
||||
for x := range sm.serviceInstances {
|
||||
if sm.serviceInstances[x].UID == uid {
|
||||
found = true
|
||||
sm.serviceInstances[x].cluster.Stop()
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("unable to find/stop service [%s]", uid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *Manager) deleteService(uid string) error {
|
||||
var updatedInstances []Instance
|
||||
var updatedInstances []*Instance
|
||||
found := false
|
||||
for x := range sm.serviceInstances {
|
||||
// Add the running services to the new array
|
||||
@@ -45,6 +28,7 @@ func (sm *Manager) deleteService(uid string) error {
|
||||
} else {
|
||||
// Flip the found when we match
|
||||
found = true
|
||||
sm.serviceInstances[x].cluster.Stop()
|
||||
if sm.serviceInstances[x].isDHCP {
|
||||
sm.serviceInstances[x].dhcpClient.Stop()
|
||||
macvlan, err := netlink.LinkByName(sm.serviceInstances[x].dhcpInterface)
|
||||
@@ -56,7 +40,6 @@ func (sm *Manager) deleteService(uid string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting DHCP Link : %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
if sm.serviceInstances[x].vipConfig.EnableBGP {
|
||||
cidrVip := fmt.Sprintf("%s/%s", sm.serviceInstances[x].vipConfig.VIP, sm.serviceInstances[x].vipConfig.VIPCIDR)
|
||||
@@ -89,97 +72,52 @@ func (sm *Manager) syncServices(service *v1.Service, wg *sync.WaitGroup) error {
|
||||
|
||||
for x := range sm.serviceInstances {
|
||||
if sm.serviceInstances[x].UID == newServiceUID {
|
||||
// We have found this instance in the manager, we can determine if it needs updating
|
||||
log.Debugf("isDHCP: %t, newServiceAddress: %s", sm.serviceInstances[x].isDHCP, newServiceAddress)
|
||||
// If the found instance's DHCP configuration doesn't match the new service, delete it.
|
||||
if sm.serviceInstances[x].isDHCP && newServiceAddress != "0.0.0.0" ||
|
||||
!sm.serviceInstances[x].isDHCP && newServiceAddress == "0.0.0.0" ||
|
||||
!sm.serviceInstances[x].isDHCP && len(service.Status.LoadBalancer.Ingress) > 0 &&
|
||||
newServiceAddress != service.Status.LoadBalancer.Ingress[0].IP {
|
||||
if err := sm.deleteService(newServiceUID); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
foundInstance = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Detect if we're using a specific interface for services
|
||||
var serviceInterface string
|
||||
if sm.config.ServicesInterface != "" {
|
||||
serviceInterface = sm.config.ServicesInterface
|
||||
} else {
|
||||
serviceInterface = sm.config.Interface
|
||||
}
|
||||
|
||||
// Generate new Virtual IP configuration
|
||||
newVip := kubevip.Config{
|
||||
VIP: newServiceAddress, //TODO support more than one vip?
|
||||
Interface: serviceInterface,
|
||||
SingleNode: true,
|
||||
EnableARP: sm.config.EnableARP,
|
||||
EnableBGP: sm.config.EnableBGP,
|
||||
VIPCIDR: sm.config.VIPCIDR,
|
||||
}
|
||||
|
||||
// This instance wasn't found, we need to add it to the manager
|
||||
if !foundInstance {
|
||||
// Create new service
|
||||
var newService Instance
|
||||
newService.UID = newServiceUID
|
||||
newService.Vip = newServiceAddress
|
||||
newService.Type = string(service.Spec.Ports[0].Protocol) //TODO - support multiple port types
|
||||
newService.Port = service.Spec.Ports[0].Port
|
||||
newService.ServiceName = service.Name
|
||||
newService.dhcpInterfaceHwaddr = service.Annotations[hwAddrKey]
|
||||
newService.dhcpInterfaceIP = service.Annotations[requestedIP]
|
||||
|
||||
// If this was purposely created with the address 0.0.0.0 then we will create a macvlan on the main interface and try DHCP
|
||||
if newServiceAddress == "0.0.0.0" {
|
||||
err := sm.createDHCPService(newServiceUID, &newVip, &newService, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("New VIP [%s] for [%s/%s] ", newService.Vip, newService.ServiceName, newService.UID)
|
||||
|
||||
// Generate Load Balancer config
|
||||
newLB := kubevip.LoadBalancer{
|
||||
Name: fmt.Sprintf("%s-load-balancer", newService.ServiceName),
|
||||
Port: int(newService.Port),
|
||||
Type: newService.Type,
|
||||
BindToVip: true,
|
||||
}
|
||||
|
||||
// Add Load Balancer Configuration
|
||||
newVip.LoadBalancers = append(newVip.LoadBalancers, newLB)
|
||||
|
||||
// Create Add configuration to the new service
|
||||
newService.vipConfig = newVip
|
||||
|
||||
// TODO - start VIP
|
||||
c, err := cluster.InitCluster(&newService.vipConfig, false)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add Service [%s] / [%s]", newService.ServiceName, newService.UID)
|
||||
if !foundInstance && newServiceAddress != "" {
|
||||
log.Infof("add the service [%s/%s] with external address %s", service.Namespace, service.Name, newServiceAddress)
|
||||
if err := sm.addService(service); err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.StartLoadBalancerService(&newService.vipConfig, sm.bgpServer)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add Service [%s] / [%s]", newService.ServiceName, newService.UID)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *Manager) addService(service *v1.Service) error {
|
||||
newService, err := NewInstance(service, sm.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("New VIP [%s] for [%s/%s] ", newService.Vip, newService.ServiceNamespace, newService.ServiceName)
|
||||
|
||||
newService.cluster.StartLoadBalancerService(newService.vipConfig, sm.bgpServer)
|
||||
|
||||
sm.upnpMap(newService)
|
||||
|
||||
sm.serviceInstances = append(sm.serviceInstances, newService)
|
||||
|
||||
if err := sm.updateStatus(newService); err != nil {
|
||||
// delete service to collect garbage
|
||||
if deleteErr := sm.deleteService(newService.UID); err != nil {
|
||||
return deleteErr
|
||||
}
|
||||
|
||||
sm.upnpMap(newService)
|
||||
|
||||
newService.cluster = *c
|
||||
|
||||
// Begin watching this service
|
||||
// TODO - we may need this
|
||||
// go sm.serviceWatcher(&newService, sm.config.Namespace)
|
||||
|
||||
// Update the "Status" of the LoadBalancer (one or many may do this), as long as one does it
|
||||
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
service.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: newVip.VIP}}
|
||||
_, updateErr := sm.clientSet.CoreV1().Services(service.Namespace).UpdateStatus(context.TODO(), service, metav1.UpdateOptions{})
|
||||
return updateErr
|
||||
})
|
||||
if retryErr != nil {
|
||||
log.Errorf("Error updating Service [%s] Status: %v", newService.ServiceName, err)
|
||||
}
|
||||
sm.serviceInstances = append(sm.serviceInstances, newService)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("[COMPLETE] Service Sync")
|
||||
@@ -187,11 +125,10 @@ func (sm *Manager) syncServices(service *v1.Service, wg *sync.WaitGroup) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *Manager) upnpMap(s Instance) {
|
||||
func (sm *Manager) upnpMap(s *Instance) {
|
||||
// If upnp is enabled then update the gateway/router with the address
|
||||
// TODO - work out if we need to mapping.Reclaim()
|
||||
if sm.upnp != nil {
|
||||
|
||||
log.Infof("[UPNP] Adding map to [%s:%d - %s]", s.Vip, s.Port, s.ServiceName)
|
||||
if err := sm.upnp.AddPortMapping(int(s.Port), int(s.Port), 0, s.Vip, strings.ToUpper(s.Type), s.ServiceName); err == nil {
|
||||
log.Infof("Service should be accessible externally on port [%d]", s.Port)
|
||||
@@ -201,3 +138,43 @@ func (sm *Manager) upnpMap(s Instance) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *Manager) updateStatus(i *Instance) error {
|
||||
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
// Retrieve the latest version of Deployment before attempting update
|
||||
// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
|
||||
currentService, err := sm.clientSet.CoreV1().Services(i.ServiceNamespace).Get(context.TODO(), i.ServiceName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentServiceCopy := currentService.DeepCopy()
|
||||
if i.dhcpInterfaceHwaddr != "" || i.dhcpInterfaceIP != "" {
|
||||
if currentServiceCopy.Annotations == nil {
|
||||
currentServiceCopy.Annotations = make(map[string]string)
|
||||
}
|
||||
currentServiceCopy.Annotations[hwAddrKey] = i.dhcpInterfaceHwaddr
|
||||
currentServiceCopy.Annotations[requestedIP] = i.dhcpInterfaceIP
|
||||
}
|
||||
|
||||
updatedService, err := sm.clientSet.CoreV1().Services(currentService.Namespace).Update(context.TODO(), currentServiceCopy, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
log.Errorf("Error updating Service Spec [%s] : %v", i.ServiceName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
updatedService.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: i.vipConfig.VIP}}
|
||||
_, err = sm.clientSet.CoreV1().Services(updatedService.Namespace).UpdateStatus(context.TODO(), updatedService, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
log.Errorf("Error updating Service %s/%s Status: %v", i.ServiceNamespace, i.ServiceName, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if retryErr != nil {
|
||||
log.Errorf("Failed to set Services: %v", retryErr)
|
||||
return retryErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/util/retry"
|
||||
|
||||
"github.com/kube-vip/kube-vip/pkg/cluster"
|
||||
"github.com/kube-vip/kube-vip/pkg/kubevip"
|
||||
"github.com/kube-vip/kube-vip/pkg/vip"
|
||||
)
|
||||
|
||||
func (sm *Manager) createDHCPService(newServiceUID string, newVip *kubevip.Config, newService *Instance, service *v1.Service) error {
|
||||
parent, err := netlink.LinkByName(sm.config.Interface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error finding VIP Interface, for building DHCP Link : %v", err)
|
||||
}
|
||||
|
||||
// Generate name from UID
|
||||
interfaceName := fmt.Sprintf("vip-%s", newServiceUID[0:8])
|
||||
|
||||
// Check if the interface doesn't exist first
|
||||
iface, err := net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
log.Infof("Creating new macvlan interface for DHCP [%s]", interfaceName)
|
||||
|
||||
hwaddr, err := net.ParseMAC(newService.dhcpInterfaceHwaddr)
|
||||
if newService.dhcpInterfaceHwaddr != "" && err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mac := &netlink.Macvlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: interfaceName,
|
||||
ParentIndex: parent.Attrs().Index,
|
||||
HardwareAddr: hwaddr,
|
||||
},
|
||||
Mode: netlink.MACVLAN_MODE_DEFAULT,
|
||||
}
|
||||
|
||||
err = netlink.LinkAdd(mac)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not add %s: %v", interfaceName, err)
|
||||
}
|
||||
|
||||
err = netlink.LinkSetUp(mac)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not bring up interface [%s] : %v", interfaceName, err)
|
||||
}
|
||||
iface, err = net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error finding new DHCP interface by name [%v]", err)
|
||||
}
|
||||
} else {
|
||||
log.Infof("Using existing macvlan interface for DHCP [%s]", interfaceName)
|
||||
}
|
||||
|
||||
var initRebootFlag bool
|
||||
if newService.dhcpInterfaceHwaddr != "" {
|
||||
initRebootFlag = true
|
||||
}
|
||||
|
||||
client := vip.NewDHCPClient(iface, initRebootFlag, newService.dhcpInterfaceIP, func(lease *nclient4.Lease) {
|
||||
newVip.VIP = lease.ACK.YourIPAddr.String()
|
||||
|
||||
log.Infof("DHCP VIP [%s] for [%s/%s] ", newVip.VIP, newService.ServiceName, newServiceUID)
|
||||
|
||||
// Create Add configuration to the new service
|
||||
newService.vipConfig = *newVip
|
||||
|
||||
// TODO - start VIP
|
||||
c, err := cluster.InitCluster(&newService.vipConfig, false)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add Service [%s] / [%s]: %v", newService.ServiceName, newService.UID, err)
|
||||
return
|
||||
}
|
||||
err = c.StartLoadBalancerService(&newService.vipConfig, sm.bgpServer)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add Load Balancer service Service [%s] / [%s]: %v", newService.ServiceName, newService.UID, err)
|
||||
return
|
||||
}
|
||||
newService.cluster = *c
|
||||
|
||||
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
// Retrieve the latest version of Deployment before attempting update
|
||||
// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
|
||||
currentService, err := sm.clientSet.CoreV1().Services(service.Namespace).Get(context.TODO(), service.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentServiceCopy := currentService.DeepCopy()
|
||||
if currentServiceCopy.Annotations == nil {
|
||||
currentServiceCopy.Annotations = make(map[string]string)
|
||||
}
|
||||
currentServiceCopy.Annotations[hwAddrKey] = iface.HardwareAddr.String()
|
||||
currentServiceCopy.Annotations[requestedIP] = newVip.VIP
|
||||
updatedService, err := sm.clientSet.CoreV1().Services(currentService.Namespace).Update(context.TODO(), currentServiceCopy, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
log.Errorf("Error updating Service Spec [%s] : %v", newService.ServiceName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
updatedService.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: newVip.VIP}}
|
||||
_, err = sm.clientSet.CoreV1().Services(updatedService.Namespace).UpdateStatus(context.TODO(), updatedService, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
log.Errorf("Error updating Service [%s] Status: %v", newService.ServiceName, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if retryErr != nil {
|
||||
log.Errorf("Failed to set Services: %v", retryErr)
|
||||
}
|
||||
// Find an update our array
|
||||
|
||||
for x := range sm.serviceInstances {
|
||||
if sm.serviceInstances[x].UID == newServiceUID {
|
||||
sm.serviceInstances[x] = *newService
|
||||
}
|
||||
}
|
||||
sm.upnpMap(*newService)
|
||||
})
|
||||
|
||||
// Set that DHCP is enabled
|
||||
newService.isDHCP = true
|
||||
// Set the name of the interface so that it can be removed on Service deletion
|
||||
newService.dhcpInterface = interfaceName
|
||||
// Add the client so that we can call it's stop function
|
||||
newService.dhcpClient = client
|
||||
|
||||
sm.serviceInstances = append(sm.serviceInstances, *newService)
|
||||
|
||||
go client.Start()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -63,35 +63,27 @@ func (sm *Manager) servicesWatcher(ctx context.Context) error {
|
||||
break
|
||||
}
|
||||
|
||||
if svc.Spec.LoadBalancerIP == "" {
|
||||
log.Infof("Service [%s] has been added/modified, it has no assigned external addresses", svc.Name)
|
||||
} else {
|
||||
log.Infof("Service [%s] has been added/modified, it has an assigned external addresses [%s]", svc.Name, svc.Spec.LoadBalancerIP)
|
||||
wg.Add(1)
|
||||
err = sm.syncServices(svc, &wg)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
wg.Wait()
|
||||
log.Infof("Service [%s] has been added/modified it has an assigned external addresses [%s]", svc.Name, svc.Spec.LoadBalancerIP)
|
||||
wg.Add(1)
|
||||
err = sm.syncServices(svc, &wg)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
wg.Wait()
|
||||
case watch.Deleted:
|
||||
svc, ok := event.Object.(*v1.Service)
|
||||
if !ok {
|
||||
return fmt.Errorf("Unable to parse Kubernetes services from API watcher")
|
||||
}
|
||||
if svc.Annotations["kube-vip.io/ignore"] == "true" {
|
||||
log.Infof("Service [%s] has an ignore annotation for kube-vip", svc.Name)
|
||||
log.Infof("Service [%s/%s] has an ignore annotation for kube-vip", svc.Namespace, svc.Name)
|
||||
break
|
||||
}
|
||||
err = sm.stopService(string(svc.UID))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
err = sm.deleteService(string(svc.UID))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infof("Service [%s] has been deleted", svc.Name)
|
||||
log.Infof("Service [%s/%s] has been deleted", svc.Namespace, svc.Name)
|
||||
|
||||
case watch.Bookmark:
|
||||
// Un-used
|
||||
|
||||
@@ -139,11 +139,7 @@ func (sm *Manager) syncServices(service *v1.Service) error {
|
||||
log.Errorf("Failed to add Service [%s] / [%s]", newService.ServiceName, newService.UID)
|
||||
return err
|
||||
}
|
||||
err = c.StartLoadBalancerService(&newService.vipConfig, sm.bgpServer)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add Service [%s] / [%s]", newService.ServiceName, newService.UID)
|
||||
return err
|
||||
}
|
||||
c.StartLoadBalancerService(&newService.vipConfig, sm.bgpServer)
|
||||
|
||||
sm.upnpMap(newService)
|
||||
|
||||
|
||||
@@ -83,11 +83,7 @@ func (sm *Manager) createDHCPService(newServiceUID string, newVip *kubevip.Confi
|
||||
log.Errorf("Failed to add Service [%s] / [%s]: %v", newService.ServiceName, newService.UID, err)
|
||||
return
|
||||
}
|
||||
err = c.StartLoadBalancerService(&newService.vipConfig, sm.bgpServer)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add Load Balancer service Service [%s] / [%s]: %v", newService.ServiceName, newService.UID, err)
|
||||
return
|
||||
}
|
||||
c.StartLoadBalancerService(&newService.vipConfig, sm.bgpServer)
|
||||
newService.cluster = *c
|
||||
|
||||
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
|
||||
Reference in New Issue
Block a user