func (c *Controller) purgeStaleServiceRefs(fip *types.FloatingIp, refs contrail.ReferenceList, podIdMap map[string]*api.Pod) { update := false for _, ref := range refs { vmi, err := types.VirtualMachineInterfaceByUuid(c.client, ref.Uuid) if err != nil { glog.Errorf("%v", err) continue } instanceRefs, err := vmi.GetVirtualMachineRefs() if err != nil { glog.Errorf("%v", err) continue } if len(instanceRefs) == 0 { continue } if _, ok := podIdMap[instanceRefs[0].Uuid]; ok { continue } glog.V(3).Infof("Delete reference from pod %s to %s", ref.Uuid, fip.GetFloatingIpAddress()) fip.DeleteVirtualMachineInterface(vmi.GetUuid()) update = true } if update { err := c.client.Update(fip) if err != nil { glog.Errorf("%v", err) } } }
func (c *Controller) updateServicePublicIP(service *api.Service) (*types.FloatingIp, error) { var publicIp *types.FloatingIp = nil var err error resourceName := publicIpResourceName(service) if service.Spec.ExternalIPs != nil { // Allocate a floating-ip from the public pool. publicIp, err = c.networkMgr.LocateFloatingIp( c.networkMgr.GetPublicNetwork(), resourceName, service.Spec.ExternalIPs[0]) } else if service.Spec.Type == api.ServiceTypeLoadBalancer { publicIp, err = c.networkMgr.LocateFloatingIp(c.networkMgr.GetPublicNetwork(), resourceName, "") if err == nil { status := api.LoadBalancerStatus{Ingress: []api.LoadBalancerIngress{ api.LoadBalancerIngress{IP: publicIp.GetFloatingIpAddress()}, }} if !reflect.DeepEqual(service.Status.LoadBalancer, status) { service.Status.LoadBalancer = status _, err := c.kube.Services(service.Namespace).Update(service) if err != nil { glog.Infof("Update service %s LB status: %v", service.Name, err) } } } } return publicIp, err }
// Create floating-ip with 2 vmi references, delete it and verify that // the back_refs are updated as expected. func TestDeleteRefs(t *testing.T) { db := NewInMemDatabase() vmi1 := new(types.VirtualMachineInterface) vmi1.SetUuid(uuid.New()) vmi1.SetName("port1") assert.NoError(t, db.Put(vmi1, nil, GetReferenceList(vmi1))) vmi2 := new(types.VirtualMachineInterface) vmi2.SetUuid(uuid.New()) vmi2.SetName("port2") assert.NoError(t, db.Put(vmi2, nil, GetReferenceList(vmi2))) fip := new(types.FloatingIp) fip.SetUuid(uuid.New()) fip.SetName("fip") fip.AddVirtualMachineInterface(vmi1) fip.AddVirtualMachineInterface(vmi2) assert.NoError(t, db.Put(fip, nil, GetReferenceList(fip))) assert.Error(t, db.Delete(vmi1)) result, err := db.GetBackReferences(parseUID(vmi2.GetUuid()), "floating_ip") assert.NoError(t, err) assert.Len(t, result, 1) assert.NoError(t, db.Delete(fip)) result, err = db.GetBackReferences(parseUID(vmi2.GetUuid()), "floating_ip") assert.NoError(t, err) assert.Len(t, result, 0) assert.NoError(t, db.Delete(vmi1)) assert.NoError(t, db.Delete(vmi2)) }
func getFloatingIpToInstanceList(client contrail.ApiClient, fip *types.FloatingIp) ([]string, error) { vmList := make([]string, 0) refs, err := fip.GetVirtualMachineInterfaceRefs() if err != nil { return vmList, err } for _, ref := range refs { vmi, err := types.VirtualMachineInterfaceByUuid(client, ref.Uuid) if err != nil { continue } instanceRefs, err := vmi.GetVirtualMachineRefs() if err != nil || len(instanceRefs) == 0 { continue } vmList = append(vmList, instanceRefs[0].Uuid) } return vmList, nil }
func (m *InstanceManager) AttachFloatingIp( podName, projectName string, floatingIp *types.FloatingIp) { fqn := []string{m.config.DefaultDomain, projectName, podName} obj, err := m.client.FindByName( "virtual-machine-interface", strings.Join(fqn, ":")) if err != nil { glog.Errorf("GET vmi %s: %v", podName, err) return } vmi := obj.(*types.VirtualMachineInterface) refs, err := floatingIp.GetVirtualMachineInterfaceRefs() if err != nil { glog.Errorf("GET floating-ip %s: %v", floatingIp.GetUuid(), err) return } for _, ref := range refs { if ref.Uuid == vmi.GetUuid() { return } } floatingIp.AddVirtualMachineInterface(vmi) err = m.client.Update(floatingIp) if err != nil { glog.Errorf("Update floating-ip %s: %v", podName, err) } }
func TestConsistencyServiceIp(t *testing.T) { env := new(TestFramework) env.SetUp("192.0.2.0/24") config := env.config client := env.client netnsProject := new(types.Project) netnsProject.SetFQName("domain", []string{"default-domain", "testns"}) client.Create(netnsProject) service1 := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "s1", Namespace: "testns", Labels: map[string]string{ config.NetworkTag: "services", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{ "Name": "pod01", }, ClusterIP: "10.254.42.42", Type: api.ServiceTypeLoadBalancer, }, } service2 := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "s2", Namespace: "testns", Labels: map[string]string{ config.NetworkTag: "services", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{ "Name": "pod02", }, ClusterIP: "10.254.42.43", ExternalIPs: []string{"10.1.4.89"}, }, } service3 := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "s3", Namespace: "testns", Labels: map[string]string{ config.NetworkTag: "services", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{ "Name": "pod01", }, ClusterIP: "10.254.42.44", }, } env.Start() installPods(env, "testns", 3) env.AddService(service1, "pod01") env.AddService(service2, "pod02") env.AddService(service3, "pod01") env.SyncBarrier() env.Shutdown() assert.True(t, env.checker.Check()) pool, err := types.FloatingIpPoolByName(client, "default-domain:testns:service-services:service-services") assert.NoError(t, err) vmi, err := types.VirtualMachineInterfaceByName(client, "default-domain:testns:pod01") assert.NoError(t, err) vip := new(types.FloatingIp) fqn := make([]string, len(pool.GetFQName())+1) copy(fqn, pool.GetFQName()) fqn[len(pool.GetFQName())] = "s4" vip.SetFQName(vip.GetDefaultParentType(), fqn) vip.AddVirtualMachineInterface(vmi) assert.NoError(t, client.Create(vip)) assert.False(t, env.checker.Check()) assert.NoError(t, client.Delete(vip)) assert.True(t, env.checker.Check()) vip, err = types.FloatingIpByName(client, "default-domain:testns:service-services:service-services:s3") assert.NoError(t, err) assert.NoError(t, client.Delete(vip)) assert.False(t, env.checker.Check()) }
func (m *NetworkManagerImpl) LocateFloatingIp(network *types.VirtualNetwork, resourceName, targetAddress string) (*types.FloatingIp, error) { obj, err := m.client.FindByName("floating-ip-pool", makePoolName(network)) if err != nil { glog.Errorf("Get floating-ip-pool %s: %v", network.GetName(), err) return nil, err } pool := obj.(*types.FloatingIpPool) fqn := AppendConst(pool.GetFQName(), resourceName) obj, err = m.client.FindByName("floating-ip", strings.Join(fqn, ":")) if err == nil { fip := obj.(*types.FloatingIp) if targetAddress != "" && fip.GetFloatingIpAddress() != targetAddress { fip.SetFloatingIpAddress(targetAddress) err = m.client.Update(fip) if err != nil { glog.Errorf("Update floating-ip %s: %v", resourceName, err) return nil, err } } return fip, nil } projectFQN := network.GetFQName()[0 : len(network.GetFQName())-1] obj, err = m.client.FindByName("project", strings.Join(projectFQN, ":")) if err != nil { glog.Errorf("Get project %s: %v", projectFQN[len(projectFQN)-1], err) return nil, err } project := obj.(*types.Project) fip := new(types.FloatingIp) fip.SetParent(pool) fip.SetName(resourceName) if targetAddress != "" { fip.SetFloatingIpAddress(targetAddress) } fip.AddProject(project) err = m.client.Create(fip) if err != nil { glog.Errorf("Create floating-ip %s: %v", resourceName, err) return nil, err } if targetAddress == "" { fip, err = types.FloatingIpByUuid(m.client, fip.GetUuid()) } return fip, err }
func (c *Controller) updateService(service *api.Service) { glog.Infof("Update Service %s", service.Name) serviceName := ServiceName(c.config, service.Labels) err := c.serviceMgr.Create(service.Namespace, serviceName) if err != nil { return } pods, err := c.kube.Pods(service.Namespace).List(makeListOptSelector(service.Spec.Selector)) if err != nil { glog.Errorf("List pods by service %s: %v", service.Name, err) return } var serviceIp *types.FloatingIp = nil if service.Spec.ClusterIP != "" { serviceNetwork, err := c.serviceMgr.LocateServiceNetwork(service.Namespace, serviceName) if err == nil { serviceIp, err = c.networkMgr.LocateFloatingIp( serviceNetwork, service.Name, service.Spec.ClusterIP) } } else { serviceNetwork, err := c.serviceMgr.LookupServiceNetwork(service.Namespace, serviceName) if err == nil { c.networkMgr.DeleteFloatingIp(serviceNetwork, service.Name) } } publicIp, err := c.updateServicePublicIP(service) if err == nil && publicIp == nil { resourceName := publicIpResourceName(service) c.networkMgr.DeleteFloatingIp(c.networkMgr.GetPublicNetwork(), resourceName) } podIdMap := make(map[string]*api.Pod) for _, pod := range pods.Items { podIdMap[string(pod.UID)] = &pod if serviceIp != nil { // Connect serviceIp to VMI. c.instanceMgr.AttachFloatingIp(pod.Name, pod.Namespace, serviceIp) } if publicIp != nil { c.instanceMgr.AttachFloatingIp(pod.Name, pod.Namespace, publicIp) } } // Detach the VIPs from pods which are no longer selected. if serviceIp != nil { refs, err := serviceIp.GetVirtualMachineInterfaceRefs() if err == nil { c.purgeStaleServiceRefs(serviceIp, refs, podIdMap) } } if publicIp != nil { refs, err := publicIp.GetVirtualMachineInterfaceRefs() if err == nil { c.purgeStaleServiceRefs(publicIp, refs, podIdMap) } } }
func TestConsistencyServiceIp(t *testing.T) { client := createTestClient() podStore := new(mocks.Store) serviceStore := new(mocks.Store) checker := NewConsistencyChecker(client, NewConfig(), podStore, serviceStore, nil) kube := mocks.NewKubeClient() controller := NewTestController(kube, client, nil, nil) config := controller.config netnsProject := new(types.Project) netnsProject.SetFQName("domain", []string{"default-domain", "testns"}) client.Create(netnsProject) installPods(controller, &kube.PodInterface.Mock, &podStore.Mock, "testns", 3) service1 := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "s1", Namespace: "testns", Labels: map[string]string{ config.NetworkTag: "services", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{ "app": "pod01", }, ClusterIP: "10.254.42.42", Type: api.ServiceTypeLoadBalancer, }, } service2 := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "s2", Namespace: "testns", Labels: map[string]string{ config.NetworkTag: "services", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{ "app": "pod02", }, ClusterIP: "10.254.42.43", ExternalIPs: []string{"10.1.4.89"}, }, } service3 := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "s3", Namespace: "testns", Labels: map[string]string{ config.NetworkTag: "services", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{ "app": "pod01", }, ClusterIP: "10.254.42.44", }, } kube.ServiceInterface.On("Update", service1).Return(service1, nil) shutdown := make(chan struct{}) go controller.Run(shutdown) controller.AddService(service1) controller.AddService(service2) controller.AddService(service3) serviceStore.On("List").Return([]interface{}{service1, service2, service3}) time.Sleep(100 * time.Millisecond) type shutdownMsg struct { } shutdown <- shutdownMsg{} time.Sleep(100 * time.Millisecond) assert.True(t, checker.Check()) pool, err := types.FloatingIpPoolByName(client, "default-domain:testns:service-services:service-services") assert.NoError(t, err) vmi, err := types.VirtualMachineInterfaceByName(client, "default-domain:testns:pod01") assert.NoError(t, err) vip := new(types.FloatingIp) fqn := make([]string, len(pool.GetFQName())+1) copy(fqn, pool.GetFQName()) fqn[len(pool.GetFQName())] = "s4" vip.SetFQName(vip.GetDefaultParentType(), fqn) vip.AddVirtualMachineInterface(vmi) assert.NoError(t, client.Create(vip)) assert.False(t, checker.Check()) assert.NoError(t, client.Delete(vip)) assert.True(t, checker.Check()) vip, err = types.FloatingIpByName(client, "default-domain:testns:service-services:service-services:s3") assert.NoError(t, err) assert.NoError(t, client.Delete(vip)) assert.False(t, checker.Check()) }