@@ -431,6 +431,15 @@ func getSecurityGroupName(service *corev1.Service) string {
431
431
return securityGroupName
432
432
}
433
433
434
+ func getSecurityGroupRules (client * gophercloud.ServiceClient , opts rules.ListOpts ) ([]rules.SecGroupRule , error ) {
435
+ mc := metrics .NewMetricContext ("security_group_rule" , "list" )
436
+ page , err := rules .List (client , opts ).AllPages ()
437
+ if mc .ObserveRequest (err ) != nil {
438
+ return nil , err
439
+ }
440
+ return rules .ExtractRules (page )
441
+ }
442
+
434
443
func getListenerProtocol (protocol corev1.Protocol , svcConf * serviceConfig ) listeners.Protocol {
435
444
// Make neutron-lbaas code work
436
445
if svcConf != nil {
@@ -2091,30 +2100,16 @@ func (lbaas *LbaasV2) listSubnetsForNetwork(networkID string, tweak ...TweakSubN
2091
2100
}
2092
2101
2093
2102
// group, if it not present.
2094
- func (lbaas * LbaasV2 ) ensureSecurityRule (
2095
- direction rules.RuleDirection ,
2096
- protocol rules.RuleProtocol ,
2097
- etherType rules.RuleEtherType ,
2098
- remoteIPPrefix , secGroupID string ,
2099
- portRangeMin , portRangeMax int ,
2100
- ) error {
2101
- sgRuleCreateOpts := rules.CreateOpts {
2102
- Direction : direction ,
2103
- Protocol : protocol ,
2104
- PortRangeMax : portRangeMin ,
2105
- PortRangeMin : portRangeMax ,
2106
- RemoteIPPrefix : remoteIPPrefix ,
2107
- SecGroupID : secGroupID ,
2108
- EtherType : etherType ,
2109
- }
2110
-
2103
+ func (lbaas * LbaasV2 ) ensureSecurityRule (sgRuleCreateOpts rules.CreateOpts ) error {
2111
2104
mc := metrics .NewMetricContext ("security_group_rule" , "create" )
2112
2105
_ , err := rules .Create (lbaas .network , sgRuleCreateOpts ).Extract ()
2113
2106
if err != nil && cpoerrors .IsConflictError (err ) {
2114
2107
// Conflict means the SG rule already exists, so ignoring that error.
2108
+ klog .Warningf ("Security group rule already found when trying to create it. This indicates concurrent " +
2109
+ "updates to the SG %s and is unexpected" , sgRuleCreateOpts .SecGroupID )
2115
2110
return mc .ObserveRequest (nil )
2116
2111
} else if mc .ObserveRequest (err ) != nil {
2117
- return fmt .Errorf ("failed to create rule for security group %s: %v" , secGroupID , err )
2112
+ return fmt .Errorf ("failed to create rule for security group %s: %v" , sgRuleCreateOpts . SecGroupID , err )
2118
2113
}
2119
2114
return nil
2120
2115
}
@@ -2210,6 +2205,50 @@ func (lbaas *LbaasV2) UpdateLoadBalancer(ctx context.Context, clusterName string
2210
2205
return mc .ObserveReconcile (err )
2211
2206
}
2212
2207
2208
+ func compareSecurityGroupRuleAndCreateOpts (rule rules.SecGroupRule , opts rules.CreateOpts ) bool {
2209
+ return rule .Direction == string (opts .Direction ) &&
2210
+ rule .Protocol == string (opts .Protocol ) &&
2211
+ rule .EtherType == string (opts .EtherType ) &&
2212
+ rule .RemoteIPPrefix == opts .RemoteIPPrefix &&
2213
+ rule .PortRangeMin == opts .PortRangeMin &&
2214
+ rule .PortRangeMax == opts .PortRangeMax
2215
+ }
2216
+
2217
+ func getRulesToCreateAndDelete (wantedRules []rules.CreateOpts , existingRules []rules.SecGroupRule ) ([]rules.CreateOpts , []rules.SecGroupRule ) {
2218
+ toCreate := make ([]rules.CreateOpts , 0 , len (wantedRules )) // Max is all rules need creation
2219
+ toDelete := make ([]rules.SecGroupRule , 0 , len (existingRules )) // Max will be all the existing rules to be deleted
2220
+ // Surely this can be done in a more efficient way. Is it worth optimizing if most of
2221
+ // the time we'll deal with just 1 or 2 elements in each array? I doubt it.
2222
+ for _ , existingRule := range existingRules {
2223
+ found := false
2224
+ for _ , wantedRule := range wantedRules {
2225
+ if compareSecurityGroupRuleAndCreateOpts (existingRule , wantedRule ) {
2226
+ found = true
2227
+ break
2228
+ }
2229
+ }
2230
+ if ! found {
2231
+ // in existingRules but not in wantedRules, delete
2232
+ toDelete = append (toDelete , existingRule )
2233
+ }
2234
+ }
2235
+ for _ , wantedRule := range wantedRules {
2236
+ found := false
2237
+ for _ , existingRule := range existingRules {
2238
+ if compareSecurityGroupRuleAndCreateOpts (existingRule , wantedRule ) {
2239
+ found = true
2240
+ break
2241
+ }
2242
+ }
2243
+ if ! found {
2244
+ // in wantedRules but not in exisitngRules, create
2245
+ toCreate = append (toCreate , wantedRule )
2246
+ }
2247
+ }
2248
+
2249
+ return toCreate , toDelete
2250
+ }
2251
+
2213
2252
// ensureAndUpdateOctaviaSecurityGroup handles the creation and update of the security group and the securiry rules for the octavia load balancer
2214
2253
func (lbaas * LbaasV2 ) ensureAndUpdateOctaviaSecurityGroup (clusterName string , apiService * corev1.Service , nodes []* corev1.Node , memberSubnetID string ) error {
2215
2254
// get service ports
@@ -2256,47 +2295,75 @@ func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, ap
2256
2295
etherType = rules .EtherType6
2257
2296
}
2258
2297
2298
+ existingRules , err := getSecurityGroupRules (lbaas .network , rules.ListOpts {SecGroupID : lbSecGroupID })
2299
+ if err != nil {
2300
+ return fmt .Errorf (
2301
+ "failed to find security group rules in %s: %v" , lbSecGroupID , err )
2302
+ }
2303
+
2304
+ // List of the security group rules wanted in the SG.
2305
+ // Number of Ports plus the potential HealthCheckNodePort.
2306
+ wantedRules := make ([]rules.CreateOpts , 0 , len (ports )+ 1 )
2307
+
2259
2308
if apiService .Spec .HealthCheckNodePort != 0 {
2260
- err = lbaas .ensureSecurityRule (
2261
- rules .DirIngress ,
2262
- rules .ProtocolTCP ,
2263
- etherType ,
2264
- subnet .CIDR ,
2265
- lbSecGroupID ,
2266
- int (apiService .Spec .HealthCheckNodePort ),
2267
- int (apiService .Spec .HealthCheckNodePort ),
2309
+ wantedRules = append (wantedRules ,
2310
+ rules.CreateOpts {
2311
+ Direction : rules .DirIngress ,
2312
+ Protocol : rules .ProtocolTCP ,
2313
+ EtherType : etherType ,
2314
+ RemoteIPPrefix : subnet .CIDR ,
2315
+ SecGroupID : lbSecGroupID ,
2316
+ PortRangeMin : int (apiService .Spec .HealthCheckNodePort ),
2317
+ PortRangeMax : int (apiService .Spec .HealthCheckNodePort ),
2318
+ },
2268
2319
)
2269
- if err != nil {
2270
- return fmt .Errorf (
2271
- "failed to apply security rule for health check node port, %w" ,
2272
- err )
2273
- }
2274
2320
}
2275
2321
2276
- // ensure rules for node security group
2277
2322
for _ , port := range ports {
2278
2323
if port .NodePort == 0 { // It's 0 when AllocateLoadBalancerNodePorts=False
2279
2324
continue
2280
2325
}
2281
- err = lbaas .ensureSecurityRule (
2282
- rules .DirIngress ,
2283
- rules .RuleProtocol (port .Protocol ),
2284
- etherType ,
2285
- subnet .CIDR ,
2286
- lbSecGroupID ,
2287
- int (port .NodePort ),
2288
- int (port .NodePort ),
2326
+ wantedRules = append (wantedRules ,
2327
+ rules.CreateOpts {
2328
+ Direction : rules .DirIngress ,
2329
+ Protocol : rules .RuleProtocol (port .Protocol ),
2330
+ EtherType : etherType ,
2331
+ RemoteIPPrefix : subnet .CIDR ,
2332
+ SecGroupID : lbSecGroupID ,
2333
+ PortRangeMin : int (port .NodePort ),
2334
+ PortRangeMax : int (port .NodePort ),
2335
+ },
2289
2336
)
2337
+ }
2338
+
2339
+ toCreate , toDelete := getRulesToCreateAndDelete (wantedRules , existingRules )
2340
+
2341
+ // create new rules
2342
+ for _ , opts := range toCreate {
2343
+ err := lbaas .ensureSecurityRule (opts )
2290
2344
if err != nil {
2291
- return fmt .Errorf (
2292
- "failed to apply security rule for port %d, %w" ,
2293
- port .NodePort , err )
2345
+ return fmt .Errorf ("failed to apply security rule (%v), %w" , opts , err )
2294
2346
}
2347
+ }
2295
2348
2296
- if err := applyNodeSecurityGroupIDForLB (lbaas .compute , lbaas .network , nodes , lbSecGroupID ); err != nil {
2297
- return err
2349
+ // delete unneeded rules
2350
+ for _ , existingRule := range toDelete {
2351
+ klog .Infof ("Deleting rule %s from security group %s (%s)" , existingRule .ID , existingRule .SecGroupID , lbSecGroupName )
2352
+ mc := metrics .NewMetricContext ("security_group_rule" , "delete" )
2353
+ err := rules .Delete (lbaas .network , existingRule .ID ).ExtractErr ()
2354
+ if err != nil && cpoerrors .IsNotFound (err ) {
2355
+ // ignore 404
2356
+ klog .Warningf ("Security group rule %s found missing when trying to delete it. This indicates concurrent " +
2357
+ "updates to the SG %s and is unexpected" , existingRule .ID , existingRule .SecGroupID )
2358
+ return mc .ObserveRequest (nil )
2359
+ } else if mc .ObserveRequest (err ) != nil {
2360
+ return fmt .Errorf ("failed to delete security group rule %s: %w" , existingRule .ID , err )
2298
2361
}
2299
2362
}
2363
+
2364
+ if err := applyNodeSecurityGroupIDForLB (lbaas .compute , lbaas .network , nodes , lbSecGroupID ); err != nil {
2365
+ return err
2366
+ }
2300
2367
return nil
2301
2368
}
2302
2369
0 commit comments