-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Extend ConfigMap to store fwrule names #278
Conversation
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.
Made a first pass, I think the structure of the code will change if you have the Get/Put methods take k/v pairs instead of entire maps so I'll wait for you to upload another pr or reply to existing comments before more review.
controllers/gce/main.go
Outdated
} else if err != nil { | ||
glog.Errorf("Failed to reconcile cluster uid %v, currently set to %v", err, existing) | ||
// First look for changes in firewallConfigMap. | ||
vaultmap, found, err := uid_vault.Get() |
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 avoid "_" in names, https://golang.org/doc/effective_go.html#mixed-cap
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
controllers/gce/main.go
Outdated
// First look for changes in firewallConfigMap. | ||
vaultmap, found, err := uid_vault.Get() | ||
if !found || err != nil { | ||
glog.Errorf("Can't read uidConfigMap %v", uidConfigMapName) |
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 eat this error insted of logging it?
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
@@ -50,46 +46,59 @@ type ConfigMapVault struct { | |||
// If this method returns an error, it's guaranteed to be apiserver flake. | |||
// If the error is a not found error it sets the boolean to false and | |||
// returns and error of nil instead. | |||
func (c *ConfigMapVault) Get() (string, bool, error) { | |||
func (c *ConfigMapVault) Get() (map[string]string, 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.
The problem with returning maps is the following:
package main
import (
"fmt"
"sync"
"time"
)
type mapStore struct {
mapValue map[string]string
l sync.Mutex
}
func (m *mapStore) getMap() map[string]string {
m.l.Lock()
defer m.l.Unlock()
return m.mapValue
}
func (m *mapStore) set(k, v string) {
m.l.Lock()
defer m.l.Unlock()
m.mapValue[k] = v
}
func main() {
m := mapStore{mapValue: map[string]string{"foo": "bar"}}
retrievedMap := m.getMap()
var wg sync.WaitGroup
wg.Add(1)
go func() {
for i := 0; i < 1000; i++ {
fmt.Printf("%v", retrievedMap)
}
wg.Done()
}()
for i := 0; i < 1000; i++ {
m.set("foo", fmt.Sprintf("%d", i))
time.Sleep(10)
}
wg.Wait()
fmt.Printf("%v\n", retrievedMap)
}
running with go run -race main.go
reveals a race. Even if the race is not going to impact the actual program, go will complain about it in CI and cause tests to fail, and every programmer reading this code will probably need to convince themselves it's not racy.
Suggest passing the key down into this func, retrieving the string value under a lock and returning the 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.
Thanks for the example -- I think the summary is just that we'll be issuing more configmap reads but won't be exposing the map which I think per go is fundamentally not threadsafe.
// Put stores the given UID in the cluster config map. | ||
func (c *ConfigMapVault) Put(uid string) error { | ||
// Put stores the given UID and EUID in the cluster config map. | ||
func (c *ConfigMapVault) Put(keyvals map[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.
Same thing here, suggest Put(k, v string)
if possible, to reduce the risk of a map clobber
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.
map was not being clobbered but we are moving to key->val puts.
"testing" | ||
|
||
"k8s.io/kubernetes/pkg/api" | ||
) | ||
|
||
func TestConfigMapUID(t *testing.T) { | ||
vault := NewFakeConfigMapVault(api.NamespaceSystem, "ingress-uid") | ||
uid := "" | ||
k, exists, err := vault.Get() | ||
//uid := "" |
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 is this commendte out?
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
controllers/gce/utils/utils.go
Outdated
func (n *Namer) GetFirewallName() string { | ||
n.nameLock.Lock() | ||
defer n.nameLock.Unlock() | ||
return n.firewallName |
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.
Suggest defaulting this to clusterName if firewallName is unset, so we don't inadvertently break non-fed deployments by changing the name of the firewall and leaking it.
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.
updated_needed := false | ||
|
||
// Dump everything from keyvals into data. | ||
for key, val := range keyvals { |
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.
Yeah batch storing k,v pairs makes it harder to reason about consistency. Ideally the call sites that care about the fw name should only get/update the one key. That way, if there's a conflict, we know we own that keyspace.
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.
ack
} | ||
|
||
// Put stores the given UID in the cluster config map. | ||
func (c *ConfigMapVault) Put(uid string) error { | ||
// Put stores the given UID and EUID in the cluster config map. |
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 comment conflicts with the code a little, i.e we take a random map of any values not just UID and EUID right?
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.
right, fixed.
// Dump everything from keyvals into data. | ||
for key, val := range keyvals { | ||
if val_stored, ok := data[key]; !ok { | ||
glog.Infof("Configmap %v will be updated with %v = %v", cfgMapKey, key, val) |
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 log line is misleading, it makes me think only the given k/v pair is updated but we actually plug the entire keyval map into the apiObj right?
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.
Right. N/A now with key/val puts
controllers/gce/main.go
Outdated
glog.Infof("Using saved cluster uid %q", existingUID) | ||
return existingUID, nil | ||
} else if err != nil { | ||
if val, found := vaultmap[cm_key]; found { |
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.
s/vaultmap/vaultMap
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
Thanks for the first round of comments -- I've re-pushed the CL. The major differences are key/val-based puts and adding some unit testing. However, I still plan on holding off until I can adequately integrate this with federation and test out the functionality. |
Coverage increased (+0.04%) to 46.014% when pulling c25aebca924449bdcac34326f309a8166c125e2a on csbell:fw-name into 9bba947 on kubernetes:master. |
Kubernetes driver for this is here: kubernetes/kubernetes#41942 PTAL @nicksardo |
controllers/gce/utils/utils.go
Outdated
@@ -103,6 +104,14 @@ func NewNamer(clusterName string) *Namer { | |||
return namer | |||
} | |||
|
|||
// NewNamer creates a new namer with a Firewall Name | |||
func NewNamerWithFirewall(clusterName string, firewallName string) *Namer { |
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.
NewNamer is only used in tests; perhaps we should add the firewall name there instead of creating NewNamerWithFirewall. Also, the first string type can be omitted.
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.
rule information.
controllers/gce/main.go
Outdated
// This is effectively a client override via a command line flag. | ||
// else, check configmap under 'configmap_name' as a key and if found, use the associated value | ||
// else, return an empty 'name' and pass along an error iff the configmap lookup is erroneous. | ||
func getFlagOrLookupVault(cfgVault *storage.ConfigMapVault, cm_key string, name 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.
I'm not a fan of this func name since you're supplying the override and doing nothing flag specific.
Furthermore, I see there's a clusterName flag but not a firewallName flag. Do we want one?
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 name reflects the time where there was a flag fore firewallName. Since talking to beeps, there's no need to introduce a new flag since there will be a new configration mechanism coming up over the next few months that will make flags obsolete.
controllers/gce/main.go
Outdated
glog.Infof("Using saved cluster uid %q", existingUID) | ||
return existingUID, nil | ||
glog.Infof("Using %v = %q saved in ConfigMap", cm_key, val) | ||
return val, nil | ||
} else 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.
This is an master-existing nitpick of mine. Could we reorder the err != nil
before the if found
?
Thank you for fixing it in the newNamer
! https://github.com/kubernetes/ingress/blob/master/controllers/gce/main.go#L269
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, 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.
PTAL.
controllers/gce/main.go
Outdated
// This is effectively a client override via a command line flag. | ||
// else, check configmap under 'configmap_name' as a key and if found, use the associated value | ||
// else, return an empty 'name' and pass along an error iff the configmap lookup is erroneous. | ||
func getFlagOrLookupVault(cfgVault *storage.ConfigMapVault, cm_key string, name 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.
The name reflects the time where there was a flag fore firewallName. Since talking to beeps, there's no need to introduce a new flag since there will be a new configration mechanism coming up over the next few months that will make flags obsolete.
controllers/gce/main.go
Outdated
glog.Infof("Using saved cluster uid %q", existingUID) | ||
return existingUID, nil | ||
glog.Infof("Using %v = %q saved in ConfigMap", cm_key, val) | ||
return val, nil | ||
} else 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.
Yes, done.
controllers/gce/utils/utils.go
Outdated
@@ -103,6 +104,14 @@ func NewNamer(clusterName string) *Namer { | |||
return namer | |||
} | |||
|
|||
// NewNamer creates a new namer with a Firewall Name | |||
func NewNamerWithFirewall(clusterName string, firewallName string) *Namer { |
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.
/lgtm |
@nicksardo: you can't LGTM a PR unless you are an assignee. In response to this comment:
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. I understand the commands that are listed here. |
Need another lgtm |
/lgtm |
With /approve ? |
/approve |
Automatic merge from submit-queue Add ProviderUid support to Federated Ingress This PR (along with GLBC support [here](kubernetes/ingress-nginx#278)) is a proposed fix for #39989. The Ingress controller uses a configMap reconciliation process to ensure that all underlying ingresses agree on a unique UID. This works for all of GLBC's resources except firewalls which need their own cluster-unique UID. This PR introduces a ProviderUid which is maintained and synchronized cross-cluster much like the UID. We chose to derive the ProviderUid from the cluster name (via md5 hash). Testing here is augmented to guarantee that configMaps are adequately propagated prior to Ingress creation. ```release-note Federated Ingress over GCE no longer requires separate firewall rules to be created for each cluster to circumvent flapping firewall health checks. ``` cc @madhusudancs @quinton-hoole
Automatic merge from submit-queue Add ProviderUid support to Federated Ingress This PR (along with GLBC support [here](kubernetes/ingress-nginx#278)) is a proposed fix for #39989. The Ingress controller uses a configMap reconciliation process to ensure that all underlying ingresses agree on a unique UID. This works for all of GLBC's resources except firewalls which need their own cluster-unique UID. This PR introduces a ProviderUid which is maintained and synchronized cross-cluster much like the UID. We chose to derive the ProviderUid from the cluster name (via md5 hash). Testing here is augmented to guarantee that configMaps are adequately propagated prior to Ingress creation. ```release-note Federated Ingress over GCE no longer requires separate firewall rules to be created for each cluster to circumvent flapping firewall health checks. ``` cc @madhusudancs @quinton-hoole
GLBC users can store a providerUid which is used in lieu of Uid when constructing l7 firewall rule names. This can be leveraged to resolve issues seen in Kubernetes #37306.
Backwards compatibility is retained by using Uid for l7 firewall rules unless providerUid is set.