-
Notifications
You must be signed in to change notification settings - Fork 326
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
Add acls and tls to endpoints controller #470
Add acls and tls to endpoints controller #470
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.
Nice work Kyle!! I just had a few naming comments and small things. I think it makes sense that just TestReconcileUpdateEndpoint has the ACL/TLS tests since they cover the cases a client is created, I just added more to the comment on TestReconcileUpdateEndpoint to reflect that. I wish there was a way to have consistency with Create/Delete so you don't need to have context about why those tests are only for Update but I can't think of another way, so it makes sense to keep it the way it is. Maybe the next reviewer will think of something :)
review comments Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
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 like the idea of trying to get in here and test this. I thought it was a bit confusing as implemented because there's a number of things involved in getting clients:
EndpointsController.ConsulClient
EndpointsController.GetClient
We're only mocking out one of these.
I messed around with making ConsulClientCfg
a field:
EndpointsController {
ConsulClient *api.Client
ConsulClientCfg *api.Config
}
And then having GetConsulClient
(which should probably be renamed to something like RemoteConsulClient
or ExternalConsulClient
) use r.ConsulClientCfg
when generating its client config:
// GetConsulClient returns an *api.Client that points at the consul agent local to the pod.
func (r *EndpointsController) GetConsulClient(scheme, port, ip string) (*api.Client, error) {
newAddr := fmt.Sprintf("%s://%s:%s", scheme, ip, port)
localConfig := r.ConsulClientCfg
localConfig.Address = newAddr
return consul.NewClient(localConfig)
}
Then in the tests you can pass in ConsulClientCfg: cfg
which in most places is already generated. Thanks to you setting the IP of the pod to 127.0.0.1
, this will then work.
I think this approach might be better because:
- less confusion about clients and getting clients and what the difference between the two is
- requires less work in tests since you don't need to generate a mock closure, just pass in the cfg you already needed to create the client
- It's "less powerful" because it doesn't involve a closure and less powerful mocking is preferred due to simplicity
Not dead set on it though so interested in hearing why the current approach was taken.
addr := strings.Split(consul.HTTPSAddr, ":") | ||
consulPort := addr[1] | ||
|
||
ce, _ := api.MakeConfigEntry(api.ProxyDefaults, "pd") |
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.
ce, _ := api.MakeConfigEntry(api.ProxyDefaults, "pd") | |
ce, _ := api.MakeConfigEntry(api.ProxyDefaults, "global") |
I don't know how this worked because inside processUpstreams we look for a proxy defaults with name global
. I think somewhere when you call ConfigEntries().Set
it's ignoring the pd
and using global
but may as well set it to global
here to avoid confusion.
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.
Good catch on this. Not sure how it worked either..
Current approach was really just taken because I couldnt think of what you suggested, I stared at the code too long after running into issues with GetConsulClient() overwriting the token! |
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.
looks good, just have important question about which cfg
we use in command.go
.
subcommand/inject-connect/command.go
Outdated
@@ -418,6 +418,7 @@ func (c *Command) Run(args []string) int { | |||
ReleaseName: c.flagReleaseName, | |||
ReleaseNamespace: c.flagReleaseNamespace, | |||
Context: ctx, | |||
ConsulClientCfg: api.DefaultConfig(), |
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 we may need to use cfg
here because there's a bunch of code above that is doing stuff to it after it gets it out of api.DefaultConfig()
:
// create Consul API config object
cfg := api.DefaultConfig()
c.http.MergeOntoConfig(cfg)
if cfg.TLSConfig.CAFile == "" && c.flagConsulCACert != "" {
cfg.TLSConfig.CAFile = c.flagConsulCACert
}
consulURLRaw := cfg.Address
// cfg.Address may or may not be prefixed with scheme.
if !strings.Contains(cfg.Address, "://") {
consulURLRaw = fmt.Sprintf("%s://%s", cfg.Scheme, cfg.Address)
}
consulURL, err := url.Parse(consulURLRaw)
if err != nil {
c.UI.Error(fmt.Sprintf("error parsing consul address %q: %s", consulURLRaw, err))
return 1
}
// load CA file contents
var consulCACert []byte
if cfg.TLSConfig.CAFile != "" {
var err error
consulCACert, err = ioutil.ReadFile(cfg.TLSConfig.CAFile)
if err != nil {
c.UI.Error(fmt.Sprintf("error reading Consul's CA cert file %q: %s", cfg.TLSConfig.CAFile, err))
return 1
}
}
I haven't tested this out in an acceptance test but I'm guessing we need to use that config?
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 good catch. At runtime it worked for me deployed on a cluster as-is, but we definitely can use cfg instead here.
I believe api.DefaultConfig()
wouldn't have picked up flag overrides on command line, thx!
Oops, I see you requested we run in acceptance test. I haven't done that so will do so as 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.
I love the new approach, and this looks great to me! I ran the TestConnectInject acceptance test as well with secure:true and they pass 🎉 awesome work!!
@@ -413,7 +415,7 @@ func (r *EndpointsController) processUpstreams(pod corev1.Pod) ([]api.Upstream, | |||
// getConsulClient returns an *api.Client that points at the consul agent local to the pod. | |||
func (r *EndpointsController) getConsulClient(ip string) (*api.Client, 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 liked Luke's suggestion from here to name this remoteConsulClient
or externalConsulClient
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.
remoteConsulClient
looks great, switching to that, thanks!
Add support for TLS and ACLs to the endpoints controller.
Changes proposed in this PR:
How I've tested this PR:
Unit tests for ACLS+TLS have been added which cover normal paths as well as the three places that where we request a new consulClient:
Manually run the connect-inject acceptance tests using hashicorp/consul-helm#886, with
imageK8S: "kschoche/consul-k8s-dev"
and uncommenting the TLS table tests.How I expect reviewers to test this PR:
Run unit tests / manually deploy and run acceptance tests.
Note: I'm willing to table-fy and add ACL/TLS support to all of the endpoints-controller tests if we think this is really necessary.
I think the tests which use the consul client (
TestReconcileDeleteEndpoint()
,TestReconcileUpdateEndpoint()
) where I have NOT added ACL/TLS tests are already covered by the the tests I added. I'm willing to make changes but for the sake of readability I didn't want to unnecessarily add complexity to the already long+awesome tests. Thoughts?Checklist: