Skip to content

add utility for binding flags and building api server clients#2437

Merged
erictune merged 1 commit intokubernetes:masterfrom
deads2k:deads-add-utility-to-bind-auth-flags
Nov 26, 2014
Merged

add utility for binding flags and building api server clients#2437
erictune merged 1 commit intokubernetes:masterfrom
deads2k:deads-add-utility-to-bind-auth-flags

Conversation

@deads2k
Copy link
Copy Markdown
Contributor

@deads2k deads2k commented Nov 18, 2014

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:

authLoader := &PromptingAuthLoader{os.Stdin}
clientBuilder := clientcmd.NewClientBuilder(authLoader)
clientBuilder.BindFlags(cmds.PersistentFlags())
apiClient, err := clientBuilder.BuildClient()

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.

Comment thread pkg/client/cmd/client_builder.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewDefaultAuthLoader() or var Loader = &defaultAuthLoader{}, but not DefaultAuthLoader

Comment thread pkg/kubectl/cmd/cmd.go Outdated
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

  •   Client: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {
    
    @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?


Reply to this email directly or view it on GitHub.

@deads2k
Copy link
Copy Markdown
Contributor Author

deads2k commented Nov 18, 2014

@smarterclayton comments addressed in "comments round 1"

Comment thread pkg/client/cmd/client_builder.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@smarterclayton
Copy link
Copy Markdown
Contributor

@erictune you should review this as well since it's intended to help solve your problems as well.

@deads2k deads2k force-pushed the deads-add-utility-to-bind-auth-flags branch from db61110 to ff451cf Compare November 18, 2014 17:38
Comment thread pkg/kubectl/cmd/cmd.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't remove cmd from these methods - a consumer may need to look at flags in order to properly retrieve the values.

@deads2k
Copy link
Copy Markdown
Contributor Author

deads2k commented Nov 18, 2014

@smarterclayton comments addressed in "comments round 2"

Comment thread pkg/kubectl/cmd/createall.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be f.Client(cmd,mapping)? I don't think you need to look at the builder here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

@smarterclayton
Copy link
Copy Markdown
Contributor

One last comment then this is LGTM after squash. Eric, did you have other feedback?

@erictune
Copy link
Copy Markdown
Contributor

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.

Comment thread pkg/client/cmd/doc.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"cmd" doesn't convey to me what the docs describe. Any other names?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@lavalamp
Copy link
Copy Markdown
Contributor

@satnam6502 this is the thingy I was talking about.

@smarterclayton
Copy link
Copy Markdown
Contributor

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.

On Nov 19, 2014, at 6:35 PM, Eric Tune notifications@github.com wrote:

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.


Reply to this email directly or view it on GitHub.

@smarterclayton
Copy link
Copy Markdown
Contributor

Other than that this LGTM. Would like another sign off from someone else.

@deads2k deads2k force-pushed the deads-add-utility-to-bind-auth-flags branch from f636fdd to a43a01a Compare November 24, 2014 15:28
@deads2k
Copy link
Copy Markdown
Contributor Author

deads2k commented Nov 24, 2014

@smarterclayton Constants moved and commits squashed. Do you have anyone else in mind?

@smarterclayton
Copy link
Copy Markdown
Contributor

Folks already on this pull

On Nov 24, 2014, at 9:29 AM, David Eads notifications@github.com wrote:

@smarterclayton Constants moved and commits squashed. Do you have anyone else in mind?


Reply to this email directly or view it on GitHub.

Comment thread pkg/client/clientcmd/doc.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No function BuildClient defined in this PR that I can see.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed to .Client()

@deads2k deads2k force-pushed the deads-add-utility-to-bind-auth-flags branch from a43a01a to 18e4ac0 Compare November 24, 2014 21:07
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you just want a more realistic example?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Builder feels to me like using a pattern for the sake of using patterns.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@kubernetes-bot
Copy link
Copy Markdown

Can one of the admins verify this patch?

@deads2k deads2k force-pushed the deads-add-utility-to-bind-auth-flags branch from 18e4ac0 to 2dbfb80 Compare November 26, 2014 14:46
@erictune
Copy link
Copy Markdown
Contributor

Let's get a third opinion. @thockin Please try to ignore the conversation, and read the code and see what you think.

@smarterclayton
Copy link
Copy Markdown
Contributor

Also, David can you do an example of an existing cli command (as simply as possible to get it to work)?

On Nov 26, 2014, at 3:47 PM, Eric Tune notifications@github.com wrote:

Let's get a third opinion. @thockin Please try to ignore the conversation, and read the code and see what you think.


Reply to this email directly or view it on GitHub.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BuildConfig is not defined anywhere - Config?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@thockin
Copy link
Copy Markdown
Member

thockin commented Nov 26, 2014

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.

erictune added a commit that referenced this pull request Nov 26, 2014
…flags

add utility for binding flags and building api server clients
@erictune erictune merged commit 7eceafa into kubernetes:master Nov 26, 2014
@erictune
Copy link
Copy Markdown
Contributor

Please use Builders, Factories, and BuilderFactoryBuilders with restraint. Especially BuilderFactoryBuilders.

@smarterclayton
Copy link
Copy Markdown
Contributor

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

  1. Declare config struct
  2. Set defaults
  3. Bind that as closure to a method that implements the actual cmd (which is decoupled from flags)
  4. Bind flags to the config struct

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.

@mfojtik mfojtik mentioned this pull request Dec 1, 2014
10 tasks
@deads2k deads2k deleted the deads-add-utility-to-bind-auth-flags branch December 15, 2014 13:42
seans3 pushed a commit to seans3/kubernetes that referenced this pull request Apr 10, 2019
…ins-kep

Add kubectl plugin mechanism KEP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants