add utility for binding flags and building api server clients#2437
Conversation
There was a problem hiding this comment.
NewDefaultAuthLoader() or var Loader = &defaultAuthLoader{}, but not DefaultAuthLoader
There was a problem hiding this comment.
@smarterclayton Do you know what this "mapping *meta.RESTMapping" is supposed for? Looks like it was a dead parameter even before I started. Was it just cruft of did someone forget to wire it up?
There was a problem hiding this comment.
It's used by Openshift. This implementation does not need it.
On Nov 18, 2014, at 11:15 AM, David Eads notifications@github.com wrote:
In pkg/kubectl/cmd/cmd.go:
@@ -46,11 +49,11 @@ func NewFactory() *Factory {
return &Factory{
Mapper: latest.RESTMapper,
Typer: api.Scheme,
@smarterclayton Do you know what this "mapping *meta.RESTMapping" is supposed for? Looks like it was a dead parameter even before I started. Was it just cruft of did someone forget to wire it up?Client: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {—
Reply to this email directly or view it on GitHub.
|
@smarterclayton comments addressed in "comments round 1" |
There was a problem hiding this comment.
Do people want to put a secret as a flag argument. Seems like that risks exposure via proc or accidental pasting.
The other secrets are in files.
There was a problem hiding this comment.
Do people want to put a secret as a flag argument.
Allowing the token as a command line parameter makes end-to-end testing more convenient. The recommended path would still be to use the _auth file to avoid having secrets in your command history.
There was a problem hiding this comment.
On Nov 18, 2014, at 11:42 AM, Eric Tune notifications@github.com wrote:
In pkg/client/cmd/client_builder.go:
- "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
- "github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
- "github.com/GoogleCloudPlatform/kubernetes/pkg/version"
+)
+const (
- FlagApiServer = "server"
- FlagMatchApiVersion = "match-server-version"
- FlagApiVersion = "api-version"
- FlagAuthPath = "auth-path"
- FlagInsecure = "insecure-skip-tls-verify"
- FlagCertFile = "client-certificate"
- FlagKeyFile = "client-key"
- FlagCAFile = "certificate-authority"
- FlagBearerToken = "token"
Do people want to put a secret as a flag argument. Seems like that risks exposure via proc or accidental pasting.
The other secrets are in files.Is there no scenario in which you would need/want to use the CLI flag? Agree it's not best practice, but it seems occasionally useful.
|
@erictune you should review this as well since it's intended to help solve your problems as well. |
db61110 to
ff451cf
Compare
There was a problem hiding this comment.
Don't remove cmd from these methods - a consumer may need to look at flags in order to properly retrieve the values.
|
@smarterclayton comments addressed in "comments round 2" |
There was a problem hiding this comment.
Shouldn't this be f.Client(cmd,mapping)? I don't think you need to look at the builder here.
There was a problem hiding this comment.
Shouldn't this be f.Client(cmd,mapping)? I don't think you need to look at the builder here.
The types are not directly compatible. The clientFunc needs to return a client.RESTClient, but the Factory.Client() returns a kubectl.RESTClient. All client.RESTClients are kubectl.RESTClients, but not the other way around.
There was a problem hiding this comment.
@smarterclayton I made the changes required to directly call the Client() method, but I'm not sure whether you want the pkg/config depending on pkg/kubectl. I've left it as a separate commit so you can decide.
|
One last comment then this is LGTM after squash. Eric, did you have other feedback? |
|
How does this code relate to client.BindClientConfigFlags (pkg/client/flags.go) Do we need both? If so, when do I use one or the other? Would like to sort that out before this is merged. |
There was a problem hiding this comment.
"cmd" doesn't convey to me what the docs describe. Any other names?
There was a problem hiding this comment.
"cmd" doesn't convey to me what the docs describe. Any other names?
clientcmd? That would make the qualified package pkg/client/clientcmd. http does this with net/http/httputil, but I had noted the kubernetes had pkg/kubectl/cmd.
There was a problem hiding this comment.
On Nov 20, 2014, at 6:46 AM, David Eads notifications@github.com wrote:
In pkg/client/cmd/doc.go:
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+/
+
+/
+Package cmd provides one stop shopping for a command line executable to bind the correct flags,
"cmd" doesn't convey to me what the docs describe. Any other names?clientcmd? That would make the qualified package pkg/client/clientcmd. http does this with net/http/httputil, but I had noted the kubernetes had pkg/kubectl/cmd.
I wouldn't take that as a constraint. clientcmd sounds fine.
—
Reply to this email directly or view it on GitHub.
|
@satnam6502 this is the thingy I was talking about. |
|
I think we would remove that in preference to this. We might want to replace the flagset pointer with an interface that exposed the methods we use.
|
|
Other than that this LGTM. Would like another sign off from someone else. |
f636fdd to
a43a01a
Compare
|
@smarterclayton Constants moved and commits squashed. Do you have anyone else in mind? |
|
Folks already on this pull
|
There was a problem hiding this comment.
No function BuildClient defined in this PR that I can see.
a43a01a to
18e4ac0
Compare
There was a problem hiding this comment.
I don't think it is necessary to introduce a Builder type.
You can do this in an interactive client:
apiClient, err := clientcmd.NewFromPersistentFlags(cmds.PersistentFlags())
if os.IsNotExist(err) {
apiClient, err := clientcmd.NewFromPrompt(cmds.PersistentFlags())
}
if err {
fmt.Println("Could not make a client", err)
}
For the case of a non-interactive client things simplify to:
apiClient, err := clientcmd.New()
if err {
fmt.Println("Could not make a client", err)
}
This makes non-interactive client code more concise, and I expect there will be more non-interactive clients than interactive ones.
Even for interactive clients, I find it preferable, in terms of readability, to get forego introducing a new type and gain a few more lines of error handling (which will be familiar to any go programmer.)
There was a problem hiding this comment.
One of the issues today is keeping the flag names consistent across multiple different commands. That means that a BindFlags method is still required (or at least a call to create the flags). So we're looking having two calls no matter what we do.
By having an object holding that state information (the builder), we avoid having two discrete calls
clientcmd.BindFlags(cmd.PersistentFlags())
clientcmd.Client(cmd.PersistentFlags))
that rely upon having two different methods being called with matching arguments. This is especially useful when you look at the common kubernetes usage that has methods trying to build clients relatively removed from the the point where the flags are bound.
There was a problem hiding this comment.
Also we may be instantiating multiple clients - the kubectl use needs that (so you map restclients across multiple component plugins). Builder just happened to be a convenient holder for that. Having a simpler path for single client types isn't bad, and seems like that could be a simple static call.
On Nov 24, 2014, at 3:31 PM, David Eads notifications@github.com wrote:
In pkg/client/clientcmd/doc.go:
+You may obtain a copy of the License at
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+/
+
+/
+Package cmd provides one stop shopping for a command line executable to bind the correct flags,
+build the client config, and create a working client. The code for usage looks like this:
+
- clientBuilder := clientcmd.NewBuilder(clientcmd.NewDefaultAuthLoader())
One of the issues today is keeping the flag names consistent across multiple different commands. That means that a BindFlags method is still required (or at least a call to create the flags). So we're looking having two calls no matter what we do.By having an object holding that state information (the builder), we avoid having two discrete calls
clientcmd.BindFlags(cmd.PersistentFlags())
clientcmd.Client(cmd.PersistentFlags))
that rely upon having two different methods being called with matching arguments. This is especially useful when you look at the common kubernetes usage that has methods trying to build clients relatively removed from the the point where the flags are bound.—
Reply to this email directly or view it on GitHub.
There was a problem hiding this comment.
I'm skeptical that clients other than kubectl (non-interactive ones) will want to use persistent flags. And, in the case of kubectl, the two lines in question are right next to each other.
There was a problem hiding this comment.
Call site for binding seems like it could be in many places (usually in init, while client is going to be in main), and persistent flags and other flags are both FlagSet.
There was a problem hiding this comment.
Did you just want a more realistic example?
There was a problem hiding this comment.
Builder feels to me like using a pattern for the sake of using patterns.
There was a problem hiding this comment.
Hrm. Usually you create an object and then bind its params with *VarP, and then use the object you bound to get a client. So it feels patterny, but that because it's a pattern :)
You have to bind before you can create a client, and you can't read the values you bound until after flags are parsed. The example doesn't show that and it should. So it's always a two step process.
On Nov 25, 2014, at 7:19 PM, Eric Tune notifications@github.com wrote:
In pkg/client/clientcmd/doc.go:
+You may obtain a copy of the License at
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+/
+
+/
+Package cmd provides one stop shopping for a command line executable to bind the correct flags,
+build the client config, and create a working client. The code for usage looks like this:
+
- clientBuilder := clientcmd.NewBuilder(clientcmd.NewDefaultAuthLoader())
Builder feels to me like using a pattern for the sake of using patterns.—
Reply to this email directly or view it on GitHub.
|
Can one of the admins verify this patch? |
18e4ac0 to
2dbfb80
Compare
|
Let's get a third opinion. @thockin Please try to ignore the conversation, and read the code and see what you think. |
|
Also, David can you do an example of an existing cli command (as simply as possible to get it to work)?
|
There was a problem hiding this comment.
BuildConfig is not defined anywhere - Config?
There was a problem hiding this comment.
Typo from an early refactor
On Nov 26, 2014, at 4:56 PM, Tim Hockin notifications@github.com wrote:
In pkg/client/clientcmd/client_builder.go:
- "github.com/spf13/pflag"
- "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
- "github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
- "github.com/GoogleCloudPlatform/kubernetes/pkg/version"
+)
+// Builder are used to bind and interpret command line flags to make it easy to get an api server client
+type Builder interface {
- // BindFlags must bind and keep track of all the flags required to build a client config object
- BindFlags(flags *pflag.FlagSet)
- // Config uses the values of the bound flags and builds a complete client config
- Config() (*client.Config, error)
- // Client calls BuildConfig under the covers and uses that config to return a client
BuildConfig is not defined anywhere - Config?—
Reply to this email directly or view it on GitHub.
|
I don't think a builder is NECESSARY here, but I don't mind it much. It's not how I would have written it - I would agree with Eric's basic sketch but I'd hope for less fragile error returning. That said, this is done, and my bias is towards moving forward. I don't think this imposes much of a maintenance burden over time. The biggest concerns I have are whether libs should be mucking with flags AT ALL, and the precedent that this sets for other code. I'd rather see a Config struct that has a half dozen fields that can be filled in and passed in, which is a pattern we see all over the place, but maybe that ship has sailed for this? I think that is more detrimental to the overall design than a builder. |
…flags add utility for binding flags and building api server clients
|
Please use Builders, Factories, and BuilderFactoryBuilders with restraint. Especially BuilderFactoryBuilders. |
|
I don't think that ship has sailed. It's possible that the builder became too important over the evolution of the pull. Here's the pattern I see in every cobra cmd I write: https://github.com/openshift/origin/blob/master/pkg/cmd/server/start.go#L92
Client commands are one part. I'd want to nest a client config object into my regular config and bind to it I dont want it read flags off a cmd or flagset - I think that's antipattern. We should reorganize this to reflect it. |
…ins-kep Add kubectl plugin mechanism KEP
This utility provides one stop shopping for a command line executable to get bind the correct flags, build the client config, and create a working client. The code for usage reduces to something like this:
I made the flags congruent with kubectl and used kubectl as an example spot for usage. This makes use of the auth config work in #2340 and the Bind and Build approach could be used to unify client creation and flag names.
@smarterclayton You've had an interest in this area.