add utility for binding flags and building api server clients
This commit is contained in:
82
pkg/client/clientcmd/auth_loaders.go
Normal file
82
pkg/client/clientcmd/auth_loaders.go
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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 clientcmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
|
||||
)
|
||||
|
||||
// AuthLoaders are used to build clientauth.Info objects.
|
||||
type AuthLoader interface {
|
||||
// LoadAuth takes a path to a config file and can then do anything it needs in order to return a valid clientauth.Info
|
||||
LoadAuth(path string) (*clientauth.Info, error)
|
||||
}
|
||||
|
||||
// default implementation of an AuthLoader
|
||||
type defaultAuthLoader struct{}
|
||||
|
||||
// LoadAuth for defaultAuthLoader simply delegates to clientauth.LoadFromFile
|
||||
func (*defaultAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
|
||||
return clientauth.LoadFromFile(path)
|
||||
}
|
||||
|
||||
type promptingAuthLoader struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
// LoadAuth parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
|
||||
func (a *promptingAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
|
||||
var auth clientauth.Info
|
||||
// Prompt for user/pass and write a file if none exists.
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
auth.User = promptForString("Username", a.reader)
|
||||
auth.Password = promptForString("Password", a.reader)
|
||||
data, err := json.Marshal(auth)
|
||||
if err != nil {
|
||||
return &auth, err
|
||||
}
|
||||
err = ioutil.WriteFile(path, data, 0600)
|
||||
return &auth, err
|
||||
}
|
||||
authPtr, err := clientauth.LoadFromFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return authPtr, nil
|
||||
}
|
||||
func promptForString(field string, r io.Reader) string {
|
||||
fmt.Printf("Please enter %s: ", field)
|
||||
var result string
|
||||
fmt.Fscan(r, &result)
|
||||
return result
|
||||
}
|
||||
|
||||
// NewDefaultAuthLoader is an AuthLoader that parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
|
||||
func NewPromptingAuthLoader(reader io.Reader) AuthLoader {
|
||||
return &promptingAuthLoader{reader}
|
||||
}
|
||||
|
||||
// NewDefaultAuthLoader returns a default implementation of an AuthLoader that only reads from a config file
|
||||
func NewDefaultAuthLoader() AuthLoader {
|
||||
return &defaultAuthLoader{}
|
||||
}
|
181
pkg/client/clientcmd/client_builder.go
Normal file
181
pkg/client/clientcmd/client_builder.go
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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 clientcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"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
|
||||
Client() (*client.Client, error)
|
||||
}
|
||||
|
||||
// cmdAuthInfo is used to track whether flags have been set
|
||||
type cmdAuthInfo struct {
|
||||
User StringFlag
|
||||
Password StringFlag
|
||||
CAFile StringFlag
|
||||
CertFile StringFlag
|
||||
KeyFile StringFlag
|
||||
BearerToken StringFlag
|
||||
Insecure BoolFlag
|
||||
}
|
||||
|
||||
// builder is a default implementation of a Builder
|
||||
type builder struct {
|
||||
authLoader AuthLoader
|
||||
cmdAuthInfo cmdAuthInfo
|
||||
authPath string
|
||||
apiserver string
|
||||
apiVersion string
|
||||
matchApiVersion bool
|
||||
}
|
||||
|
||||
// NewBuilder returns a valid Builder that uses the passed authLoader. If authLoader is nil, the NewDefaultAuthLoader is used.
|
||||
func NewBuilder(authLoader AuthLoader) Builder {
|
||||
if authLoader == nil {
|
||||
authLoader = NewDefaultAuthLoader()
|
||||
}
|
||||
|
||||
return &builder{
|
||||
authLoader: authLoader,
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// BindFlags implements Builder
|
||||
func (builder *builder) BindFlags(flags *pflag.FlagSet) {
|
||||
flags.StringVarP(&builder.apiserver, FlagApiServer, "s", builder.apiserver, "The address of the Kubernetes API server")
|
||||
flags.BoolVar(&builder.matchApiVersion, FlagMatchApiVersion, false, "Require server version to match client version")
|
||||
flags.StringVar(&builder.apiVersion, FlagApiVersion, latest.Version, "The API version to use when talking to the server")
|
||||
flags.StringVarP(&builder.authPath, FlagAuthPath, "a", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file. If missing, prompt the user. Only used if using https.")
|
||||
flags.Var(&builder.cmdAuthInfo.Insecure, FlagInsecure, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
|
||||
flags.Var(&builder.cmdAuthInfo.CertFile, FlagCertFile, "Path to a client key file for TLS.")
|
||||
flags.Var(&builder.cmdAuthInfo.KeyFile, FlagKeyFile, "Path to a client key file for TLS.")
|
||||
flags.Var(&builder.cmdAuthInfo.CAFile, FlagCAFile, "Path to a cert. file for the certificate authority.")
|
||||
flags.Var(&builder.cmdAuthInfo.BearerToken, FlagBearerToken, "Bearer token for authentication to the API server.")
|
||||
}
|
||||
|
||||
// Client implements Builder
|
||||
func (builder *builder) Client() (*client.Client, error) {
|
||||
clientConfig, err := builder.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := client.New(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if builder.matchApiVersion {
|
||||
clientVersion := version.Get()
|
||||
serverVersion, err := c.ServerVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't read version from server: %v\n", err)
|
||||
}
|
||||
if s := *serverVersion; !reflect.DeepEqual(clientVersion, s) {
|
||||
return nil, fmt.Errorf("server version (%#v) differs from client version (%#v)!\n", s, clientVersion)
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Config implements Builder
|
||||
func (builder *builder) Config() (*client.Config, error) {
|
||||
clientConfig := client.Config{}
|
||||
if len(builder.apiserver) > 0 {
|
||||
clientConfig.Host = builder.apiserver
|
||||
} else if len(os.Getenv("KUBERNETES_MASTER")) > 0 {
|
||||
clientConfig.Host = os.Getenv("KUBERNETES_MASTER")
|
||||
} else {
|
||||
// TODO: eventually apiserver should start on 443 and be secure by default
|
||||
clientConfig.Host = "http://localhost:8080"
|
||||
}
|
||||
clientConfig.Version = builder.apiVersion
|
||||
|
||||
// only try to read the auth information if we are secure
|
||||
if client.IsConfigTransportTLS(&clientConfig) {
|
||||
authInfoFileFound := true
|
||||
authInfo, err := builder.authLoader.LoadAuth(builder.authPath)
|
||||
if authInfo == nil && err != nil { // only consider failing if we don't have any auth info
|
||||
if os.IsNotExist(err) { // if it's just a case of a missing file, simply flag the auth as not found and use the command line arguments
|
||||
authInfoFileFound = false
|
||||
authInfo = &clientauth.Info{}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If provided, the command line options override options from the auth file
|
||||
if !authInfoFileFound || builder.cmdAuthInfo.User.Provided() {
|
||||
authInfo.User = builder.cmdAuthInfo.User.Value
|
||||
}
|
||||
if !authInfoFileFound || builder.cmdAuthInfo.Password.Provided() {
|
||||
authInfo.Password = builder.cmdAuthInfo.Password.Value
|
||||
}
|
||||
if !authInfoFileFound || builder.cmdAuthInfo.CAFile.Provided() {
|
||||
authInfo.CAFile = builder.cmdAuthInfo.CAFile.Value
|
||||
}
|
||||
if !authInfoFileFound || builder.cmdAuthInfo.CertFile.Provided() {
|
||||
authInfo.CertFile = builder.cmdAuthInfo.CertFile.Value
|
||||
}
|
||||
if !authInfoFileFound || builder.cmdAuthInfo.KeyFile.Provided() {
|
||||
authInfo.KeyFile = builder.cmdAuthInfo.KeyFile.Value
|
||||
}
|
||||
if !authInfoFileFound || builder.cmdAuthInfo.BearerToken.Provided() {
|
||||
authInfo.BearerToken = builder.cmdAuthInfo.BearerToken.Value
|
||||
}
|
||||
if !authInfoFileFound || builder.cmdAuthInfo.Insecure.Provided() {
|
||||
authInfo.Insecure = &builder.cmdAuthInfo.Insecure.Value
|
||||
}
|
||||
|
||||
clientConfig, err = authInfo.MergeWithConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &clientConfig, nil
|
||||
}
|
321
pkg/client/clientcmd/client_builder_test.go
Normal file
321
pkg/client/clientcmd/client_builder_test.go
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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 clientcmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
|
||||
)
|
||||
|
||||
func TestSetAllArgumentsOnly(t *testing.T) {
|
||||
flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
|
||||
clientBuilder := NewBuilder(nil)
|
||||
clientBuilder.BindFlags(flags)
|
||||
|
||||
args := argValues{"https://localhost:8080", "v1beta1", "/auth-path", "cert-file", "key-file", "ca-file", "bearer-token", true, true}
|
||||
flags.Parse(strings.Split(args.toArguments(), " "))
|
||||
|
||||
castBuilder, ok := clientBuilder.(*builder)
|
||||
if !ok {
|
||||
t.Errorf("Got unexpected cast result: %#v", castBuilder)
|
||||
}
|
||||
|
||||
matchStringArg(args.server, castBuilder.apiserver, t)
|
||||
matchStringArg(args.apiVersion, castBuilder.apiVersion, t)
|
||||
matchStringArg(args.authPath, castBuilder.authPath, t)
|
||||
matchStringArg(args.certFile, castBuilder.cmdAuthInfo.CertFile.Value, t)
|
||||
matchStringArg(args.keyFile, castBuilder.cmdAuthInfo.KeyFile.Value, t)
|
||||
matchStringArg(args.caFile, castBuilder.cmdAuthInfo.CAFile.Value, t)
|
||||
matchStringArg(args.bearerToken, castBuilder.cmdAuthInfo.BearerToken.Value, t)
|
||||
matchBoolArg(args.insecure, castBuilder.cmdAuthInfo.Insecure.Value, t)
|
||||
matchBoolArg(args.matchApiVersion, castBuilder.matchApiVersion, t)
|
||||
|
||||
clientConfig, err := clientBuilder.Config()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(args.server, clientConfig.Host, t)
|
||||
matchStringArg(args.apiVersion, clientConfig.Version, t)
|
||||
matchStringArg(args.certFile, clientConfig.CertFile, t)
|
||||
matchStringArg(args.keyFile, clientConfig.KeyFile, t)
|
||||
matchStringArg(args.caFile, clientConfig.CAFile, t)
|
||||
matchStringArg(args.bearerToken, clientConfig.BearerToken, t)
|
||||
matchBoolArg(args.insecure, clientConfig.Insecure, t)
|
||||
}
|
||||
|
||||
func TestSetInsecureArgumentsOnly(t *testing.T) {
|
||||
flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
|
||||
clientBuilder := NewBuilder(nil)
|
||||
clientBuilder.BindFlags(flags)
|
||||
|
||||
args := argValues{"http://localhost:8080", "v1beta1", "/auth-path", "cert-file", "key-file", "ca-file", "bearer-token", true, true}
|
||||
flags.Parse(strings.Split(args.toArguments(), " "))
|
||||
|
||||
clientConfig, err := clientBuilder.Config()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(args.server, clientConfig.Host, t)
|
||||
matchStringArg(args.apiVersion, clientConfig.Version, t)
|
||||
|
||||
// all security related params should be empty in the resulting config even though we set them because we're using http transport
|
||||
matchStringArg("", clientConfig.CertFile, t)
|
||||
matchStringArg("", clientConfig.KeyFile, t)
|
||||
matchStringArg("", clientConfig.CAFile, t)
|
||||
matchStringArg("", clientConfig.BearerToken, t)
|
||||
matchBoolArg(false, clientConfig.Insecure, t)
|
||||
}
|
||||
|
||||
func TestReadAuthFile(t *testing.T) {
|
||||
flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
|
||||
clientBuilder := NewBuilder(nil)
|
||||
clientBuilder.BindFlags(flags)
|
||||
authFileContents := fmt.Sprintf(`{"user": "alfa-user", "password": "bravo-password", "cAFile": "charlie", "certFile": "delta", "keyFile": "echo", "bearerToken": "foxtrot"}`)
|
||||
authFile := writeTempAuthFile(authFileContents, t)
|
||||
|
||||
args := argValues{"https://localhost:8080", "v1beta1", authFile, "", "", "", "", true, true}
|
||||
flags.Parse(strings.Split(args.toArguments(), " "))
|
||||
|
||||
castBuilder, ok := clientBuilder.(*builder)
|
||||
if !ok {
|
||||
t.Errorf("Got unexpected cast result: %#v", castBuilder)
|
||||
}
|
||||
|
||||
matchStringArg(args.server, castBuilder.apiserver, t)
|
||||
matchStringArg(args.apiVersion, castBuilder.apiVersion, t)
|
||||
matchStringArg(args.authPath, castBuilder.authPath, t)
|
||||
matchStringArg(args.certFile, castBuilder.cmdAuthInfo.CertFile.Value, t)
|
||||
matchStringArg(args.keyFile, castBuilder.cmdAuthInfo.KeyFile.Value, t)
|
||||
matchStringArg(args.caFile, castBuilder.cmdAuthInfo.CAFile.Value, t)
|
||||
matchStringArg(args.bearerToken, castBuilder.cmdAuthInfo.BearerToken.Value, t)
|
||||
matchBoolArg(args.insecure, castBuilder.cmdAuthInfo.Insecure.Value, t)
|
||||
matchBoolArg(args.matchApiVersion, castBuilder.matchApiVersion, t)
|
||||
|
||||
clientConfig, err := clientBuilder.Config()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(args.server, clientConfig.Host, t)
|
||||
matchStringArg(args.apiVersion, clientConfig.Version, t)
|
||||
matchStringArg("delta", clientConfig.CertFile, t)
|
||||
matchStringArg("echo", clientConfig.KeyFile, t)
|
||||
matchStringArg("charlie", clientConfig.CAFile, t)
|
||||
matchStringArg("foxtrot", clientConfig.BearerToken, t)
|
||||
matchStringArg("alfa-user", clientConfig.Username, t)
|
||||
matchStringArg("bravo-password", clientConfig.Password, t)
|
||||
matchBoolArg(args.insecure, clientConfig.Insecure, t)
|
||||
}
|
||||
|
||||
func TestAuthFileOverridden(t *testing.T) {
|
||||
flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
|
||||
clientBuilder := NewBuilder(nil)
|
||||
clientBuilder.BindFlags(flags)
|
||||
authFileContents := fmt.Sprintf(`{"user": "alfa-user", "password": "bravo-password", "cAFile": "charlie", "certFile": "delta", "keyFile": "echo", "bearerToken": "foxtrot"}`)
|
||||
authFile := writeTempAuthFile(authFileContents, t)
|
||||
|
||||
args := argValues{"https://localhost:8080", "v1beta1", authFile, "cert-file", "key-file", "ca-file", "bearer-token", true, true}
|
||||
flags.Parse(strings.Split(args.toArguments(), " "))
|
||||
|
||||
castBuilder, ok := clientBuilder.(*builder)
|
||||
if !ok {
|
||||
t.Errorf("Got unexpected cast result: %#v", castBuilder)
|
||||
}
|
||||
|
||||
matchStringArg(args.server, castBuilder.apiserver, t)
|
||||
matchStringArg(args.apiVersion, castBuilder.apiVersion, t)
|
||||
matchStringArg(args.authPath, castBuilder.authPath, t)
|
||||
matchStringArg(args.certFile, castBuilder.cmdAuthInfo.CertFile.Value, t)
|
||||
matchStringArg(args.keyFile, castBuilder.cmdAuthInfo.KeyFile.Value, t)
|
||||
matchStringArg(args.caFile, castBuilder.cmdAuthInfo.CAFile.Value, t)
|
||||
matchStringArg(args.bearerToken, castBuilder.cmdAuthInfo.BearerToken.Value, t)
|
||||
matchBoolArg(args.insecure, castBuilder.cmdAuthInfo.Insecure.Value, t)
|
||||
matchBoolArg(args.matchApiVersion, castBuilder.matchApiVersion, t)
|
||||
|
||||
clientConfig, err := clientBuilder.Config()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(args.server, clientConfig.Host, t)
|
||||
matchStringArg(args.apiVersion, clientConfig.Version, t)
|
||||
matchStringArg(args.certFile, clientConfig.CertFile, t)
|
||||
matchStringArg(args.keyFile, clientConfig.KeyFile, t)
|
||||
matchStringArg(args.caFile, clientConfig.CAFile, t)
|
||||
matchStringArg(args.bearerToken, clientConfig.BearerToken, t)
|
||||
matchStringArg("alfa-user", clientConfig.Username, t)
|
||||
matchStringArg("bravo-password", clientConfig.Password, t)
|
||||
matchBoolArg(args.insecure, clientConfig.Insecure, t)
|
||||
}
|
||||
|
||||
func TestUseDefaultArgumentsOnly(t *testing.T) {
|
||||
flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
|
||||
clientBuilder := NewBuilder(nil)
|
||||
clientBuilder.BindFlags(flags)
|
||||
|
||||
flags.Parse(strings.Split("", " "))
|
||||
|
||||
castBuilder, ok := clientBuilder.(*builder)
|
||||
if !ok {
|
||||
t.Errorf("Got unexpected cast result: %#v", castBuilder)
|
||||
}
|
||||
|
||||
matchStringArg("", castBuilder.apiserver, t)
|
||||
matchStringArg(latest.Version, castBuilder.apiVersion, t)
|
||||
matchStringArg(os.Getenv("HOME")+"/.kubernetes_auth", castBuilder.authPath, t)
|
||||
matchStringArg("", castBuilder.cmdAuthInfo.CertFile.Value, t)
|
||||
matchStringArg("", castBuilder.cmdAuthInfo.KeyFile.Value, t)
|
||||
matchStringArg("", castBuilder.cmdAuthInfo.CAFile.Value, t)
|
||||
matchStringArg("", castBuilder.cmdAuthInfo.BearerToken.Value, t)
|
||||
matchBoolArg(false, castBuilder.matchApiVersion, t)
|
||||
}
|
||||
|
||||
func TestLoadClientAuthInfoOrPrompt(t *testing.T) {
|
||||
loadAuthInfoTests := []struct {
|
||||
authData string
|
||||
authInfo *clientauth.Info
|
||||
r io.Reader
|
||||
}{
|
||||
{
|
||||
`{"user": "user", "password": "pass"}`,
|
||||
&clientauth.Info{User: "user", Password: "pass"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"", nil, nil,
|
||||
},
|
||||
{
|
||||
"missing",
|
||||
&clientauth.Info{User: "user", Password: "pass"},
|
||||
bytes.NewBufferString("user\npass"),
|
||||
},
|
||||
}
|
||||
for _, loadAuthInfoTest := range loadAuthInfoTests {
|
||||
tt := loadAuthInfoTest
|
||||
aifile, err := ioutil.TempFile("", "testAuthInfo")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if tt.authData != "missing" {
|
||||
defer os.Remove(aifile.Name())
|
||||
defer aifile.Close()
|
||||
_, err = aifile.WriteString(tt.authData)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
} else {
|
||||
aifile.Close()
|
||||
os.Remove(aifile.Name())
|
||||
}
|
||||
prompter := NewPromptingAuthLoader(tt.r)
|
||||
authInfo, err := prompter.LoadAuth(aifile.Name())
|
||||
if len(tt.authData) == 0 && tt.authData != "missing" {
|
||||
if err == nil {
|
||||
t.Error("LoadAuth didn't fail on empty file")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(authInfo, tt.authInfo) {
|
||||
t.Errorf("Expected %#v, got %#v", tt.authInfo, authInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func matchStringArg(expected, got string, t *testing.T) {
|
||||
if expected != got {
|
||||
t.Errorf("Expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func matchBoolArg(expected, got bool, t *testing.T) {
|
||||
if expected != got {
|
||||
t.Errorf("Expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func writeTempAuthFile(contents string, t *testing.T) string {
|
||||
file, err := ioutil.TempFile("", "testAuthInfo")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to write config file. Test cannot continue due to: %v", err)
|
||||
return ""
|
||||
}
|
||||
_, err = file.WriteString(contents)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return ""
|
||||
}
|
||||
file.Close()
|
||||
|
||||
return file.Name()
|
||||
}
|
||||
|
||||
type argValues struct {
|
||||
server string
|
||||
apiVersion string
|
||||
authPath string
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
bearerToken string
|
||||
insecure bool
|
||||
matchApiVersion bool
|
||||
}
|
||||
|
||||
func (a *argValues) toArguments() string {
|
||||
args := ""
|
||||
if len(a.server) > 0 {
|
||||
args += "--" + FlagApiServer + "=" + a.server + " "
|
||||
}
|
||||
if len(a.apiVersion) > 0 {
|
||||
args += "--" + FlagApiVersion + "=" + a.apiVersion + " "
|
||||
}
|
||||
if len(a.authPath) > 0 {
|
||||
args += "--" + FlagAuthPath + "=" + a.authPath + " "
|
||||
}
|
||||
if len(a.certFile) > 0 {
|
||||
args += "--" + FlagCertFile + "=" + a.certFile + " "
|
||||
}
|
||||
if len(a.keyFile) > 0 {
|
||||
args += "--" + FlagKeyFile + "=" + a.keyFile + " "
|
||||
}
|
||||
if len(a.caFile) > 0 {
|
||||
args += "--" + FlagCAFile + "=" + a.caFile + " "
|
||||
}
|
||||
if len(a.bearerToken) > 0 {
|
||||
args += "--" + FlagBearerToken + "=" + a.bearerToken + " "
|
||||
}
|
||||
args += "--" + FlagInsecure + "=" + fmt.Sprintf("%v", a.insecure) + " "
|
||||
args += "--" + FlagMatchApiVersion + "=" + fmt.Sprintf("%v", a.matchApiVersion) + " "
|
||||
|
||||
return args
|
||||
}
|
25
pkg/client/clientcmd/doc.go
Normal file
25
pkg/client/clientcmd/doc.go
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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())
|
||||
clientBuilder.BindFlags(cmds.PersistentFlags())
|
||||
apiClient, err := clientBuilder.Client()
|
||||
*/
|
||||
package clientcmd
|
100
pkg/client/clientcmd/provided_flags.go
Normal file
100
pkg/client/clientcmd/provided_flags.go
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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 clientcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// FlagProvider adds a check for whether .Set was called on this flag variable
|
||||
type FlagProvider interface {
|
||||
// Provided returns true iff .Set was called on this flag
|
||||
Provided() bool
|
||||
pflag.Value
|
||||
}
|
||||
|
||||
// StringFlag implements FlagProvider
|
||||
type StringFlag struct {
|
||||
Default string
|
||||
Value string
|
||||
WasProvided bool
|
||||
}
|
||||
|
||||
// SetDefault sets a default value for a flag while keeping Provided() false
|
||||
func (flag *StringFlag) SetDefault(value string) {
|
||||
flag.Value = value
|
||||
flag.WasProvided = false
|
||||
}
|
||||
|
||||
func (flag *StringFlag) Set(value string) error {
|
||||
flag.Value = value
|
||||
flag.WasProvided = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flag *StringFlag) Type() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
func (flag *StringFlag) Provided() bool {
|
||||
return flag.WasProvided
|
||||
}
|
||||
|
||||
func (flag *StringFlag) String() string {
|
||||
return flag.Value
|
||||
}
|
||||
|
||||
// BoolFlag implements FlagProvider
|
||||
type BoolFlag struct {
|
||||
Default bool
|
||||
Value bool
|
||||
WasProvided bool
|
||||
}
|
||||
|
||||
// SetDefault sets a default value for a flag while keeping Provided() false
|
||||
func (flag *BoolFlag) SetDefault(value bool) {
|
||||
flag.Value = value
|
||||
flag.WasProvided = false
|
||||
}
|
||||
|
||||
func (flag *BoolFlag) Set(value string) error {
|
||||
boolValue, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flag.Value = boolValue
|
||||
flag.WasProvided = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flag *BoolFlag) Type() string {
|
||||
return "bool"
|
||||
}
|
||||
|
||||
func (flag *BoolFlag) Provided() bool {
|
||||
return flag.WasProvided
|
||||
}
|
||||
|
||||
func (flag *BoolFlag) String() string {
|
||||
return fmt.Sprintf("%s", flag.Value)
|
||||
}
|
@@ -25,6 +25,7 @@ type FlagSet interface {
|
||||
}
|
||||
|
||||
// BindClientConfigFlags registers a standard set of CLI flags for connecting to a Kubernetes API server.
|
||||
// TODO this method is superceded by pkg/client/clientcmd/client_builder.go
|
||||
func BindClientConfigFlags(flags FlagSet, config *Config) {
|
||||
flags.StringVar(&config.Host, "master", config.Host, "The address of the Kubernetes API server")
|
||||
flags.StringVar(&config.Version, "api_version", config.Version, "The API version to use when talking to the server")
|
||||
|
Reference in New Issue
Block a user