-
Notifications
You must be signed in to change notification settings - Fork 301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite L4 healthchecks creation and deletion #1705
Rewrite L4 healthchecks creation and deletion #1705
Conversation
Hi @cezarygerard. Thanks for your PR. I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
/assign @bowei |
/ok-to-test |
/assign |
@bowei I have just answered all questions from Katarzyna. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Biggest question is why introduce a singleton? Seems like it's mostly to reduce verbosity?
cmd/glbc/main.go
Outdated
@@ -19,6 +19,7 @@ package main | |||
import ( | |||
"context" | |||
"fmt" | |||
"k8s.io/ingress-gce/pkg/healthchecks" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put non-golang libs on a separate block for imports
hack/run-local-glbc.sh
Outdated
@@ -43,5 +43,7 @@ ${GLBC} \ | |||
--running-in-cluster=false \ | |||
--logtostderr --v=${V} \ | |||
--config-file-path=${GCECONF} \ | |||
--run-l4-controller \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put this in a separate commit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undone the change
pkg/firewalls/firewalls_l4.go
Outdated
@@ -104,8 +104,8 @@ func EnsureL4FirewallRuleDeleted(cloud *gce.Cloud, fwName string) error { | |||
} | |||
|
|||
func firewallRuleEqual(a, b *compute.Firewall) bool { | |||
return a.Description == b.Description && | |||
len(a.Allowed) == 1 && len(a.Allowed) == len(b.Allowed) && | |||
// let's skip description not to trigger flood of updates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Don't compare the "description" field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
changed the logic a little bit for non-shared health checks and firewalls according to #1705 (comment)
pkg/firewalls/firewalls_l4.go
Outdated
return a.Description == b.Description && | ||
len(a.Allowed) == 1 && len(a.Allowed) == len(b.Allowed) && | ||
// let's skip description not to trigger flood of updates | ||
return len(a.Allowed) == 1 && len(a.Allowed) == len(b.Allowed) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
put one predicate per line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
changed the logic a little bit for non-shared health checks and firewalls according to #1705 (comment)
} | ||
|
||
func Initialize(cloud *gce.Cloud, recorderFactory events.RecorderProducer) { | ||
instance = &l4HealthChecks{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this need to take the mutex?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a mutex guard,
btw see my comment on singleton pattern
pkg/healthchecks/healthchecks_l4.go
Outdated
@@ -42,44 +47,111 @@ const ( | |||
gceHcUnhealthyThreshold = int64(3) | |||
) | |||
|
|||
// EnsureL4HealthCheck creates a new HTTP health check for an L4 LoadBalancer service, based on the parameters provided. | |||
var instance *l4HealthChecks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain the rationale behind adding a singleton. Seems like it mostly just removes the Cloud and recorder argument? If we find that a problem because it's too verbose -- maybe just put those in a single struct and pass that as an argument instead of having to deal with the complexities of having a Singleton?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not about cloud and recorder args.
We have a real bug in healthcheck deletion.
Deletion of shared health check tries to remove firewall rule for health check
and the healthcheck from ILB and NetLB use the same firewall rule
Checking if healthcheck-under-deletion is in use is not enough (deleting NetLB Healthcheck will not fail if ILB Healthcheck is in use)
cross-checking firewalls is still subject to race conditions (one healthcheck is deleted, the other is added ad the same time)
so we need a way to safely healthchecks using the same mutex object across the NetLB controller and ILB controller.
Creating a common l4HealthChecks struct makes a good place for cross-checking healthchecks and simplifies code in NetLB controller and ILB controller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now the open question whether it is worth to create the singleton
it make initialisation in l4.go and l4netlb.go much easier
but on the other hand the tests setups are more complicated
maybe it is enough to create an instance in main and pass it via context, just like any other single instance struct in this code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tried to sketch the old way for dependency injection: from context down to controllers na handlers
cezarygerard#3
it does not seem much simpler
pkg/healthchecks/healthchecks_l4.go
Outdated
} | ||
} | ||
|
||
func Fake(cloud *gce.Cloud, recorderFactory events.RecorderProducer) *l4HealthChecks { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it doesn't actually create a fake?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right,
My rationale was to name it Fake so it is clearly only for test purposes.
pkg/firewalls/firewalls_l4.go
Outdated
@@ -104,8 +102,8 @@ func EnsureL4FirewallRuleDeleted(cloud *gce.Cloud, fwName string) error { | |||
} | |||
|
|||
func firewallRuleEqual(a, b *compute.Firewall) bool { | |||
return a.Description == b.Description && | |||
len(a.Allowed) == 1 && len(a.Allowed) == len(b.Allowed) && | |||
// let's skip description not to trigger flood of updates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we wants to do this. For non-shared HC description contains ServiceIP: ip
, so if IP will change then the health check description will be obsolete, it probably is needed only for debugging purposes but still it might be confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed this logic, for non shared health checks
5720795
to
9e17971
Compare
Can you make sure the Git commits follow good conventions (e.g. have one line + description describing the bug being fixed etc.) |
pkg/firewalls/firewalls_l4.go
Outdated
len(a.Allowed) == 1 && len(a.Allowed) == len(b.Allowed) && | ||
func firewallRuleEqual(a, b *compute.Firewall, skipDescription bool) bool { | ||
fwrEqual := len(a.Allowed) == 1 && | ||
len(a.Allowed) == len(b.Allowed) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe instaed of creating new variable fwrEqual
you can do :
.. previous check..
&& (skipDescription || a.Description == b.Description)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored this whole func
pkg/healthchecks/healthchecks_l4.go
Outdated
} | ||
|
||
// GetL4 returns singleton instance, must be run after InitializeL4 | ||
func GetL4() *l4HealthChecks { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why don't you put Init func here? (and make Init function not exported).
Checking instance == nil
is cheap and then you don't have to worry that someone will call GetL4 before Init function.
I also think that it would be better to create this object inside context and keep it there (sinc all controllers have context and context is explicitly initialise before controllers are run so it will be safe) instead of this singleton pattern but this is just my opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[minor] It's probably more "Go"ish to call this L4()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pkg/healthchecks/healthchecks_l4.go
Outdated
|
||
// EnsureL4HealthCheck creates a new HTTP health check for an L4 LoadBalancer service, and the firewall rule required | ||
// for the healthcheck. If healthcheck is shared (external traffic policy 'cluster') then firewall rules will be shared | ||
// regardless of scope param. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this comment is not complete. I don't understand (without knowing the context) firewall rules will be shared regardless of scope param
what is scope param? Just write that ILB creates Global HC and NetLB Regional and firewall rule is shared for both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 we should be very explicit in our comments, otherwise it ends up with more questions than answers:
Suggest (if accurate)
EnsureL4HealthCheck and firewall rules exist for the L4
LoadBalancer Service.
The healthcheck and firewall will be shared between different K8s
Services for ExternalTrafficPolicy = Cluster, as the same
configuration is used across all Services of this type.
Firewall rules are always created at in the Global scope (vs
Regional). This means that one Firewall rule is created for
Services of different scope (Global vs Regional).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, used your comment
570dc6e
to
a10c879
Compare
@bowei done |
pkg/healthchecks/healthchecks_l4.go
Outdated
var ( | ||
// instance is a sinngleton instance, created by InitializeL4 | ||
instance *l4HealthChecks | ||
// mutex for preventing multiple initialization |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's call this instanceLock
// instanceLock to prevent duplicate initialization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure we have sufficient logging for debugging.
pkg/healthchecks/healthchecks_l4.go
Outdated
) | ||
|
||
type l4HealthChecks struct { | ||
mutex sync.Mutex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document what this mutex
is for? what is it used to protect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
added comment
renamed to sharedResourcesLock as it was in previous implementation
pkg/healthchecks/healthchecks_l4.go
Outdated
|
||
// InitializeL4 creates singleton instance, must be run before GetL4() func | ||
func InitializeL4(cloud *gce.Cloud, recorderFactory events.RecorderProducer) { | ||
if instance == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if instance != nil {
log.Error double init
return
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error, not warning?
ok, done error
pkg/healthchecks/healthchecks_l4.go
Outdated
// If the healthcheck already exists, it is updated as needed. | ||
func EnsureL4HealthCheck(cloud *gce.Cloud, svc *corev1.Service, namer namer.L4ResourcesNamer, sharedHC bool, scope meta.KeyType, l4Type utils.L4LBType) (string, string, int32, string, error) { | ||
func (l4hc *l4HealthChecks) EnsureL4HealthCheck(svc *corev1.Service, namer namer.L4ResourcesNamer, sharedHC bool, scope meta.KeyType, l4Type utils.L4LBType, nodeNames []string) (string, string, string, string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not a huge change, we should change the return value to a struct with named fields.
type EnsureL4HealthCheckResult struct {
HCLink string
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
but to declare the return struct I must have moved the interfaces to the to the provider package instead of client package to avoid circular dependencies
hcPath, hcPort = helpers.GetServiceHealthCheckPathPort(svc) | ||
hcPath, hcPort := helpers.GetServiceHealthCheckPathPort(svc) | ||
if sharedHC { | ||
hcPath, hcPort = gce.GetNodesHealthCheckPath(), gce.GetNodesHealthCheckPort() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should log.V(3).Infof or higher
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
return "", nil | ||
} | ||
|
||
func (l4hc *l4HealthChecks) healthCheckFirewallSafeToDelete(hcName string, sharedHC bool, l4Type utils.L4LBType) (bool, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should name this func closer to what it indicates rather than the interpreted meaning.
isn't this just healthcheckExists()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is actually inversed
if healthcheck exists then the function will return false
also it always returns true for shared healthchecks
maybe: healthCheckFirewallInUse ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but it still inverst entire logic
pkg/healthchecks/healthchecks_l4.go
Outdated
|
||
func (l4hc *l4HealthChecks) deleteFirewall(name string, svc *corev1.Service) error { | ||
err := firewalls.EnsureL4FirewallRuleDeleted(l4hc.cloud, name) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if err == nil { return nil }
log.Error the error
Also, we should have a comment saying why we ignore the FIrewallXPNError
should we use err.As() instead of the type case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added comment
Log is written inside firewalls.EnsureL4FirewallRuleDeleted for XPN firewall rules errors
The type cast code has been moved from l4.go/l4netlb.go without changing the logic, therefore I am hesitant to change to errors.As(...)
pkg/loadbalancers/interfaces.go
Outdated
// L4HealthChecks defines methods for creating and deleting health checks (and their firewall rules) for l4 services | ||
type L4HealthChecks interface { | ||
// EnsureL4HealthCheck creates health check (and firewall rule) for l4 service | ||
EnsureL4HealthCheck(svc *corev1.Service, namer namer.L4ResourcesNamer, sharedHC bool, scope meta.KeyType, l4Type utils.L4LBType, nodeNames []string) (string, string, string, string, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comment about this return type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
but to declare the struct I must have moved the interfaces to the to the provider package instead of client package to avoid circular dependencies
err := firewalls.EnsureL4FirewallRuleDeleted(l.cloud, name) | ||
if err != nil { | ||
if fwErr, ok := err.(*firewalls.FirewallXPNError); ok { | ||
l.recorder.Eventf(l.Service, corev1.EventTypeNormal, "XPN", fwErr.Message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably log info here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the info is logged in the firewalls.EnsureL4FirewallRuleDeleted if XPN error occurs
result.Error = err | ||
} | ||
result.GCEResourceInError = resourceInError | ||
result.Error = err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to break if we get an error from this loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, customer might have updated the service from ExternalTrafficPolicy=Local to ExternalTrafficPolicy=Cluster, and now they are deleting the service.
We do not try to cleanup firewall rules during ExternalTrafficPolicy change, so we must cleanup here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
even if shared HC deletion failed there stil may be non shared HC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add a comment describing why
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added more comment line here
there is also full rationale in the comments just before the for loop
eccf309
to
abf0c05
Compare
@bowei |
pkg/firewalls/firewalls_l4.go
Outdated
} | ||
} | ||
|
||
srcAndTargetEqual := utils.EqualStringSets(a.SourceRanges, b.SourceRanges) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's easier to maintain if we make this straight line code:
if ! utils.EqualStringSets() { return false }
if ! utils.EqualStringSets() { return false }
if ! skipDescription && a.Description != b.Description {
return false
}
return true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// instance is a singleton instance, created by InitializeL4 | ||
instance *l4HealthChecks | ||
// instanceLock to prevent duplicate initialization. | ||
instanceLock = &sync.Mutex{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go style puts the mutex in the same block as the protected var at the top:
struct {
lock
var
var
nonProtectedVar
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
pkg/healthchecks/healthchecks_l4.go
Outdated
) | ||
|
||
type l4HealthChecks struct { | ||
// Lock access to shared resources - node healthcecks and their firewall rules |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be more explicit:
// sharedResourceLock serializes operations on the healthcheck and firewall
// resources shared across multiple Services.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
pkg/healthchecks/healthchecks_l4.go
Outdated
return | ||
} | ||
|
||
instanceLock.Lock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to put this first, no? the instance != nil needs to be guarded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually the singleton has 2 checks,
- to only lock the mutex if the instance is not initialised
and - to make sure that instance was not initialised between first check and lock acquisition.
The code used to look like this:
if instance == nil {
initLock.Lock()
defer initLock.Unlock()
if instance == nil {
instance = &l4HealthChecks{
cloud: cloud,
recorderFactory: recorderFactory,
}
}
}
in the comnent #1705 (comment)
you asked me to log double init attempts
so I changed the above code into
if instance != nil {
klog.Error("Multiple L4 Healthchecks initialization attempts")
return
}
instanceLock.Lock()
defer instanceLock.Unlock()
if instance == nil {
instance = &l4HealthChecks{
cloud: cloud,
recorderFactory: recorderFactory,
}
klog.V(3).Infof("Initialized L4 Healthchecks")
}
which is equivalent of double checked locking.
I can just lock out entire function,
instanceLock.Lock()
defer instanceLock.Unlock()
if instance != nil {
klog.Error("Multiple L4 Healthchecks initialization attempts")
return
}
instance = &l4HealthChecks{
cloud: cloud,
recorderFactory: recorderFactory,
}
klog.V(3).Infof("Initialized L4 Healthchecks")
as InitializeL4 is only called once (not in every healthchecks.GetL4() call )
are you ok with the last option?
pkg/healthchecks/healthchecks_l4.go
Outdated
instanceLock.Lock() | ||
defer instanceLock.Unlock() | ||
|
||
if instance == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you don't need this if
any more
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please see the previous comment
if err != nil { | ||
return false, fmt.Errorf("Failed to create composite key for healthcheck %s - %w", hcName, err) | ||
} | ||
_, err = composite.GetHealthCheck(l4hc.cloud, key, meta.VersionGA) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need to take the lock here to avoid something creating in parallel to the deletion?
This has a lock read unlock; A ; lock write unlock issue -- there might be some operation occurring at A that is a race.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, I don't get this race issue here
Could you please elaborate?
please note that EnsureL4HealthCheck and DeleteHealthCheck execute
l4hc.sharedResourcesLock.Lock()
very early before any API calls
so
between healthCheckFirewallSafeToDelete call and actual deletion, the sharedResourcesLock is still possessed by the same thread
abf0c05
to
d65a8ca
Compare
- create a common singleton-like struct fot l4 health checks - new struct holds mutex for common resources (healthchecks and their firewall rules used for - [bugfix] delete shared healtcheck firewall rules safely - cross-check between ILB and NLB healthchecks is firewall rules are in use Logging New log line idicating firewall rule not deleted due to cross-check: "Failed to delete health check firewall rule %s: health check in use." Testing - healthcheck management is mostly covered (by existing tests), they required little update. - added test cases for sahred firewall rule deletion(lack of), named TestHealthCheckFirewallDeletionWithILB and TestHealthCheckFirewallDeletionWithNetLB - run test manual tests
d65a8ca
to
217b108
Compare
217b108
to
91c10b1
Compare
/test pull-ingress-gce-test |
EnsureL4HealthCheck: replace lengthy return value list in with named struct Improve firewall rule comparison Added debug logs Improved go fmt Renamed function names And many more small ones
91c10b1
to
f8353dc
Compare
HCName string | ||
HCLink string | ||
HCFirewallRuleName string | ||
GceResourceInError string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's rename this to GCEResourceInError
/approve |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: bowei, cezarygerard, kl52752 The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
without proper guarding of shared healtcheck firewall rules, the NetLB controller may delete the ILB healthcheck firewall or vice versa