-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Persist proxies from config files #4407
Conversation
f19baaa
to
1ff43ba
Compare
To be clear for other readers, that is the same issue that occurs now - if you update a config-file registration via API it's new config will survive until agent reloads. After reload, agent will see both states and will treat the config file is authoritative so effectively "undoes" the changes made via the API. I think that edge case is arguably the desired behaviour but might confuse someone. In practice you should choose one of files or API for registering a particular instance so this is moot now and after this PR. |
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 looks great!
Mostly nit picks, one thing that seems worth changing to not end up in a worse state than now in case snapshot files are corrupt.
agent/agent.go
Outdated
@@ -208,6 +208,9 @@ type Agent struct { | |||
|
|||
// proxyManager is the proxy process manager for managed Connect proxies. | |||
proxyManager *proxy.Manager | |||
|
|||
// Protects proxy information in the local state from concurrent modification |
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.
nit: idiomatic go comments always start with the name of the variable or function being documented. Even though this is private and so will never show up in Godoc, it seems better to stick to the convention unless it's really awkward for some reason.
In this case it's trivial to add // proxyLock protects proxy...
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 never knew that was a thing. I will update it.
agent/agent.go
Outdated
@@ -1616,16 +1619,18 @@ func (a *Agent) purgeService(serviceID string) error { | |||
type persistedProxy struct { | |||
ProxyToken string | |||
Proxy *structs.ConnectManagedProxy | |||
FromConfig bool |
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.
bikeshed: is it clearer to say FromFile
? Technically "config" is also provided via the API, the distinction here is really the source (file vs API).
agent/agent.go
Outdated
@@ -1616,16 +1619,18 @@ func (a *Agent) purgeService(serviceID string) error { | |||
type persistedProxy struct { | |||
ProxyToken string | |||
Proxy *structs.ConnectManagedProxy | |||
FromConfig bool | |||
} | |||
|
|||
// persistProxy saves a proxy definition to a JSON file in the data dir |
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.
nit: document the new param semantics.
agent/agent.go
Outdated
} | ||
return nil | ||
} | ||
|
||
func (a *Agent) AddProxy(proxy *structs.ConnectManagedProxy, persist, fromConfig bool, |
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.
nit: go vet will complain a public function has no doc comment. Maybe just copy paste the old one here?
agent/agent.go
Outdated
@@ -2316,6 +2332,12 @@ func (a *Agent) RemoveProxy(proxyID string, persist bool) error { | |||
return nil | |||
} | |||
|
|||
func (a *Agent) RemoveProxy(proxyID string, persist 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.
ditto
agent/agent.go
Outdated
if err != nil { | ||
if !os.IsNotExist(err) { | ||
return fmt.Errorf("Failed reading proxies dir %q: %s", proxyDir, 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.
nit: it's more idiomatic in go to prefer to return in the branch rather than indent the rest of the method in an else
. We could just return nil here and then remove the else below.
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 forgot for a moment you can do a range on a nil slice and it will do nothing. Otherwise the else would be necessary to prevent that as we wouldn't return for an IsNotExist 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.
Ah true, I thought all of the rest of the method was guarded by else
so return nil here was fine but yeah all good 😄
agent/agent.go
Outdated
buf, err := ioutil.ReadAll(fh) | ||
fh.Close() | ||
if err != nil { | ||
return fmt.Errorf("failed reading proxy file %q: %s", file, 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.
Interesting thought: these failures have changed with this refactor. Before if a snapshot file was corrupt or broken in some way, the proxies from config would still be loaded and just the API persisted ones after the failing one in the dir list would fail to load.
I don't think that was strongly principled but it's now the case that a corrupt snapshot file prevents us from loading any proxies at all even from config...
Maybe we should move this whole loop out into another method and then still continue if it errors but log the error. That way we can still load proxies from config file as before even if the state files are jank. (and then we'd blow away the state which "fixes" the problem with the caveat that API registered stuff might dissappear. That at least is the same as now though not worse.
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.
Moving it out isn't really necessary. Instead I can just change the return fmt.Errorf into a.logger.Printf and all will be well.
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 but you need to stop the current loop iteration too. Return and separate method seems clean to me just like you did 👍
Also change how loadProxies works. Now it will load all persisted proxies into a map, then when loading config file proxies will look up the previous proxy token in that 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.
Great 🎉
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.
Hmm the test failures actually look legit:
--- FAIL: TestAgent_PurgeProxyOnDuplicate (0.03s)
agent_test.go:1527:
Error Trace: agent_test.go:1527
Error: An error is expected but got nil.
Test: TestAgent_PurgeProxyOnDuplicate
Messages: should have removed remote state
--- FAIL: TestAgent_ReLoadProxiesFromConfig (0.19s)
agent_test.go:2890:
Error Trace: agent_test.go:2890
Error: "map[web-proxy:%!s(*local.ManagedProxy=&{0xc009de59c0 9336b181-3b34-a340-b656-a90f3ae3a186 0xc009dc8300})]" should have 0 item(s), but has 1
Test: TestAgent_ReLoadProxiesFromConfig
Can you check those out?
So they do. Apparently my |
@banks Fortunately it was the tests that needed updating. Serves me right for trying to use two wildcards in a test name match. |
Locally ran |
FWIW I think Yep:
|
Also change how loadProxies works. Now it will load all persisted proxies into a map, then when loading config file proxies will look up the previous proxy token in that map.
The new test is actually testing that proxy tokens are saved/restored between unloading/reloading of the proxy configuration (happens during a restart or during a online config reload). Additionally its also testing that removing a proxy from the config will remove it from what gets persisted to disk and wont get reload the next time the proxies are read from disk.
Additionally I tested manually with running a config file managed proxy, performing online reloads and restarting the process. It no longer kills the existing proxy and restarts it.
Some behavior that is currently still in there is that proxy configurations can be overwritten for config-file based proxies via the API. During the next reload the configuration will be reloaded from the config and the API registration will be effectively gone.
There is a edge case where you have a proxy in the config, do an online register via the API, remove it from the config and when you reload/restart the API proxy is still valid and gets loaded properly. I don't think this is incorrect, just behavior that might not be immediately apparent.