@@ -28,7 +28,7 @@ import (
2828type addressManager struct {
2929 logPrefix string
3030 svc CloudAddressService
31- lbName string
31+ name string
3232 serviceName string
3333 targetIP string
3434 addressType lbScheme
@@ -37,13 +37,13 @@ type addressManager struct {
3737 tryRelease bool
3838}
3939
40- func newAddressManager (svc CloudAddressService , serviceName , region , subnetURL , lbName , targetIP string , addressType lbScheme ) * addressManager {
40+ func newAddressManager (svc CloudAddressService , serviceName , region , subnetURL , name , targetIP string , addressType lbScheme ) * addressManager {
4141 return & addressManager {
4242 svc : svc ,
43- logPrefix : fmt .Sprintf ("AddressManager(%q)" , lbName ),
43+ logPrefix : fmt .Sprintf ("AddressManager(%q)" , name ),
4444 region : region ,
4545 serviceName : serviceName ,
46- lbName : lbName ,
46+ name : name ,
4747 targetIP : targetIP ,
4848 addressType : addressType ,
4949 tryRelease : true ,
@@ -53,7 +53,7 @@ func newAddressManager(svc CloudAddressService, serviceName, region, subnetURL,
5353
5454// HoldAddress will ensure that the IP is reserved with an address - either owned by the controller
5555// or by a user. If the address is not the loadBalancerName, then it's assumed to be a user's address.
56- func (am * addressManager ) HoldAddress () error {
56+ func (am * addressManager ) HoldAddress () ( string , error ) {
5757 // HoldAddress starts with retrieving the address that we use for this load balancer (by name).
5858 // Retrieving an address by IP will indicate if the IP is reserved and if reserved by the user
5959 // or the controller, but won't tell us the current state of the controller's IP. The address
@@ -62,28 +62,28 @@ func (am *addressManager) HoldAddress() error {
6262 // calls since it indicates whether a Delete is necessary before Reserve.
6363 glog .V (3 ).Infof ("%v: attempting hold of IP %v with type %v" , am .logPrefix , am .targetIP , am .addressType )
6464 // Get the address in case it was orphaned earlier
65- addr , err := am .svc .GetBetaRegionAddress (am .lbName , am .region )
65+ addr , err := am .svc .GetBetaRegionAddress (am .name , am .region )
6666 if err != nil && ! isNotFound (err ) {
67- return err
67+ return "" , err
6868 }
6969
7070 if addr != nil {
71- // If address exists, check if the address IP is different from what's expected.
72- if addr .Address != am .targetIP || addr .AddressType != string (am .addressType ) {
73- glog .V (3 ).Infof ("%v: existing address %q has IP %v Type %v which does not match targeted IP %v Type %v. Attemping to delete." , am .logPrefix , addr .Name , addr .Address , addr .AddressType , am .targetIP , am .addressType )
74- err := am .svc .DeleteRegionAddress (addr .Name , am .region )
75- if err != nil {
76- if isNotFound (err ) {
77- glog .V (3 ).Infof ("%v: address %q was not found. Ignoring." , am .logPrefix , addr .Name )
78- } else {
79- return err
80- }
71+ // If address exists, check if the address had the expected attributes.
72+ if (am .targetIP == "" || am .targetIP == addr .Address ) && addr .AddressType == string (am .addressType ) {
73+ glog .V (3 ).Infof ("%v: address %q already reserves IP %v of type %v. No further action required." , am .logPrefix , addr .Name , addr .Address , addr .AddressType )
74+ return addr .Address , nil
75+ }
76+
77+ glog .V (3 ).Infof ("%v: existing address %q has IP %v Type %v which does not match targeted IP %v Type %v. Attemping to delete." , am .logPrefix , addr .Name , addr .Address , addr .AddressType , am .targetIP , am .addressType )
78+ err := am .svc .DeleteRegionAddress (addr .Name , am .region )
79+ if err != nil {
80+ if isNotFound (err ) {
81+ glog .V (3 ).Infof ("%v: address %q was not found. Ignoring." , am .logPrefix , addr .Name )
8182 } else {
82- glog . V ( 3 ). Infof ( "%v: successfully deleted previous address %q" , am . logPrefix , addr . Name )
83+ return "" , err
8384 }
8485 } else {
85- glog .V (3 ).Infof ("%v: address %q already reserves targeted IP %v of type %v. No further action required." , am .logPrefix , addr .Name , am .targetIP , am .addressType )
86- return nil
86+ glog .V (3 ).Infof ("%v: successfully deleted previous address %q" , am .logPrefix , addr .Name )
8787 }
8888 }
8989
@@ -97,59 +97,83 @@ func (am *addressManager) ReleaseAddress() error {
9797 return nil
9898 }
9999
100- glog .V (3 ).Infof ("%v: releasing address %v named %q" , am .logPrefix , am .targetIP , am .lbName )
100+ glog .V (3 ).Infof ("%v: releasing address %v named %q" , am .logPrefix , am .targetIP , am .name )
101101 // Controller only ever tries to unreserve the address named with the load balancer's name.
102- err := am .svc .DeleteRegionAddress (am .lbName , am .region )
102+ err := am .svc .DeleteRegionAddress (am .name , am .region )
103103 if err != nil {
104104 if isNotFound (err ) {
105- glog .Warningf ("%v: address %q was not found. Ignoring." , am .logPrefix , am .targetIP , am . lbName )
105+ glog .Warningf ("%v: address %q was not found. Ignoring." , am .logPrefix , am .name )
106106 return nil
107107 }
108108
109109 return err
110110 }
111111
112- glog .V (3 ).Infof ("%v: successfully released IP %v named %q" , am .logPrefix , am .targetIP , am .lbName )
112+ glog .V (3 ).Infof ("%v: successfully released IP %v named %q" , am .logPrefix , am .targetIP , am .name )
113113 return nil
114114}
115115
116- func (am * addressManager ) ensureAddressReservation () error {
116+ func (am * addressManager ) ensureAddressReservation () ( string , error ) {
117117 // Try reserving the IP with controller-owned address name
118+ // If am.targetIP is an empty string, a new IP will be created.
118119 newAddr := & computebeta.Address {
119- Name : am .lbName ,
120+ Name : am .name ,
120121 Description : fmt .Sprintf (`{"kubernetes.io/service-name":"%s"}` , am .serviceName ),
121122 Address : am .targetIP ,
122123 AddressType : string (am .addressType ),
123124 Subnetwork : am .subnetURL ,
124125 }
125126
126127 err := am .svc .ReserveBetaRegionAddress (newAddr , am .region )
127- if err != nil {
128- if ! isHTTPErrorCode (err , http .StatusConflict ) {
129- return err
128+ if err == nil {
129+ if newAddr .Address != "" {
130+ glog .V (3 ).Infof ("%v: successfully reserved IP %v with name %q" , am .logPrefix , newAddr .Address , newAddr .Name )
131+ return newAddr .Address , nil
132+ }
133+
134+ addr , err := am .svc .GetRegionAddress (newAddr .Name , am .region )
135+ if err != nil {
136+ return "" , err
130137 }
131- } else {
132- glog .V (3 ).Infof ("%v: successfully reserved IP %v with name %q" , am .logPrefix , am .targetIP , newAddr .Name )
133- return nil
134- }
135138
136- glog .V (3 ).Infof ("%v: could not reserve IP %v due to err: %v" , am .logPrefix , am .targetIP , err )
139+ glog .V (3 ).Infof ("%v: successfully created address %q which reserved IP %q" , am .logPrefix , addr .Name , addr .Address )
140+ return addr .Address , nil
141+ } else if ! isHTTPErrorCode (err , http .StatusConflict ) && ! isHTTPErrorCode (err , http .StatusBadRequest ) {
142+ // If the IP is already reserved:
143+ // by an internal address: a StatusConflict is returned
144+ // by an external address: a BadRequest is returned
145+ return "" , err
146+ }
137147
138148 // Reserving the address failed due to a conflict. The address manager just checked that no address
139- // exists with the lbName , so it may belong to the user.
149+ // exists with the name , so it may belong to the user.
140150 addr , err := am .svc .GetBetaRegionAddressByIP (am .region , am .targetIP )
141151 if err != nil {
142- return fmt .Errorf ("could not find address with IP %v after getting conflict error while creating address: %v " , am .targetIP , err )
152+ return "" , fmt .Errorf ("could not find address with IP %q after getting conflict error while creating address: %q " , am .targetIP , err )
143153 }
144154
145- // If the retrieved address is not named with the loadbalancer name, then the controller does not own it.
146- if addr .Name != am .lbName {
147- glog .V (3 ).Infof ("%v: address %q was already reserved with name: %q, description: %q" , am .logPrefix , am .targetIP , addr .Name , addr .Description )
148- am .tryRelease = false
149- } else {
155+ // Check that the address attributes are as required.
156+ if addr .AddressType != string (am .addressType ) {
157+ return "" , fmt .Errorf ("address %q does not have the expected address type %v, actual: %v" , addr .Name , am .addressType , addr .AddressType )
158+ }
159+
160+ if am .isManagedAddress (addr ) {
150161 // The address with this name is checked at the beginning of 'HoldAddress()', but for some reason
151162 // it was re-created by this point. May be possible that two controllers are running.
152163 glog .Warning ("%v: address %q unexpectedly existed with IP %q." , am .logPrefix , addr .Name , am .targetIP )
164+ } else {
165+ // If the retrieved address is not named with the loadbalancer name, then the controller does not own it.
166+ glog .V (3 ).Infof ("%v: address %q was already reserved with name: %q, description: %q" , am .logPrefix , am .targetIP , addr .Name , addr .Description )
167+ am .tryRelease = false
153168 }
154- return nil
169+
170+ return addr .Address , nil
171+ }
172+
173+ func (am * addressManager ) isManagedAddress (addr * computebeta.Address ) bool {
174+ return addr .Name == am .name
175+ }
176+
177+ func ensureAddressDeleted (svc CloudAddressService , name , region string ) error {
178+ return ignoreNotFound (svc .DeleteRegionAddress (name , region ))
155179}
0 commit comments