MutatingWebHookConfiguration for GameServer creation & Validation.#95
MutatingWebHookConfiguration for GameServer creation & Validation.#95markmandel merged 1 commit intomasterfrom
Conversation
|
/cc @Kuqd - you might find this interesting as well. I implemented a 1 rule server side validation on a GameServer, to provide an example: As an aside - if anyone wants to understand how the |
|
Interesting I ´ll have look |
7a3c34d to
af43e71
Compare
enocom
left a comment
There was a problem hiding this comment.
Mostly small little things. Otherwise, LGTM.
pkg/apis/stable/v1alpha1/types.go
Outdated
| // Validate validates the GameServer configuration. | ||
| // If a GameServer is invalid there will be > 0 values in | ||
| // the returned array | ||
| func (gs *GameServer) Validate() []metav1.StatusCause { |
There was a problem hiding this comment.
The callers to Validate will have to check the length of the returned slice to determine if the GameServer has valid configuration. Instead, I think it might be nicer to return (bool, []metav1.StatusCause), so callers could simple check an ok status, and only then look at the slice if necessary, e.g.:
if ok, causes := gs.Validate(); !ok {
// do something with causes here
}
What do you think?
There was a problem hiding this comment.
I like this idea - it's far more explicit. I'll make this change.
pkg/gameservers/controller.go
Outdated
| // This is the main logic of this function | ||
| // the rest is really just json plumbing | ||
| gs.ApplyDefaults() | ||
| causes := gs.Validate() |
There was a problem hiding this comment.
Again, an ok value in addition to causes would be nice.
| apiGroup: rbac.authorization.k8s.io | ||
| kind: ClusterRole | ||
| name: agones-controller | ||
| name: agones-controller No newline at end of file |
There was a problem hiding this comment.
I noticed that in Gopkg.toml as well.
There was a problem hiding this comment.
Want to do this as a separate PR? Sounds like you have much more experience with this than I do.
But this sounds like a great idea.
There was a problem hiding this comment.
Yeah, sure. I'll pick it up. 👍
| logrus.WithError(err).Fatal("Could not get executable path") | ||
| } | ||
|
|
||
| base := filepath.Dir(exec) |
There was a problem hiding this comment.
This is a cool trick. Haven't see this before. 👍
pkg/gameservers/controller.go
Outdated
| return review, nil | ||
| } | ||
|
|
||
| new, err := json.Marshal(gs) |
There was a problem hiding this comment.
Since new is a built-in function, maybe newGS would be better here.
There was a problem hiding this comment.
You are righ! Will change.
| }) | ||
| } | ||
| logrus.WithField("path", path).WithField("groupKind", gk).WithField("op", op).Info("Added webhook handler") | ||
| wh.handlers[path] = append(wh.handlers[path], operationHandler{groupKind: gk, operation: op, handler: h}) |
There was a problem hiding this comment.
Does this need to be thread-safe?
There was a problem hiding this comment.
Interesting question. It shouldn't have to be, since controllers (will) get created in sequence, and not at the same time. But worth keeping an eye on.
| c := gameservers.NewController(minPort, maxPort, sidecarImage, alwaysPullSidecar, kubeClient, kubeInformationFactory, extClient, agonesClient, agonesInformerFactory) | ||
|
|
||
| wh := webhooks.NewWebHook(certFile, keyFile) | ||
| c := gameservers.NewController(wh, minPort, maxPort, sidecarImage, alwaysPullSidecar, kubeClient, kubeInformationFactory, extClient, agonesClient, agonesInformerFactory) |
There was a problem hiding this comment.
Not necessarily an issue for this CL, but is there precedent in Go to support the Builder paradigm? Whenever I have to look at adding a param to this constructor it makes me nervous, and a ControllerBuilder might be a way to alleviate potential problems here.
If there is support for this I will create an issue for it.
There was a problem hiding this comment.
We could always do functional options. It's become a standard pattern.
Something like:
gameserves.NewController(minPort, maxPort, gameserves.WithWebHook(wh))
where the last argument to the constructor is variadic.
See here for more.
There was a problem hiding this comment.
I would say for Go, functional options are the more prevalent pattern:
https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
It's an interesting question. I decided not to go that route, since default values are set in the cmd binaries, and it seemed odd to have defaults in both places and keep them in sync.
@enocom what do you think?
There was a problem hiding this comment.
It's a straightforward refactor. Once we hit the 0.1 milestone, I would be interested in revisiting it, especially once I understand Agones better.
There was a problem hiding this comment.
Yeah it's less critical if we don't expect users to be calling this function directly, if all their config are provided as k8s type config. If it was a public API I'd definitely push for it, but with this I could see it either way. Would make it less error prone in the future though.
There was a problem hiding this comment.
stupid question :
Does go recommend a line return for long line of code ?
There was a problem hiding this comment.
Interesting question - from https://golang.org/doc/effective_go.html
Line length
Go has no line length limit. Don't worry about overflowing a punched card. If a line feels too long, wrap it and indent with an extra tab.
That being said, it is fairly common practice to wrap on long lines.
cmd/controller/main.go
Outdated
| pflag.Bool(pullSidecarFlag, viper.GetBool(pullSidecarFlag), "For development purposes, set the sidecar image to have a ImagePullPolicy of Always. Can also use ALWAYS_PULL_SIDECAR env variable") | ||
| pflag.Int32(minPortFlag, 0, "Required. The minimum port that that a GameServer can be allocated to. Can also use MIN_PORT env variable.") | ||
| pflag.Int32(maxPortFlag, 0, "Required. The maximum port that that a GameServer can be allocated to. Can also use MAX_PORT env variable") | ||
| pflag.String(keyFileFlag, viper.GetString(certFileFlag), "Optional. Path to the key file") |
There was a problem hiding this comment.
copy/paste issue :) , should be
pflag.String(keyFileFlag, viper.GetString(keyFileFlag), "Optional. Path to the key file")
| c := gameservers.NewController(minPort, maxPort, sidecarImage, alwaysPullSidecar, kubeClient, kubeInformationFactory, extClient, agonesClient, agonesInformerFactory) | ||
|
|
||
| wh := webhooks.NewWebHook(certFile, keyFile) | ||
| c := gameservers.NewController(wh, minPort, maxPort, sidecarImage, alwaysPullSidecar, kubeClient, kubeInformationFactory, extClient, agonesClient, agonesInformerFactory) |
There was a problem hiding this comment.
stupid question :
Does go recommend a line return for long line of code ?
| } | ||
|
|
||
| logrus.WithField("review", review).Info("Invalid GameServer") | ||
| return review, nil |
There was a problem hiding this comment.
I didn't know you could validate also in the mutating webhook. I thought you would need another webhook.
There was a problem hiding this comment.
So the validation hook runs after the mutation hook (and all the mutations are complete) - but the data structure that comes in and out is the same on both (AdmissionReview). But you can cancel an operation through either. I figured since we're already processing the data, it feels like overkill to do it twice.
There was a problem hiding this comment.
make sense, thanks for clarifying
|
|
||
| err := wh.server.ListenAndServeTLS(wh.certFile, wh.keyFile) | ||
| if err == http.ErrServerClosed { | ||
| logrus.WithError(err).Info("webhook: https server closed") |
There was a problem hiding this comment.
should you return nil here ? the server has been closed by request of the user so you should not return an error.
There was a problem hiding this comment.
oooh! Also good catch!
Webhook library to make k8s webhooks easy(er) to use, as well as setting default values on GameServers via it for when they are first created. Some refactoring of GameServer sync in the controller was required and a new PortAllocation state was created. This is also makes #70 and #10 possible to implement.
af43e71 to
495420e
Compare
Webhook library to make k8s webhooks easy(er) to use, as well as setting default values on GameServers via it for when they are first created.
Some refactoring of GameServer sync in the controller was required and a new PortAllocation state was created.
This is also makes #70 and #10 possible to implement.