-
Notifications
You must be signed in to change notification settings - Fork 1.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
Add validation webhook for service profiles #2623
Conversation
Integration test results for 956d8f3: success 🎉 |
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 webhook package is really slick! Most of my feedback is questions to help my own understanding of how this all works. Thanks!
} | ||
return admissionReview | ||
} | ||
log.Infof("received admission review request %s", admissionReview.Request.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.
consider logging the namespace/name rather than UID as a more user-friendly message.
curiously when i turned on debug logging, i wasn't seeing the name in the debug output, any idea what's up?
$ bin/linkerd logs --control-plane-component controller --container sp-validator
...
linkerd linkerd-controller-c689f4fd9-hpqf6 sp-validator time="2019-04-03T01:22:47Z" level=debug msg="admission request: &AdmissionRequest{UID:fd3716f1-55ae-11e9-adbd-025000000001,Kind:linkerd.io/v1alpha1, Kind=ServiceProfile,Resource:linkerd.io/v1alpha1, Resource=serviceprofiles,SubResource:,Name:,Namespace:linkerd,Operation:CREATE,UserInfo:k8s_io_api_authentication_v1.UserInfo{Username:docker-for-desktop,UID:,Groups:[system:masters system:authenticated],Extra:map[string]ExtraValue{},},Object:k8s_io_apimachinery_pkg_runtime.RawExtension{Raw:*[123
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've found that Request.Name
is always sent empty by k8s. From their godoc:
// Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and
// rely on the server to generate the name. If that is the case, this method will return the empty string.
// +optional
Name string `json:"name,omitempty" protobuf:"bytes,5,opt,name=name"`
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.
😮
controller/webhook/launcher.go
Outdated
} | ||
log.Infof("created webhook configuration: %s", selfLink) | ||
|
||
s, err := NewServer(k8sAPI, *addr, config.WebhookServiceName, *controllerNamespace, rootCA, config.Handler) |
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 config.Handler
only exists to get passed through Launch()
into NewServer()
, consider just making it a separate param on Launch()
. similar question for MetricsPort
and others.
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 kinda did that to avoid having to pass so many arguments, but I think I can reach a compromise in my next push 👍
// Start starts the https server | ||
func (s *Server) Start() { | ||
log.Infof("listening at %s", s.Server.Addr) | ||
if err := s.ListenAndServeTLS("", ""); 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.
is ListenAndServeTLS
eventually going to take params?
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.
Nope. The certs are added in s
's setup above in the NewServer
function.
What happens when |
I think our options are:
|
I like this idea in general. The controller pod is getting a little large and hard to reason about. Are there any downsides? |
+1 to #2628 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.
Some thoughts around the structure of the code. (I will review the webhook handlers separately, but looking good so far).
Also, instead of having the different proxy-injector
and sp-validator
packages with different webhook.go
files and a separate webhook
package, how about just put everything into the webhook
package and name the different webhook files after their component? I see the case for a separate webhook
package only if it's a lib in pkg
.
Something like:
controller/webhook/proxy_injector.go
controller/webhook/proxy_injector_ops.go
controller/webhook/sp_validator.go
controller/webhook/sp_validator_ops.go
controller/webhook/tmpl
if err := s.Shutdown(); err != nil { | ||
log.Error(err) | ||
} | ||
webhook.Launch(&webhook.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.
I find it hard to follow (and sometimes write tests for) objects that are half-created. IIUC, the other half of the webhook.Config
is in the launcher. I know there is a bit of a trade-off here, but personally, I'm ok with duplicating the flags code if it helps to make things clearer.
TIOLI: Along with my comment below on removing the ConfigOps
interface and Ops
struct, this part can look like:
// configure and parse flags code
// ...
config := NewMWC() // or config := NewVWC()
webhook.Launch(config)
) | ||
|
||
// Ops satisfies the ConfigOps interface for managing MutatingWebhook configs | ||
type Ops struct{} |
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.
TIOLI: I wonder if we can reduce the amount of code by just creating a MWC and a VWC, and not have the ConfigOps
interface and Ops
struct.
What about this?
type MWC struct {
// fields
}
func NewMWC(/* args */) *MWC { /* code */ }
func (m *MWC) Create() { /* code /* }
func (m *MWC) Get() { /* code */ }
func (m *MWC) Delete() { /* code */ }
And the same for the VWC.
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 if we move all the code into one package as suggested above, we don't need an ConfigOps
interface.
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 webhook handlers LGTM. Just two comments on the admission response and VWC.
If we ever made the proxy injector a required component, we can consider having just one admission controller webhook (and rename the proxy injector to a more generic admission controller or something). The k8s repo has a similar example with one webhook, but multiple handlers. |
@ihcsim is there a reason to keep them separate right now? Should we write up an issue to fix that in the 2.4 cycle? |
Right now, the proxy injector webhook is an optional component. AFAIK, this validating webhook is required. I can create an issue for it in 2.4. |
The webhook feels like a nice to have (though with minimal impact), I can go either way. |
@ihcsim thanks for your reviews, I'm still going through them. One comment though:
If we go down the route of having separate deployments for the proxy-injector and the validator, it would make sense to keep them under different directories under |
I don't think the kind of workloads we deploy to k8s should have any inference on the code packaging structure. Per stand-up, let's make this a TIOLI. |
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, this approach looks good to me overall, but I suspect it will change somewhat once you rebase against master and move the profile validator to a separate deployment. Will give it a more thorough look once that's pushed.
956d8f3
to
b7f7baf
Compare
Integration test results for 2ecb804: fail 😕 |
2ecb804
to
5998454
Compare
Integration test results for 5998454: success 🎉 |
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.
Update looks great! Just had a few bits of miscellaneous feedback, but otherwise looks good to me.
chart/templates/proxy_injector.yaml
Outdated
@@ -78,6 +78,7 @@ rules: | |||
- apiGroups: [""] | |||
resources: ["namespaces"] | |||
verbs: ["list", "get", "watch"] | |||
# TODO: this is for creating the webhook config. Can be removed when #2176 lands. |
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.
Does this comment belong on line 75, above the privs for "mutatingwebhookconfigurations"? This comment also gets printed in the install YAML that the CLI emits, so I'd call it semi-user-facing. I'm inclined to omit TODOs from our YAML files, unless you feel strongly that we need them.
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 actually belongs in both places. The pod listing ability is needed more precisely because of #2559.
I tried using Helm comments ({{- /* comment */ -}}
) but for some reason that didn't fly. I'll remove anyways these comments and I'll make a note instead 😉
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.
looking good! a few more comments...
Add sp-validator
here (note that proxy-injector
is added dynamically when --proxy-auto-inject
is set):
Lines 36 to 50 in a3bba0e
linkerdSvcs = []string{ | |
"linkerd-controller-api", | |
"linkerd-destination", | |
"linkerd-grafana", | |
"linkerd-identity", | |
"linkerd-prometheus", | |
"linkerd-web", | |
} | |
linkerdDeployReplicas = map[string]deploySpec{ | |
"linkerd-controller": {1, []string{"destination", "public-api", "tap"}}, | |
"linkerd-grafana": {1, []string{}}, | |
"linkerd-identity": {1, []string{"identity"}}, | |
"linkerd-prometheus": {1, []string{}}, | |
"linkerd-web": {1, []string{"web"}}, |
} | ||
|
||
if len(data) == 0 { | ||
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.
is this an error or some condition that should be logged?
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 believe it's just a sanity check against empty payloads. I'll add some logging.
2a6379e
to
d2acce5
Compare
Alright, thanks for all the feedback 🙏 |
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 great, thanks for making those updates!
Fixes #2075 (This supersedes #2508 because it was easier to redo this in master than rebasing that PR. It addresses all the feedback received in #2508) The new webhook endpoint is created in a new container in the linkerd-controller pod. The proxy injector webhook was refactored to extract-out the scaffolding to be reused by the validation webhook (and eventually by any other admission webhooks we'd like to create in the future). The new common stuff now reside under controller/webhook. The validation webhook simply sets up the endpoint server and the webhook config using pkg/webhook, and calls the old profiles.Validate to perform the actual validation. I Also got rid of the unused -no-init-container flag in the proxy injector. Todo in a follow-up PRs: remove the SP check from the CLI check. Signed-off-by: Alejandro Pedraza <[email protected]>
Signed-off-by: Alejandro Pedraza <[email protected]>
Signed-off-by: Alejandro Pedraza <[email protected]>
Signed-off-by: Alejandro Pedraza <[email protected]>
Signed-off-by: Alejandro Pedraza <[email protected]>
Signed-off-by: Alejandro Pedraza <[email protected]>
Signed-off-by: Alejandro Pedraza <[email protected]>
833a167
to
bf35a14
Compare
Integration test results for bf35a14: success 🎉 |
Add validation webhook for service profiles Fixes linkerd#2075 Todo in a follow-up PRs: remove the SP check from the CLI check. Signed-off-by: Alejandro Pedraza <[email protected]> Signed-off-by: [email protected] <[email protected]>
Fixes #2075
(This supersedes #2508 because it was easier to redo this in master than
rebasing that PR. It addresses all the feedback received in #2508)
The new webhook endpoint is created in a new container in the
linkerd-controller pod.
The proxy injector webhook was refactored to extract-out the scaffolding
to be reused by the validation webhook (and eventually by any other
admission webhooks we'd like to create in the future). The new common
stuff now reside under controller/webhook.
The validation webhook simply sets up the endpoint server and the
webhook config using pkg/webhook, and calls the old
profiles.Validate to perform the actual validation.
I Also got rid of the unused -no-init-container flag in the proxy injector.
Todo in a follow-up PRs: remove the SP check from the CLI check.