2016-05-17 18:05:03 -04:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2016 The Kubernetes Authors .
2016-05-17 18:05:03 -04:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package openstack
import (
2018-02-02 16:12:07 -05:00
"context"
2016-09-06 20:28:09 -04:00
"fmt"
"net"
2018-01-22 20:46:15 -05:00
"reflect"
2016-09-06 20:28:09 -04:00
"strings"
2016-05-17 18:05:03 -04:00
"time"
2016-11-07 03:35:42 -05:00
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions"
2017-11-21 20:14:50 -05:00
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external"
2016-11-07 03:35:42 -05:00
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
v2monitors "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
v2pools "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
2017-11-21 20:14:50 -05:00
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
2016-11-07 03:35:42 -05:00
neutronports "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"github.com/gophercloud/gophercloud/pagination"
2018-11-09 13:49:10 -05:00
"k8s.io/klog"
2016-05-17 18:05:03 -04:00
2017-06-22 13:25:57 -04:00
"k8s.io/api/core/v1"
2017-10-10 03:04:32 -04:00
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
2017-06-22 14:24:23 -04:00
"k8s.io/apimachinery/pkg/util/wait"
2018-09-05 18:58:22 -04:00
cloudprovider "k8s.io/cloud-provider"
2019-02-04 16:48:41 -05:00
servicehelpers "k8s.io/cloud-provider/service/helpers"
2016-05-17 18:05:03 -04:00
)
// Note: when creating a new Loadbalancer (VM), it can take some time before it is ready for use,
// this timeout is used for waiting until the Loadbalancer provisioning status goes to ACTIVE state.
2017-05-04 01:53:14 -04:00
const (
// loadbalancerActive* is configuration of exponential backoff for
// going into ACTIVE loadbalancer provisioning status. Starting with 1
// seconds, multiplying by 1.2 with each step and taking 19 steps at maximum
// it will time out after 128s, which roughly corresponds to 120s
2017-11-22 21:48:23 -05:00
loadbalancerActiveInitDelay = 1 * time . Second
2017-05-04 01:53:14 -04:00
loadbalancerActiveFactor = 1.2
loadbalancerActiveSteps = 19
// loadbalancerDelete* is configuration of exponential backoff for
// waiting for delete operation to complete. Starting with 1
// seconds, multiplying by 1.2 with each step and taking 13 steps at maximum
// it will time out after 32s, which roughly corresponds to 30s
2017-11-22 21:48:23 -05:00
loadbalancerDeleteInitDelay = 1 * time . Second
2017-05-04 01:53:14 -04:00
loadbalancerDeleteFactor = 1.2
loadbalancerDeleteSteps = 13
activeStatus = "ACTIVE"
errorStatus = "ERROR"
2017-07-27 05:36:25 -04:00
2018-02-03 18:44:57 -05:00
ServiceAnnotationLoadBalancerFloatingNetworkID = "loadbalancer.openstack.org/floating-network-id"
2018-04-04 07:13:48 -04:00
ServiceAnnotationLoadBalancerSubnetID = "loadbalancer.openstack.org/subnet-id"
2017-08-22 08:40:56 -04:00
// ServiceAnnotationLoadBalancerInternal is the annotation used on the service
// to indicate that we want an internal loadbalancer service.
2017-09-26 23:12:51 -04:00
// If the value of ServiceAnnotationLoadBalancerInternal is false, it indicates that we want an external loadbalancer service. Default to false.
2017-08-22 08:40:56 -04:00
ServiceAnnotationLoadBalancerInternal = "service.beta.kubernetes.io/openstack-internal-load-balancer"
2017-05-04 01:53:14 -04:00
)
2016-05-17 18:05:03 -04:00
2018-11-28 22:47:07 -05:00
var _ cloudprovider . LoadBalancer = ( * LbaasV2 ) ( nil )
2018-02-03 18:44:57 -05:00
// LbaasV2 is a LoadBalancer implementation for Neutron LBaaS v2 API
2016-05-17 18:05:03 -04:00
type LbaasV2 struct {
LoadBalancer
}
2016-08-11 19:51:13 -04:00
type empty struct { }
2016-07-28 03:07:10 -04:00
func networkExtensions ( client * gophercloud . ServiceClient ) ( map [ string ] bool , error ) {
seen := make ( map [ string ] bool )
pager := extensions . List ( client )
err := pager . EachPage ( func ( page pagination . Page ) ( bool , error ) {
exts , err := extensions . ExtractExtensions ( page )
if err != nil {
return false , err
}
for _ , ext := range exts {
seen [ ext . Alias ] = true
}
return true , nil
} )
return seen , err
}
2016-05-17 18:05:03 -04:00
func getFloatingIPByPortID ( client * gophercloud . ServiceClient , portID string ) ( * floatingips . FloatingIP , error ) {
opts := floatingips . ListOpts {
PortID : portID ,
}
pager := floatingips . List ( client , opts )
floatingIPList := make ( [ ] floatingips . FloatingIP , 0 , 1 )
err := pager . EachPage ( func ( page pagination . Page ) ( bool , error ) {
f , err := floatingips . ExtractFloatingIPs ( page )
if err != nil {
return false , err
}
floatingIPList = append ( floatingIPList , f ... )
if len ( floatingIPList ) > 1 {
return false , ErrMultipleResults
}
return true , nil
} )
if err != nil {
if isNotFound ( err ) {
return nil , ErrNotFound
}
return nil , err
}
if len ( floatingIPList ) == 0 {
return nil , ErrNotFound
} else if len ( floatingIPList ) > 1 {
return nil , ErrMultipleResults
}
return & floatingIPList [ 0 ] , nil
}
func getLoadbalancerByName ( client * gophercloud . ServiceClient , name string ) ( * loadbalancers . LoadBalancer , error ) {
opts := loadbalancers . ListOpts {
Name : name ,
}
pager := loadbalancers . List ( client , opts )
loadbalancerList := make ( [ ] loadbalancers . LoadBalancer , 0 , 1 )
err := pager . EachPage ( func ( page pagination . Page ) ( bool , error ) {
2016-11-07 03:35:42 -05:00
v , err := loadbalancers . ExtractLoadBalancers ( page )
2016-05-17 18:05:03 -04:00
if err != nil {
return false , err
}
loadbalancerList = append ( loadbalancerList , v ... )
if len ( loadbalancerList ) > 1 {
return false , ErrMultipleResults
}
return true , nil
} )
if err != nil {
if isNotFound ( err ) {
return nil , ErrNotFound
}
return nil , err
}
if len ( loadbalancerList ) == 0 {
return nil , ErrNotFound
} else if len ( loadbalancerList ) > 1 {
return nil , ErrMultipleResults
}
return & loadbalancerList [ 0 ] , nil
}
2016-09-28 02:20:59 -04:00
func getListenersByLoadBalancerID ( client * gophercloud . ServiceClient , id string ) ( [ ] listeners . Listener , error ) {
var existingListeners [ ] listeners . Listener
err := listeners . List ( client , listeners . ListOpts { LoadbalancerID : id } ) . EachPage ( func ( page pagination . Page ) ( bool , error ) {
listenerList , err := listeners . ExtractListeners ( page )
if err != nil {
return false , err
}
2016-09-29 13:50:35 -04:00
for _ , l := range listenerList {
for _ , lb := range l . Loadbalancers {
if lb . ID == id {
existingListeners = append ( existingListeners , l )
break
}
}
}
2016-09-28 02:20:59 -04:00
return true , nil
} )
if err != nil {
return nil , err
}
return existingListeners , nil
}
// get listener for a port or nil if does not exist
2016-11-18 15:58:42 -05:00
func getListenerForPort ( existingListeners [ ] listeners . Listener , port v1 . ServicePort ) * listeners . Listener {
2016-09-28 02:20:59 -04:00
for _ , l := range existingListeners {
2016-11-07 03:35:42 -05:00
if listeners . Protocol ( l . Protocol ) == toListenersProtocol ( port . Protocol ) && l . ProtocolPort == int ( port . Port ) {
2016-09-28 02:20:59 -04:00
return & l
}
}
return nil
}
// Get pool for a listener. A listener always has exactly one pool.
2016-11-30 02:27:27 -05:00
func getPoolByListenerID ( client * gophercloud . ServiceClient , loadbalancerID string , listenerID string ) ( * v2pools . Pool , error ) {
listenerPools := make ( [ ] v2pools . Pool , 0 , 1 )
err := v2pools . List ( client , v2pools . ListOpts { LoadbalancerID : loadbalancerID } ) . EachPage ( func ( page pagination . Page ) ( bool , error ) {
poolsList , err := v2pools . ExtractPools ( page )
2016-09-28 02:20:59 -04:00
if err != nil {
return false , err
}
for _ , p := range poolsList {
for _ , l := range p . Listeners {
if l . ID == listenerID {
listenerPools = append ( listenerPools , p )
}
}
}
if len ( listenerPools ) > 1 {
return false , ErrMultipleResults
}
return true , nil
} )
if err != nil {
if isNotFound ( err ) {
return nil , ErrNotFound
}
return nil , err
}
if len ( listenerPools ) == 0 {
return nil , ErrNotFound
} else if len ( listenerPools ) > 1 {
return nil , ErrMultipleResults
}
return & listenerPools [ 0 ] , nil
}
2016-11-30 02:27:27 -05:00
func getMembersByPoolID ( client * gophercloud . ServiceClient , id string ) ( [ ] v2pools . Member , error ) {
var members [ ] v2pools . Member
2016-11-07 03:35:42 -05:00
err := v2pools . ListMembers ( client , id , v2pools . ListMembersOpts { } ) . EachPage ( func ( page pagination . Page ) ( bool , error ) {
2016-11-30 02:27:27 -05:00
membersList , err := v2pools . ExtractMembers ( page )
2016-09-28 02:20:59 -04:00
if err != nil {
return false , err
}
members = append ( members , membersList ... )
return true , nil
} )
if err != nil {
return nil , err
}
return members , nil
}
// Check if a member exists for node
2016-11-30 02:27:27 -05:00
func memberExists ( members [ ] v2pools . Member , addr string , port int ) bool {
2016-09-28 02:20:59 -04:00
for _ , member := range members {
2016-10-09 23:22:00 -04:00
if member . Address == addr && member . ProtocolPort == port {
2016-09-28 02:20:59 -04:00
return true
}
}
return false
}
func popListener ( existingListeners [ ] listeners . Listener , id string ) [ ] listeners . Listener {
for i , existingListener := range existingListeners {
if existingListener . ID == id {
existingListeners [ i ] = existingListeners [ len ( existingListeners ) - 1 ]
existingListeners = existingListeners [ : len ( existingListeners ) - 1 ]
break
}
}
return existingListeners
}
2016-11-30 02:27:27 -05:00
func popMember ( members [ ] v2pools . Member , addr string , port int ) [ ] v2pools . Member {
2016-09-28 02:20:59 -04:00
for i , member := range members {
2016-10-09 23:22:00 -04:00
if member . Address == addr && member . ProtocolPort == port {
2016-09-28 02:20:59 -04:00
members [ i ] = members [ len ( members ) - 1 ]
members = members [ : len ( members ) - 1 ]
}
}
return members
}
2017-10-26 08:23:16 -04:00
func getSecurityGroupName ( service * v1 . Service ) string {
securityGroupName := fmt . Sprintf ( "lb-sg-%s-%s-%s" , service . UID , service . Namespace , service . Name )
//OpenStack requires that the name of a security group is shorter than 255 bytes.
if len ( securityGroupName ) > 255 {
securityGroupName = securityGroupName [ : 255 ]
}
return securityGroupName
2016-09-06 20:28:09 -04:00
}
func getSecurityGroupRules ( client * gophercloud . ServiceClient , opts rules . ListOpts ) ( [ ] rules . SecGroupRule , error ) {
pager := rules . List ( client , opts )
var securityRules [ ] rules . SecGroupRule
err := pager . EachPage ( func ( page pagination . Page ) ( bool , error ) {
ruleList , err := rules . ExtractRules ( page )
if err != nil {
return false , err
}
securityRules = append ( securityRules , ruleList ... )
return true , nil
} )
if err != nil {
return nil , err
}
return securityRules , nil
}
2016-08-11 19:51:13 -04:00
func waitLoadbalancerActiveProvisioningStatus ( client * gophercloud . ServiceClient , loadbalancerID string ) ( string , error ) {
2017-05-04 01:53:14 -04:00
backoff := wait . Backoff {
2017-11-22 21:48:23 -05:00
Duration : loadbalancerActiveInitDelay ,
2017-05-04 01:53:14 -04:00
Factor : loadbalancerActiveFactor ,
Steps : loadbalancerActiveSteps ,
}
var provisioningStatus string
err := wait . ExponentialBackoff ( backoff , func ( ) ( bool , error ) {
2016-05-17 18:05:03 -04:00
loadbalancer , err := loadbalancers . Get ( client , loadbalancerID ) . Extract ( )
if err != nil {
2017-05-04 01:53:14 -04:00
return false , err
2016-05-17 18:05:03 -04:00
}
2017-05-04 01:53:14 -04:00
provisioningStatus = loadbalancer . ProvisioningStatus
if loadbalancer . ProvisioningStatus == activeStatus {
return true , nil
} else if loadbalancer . ProvisioningStatus == errorStatus {
2017-11-13 23:40:16 -05:00
return true , fmt . Errorf ( "loadbalancer has gone into ERROR state" )
2017-05-04 01:53:14 -04:00
} else {
return false , nil
2016-05-17 18:05:03 -04:00
}
2017-05-04 01:53:14 -04:00
} )
2016-05-17 18:05:03 -04:00
2017-05-04 01:53:14 -04:00
if err == wait . ErrWaitTimeout {
2017-11-13 23:40:16 -05:00
err = fmt . Errorf ( "loadbalancer failed to go into ACTIVE provisioning status within alloted time" )
2016-05-17 18:05:03 -04:00
}
2017-05-04 01:53:14 -04:00
return provisioningStatus , err
2016-05-17 18:05:03 -04:00
}
func waitLoadbalancerDeleted ( client * gophercloud . ServiceClient , loadbalancerID string ) error {
2017-05-04 01:53:14 -04:00
backoff := wait . Backoff {
2017-11-22 21:48:23 -05:00
Duration : loadbalancerDeleteInitDelay ,
2017-05-04 01:53:14 -04:00
Factor : loadbalancerDeleteFactor ,
Steps : loadbalancerDeleteSteps ,
}
err := wait . ExponentialBackoff ( backoff , func ( ) ( bool , error ) {
2016-05-17 18:05:03 -04:00
_ , err := loadbalancers . Get ( client , loadbalancerID ) . Extract ( )
if err != nil {
2018-03-19 17:28:39 -04:00
if isNotFound ( err ) {
2017-05-04 01:53:14 -04:00
return true , nil
2016-05-17 18:05:03 -04:00
}
2018-02-03 18:44:57 -05:00
return false , err
2016-05-17 18:05:03 -04:00
}
2018-02-03 18:44:57 -05:00
return false , nil
2017-05-04 01:53:14 -04:00
} )
2016-05-17 18:05:03 -04:00
2017-05-04 01:53:14 -04:00
if err == wait . ErrWaitTimeout {
2017-11-13 23:40:16 -05:00
err = fmt . Errorf ( "loadbalancer failed to delete within the alloted time" )
2016-05-17 18:05:03 -04:00
}
2017-05-04 01:53:14 -04:00
return err
2016-05-17 18:05:03 -04:00
}
2016-11-07 03:35:42 -05:00
func toRuleProtocol ( protocol v1 . Protocol ) rules . RuleProtocol {
switch protocol {
case v1 . ProtocolTCP :
return rules . ProtocolTCP
case v1 . ProtocolUDP :
return rules . ProtocolUDP
default :
return rules . RuleProtocol ( strings . ToLower ( string ( protocol ) ) )
}
}
func toListenersProtocol ( protocol v1 . Protocol ) listeners . Protocol {
switch protocol {
case v1 . ProtocolTCP :
return listeners . ProtocolTCP
default :
return listeners . Protocol ( string ( protocol ) )
}
}
func createNodeSecurityGroup ( client * gophercloud . ServiceClient , nodeSecurityGroupID string , port int , protocol v1 . Protocol , lbSecGroup string ) error {
2016-09-06 20:28:09 -04:00
v4NodeSecGroupRuleCreateOpts := rules . CreateOpts {
2016-11-07 03:35:42 -05:00
Direction : rules . DirIngress ,
2016-09-06 20:28:09 -04:00
PortRangeMax : port ,
PortRangeMin : port ,
2016-11-07 03:35:42 -05:00
Protocol : toRuleProtocol ( protocol ) ,
2016-09-06 20:28:09 -04:00
RemoteGroupID : lbSecGroup ,
SecGroupID : nodeSecurityGroupID ,
2016-11-07 03:35:42 -05:00
EtherType : rules . EtherType4 ,
2016-09-06 20:28:09 -04:00
}
v6NodeSecGroupRuleCreateOpts := rules . CreateOpts {
2016-11-07 03:35:42 -05:00
Direction : rules . DirIngress ,
2016-09-06 20:28:09 -04:00
PortRangeMax : port ,
PortRangeMin : port ,
2016-11-07 03:35:42 -05:00
Protocol : toRuleProtocol ( protocol ) ,
2016-09-06 20:28:09 -04:00
RemoteGroupID : lbSecGroup ,
SecGroupID : nodeSecurityGroupID ,
2016-11-07 03:35:42 -05:00
EtherType : rules . EtherType6 ,
2016-09-06 20:28:09 -04:00
}
_ , err := rules . Create ( client , v4NodeSecGroupRuleCreateOpts ) . Extract ( )
if err != nil {
return err
}
_ , err = rules . Create ( client , v6NodeSecGroupRuleCreateOpts ) . Extract ( )
if err != nil {
return err
}
return nil
}
2017-08-22 08:40:56 -04:00
func ( lbaas * LbaasV2 ) createLoadBalancer ( service * v1 . Service , name string , internalAnnotation bool ) ( * loadbalancers . LoadBalancer , error ) {
2016-09-28 02:20:59 -04:00
createOpts := loadbalancers . CreateOpts {
Name : name ,
Description : fmt . Sprintf ( "Kubernetes external service %s" , name ) ,
2018-02-03 18:44:57 -05:00
VipSubnetID : lbaas . opts . SubnetID ,
2017-10-18 18:00:12 -04:00
Provider : lbaas . opts . LBProvider ,
2016-09-28 02:20:59 -04:00
}
loadBalancerIP := service . Spec . LoadBalancerIP
2017-08-22 08:40:56 -04:00
if loadBalancerIP != "" && internalAnnotation {
2016-09-28 02:20:59 -04:00
createOpts . VipAddress = loadBalancerIP
}
2017-11-16 04:52:46 -05:00
loadbalancer , err := loadbalancers . Create ( lbaas . lb , createOpts ) . Extract ( )
2016-09-28 02:20:59 -04:00
if err != nil {
2017-11-13 23:40:16 -05:00
return nil , fmt . Errorf ( "error creating loadbalancer %v: %v" , createOpts , err )
2016-09-28 02:20:59 -04:00
}
return loadbalancer , nil
}
2018-02-03 18:44:57 -05:00
// GetLoadBalancer returns whether the specified load balancer exists and its status
2018-02-02 16:12:07 -05:00
func ( lbaas * LbaasV2 ) GetLoadBalancer ( ctx context . Context , clusterName string , service * v1 . Service ) ( * v1 . LoadBalancerStatus , bool , error ) {
2018-08-04 00:36:48 -04:00
loadBalancerName := lbaas . GetLoadBalancerName ( ctx , clusterName , service )
2017-11-16 04:52:46 -05:00
loadbalancer , err := getLoadbalancerByName ( lbaas . lb , loadBalancerName )
2016-05-17 18:05:03 -04:00
if err == ErrNotFound {
return nil , false , nil
}
if loadbalancer == nil {
return nil , false , err
}
2016-11-18 15:58:42 -05:00
status := & v1 . LoadBalancerStatus { }
2017-09-18 23:09:13 -04:00
portID := loadbalancer . VipPortID
if portID != "" {
floatIP , err := getFloatingIPByPortID ( lbaas . network , portID )
2018-09-03 03:25:11 -04:00
if err != nil && err != ErrNotFound {
2017-11-13 23:40:16 -05:00
return nil , false , fmt . Errorf ( "error getting floating ip for port %s: %v" , portID , err )
2017-09-18 23:09:13 -04:00
}
2018-09-03 03:25:11 -04:00
if floatIP != nil {
status . Ingress = [ ] v1 . LoadBalancerIngress { { IP : floatIP . FloatingIP } }
}
2017-09-18 23:09:13 -04:00
} else {
status . Ingress = [ ] v1 . LoadBalancerIngress { { IP : loadbalancer . VipAddress } }
}
2016-05-17 18:05:03 -04:00
return status , true , err
}
2018-08-04 00:36:48 -04:00
// GetLoadBalancerName is an implementation of LoadBalancer.GetLoadBalancerName.
func ( lbaas * LbaasV2 ) GetLoadBalancerName ( ctx context . Context , clusterName string , service * v1 . Service ) string {
2018-08-15 21:03:44 -04:00
// TODO: replace DefaultLoadBalancerName to generate more meaningful loadbalancer names.
2018-08-04 00:36:48 -04:00
return cloudprovider . DefaultLoadBalancerName ( service )
}
2016-08-23 00:36:34 -04:00
// The LB needs to be configured with instance addresses on the same
2018-02-03 18:44:57 -05:00
// subnet as the LB (aka opts.SubnetID). Currently we're just
2018-05-24 08:19:20 -04:00
// guessing that the node's InternalIP is the right address.
// In case no InternalIP can be found, ExternalIP is tried.
// If neither InternalIP nor ExternalIP can be found an error is
// returned.
2016-08-23 00:36:34 -04:00
func nodeAddressForLB ( node * v1 . Node ) ( string , error ) {
addrs := node . Status . Addresses
if len ( addrs ) == 0 {
return "" , ErrNoAddressFound
}
2018-05-24 08:19:20 -04:00
allowedAddrTypes := [ ] v1 . NodeAddressType { v1 . NodeInternalIP , v1 . NodeExternalIP }
for _ , allowedAddrType := range allowedAddrTypes {
for _ , addr := range addrs {
if addr . Type == allowedAddrType {
return addr . Address , nil
}
2016-08-23 00:36:34 -04:00
}
}
2018-05-24 08:19:20 -04:00
return "" , ErrNoAddressFound
2016-08-23 00:36:34 -04:00
}
2017-07-27 05:36:25 -04:00
//getStringFromServiceAnnotation searches a given v1.Service for a specific annotationKey and either returns the annotation's value or a specified defaultSetting
func getStringFromServiceAnnotation ( service * v1 . Service , annotationKey string , defaultSetting string ) string {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "getStringFromServiceAnnotation(%v, %v, %v)" , service , annotationKey , defaultSetting )
2017-07-27 05:36:25 -04:00
if annotationValue , ok := service . Annotations [ annotationKey ] ; ok {
//if there is an annotation for this setting, set the "setting" var to it
// annotationValue can be empty, it is working as designed
// it makes possible for instance provisioning loadbalancer without floatingip
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Found a Service Annotation: %v = %v" , annotationKey , annotationValue )
2017-07-27 05:36:25 -04:00
return annotationValue
}
//if there is no annotation, set "settings" var to the value from cloud config
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Could not find a Service Annotation; falling back on cloud-config setting: %v = %v" , annotationKey , defaultSetting )
2017-07-27 05:36:25 -04:00
return defaultSetting
}
2017-08-16 02:20:43 -04:00
// getSubnetIDForLB returns subnet-id for a specific node
func getSubnetIDForLB ( compute * gophercloud . ServiceClient , node v1 . Node ) ( string , error ) {
ipAddress , err := nodeAddressForLB ( & node )
if err != nil {
return "" , err
}
instanceID := node . Spec . ProviderID
if ind := strings . LastIndex ( instanceID , "/" ) ; ind >= 0 {
instanceID = instanceID [ ( ind + 1 ) : ]
}
interfaces , err := getAttachedInterfacesByID ( compute , instanceID )
if err != nil {
return "" , err
}
for _ , intf := range interfaces {
for _ , fixedIP := range intf . FixedIPs {
if fixedIP . IPAddress == ipAddress {
2018-01-12 05:57:46 -05:00
return fixedIP . SubnetID , nil
2017-08-16 02:20:43 -04:00
}
}
}
return "" , ErrNotFound
}
2017-10-10 03:04:32 -04:00
// getNodeSecurityGroupIDForLB lists node-security-groups for specific nodes
2018-06-21 18:10:37 -04:00
func getNodeSecurityGroupIDForLB ( compute * gophercloud . ServiceClient , network * gophercloud . ServiceClient , nodes [ ] * v1 . Node ) ( [ ] string , error ) {
secGroupNames := sets . NewString ( )
2017-10-10 03:04:32 -04:00
for _ , node := range nodes {
nodeName := types . NodeName ( node . Name )
2018-02-28 17:16:47 -05:00
srv , err := getServerByName ( compute , nodeName )
2017-10-10 03:04:32 -04:00
if err != nil {
2018-06-21 18:10:37 -04:00
return [ ] string { } , err
2017-10-10 03:04:32 -04:00
}
// use the first node-security-groups
// case 0: node1:SG1 node2:SG1 return SG1
// case 1: node1:SG1 node2:SG2 return SG1,SG2
// case 2: node1:SG1,SG2 node2:SG3,SG4 return SG1,SG3
// case 3: node1:SG1,SG2 node2:SG2,SG3 return SG1,SG2
2018-06-21 18:10:37 -04:00
secGroupNames . Insert ( srv . SecurityGroups [ 0 ] [ "name" ] . ( string ) )
2017-10-10 03:04:32 -04:00
}
2018-06-21 18:10:37 -04:00
secGroupIDs := make ( [ ] string , secGroupNames . Len ( ) )
for i , name := range secGroupNames . List ( ) {
secGroupID , err := groups . IDFromName ( network , name )
if err != nil {
return [ ] string { } , err
}
secGroupIDs [ i ] = secGroupID
}
return secGroupIDs , nil
2017-10-10 03:04:32 -04:00
}
2018-01-22 20:46:15 -05:00
// isSecurityGroupNotFound return true while 'err' is object of gophercloud.ErrResourceNotFound
func isSecurityGroupNotFound ( err error ) bool {
errType := reflect . TypeOf ( err ) . String ( )
errTypeSlice := strings . Split ( errType , "." )
errTypeValue := ""
if len ( errTypeSlice ) != 0 {
errTypeValue = errTypeSlice [ len ( errTypeSlice ) - 1 ]
}
if errTypeValue == "ErrResourceNotFound" {
return true
}
return false
}
2018-02-03 18:44:57 -05:00
// getFloatingNetworkIDForLB returns a floating-network-id for cluster.
func getFloatingNetworkIDForLB ( client * gophercloud . ServiceClient ) ( string , error ) {
2017-11-21 20:14:50 -05:00
var floatingNetworkIds [ ] string
type NetworkWithExternalExt struct {
networks . Network
external . NetworkExternalExt
}
err := networks . List ( client , networks . ListOpts { } ) . EachPage ( func ( page pagination . Page ) ( bool , error ) {
var externalNetwork [ ] NetworkWithExternalExt
err := networks . ExtractNetworksInto ( page , & externalNetwork )
if err != nil {
return false , err
}
for _ , externalNet := range externalNetwork {
if externalNet . External {
floatingNetworkIds = append ( floatingNetworkIds , externalNet . ID )
}
}
if len ( floatingNetworkIds ) > 1 {
return false , ErrMultipleResults
}
return true , nil
} )
if err != nil {
if isNotFound ( err ) {
return "" , ErrNotFound
}
if err == ErrMultipleResults {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "find multiple external networks, pick the first one when there are no explicit configuration." )
2017-11-21 20:14:50 -05:00
return floatingNetworkIds [ 0 ] , nil
}
return "" , err
}
if len ( floatingNetworkIds ) == 0 {
return "" , ErrNotFound
}
return floatingNetworkIds [ 0 ] , nil
}
2016-05-17 18:05:03 -04:00
// TODO: This code currently ignores 'region' and always creates a
// loadbalancer in only the current OpenStack region. We should take
// a list of regions (from config) and query/create loadbalancers in
// each region.
2018-02-03 18:44:57 -05:00
// EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one.
2018-02-02 16:12:07 -05:00
func ( lbaas * LbaasV2 ) EnsureLoadBalancer ( ctx context . Context , clusterName string , apiService * v1 . Service , nodes [ ] * v1 . Node ) ( * v1 . LoadBalancerStatus , error ) {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "EnsureLoadBalancer(%v, %v, %v, %v, %v, %v, %v)" , clusterName , apiService . Namespace , apiService . Name , apiService . Spec . LoadBalancerIP , apiService . Spec . Ports , nodes , apiService . Annotations )
2016-05-17 18:05:03 -04:00
2017-10-08 22:01:49 -04:00
if len ( nodes ) == 0 {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "there are no available nodes for LoadBalancer service %s/%s" , apiService . Namespace , apiService . Name )
2017-10-08 22:01:49 -04:00
}
2018-04-04 07:13:48 -04:00
lbaas . opts . SubnetID = getStringFromServiceAnnotation ( apiService , ServiceAnnotationLoadBalancerSubnetID , lbaas . opts . SubnetID )
2018-02-03 18:44:57 -05:00
if len ( lbaas . opts . SubnetID ) == 0 {
// Get SubnetID automatically.
// The LB needs to be configured with instance addresses on the same subnet, so get SubnetID by one node.
2017-08-16 02:20:43 -04:00
subnetID , err := getSubnetIDForLB ( lbaas . compute , * nodes [ 0 ] )
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Warningf ( "Failed to find subnet-id for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "no subnet-id for service %s/%s : subnet-id not set in cloud provider config, " +
2017-08-16 02:20:43 -04:00
"and failed to find subnet-id from OpenStack: %v" , apiService . Namespace , apiService . Name , err )
}
2018-02-03 18:44:57 -05:00
lbaas . opts . SubnetID = subnetID
2017-07-19 16:25:33 -04:00
}
2016-05-17 18:05:03 -04:00
ports := apiService . Spec . Ports
2016-08-11 19:51:13 -04:00
if len ( ports ) == 0 {
2016-05-17 18:05:03 -04:00
return nil , fmt . Errorf ( "no ports provided to openstack load balancer" )
}
2018-02-03 18:44:57 -05:00
floatingPool := getStringFromServiceAnnotation ( apiService , ServiceAnnotationLoadBalancerFloatingNetworkID , lbaas . opts . FloatingNetworkID )
2017-11-21 20:14:50 -05:00
if len ( floatingPool ) == 0 {
var err error
2018-02-03 18:44:57 -05:00
floatingPool , err = getFloatingNetworkIDForLB ( lbaas . network )
2017-11-21 20:14:50 -05:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Warningf ( "Failed to find floating-network-id for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2017-11-21 20:14:50 -05:00
}
}
2017-07-27 05:36:25 -04:00
2017-08-22 08:40:56 -04:00
var internalAnnotation bool
2017-09-26 23:12:51 -04:00
internal := getStringFromServiceAnnotation ( apiService , ServiceAnnotationLoadBalancerInternal , "false" )
2017-08-22 08:40:56 -04:00
switch internal {
case "true" :
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Ensure an internal loadbalancer service." )
2017-08-22 08:40:56 -04:00
internalAnnotation = true
case "false" :
if len ( floatingPool ) != 0 {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Ensure an external loadbalancer service, using floatingPool: %v" , floatingPool )
2017-08-22 08:40:56 -04:00
internalAnnotation = false
} else {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "floating-network-id or loadbalancer.openstack.org/floating-network-id should be specified when ensuring an external loadbalancer service" )
2017-08-22 08:40:56 -04:00
}
default :
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "unknown service.beta.kubernetes.io/openstack-internal-load-balancer annotation: %v, specify \"true\" or \"false\" " ,
2017-08-22 08:40:56 -04:00
internal )
}
2016-08-11 19:51:13 -04:00
// Check for TCP protocol on each port
2016-05-17 18:05:03 -04:00
// TODO: Convert all error messages to use an event recorder
2016-08-11 19:51:13 -04:00
for _ , port := range ports {
2016-11-18 15:58:42 -05:00
if port . Protocol != v1 . ProtocolTCP {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "only TCP LoadBalancer is supported for openstack load balancers" )
2016-08-11 19:51:13 -04:00
}
2016-05-17 18:05:03 -04:00
}
2019-02-04 16:48:41 -05:00
sourceRanges , err := servicehelpers . GetLoadBalancerSourceRanges ( apiService )
2016-09-06 20:28:09 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "failed to get source ranges for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2016-09-06 20:28:09 -04:00
}
2019-02-04 16:48:41 -05:00
if ! servicehelpers . IsAllowAll ( sourceRanges ) && ! lbaas . opts . ManageSecurityGroups {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "source range restrictions are not supported for openstack load balancers without managing security groups" )
2016-09-06 20:28:09 -04:00
}
2017-07-05 21:25:31 -04:00
affinity := apiService . Spec . SessionAffinity
2016-11-30 02:27:27 -05:00
var persistence * v2pools . SessionPersistence
2016-05-17 18:05:03 -04:00
switch affinity {
2016-11-18 15:58:42 -05:00
case v1 . ServiceAffinityNone :
2016-05-17 18:05:03 -04:00
persistence = nil
2016-11-18 15:58:42 -05:00
case v1 . ServiceAffinityClientIP :
2016-11-30 02:27:27 -05:00
persistence = & v2pools . SessionPersistence { Type : "SOURCE_IP" }
2016-05-17 18:05:03 -04:00
default :
return nil , fmt . Errorf ( "unsupported load balancer affinity: %v" , affinity )
}
2018-08-04 00:36:48 -04:00
name := lbaas . GetLoadBalancerName ( ctx , clusterName , apiService )
2017-11-16 04:52:46 -05:00
loadbalancer , err := getLoadbalancerByName ( lbaas . lb , name )
2016-05-17 18:05:03 -04:00
if err != nil {
2016-09-28 02:20:59 -04:00
if err != ErrNotFound {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error getting loadbalancer %s: %v" , name , err )
2016-09-28 02:20:59 -04:00
}
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "Creating loadbalancer %s" , name )
2017-08-22 08:40:56 -04:00
loadbalancer , err = lbaas . createLoadBalancer ( apiService , name , internalAnnotation )
2016-05-17 18:05:03 -04:00
if err != nil {
2016-09-28 02:20:59 -04:00
// Unknown error, retry later
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error creating loadbalancer %s: %v" , name , err )
2016-05-17 18:05:03 -04:00
}
2016-09-28 02:20:59 -04:00
} else {
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "LoadBalancer %s already exists" , name )
2016-05-17 18:05:03 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-09-28 02:20:59 -04:00
2016-11-30 02:27:27 -05:00
lbmethod := v2pools . LBMethod ( lbaas . opts . LBMethod )
2016-05-17 18:05:03 -04:00
if lbmethod == "" {
2016-11-30 02:27:27 -05:00
lbmethod = v2pools . LBMethodRoundRobin
2016-05-17 18:05:03 -04:00
}
2017-11-16 04:52:46 -05:00
oldListeners , err := getListenersByLoadBalancerID ( lbaas . lb , loadbalancer . ID )
2016-05-17 18:05:03 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error getting LB %s listeners: %v" , name , err )
2016-05-17 18:05:03 -04:00
}
2016-08-11 19:51:13 -04:00
for portIndex , port := range ports {
2016-09-28 02:20:59 -04:00
listener := getListenerForPort ( oldListeners , port )
if listener == nil {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Creating listener for port %d" , int ( port . Port ) )
2017-11-16 04:52:46 -05:00
listener , err = listeners . Create ( lbaas . lb , listeners . CreateOpts {
2016-09-28 02:20:59 -04:00
Name : fmt . Sprintf ( "listener_%s_%d" , name , portIndex ) ,
Protocol : listeners . Protocol ( port . Protocol ) ,
ProtocolPort : int ( port . Port ) ,
LoadbalancerID : loadbalancer . ID ,
} ) . Extract ( )
if err != nil {
// Unknown error, retry later
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error creating LB listener: %v" , err )
2016-09-28 02:20:59 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-05-17 18:05:03 -04:00
}
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Listener for %s port %d: %s" , string ( port . Protocol ) , int ( port . Port ) , listener . ID )
2016-05-17 18:05:03 -04:00
2016-09-28 02:20:59 -04:00
// After all ports have been processed, remaining listeners are removed as obsolete.
// Pop valid listeners.
oldListeners = popListener ( oldListeners , listener . ID )
2017-11-16 04:52:46 -05:00
pool , err := getPoolByListenerID ( lbaas . lb , loadbalancer . ID , listener . ID )
2016-09-28 02:20:59 -04:00
if err != nil && err != ErrNotFound {
// Unknown error, retry later
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error getting pool for listener %s: %v" , listener . ID , err )
2016-09-28 02:20:59 -04:00
}
if pool == nil {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Creating pool for listener %s" , listener . ID )
2017-11-16 04:52:46 -05:00
pool , err = v2pools . Create ( lbaas . lb , v2pools . CreateOpts {
2016-09-28 02:20:59 -04:00
Name : fmt . Sprintf ( "pool_%s_%d" , name , portIndex ) ,
2016-11-30 02:27:27 -05:00
Protocol : v2pools . Protocol ( port . Protocol ) ,
2016-09-28 02:20:59 -04:00
LBMethod : lbmethod ,
ListenerID : listener . ID ,
Persistence : persistence ,
} ) . Extract ( )
if err != nil {
// Unknown error, retry later
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error creating pool for listener %s: %v" , listener . ID , err )
2016-09-28 02:20:59 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-05-17 18:05:03 -04:00
}
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Pool for listener %s: %s" , listener . ID , pool . ID )
2017-11-16 04:52:46 -05:00
members , err := getMembersByPoolID ( lbaas . lb , pool . ID )
2016-09-28 02:20:59 -04:00
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error getting pool members %s: %v" , pool . ID , err )
2016-09-28 02:20:59 -04:00
}
2016-08-23 00:36:34 -04:00
for _ , node := range nodes {
addr , err := nodeAddressForLB ( node )
2016-08-11 19:51:13 -04:00
if err != nil {
2016-09-28 02:20:59 -04:00
if err == ErrNotFound {
// Node failure, do not create member
2018-11-09 13:49:10 -05:00
klog . Warningf ( "Failed to create LB pool member for node %s: %v" , node . Name , err )
2016-09-28 02:20:59 -04:00
continue
} else {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error getting address for node %s: %v" , node . Name , err )
2016-09-28 02:20:59 -04:00
}
2016-08-11 19:51:13 -04:00
}
2016-10-09 23:22:00 -04:00
if ! memberExists ( members , addr , int ( port . NodePort ) ) {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Creating member for pool %s" , pool . ID )
2017-11-16 04:52:46 -05:00
_ , err := v2pools . CreateMember ( lbaas . lb , pool . ID , v2pools . CreateMemberOpts {
2018-04-24 12:22:16 -04:00
Name : fmt . Sprintf ( "member_%s_%d_%s" , name , portIndex , node . Name ) ,
2016-09-28 02:20:59 -04:00
ProtocolPort : int ( port . NodePort ) ,
Address : addr ,
2018-02-03 18:44:57 -05:00
SubnetID : lbaas . opts . SubnetID ,
2016-09-28 02:20:59 -04:00
} ) . Extract ( )
if err != nil {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error creating LB pool member for node: %s, %v" , node . Name , err )
2016-09-28 02:20:59 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-10-09 23:22:00 -04:00
} else {
// After all members have been processed, remaining members are deleted as obsolete.
members = popMember ( members , addr , int ( port . NodePort ) )
2016-08-11 19:51:13 -04:00
}
2016-09-28 02:20:59 -04:00
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Ensured pool %s has member for %s at %s" , pool . ID , node . Name , addr )
2016-09-28 02:20:59 -04:00
}
2016-08-11 19:51:13 -04:00
2016-09-28 02:20:59 -04:00
// Delete obsolete members for this pool
for _ , member := range members {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Deleting obsolete member %s for pool %s address %s" , member . ID , pool . ID , member . Address )
2017-11-16 04:52:46 -05:00
err := v2pools . DeleteMember ( lbaas . lb , pool . ID , member . ID ) . ExtractErr ( )
2016-09-28 02:20:59 -04:00
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error deleting obsolete member %s for pool %s address %s: %v" , member . ID , pool . ID , member . Address , err )
2016-09-28 02:20:59 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-08-11 19:51:13 -04:00
}
2016-09-28 02:20:59 -04:00
monitorID := pool . MonitorID
if monitorID == "" && lbaas . opts . CreateMonitor {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Creating monitor for pool %s" , pool . ID )
2017-11-16 04:52:46 -05:00
monitor , err := v2monitors . Create ( lbaas . lb , v2monitors . CreateOpts {
2018-04-24 12:22:16 -04:00
Name : fmt . Sprintf ( "monitor_%s_%d" , name , portIndex ) ,
2016-08-11 19:51:13 -04:00
PoolID : pool . ID ,
Type : string ( port . Protocol ) ,
Delay : int ( lbaas . opts . MonitorDelay . Duration . Seconds ( ) ) ,
Timeout : int ( lbaas . opts . MonitorTimeout . Duration . Seconds ( ) ) ,
MaxRetries : int ( lbaas . opts . MonitorMaxRetries ) ,
} ) . Extract ( )
if err != nil {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error creating LB pool healthmonitor: %v" , err )
2016-08-11 19:51:13 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-09-28 02:20:59 -04:00
monitorID = monitor . ID
2017-06-30 06:55:53 -04:00
} else if lbaas . opts . CreateMonitor == false {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Do not create monitor for pool %s when create-monitor is false" , pool . ID )
2016-05-17 18:05:03 -04:00
}
2016-09-28 02:20:59 -04:00
2017-06-30 06:55:53 -04:00
if monitorID != "" {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Monitor for pool %s: %s" , pool . ID , monitorID )
2017-06-30 06:55:53 -04:00
}
2016-09-28 02:20:59 -04:00
}
// All remaining listeners are obsolete, delete
for _ , listener := range oldListeners {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Deleting obsolete listener %s:" , listener . ID )
2016-09-28 02:20:59 -04:00
// get pool for listener
2017-11-16 04:52:46 -05:00
pool , err := getPoolByListenerID ( lbaas . lb , loadbalancer . ID , listener . ID )
2016-09-28 02:20:59 -04:00
if err != nil && err != ErrNotFound {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error getting pool for obsolete listener %s: %v" , listener . ID , err )
2016-09-28 02:20:59 -04:00
}
if pool != nil {
// get and delete monitor
monitorID := pool . MonitorID
if monitorID != "" {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Deleting obsolete monitor %s for pool %s" , monitorID , pool . ID )
2017-11-16 04:52:46 -05:00
err = v2monitors . Delete ( lbaas . lb , monitorID ) . ExtractErr ( )
2016-09-28 02:20:59 -04:00
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error deleting obsolete monitor %s for pool %s: %v" , monitorID , pool . ID , err )
2016-09-28 02:20:59 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-09-28 02:20:59 -04:00
}
// get and delete pool members
2017-11-16 04:52:46 -05:00
members , err := getMembersByPoolID ( lbaas . lb , pool . ID )
2016-09-28 02:20:59 -04:00
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error getting members for pool %s: %v" , pool . ID , err )
2016-09-28 02:20:59 -04:00
}
if members != nil {
for _ , member := range members {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Deleting obsolete member %s for pool %s address %s" , member . ID , pool . ID , member . Address )
2017-11-16 04:52:46 -05:00
err := v2pools . DeleteMember ( lbaas . lb , pool . ID , member . ID ) . ExtractErr ( )
2016-09-28 02:20:59 -04:00
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error deleting obsolete member %s for pool %s address %s: %v" , member . ID , pool . ID , member . Address , err )
2016-09-28 02:20:59 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-09-28 02:20:59 -04:00
}
}
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Deleting obsolete pool %s for listener %s" , pool . ID , listener . ID )
2016-09-28 02:20:59 -04:00
// delete pool
2017-11-16 04:52:46 -05:00
err = v2pools . Delete ( lbaas . lb , pool . ID ) . ExtractErr ( )
2016-09-28 02:20:59 -04:00
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error deleting obsolete pool %s for listener %s: %v" , pool . ID , listener . ID , err )
2016-09-28 02:20:59 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-09-28 02:20:59 -04:00
}
// delete listener
2017-11-16 04:52:46 -05:00
err = listeners . Delete ( lbaas . lb , listener . ID ) . ExtractErr ( )
2016-09-28 02:20:59 -04:00
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error deleteting obsolete listener: %v" , err )
2016-09-28 02:20:59 -04:00
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return nil , fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "Deleted obsolete listener: %s" , listener . ID )
2016-05-17 18:05:03 -04:00
}
2017-04-12 06:50:37 -04:00
portID := loadbalancer . VipPortID
2018-01-30 19:05:04 -05:00
floatIP , err := getFloatingIPByPortID ( lbaas . network , portID )
2016-09-28 02:20:59 -04:00
if err != nil && err != ErrNotFound {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error getting floating ip for port %s: %v" , portID , err )
2016-09-28 02:20:59 -04:00
}
2017-08-22 08:40:56 -04:00
if floatIP == nil && floatingPool != "" && ! internalAnnotation {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "Creating floating ip for loadbalancer %s port %s" , loadbalancer . ID , portID )
2016-05-17 18:05:03 -04:00
floatIPOpts := floatingips . CreateOpts {
2017-07-27 05:36:25 -04:00
FloatingNetworkID : floatingPool ,
2017-04-12 06:50:37 -04:00
PortID : portID ,
2016-05-17 18:05:03 -04:00
}
2017-08-22 08:40:56 -04:00
loadBalancerIP := apiService . Spec . LoadBalancerIP
if loadBalancerIP != "" {
floatIPOpts . FloatingIP = loadBalancerIP
}
2016-09-28 02:20:59 -04:00
floatIP , err = floatingips . Create ( lbaas . network , floatIPOpts ) . Extract ( )
2016-05-17 18:05:03 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return nil , fmt . Errorf ( "error creating LB floatingip %+v: %v" , floatIPOpts , err )
2016-05-17 18:05:03 -04:00
}
}
2017-09-18 21:43:24 -04:00
status := & v1 . LoadBalancerStatus { }
2016-09-06 20:28:09 -04:00
if floatIP != nil {
2017-09-18 21:43:24 -04:00
status . Ingress = [ ] v1 . LoadBalancerIngress { { IP : floatIP . FloatingIP } }
} else {
status . Ingress = [ ] v1 . LoadBalancerIngress { { IP : loadbalancer . VipAddress } }
2016-09-06 20:28:09 -04:00
}
2016-05-17 18:05:03 -04:00
2016-09-06 20:28:09 -04:00
if lbaas . opts . ManageSecurityGroups {
2017-10-10 03:04:32 -04:00
err := lbaas . ensureSecurityGroup ( clusterName , apiService , nodes , loadbalancer )
if err != nil {
// cleanup what was created so far
2018-02-02 16:12:07 -05:00
_ = lbaas . EnsureLoadBalancerDeleted ( ctx , clusterName , apiService )
2017-10-10 03:04:32 -04:00
return status , err
}
}
return status , nil
}
// ensureSecurityGroup ensures security group exist for specific loadbalancer service.
// Creating security group for specific loadbalancer service when it does not exist.
func ( lbaas * LbaasV2 ) ensureSecurityGroup ( clusterName string , apiService * v1 . Service , nodes [ ] * v1 . Node , loadbalancer * loadbalancers . LoadBalancer ) error {
// find node-security-group for service
var err error
if len ( lbaas . opts . NodeSecurityGroupIDs ) == 0 {
2018-06-21 18:10:37 -04:00
lbaas . opts . NodeSecurityGroupIDs , err = getNodeSecurityGroupIDForLB ( lbaas . compute , lbaas . network , nodes )
2017-10-10 03:04:32 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "failed to find node-security-group for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2017-10-10 03:04:32 -04:00
}
}
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "find node-security-group %v for loadbalancer service %s/%s" , lbaas . opts . NodeSecurityGroupIDs , apiService . Namespace , apiService . Name )
2017-10-10 03:04:32 -04:00
// get service ports
ports := apiService . Spec . Ports
if len ( ports ) == 0 {
return fmt . Errorf ( "no ports provided to openstack load balancer" )
}
// get service source ranges
2019-02-04 16:48:41 -05:00
sourceRanges , err := servicehelpers . GetLoadBalancerSourceRanges ( apiService )
2017-10-10 03:04:32 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "failed to get source ranges for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2017-10-10 03:04:32 -04:00
}
// ensure security group for LB
2017-10-26 08:23:16 -04:00
lbSecGroupName := getSecurityGroupName ( apiService )
2017-10-10 03:04:32 -04:00
lbSecGroupID , err := groups . IDFromName ( lbaas . network , lbSecGroupName )
if err != nil {
2018-01-22 20:46:15 -05:00
// If the security group of LB not exist, create it later
if isSecurityGroupNotFound ( err ) {
2017-10-10 03:04:32 -04:00
lbSecGroupID = ""
} else {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error occurred finding security group: %s: %v" , lbSecGroupName , err )
2017-10-10 03:04:32 -04:00
}
}
if len ( lbSecGroupID ) == 0 {
// create security group
2016-09-06 20:28:09 -04:00
lbSecGroupCreateOpts := groups . CreateOpts {
2017-10-26 08:23:16 -04:00
Name : getSecurityGroupName ( apiService ) ,
2017-11-20 20:38:43 -05:00
Description : fmt . Sprintf ( "Security Group for %s/%s Service LoadBalancer in cluster %s" , apiService . Namespace , apiService . Name , clusterName ) ,
2016-09-06 20:28:09 -04:00
}
lbSecGroup , err := groups . Create ( lbaas . network , lbSecGroupCreateOpts ) . Extract ( )
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "failed to create Security Group for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2016-09-06 20:28:09 -04:00
}
2017-10-10 03:04:32 -04:00
lbSecGroupID = lbSecGroup . ID
2016-09-06 20:28:09 -04:00
2017-10-10 03:04:32 -04:00
//add rule in security group
2016-09-06 20:28:09 -04:00
for _ , port := range ports {
for _ , sourceRange := range sourceRanges . StringSlice ( ) {
2016-11-07 03:35:42 -05:00
ethertype := rules . EtherType4
2016-09-06 20:28:09 -04:00
network , _ , err := net . ParseCIDR ( sourceRange )
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error parsing source range %s as a CIDR: %v" , sourceRange , err )
2016-09-06 20:28:09 -04:00
}
if network . To4 ( ) == nil {
2016-11-07 03:35:42 -05:00
ethertype = rules . EtherType6
2016-09-06 20:28:09 -04:00
}
lbSecGroupRuleCreateOpts := rules . CreateOpts {
2016-11-07 03:35:42 -05:00
Direction : rules . DirIngress ,
2016-09-06 20:28:09 -04:00
PortRangeMax : int ( port . Port ) ,
PortRangeMin : int ( port . Port ) ,
2016-11-07 03:35:42 -05:00
Protocol : toRuleProtocol ( port . Protocol ) ,
2016-09-06 20:28:09 -04:00
RemoteIPPrefix : sourceRange ,
SecGroupID : lbSecGroup . ID ,
EtherType : ethertype ,
}
_ , err = rules . Create ( lbaas . network , lbSecGroupRuleCreateOpts ) . Extract ( )
if err != nil {
2018-02-09 01:53:53 -05:00
return fmt . Errorf ( "error occurred creating rule for SecGroup %s: %v" , lbSecGroup . ID , err )
2016-09-06 20:28:09 -04:00
}
}
}
lbSecGroupRuleCreateOpts := rules . CreateOpts {
2016-11-07 03:35:42 -05:00
Direction : rules . DirIngress ,
2016-09-06 20:28:09 -04:00
PortRangeMax : 4 , // ICMP: Code - Values for ICMP "Destination Unreachable: Fragmentation Needed and Don't Fragment was Set"
PortRangeMin : 3 , // ICMP: Type
2016-11-07 03:35:42 -05:00
Protocol : rules . ProtocolICMP ,
2016-09-06 20:28:09 -04:00
RemoteIPPrefix : "0.0.0.0/0" , // The Fragmentation packet can come from anywhere along the path back to the sourceRange - we need to all this from all
SecGroupID : lbSecGroup . ID ,
2016-11-07 03:35:42 -05:00
EtherType : rules . EtherType4 ,
2016-09-06 20:28:09 -04:00
}
_ , err = rules . Create ( lbaas . network , lbSecGroupRuleCreateOpts ) . Extract ( )
if err != nil {
2018-02-09 01:53:53 -05:00
return fmt . Errorf ( "error occurred creating rule for SecGroup %s: %v" , lbSecGroup . ID , err )
2016-09-06 20:28:09 -04:00
}
lbSecGroupRuleCreateOpts = rules . CreateOpts {
2016-11-07 03:35:42 -05:00
Direction : rules . DirIngress ,
2016-09-06 20:28:09 -04:00
PortRangeMax : 0 , // ICMP: Code - Values for ICMP "Packet Too Big"
PortRangeMin : 2 , // ICMP: Type
2016-11-07 03:35:42 -05:00
Protocol : rules . ProtocolICMP ,
2016-09-06 20:28:09 -04:00
RemoteIPPrefix : "::/0" , // The Fragmentation packet can come from anywhere along the path back to the sourceRange - we need to all this from all
SecGroupID : lbSecGroup . ID ,
2016-11-07 03:35:42 -05:00
EtherType : rules . EtherType6 ,
2016-09-06 20:28:09 -04:00
}
_ , err = rules . Create ( lbaas . network , lbSecGroupRuleCreateOpts ) . Extract ( )
if err != nil {
2018-02-09 01:53:53 -05:00
return fmt . Errorf ( "error occurred creating rule for SecGroup %s: %v" , lbSecGroup . ID , err )
2016-09-06 20:28:09 -04:00
}
2017-10-10 03:04:32 -04:00
// get security groups of port
2017-04-12 06:50:37 -04:00
portID := loadbalancer . VipPortID
2017-10-10 03:04:32 -04:00
port , err := getPortByID ( lbaas . network , portID )
if err != nil {
return err
}
// ensure the vip port has the security groups
found := false
for _ , portSecurityGroups := range port . SecurityGroups {
if portSecurityGroups == lbSecGroup . ID {
found = true
break
}
}
// update loadbalancer vip port
if ! found {
port . SecurityGroups = append ( port . SecurityGroups , lbSecGroup . ID )
2018-02-03 18:44:57 -05:00
updateOpts := neutronports . UpdateOpts { SecurityGroups : & port . SecurityGroups }
res := neutronports . Update ( lbaas . network , portID , updateOpts )
2017-10-10 03:04:32 -04:00
if res . Err != nil {
2018-02-09 01:53:53 -05:00
msg := fmt . Sprintf ( "Error occurred updating port %s for loadbalancer service %s/%s: %v" , portID , apiService . Namespace , apiService . Name , res . Err )
2017-10-10 03:04:32 -04:00
return fmt . Errorf ( msg )
}
2016-09-06 20:28:09 -04:00
}
}
2016-09-28 02:20:59 -04:00
2017-10-10 03:04:32 -04:00
// ensure rules for every node security group
for _ , port := range ports {
for _ , nodeSecurityGroupID := range lbaas . opts . NodeSecurityGroupIDs {
opts := rules . ListOpts {
Direction : string ( rules . DirIngress ) ,
SecGroupID : nodeSecurityGroupID ,
RemoteGroupID : lbSecGroupID ,
PortRangeMax : int ( port . NodePort ) ,
PortRangeMin : int ( port . NodePort ) ,
Protocol : string ( port . Protocol ) ,
}
secGroupRules , err := getSecurityGroupRules ( lbaas . network , opts )
if err != nil && ! isNotFound ( err ) {
msg := fmt . Sprintf ( "Error finding rules for remote group id %s in security group id %s: %v" , lbSecGroupID , nodeSecurityGroupID , err )
return fmt . Errorf ( msg )
}
if len ( secGroupRules ) != 0 {
// Do not add rule when find rules for remote group in the Node Security Group
continue
}
// Add the rules in the Node Security Group
err = createNodeSecurityGroup ( lbaas . network , nodeSecurityGroupID , int ( port . NodePort ) , port . Protocol , lbSecGroupID )
if err != nil {
2018-02-09 01:53:53 -05:00
return fmt . Errorf ( "error occurred creating security group for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2017-10-10 03:04:32 -04:00
}
}
}
return nil
2016-05-17 18:05:03 -04:00
}
2018-02-03 18:44:57 -05:00
// UpdateLoadBalancer updates hosts under the specified load balancer.
2018-02-02 16:12:07 -05:00
func ( lbaas * LbaasV2 ) UpdateLoadBalancer ( ctx context . Context , clusterName string , service * v1 . Service , nodes [ ] * v1 . Node ) error {
2018-08-04 00:36:48 -04:00
loadBalancerName := lbaas . GetLoadBalancerName ( ctx , clusterName , service )
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "UpdateLoadBalancer(%v, %v, %v)" , clusterName , loadBalancerName , nodes )
2016-05-17 18:05:03 -04:00
2018-04-04 07:13:48 -04:00
lbaas . opts . SubnetID = getStringFromServiceAnnotation ( service , ServiceAnnotationLoadBalancerSubnetID , lbaas . opts . SubnetID )
2018-02-03 18:44:57 -05:00
if len ( lbaas . opts . SubnetID ) == 0 && len ( nodes ) > 0 {
// Get SubnetID automatically.
// The LB needs to be configured with instance addresses on the same subnet, so get SubnetID by one node.
2017-08-16 02:20:43 -04:00
subnetID , err := getSubnetIDForLB ( lbaas . compute , * nodes [ 0 ] )
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Warningf ( "Failed to find subnet-id for loadbalancer service %s/%s: %v" , service . Namespace , service . Name , err )
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "no subnet-id for service %s/%s : subnet-id not set in cloud provider config, " +
2017-08-16 02:20:43 -04:00
"and failed to find subnet-id from OpenStack: %v" , service . Namespace , service . Name , err )
}
2018-02-03 18:44:57 -05:00
lbaas . opts . SubnetID = subnetID
2017-07-19 16:25:33 -04:00
}
2016-05-17 18:05:03 -04:00
ports := service . Spec . Ports
2016-08-11 19:51:13 -04:00
if len ( ports ) == 0 {
2016-05-17 18:05:03 -04:00
return fmt . Errorf ( "no ports provided to openstack load balancer" )
}
2017-11-16 04:52:46 -05:00
loadbalancer , err := getLoadbalancerByName ( lbaas . lb , loadBalancerName )
2016-05-17 18:05:03 -04:00
if err != nil {
return err
}
if loadbalancer == nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "loadbalancer %s does not exist" , loadBalancerName )
2016-05-17 18:05:03 -04:00
}
2016-08-11 19:51:13 -04:00
// Get all listeners for this loadbalancer, by "port key".
type portKey struct {
2016-11-07 03:35:42 -05:00
Protocol listeners . Protocol
2016-08-11 19:51:13 -04:00
Port int
}
2016-12-19 07:22:47 -05:00
var listenerIDs [ ] string
2016-08-11 19:51:13 -04:00
lbListeners := make ( map [ portKey ] listeners . Listener )
2017-11-16 04:52:46 -05:00
allListeners , err := getListenersByLoadBalancerID ( lbaas . lb , loadbalancer . ID )
2016-08-11 19:51:13 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error getting listeners for LB %s: %v" , loadBalancerName , err )
2016-12-19 07:22:47 -05:00
}
for _ , l := range allListeners {
key := portKey { Protocol : listeners . Protocol ( l . Protocol ) , Port : l . ProtocolPort }
lbListeners [ key ] = l
listenerIDs = append ( listenerIDs , l . ID )
2016-05-17 18:05:03 -04:00
}
2016-08-11 19:51:13 -04:00
// Get all pools for this loadbalancer, by listener ID.
2016-11-30 02:27:27 -05:00
lbPools := make ( map [ string ] v2pools . Pool )
2016-12-19 07:22:47 -05:00
for _ , listenerID := range listenerIDs {
2017-11-16 04:52:46 -05:00
pool , err := getPoolByListenerID ( lbaas . lb , loadbalancer . ID , listenerID )
2016-05-17 18:05:03 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error getting pool for listener %s: %v" , listenerID , err )
2016-05-17 18:05:03 -04:00
}
2016-12-19 07:22:47 -05:00
lbPools [ listenerID ] = * pool
2016-05-17 18:05:03 -04:00
}
2016-08-11 19:51:13 -04:00
// Compose Set of member (addresses) that _should_ exist
2018-04-24 12:22:16 -04:00
addrs := make ( map [ string ] * v1 . Node )
2016-08-23 00:36:34 -04:00
for _ , node := range nodes {
addr , err := nodeAddressForLB ( node )
2016-05-17 18:05:03 -04:00
if err != nil {
return err
}
2018-04-24 12:22:16 -04:00
addrs [ addr ] = node
2016-05-17 18:05:03 -04:00
}
2016-08-11 19:51:13 -04:00
// Check for adding/removing members associated with each port
2018-04-24 12:22:16 -04:00
for portIndex , port := range ports {
2016-08-11 19:51:13 -04:00
// Get listener associated with this port
listener , ok := lbListeners [ portKey {
2016-11-07 03:35:42 -05:00
Protocol : toListenersProtocol ( port . Protocol ) ,
2016-08-11 19:51:13 -04:00
Port : int ( port . Port ) ,
} ]
if ! ok {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "loadbalancer %s does not contain required listener for port %d and protocol %s" , loadBalancerName , port . Port , port . Protocol )
2016-08-11 19:51:13 -04:00
}
// Get pool associated with this listener
pool , ok := lbPools [ listener . ID ]
if ! ok {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "loadbalancer %s does not contain required pool for listener %s" , loadBalancerName , listener . ID )
2016-08-11 19:51:13 -04:00
}
// Find existing pool members (by address) for this port
2017-11-16 04:52:46 -05:00
getMembers , err := getMembersByPoolID ( lbaas . lb , pool . ID )
2016-08-11 19:51:13 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error getting pool members %s: %v" , pool . ID , err )
2016-12-19 07:22:47 -05:00
}
members := make ( map [ string ] v2pools . Member )
for _ , member := range getMembers {
members [ member . Address ] = member
2016-08-11 19:51:13 -04:00
}
// Add any new members for this port
2018-04-24 12:22:16 -04:00
for addr , node := range addrs {
2016-10-09 23:22:00 -04:00
if _ , ok := members [ addr ] ; ok && members [ addr ] . ProtocolPort == int ( port . NodePort ) {
2016-08-11 19:51:13 -04:00
// Already exists, do not create member
continue
}
2017-11-16 04:52:46 -05:00
_ , err := v2pools . CreateMember ( lbaas . lb , pool . ID , v2pools . CreateMemberOpts {
2018-04-24 12:22:16 -04:00
Name : fmt . Sprintf ( "member_%s_%d_%s" , loadbalancer . Name , portIndex , node . Name ) ,
2016-08-11 19:51:13 -04:00
Address : addr ,
ProtocolPort : int ( port . NodePort ) ,
2018-02-03 18:44:57 -05:00
SubnetID : lbaas . opts . SubnetID ,
2016-08-11 19:51:13 -04:00
} ) . Extract ( )
if err != nil {
return err
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-08-11 19:51:13 -04:00
}
// Remove any old members for this port
for _ , member := range members {
2016-10-09 23:22:00 -04:00
if _ , ok := addrs [ member . Address ] ; ok && member . ProtocolPort == int ( port . NodePort ) {
2016-08-11 19:51:13 -04:00
// Still present, do not delete member
continue
}
2017-11-16 04:52:46 -05:00
err = v2pools . DeleteMember ( lbaas . lb , pool . ID , member . ID ) . ExtractErr ( )
2016-08-11 19:51:13 -04:00
if err != nil && ! isNotFound ( err ) {
return err
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-08-11 19:51:13 -04:00
}
}
2017-10-10 03:04:32 -04:00
if lbaas . opts . ManageSecurityGroups {
err := lbaas . updateSecurityGroup ( clusterName , service , nodes , loadbalancer )
if err != nil {
2017-11-20 20:38:43 -05:00
return fmt . Errorf ( "failed to update Security Group for loadbalancer service %s/%s: %v" , service . Namespace , service . Name , err )
2017-10-10 03:04:32 -04:00
}
}
return nil
}
// updateSecurityGroup updating security group for specific loadbalancer service.
func ( lbaas * LbaasV2 ) updateSecurityGroup ( clusterName string , apiService * v1 . Service , nodes [ ] * v1 . Node , loadbalancer * loadbalancers . LoadBalancer ) error {
originalNodeSecurityGroupIDs := lbaas . opts . NodeSecurityGroupIDs
var err error
2018-06-21 18:10:37 -04:00
lbaas . opts . NodeSecurityGroupIDs , err = getNodeSecurityGroupIDForLB ( lbaas . compute , lbaas . network , nodes )
2017-10-10 03:04:32 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "failed to find node-security-group for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2017-10-10 03:04:32 -04:00
}
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "find node-security-group %v for loadbalancer service %s/%s" , lbaas . opts . NodeSecurityGroupIDs , apiService . Namespace , apiService . Name )
2017-10-10 03:04:32 -04:00
original := sets . NewString ( originalNodeSecurityGroupIDs ... )
current := sets . NewString ( lbaas . opts . NodeSecurityGroupIDs ... )
removals := original . Difference ( current )
// Generate Name
2017-10-26 08:23:16 -04:00
lbSecGroupName := getSecurityGroupName ( apiService )
2017-10-10 03:04:32 -04:00
lbSecGroupID , err := groups . IDFromName ( lbaas . network , lbSecGroupName )
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error occurred finding security group: %s: %v" , lbSecGroupName , err )
2017-10-10 03:04:32 -04:00
}
ports := apiService . Spec . Ports
if len ( ports ) == 0 {
return fmt . Errorf ( "no ports provided to openstack load balancer" )
}
for _ , port := range ports {
for removal := range removals {
// Delete the rules in the Node Security Group
opts := rules . ListOpts {
Direction : string ( rules . DirIngress ) ,
SecGroupID : removal ,
RemoteGroupID : lbSecGroupID ,
PortRangeMax : int ( port . NodePort ) ,
PortRangeMin : int ( port . NodePort ) ,
Protocol : string ( port . Protocol ) ,
}
secGroupRules , err := getSecurityGroupRules ( lbaas . network , opts )
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error finding rules for remote group id %s in security group id %s: %v" , lbSecGroupID , removal , err )
2017-10-10 03:04:32 -04:00
}
for _ , rule := range secGroupRules {
res := rules . Delete ( lbaas . network , rule . ID )
if res . Err != nil && ! isNotFound ( res . Err ) {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error occurred deleting security group rule: %s: %v" , rule . ID , res . Err )
2017-10-10 03:04:32 -04:00
}
}
}
for _ , nodeSecurityGroupID := range lbaas . opts . NodeSecurityGroupIDs {
opts := rules . ListOpts {
Direction : string ( rules . DirIngress ) ,
SecGroupID : nodeSecurityGroupID ,
RemoteGroupID : lbSecGroupID ,
PortRangeMax : int ( port . NodePort ) ,
PortRangeMin : int ( port . NodePort ) ,
Protocol : string ( port . Protocol ) ,
}
secGroupRules , err := getSecurityGroupRules ( lbaas . network , opts )
if err != nil && ! isNotFound ( err ) {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error finding rules for remote group id %s in security group id %s: %v" , lbSecGroupID , nodeSecurityGroupID , err )
2017-10-10 03:04:32 -04:00
}
if len ( secGroupRules ) != 0 {
// Do not add rule when find rules for remote group in the Node Security Group
continue
}
// Add the rules in the Node Security Group
err = createNodeSecurityGroup ( lbaas . network , nodeSecurityGroupID , int ( port . NodePort ) , port . Protocol , lbSecGroupID )
if err != nil {
2018-02-09 01:53:53 -05:00
return fmt . Errorf ( "error occurred creating security group for loadbalancer service %s/%s: %v" , apiService . Namespace , apiService . Name , err )
2017-10-10 03:04:32 -04:00
}
}
}
2016-05-17 18:05:03 -04:00
return nil
}
2018-02-03 18:44:57 -05:00
// EnsureLoadBalancerDeleted deletes the specified load balancer
2018-02-02 16:12:07 -05:00
func ( lbaas * LbaasV2 ) EnsureLoadBalancerDeleted ( ctx context . Context , clusterName string , service * v1 . Service ) error {
2018-08-04 00:36:48 -04:00
loadBalancerName := lbaas . GetLoadBalancerName ( ctx , clusterName , service )
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "EnsureLoadBalancerDeleted(%v, %v)" , clusterName , loadBalancerName )
2016-05-17 18:05:03 -04:00
2017-11-16 04:52:46 -05:00
loadbalancer , err := getLoadbalancerByName ( lbaas . lb , loadBalancerName )
2016-05-17 18:05:03 -04:00
if err != nil && err != ErrNotFound {
return err
}
if loadbalancer == nil {
return nil
}
2017-12-01 02:15:31 -05:00
if loadbalancer . VipPortID != "" {
2017-04-12 06:50:37 -04:00
portID := loadbalancer . VipPortID
floatingIP , err := getFloatingIPByPortID ( lbaas . network , portID )
2016-05-17 18:05:03 -04:00
if err != nil && err != ErrNotFound {
return err
}
if floatingIP != nil {
err = floatingips . Delete ( lbaas . network , floatingIP . ID ) . ExtractErr ( )
if err != nil && ! isNotFound ( err ) {
return err
}
}
}
// get all listeners associated with this loadbalancer
2017-11-16 04:52:46 -05:00
listenerList , err := getListenersByLoadBalancerID ( lbaas . lb , loadbalancer . ID )
2016-05-17 18:05:03 -04:00
if err != nil {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error getting LB %s listeners: %v" , loadbalancer . ID , err )
2016-05-17 18:05:03 -04:00
}
2016-09-21 16:56:51 -04:00
// get all pools (and health monitors) associated with this loadbalancer
2016-05-17 18:05:03 -04:00
var poolIDs [ ] string
2016-09-21 16:56:51 -04:00
var monitorIDs [ ] string
2016-12-19 07:22:47 -05:00
for _ , listener := range listenerList {
2017-11-16 04:52:46 -05:00
pool , err := getPoolByListenerID ( lbaas . lb , loadbalancer . ID , listener . ID )
2017-05-27 06:07:13 -04:00
if err != nil && err != ErrNotFound {
2017-10-20 03:02:37 -04:00
return fmt . Errorf ( "error getting pool for listener %s: %v" , listener . ID , err )
2016-05-17 18:05:03 -04:00
}
2017-08-18 17:49:23 -04:00
if pool != nil {
poolIDs = append ( poolIDs , pool . ID )
// If create-monitor of cloud-config is false, pool has not monitor.
if pool . MonitorID != "" {
monitorIDs = append ( monitorIDs , pool . MonitorID )
}
2017-06-30 06:55:53 -04:00
}
2016-05-17 18:05:03 -04:00
}
// delete all monitors
for _ , monitorID := range monitorIDs {
2017-11-16 04:52:46 -05:00
err := v2monitors . Delete ( lbaas . lb , monitorID ) . ExtractErr ( )
2016-05-17 18:05:03 -04:00
if err != nil && ! isNotFound ( err ) {
return err
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-05-17 18:05:03 -04:00
}
// delete all members and pools
for _ , poolID := range poolIDs {
2018-03-09 09:50:15 -05:00
// get members for current pool
membersList , err := getMembersByPoolID ( lbaas . lb , poolID )
if err != nil && ! isNotFound ( err ) {
return fmt . Errorf ( "error getting pool members %s: %v" , poolID , err )
}
2016-05-17 18:05:03 -04:00
// delete all members for this pool
2018-03-09 09:50:15 -05:00
for _ , member := range membersList {
err := v2pools . DeleteMember ( lbaas . lb , poolID , member . ID ) . ExtractErr ( )
2016-05-17 18:05:03 -04:00
if err != nil && ! isNotFound ( err ) {
return err
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-05-17 18:05:03 -04:00
}
// delete pool
2018-03-09 09:50:15 -05:00
err = v2pools . Delete ( lbaas . lb , poolID ) . ExtractErr ( )
2016-05-17 18:05:03 -04:00
if err != nil && ! isNotFound ( err ) {
return err
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-05-17 18:05:03 -04:00
}
// delete all listeners
2016-12-19 07:22:47 -05:00
for _ , listener := range listenerList {
2017-11-16 04:52:46 -05:00
err := listeners . Delete ( lbaas . lb , listener . ID ) . ExtractErr ( )
2016-05-17 18:05:03 -04:00
if err != nil && ! isNotFound ( err ) {
return err
}
2017-12-01 01:41:48 -05:00
provisioningStatus , err := waitLoadbalancerActiveProvisioningStatus ( lbaas . lb , loadbalancer . ID )
if err != nil {
return fmt . Errorf ( "failed to loadbalance ACTIVE provisioning status %v: %v" , provisioningStatus , err )
}
2016-05-17 18:05:03 -04:00
}
// delete loadbalancer
2017-11-16 04:52:46 -05:00
err = loadbalancers . Delete ( lbaas . lb , loadbalancer . ID ) . ExtractErr ( )
2016-05-17 18:05:03 -04:00
if err != nil && ! isNotFound ( err ) {
return err
}
2017-12-01 01:41:48 -05:00
err = waitLoadbalancerDeleted ( lbaas . lb , loadbalancer . ID )
if err != nil {
return fmt . Errorf ( "failed to delete loadbalancer: %v" , err )
}
2016-09-06 20:28:09 -04:00
// Delete the Security Group
if lbaas . opts . ManageSecurityGroups {
2017-11-20 20:38:43 -05:00
err := lbaas . EnsureSecurityGroupDeleted ( clusterName , service )
2016-09-06 20:28:09 -04:00
if err != nil {
2017-11-20 20:38:43 -05:00
return fmt . Errorf ( "Failed to delete Security Group for loadbalancer service %s/%s: %v" , service . Namespace , service . Name , err )
2016-09-06 20:28:09 -04:00
}
2017-11-20 20:38:43 -05:00
}
return nil
}
2016-09-06 20:28:09 -04:00
2017-11-20 20:38:43 -05:00
// EnsureSecurityGroupDeleted deleting security group for specific loadbalancer service.
func ( lbaas * LbaasV2 ) EnsureSecurityGroupDeleted ( clusterName string , service * v1 . Service ) error {
// Generate Name
lbSecGroupName := getSecurityGroupName ( service )
lbSecGroupID , err := groups . IDFromName ( lbaas . network , lbSecGroupName )
if err != nil {
2018-01-22 20:46:15 -05:00
if isSecurityGroupNotFound ( err ) {
2017-11-20 20:38:43 -05:00
// It is OK when the security group has been deleted by others.
return nil
}
2018-02-03 18:44:57 -05:00
return fmt . Errorf ( "Error occurred finding security group: %s: %v" , lbSecGroupName , err )
2017-11-20 20:38:43 -05:00
}
2016-09-06 20:28:09 -04:00
2017-11-20 20:38:43 -05:00
lbSecGroup := groups . Delete ( lbaas . network , lbSecGroupID )
if lbSecGroup . Err != nil && ! isNotFound ( lbSecGroup . Err ) {
return lbSecGroup . Err
}
if len ( lbaas . opts . NodeSecurityGroupIDs ) == 0 {
// Just happen when nodes have not Security Group, or should not happen
// UpdateLoadBalancer and EnsureLoadBalancer can set lbaas.opts.NodeSecurityGroupIDs when it is empty
// And service controller call UpdateLoadBalancer to set lbaas.opts.NodeSecurityGroupIDs when controller manager service is restarted.
2018-11-09 13:49:10 -05:00
klog . Warningf ( "Can not find node-security-group from all the nodes of this cluster when delete loadbalancer service %s/%s" ,
2017-11-20 20:38:43 -05:00
service . Namespace , service . Name )
} else {
// Delete the rules in the Node Security Group
for _ , nodeSecurityGroupID := range lbaas . opts . NodeSecurityGroupIDs {
opts := rules . ListOpts {
SecGroupID : nodeSecurityGroupID ,
RemoteGroupID : lbSecGroupID ,
}
secGroupRules , err := getSecurityGroupRules ( lbaas . network , opts )
if err != nil && ! isNotFound ( err ) {
msg := fmt . Sprintf ( "Error finding rules for remote group id %s in security group id %s: %v" , lbSecGroupID , nodeSecurityGroupID , err )
return fmt . Errorf ( msg )
}
for _ , rule := range secGroupRules {
res := rules . Delete ( lbaas . network , rule . ID )
if res . Err != nil && ! isNotFound ( res . Err ) {
return fmt . Errorf ( "Error occurred deleting security group rule: %s: %v" , rule . ID , res . Err )
2017-10-10 03:04:32 -04:00
}
2016-09-06 20:28:09 -04:00
}
}
}
2016-05-17 18:05:03 -04:00
return nil
}