add kubectl config
This commit is contained in:
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
cmdconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/config"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
@@ -168,6 +169,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
||||
cmds.AddCommand(f.NewCmdUpdate(out))
|
||||
cmds.AddCommand(f.NewCmdDelete(out))
|
||||
|
||||
cmds.AddCommand(cmdconfig.NewCmdConfig(out))
|
||||
cmds.AddCommand(NewCmdNamespace(out))
|
||||
cmds.AddCommand(f.NewCmdLog(out))
|
||||
cmds.AddCommand(f.NewCmdRollingUpdate(out))
|
||||
@@ -213,7 +215,7 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
|
||||
flags.StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
|
||||
|
||||
overrides := &clientcmd.ConfigOverrides{}
|
||||
overrides.BindFlags(flags, clientcmd.RecommendedConfigOverrideFlags(""))
|
||||
clientcmd.BindOverrideFlags(overrides, flags, clientcmd.RecommendedConfigOverrideFlags(""))
|
||||
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
|
||||
|
||||
return clientConfig
|
||||
|
111
pkg/kubectl/cmd/config/config.go
Normal file
111
pkg/kubectl/cmd/config/config.go
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
|
||||
)
|
||||
|
||||
type pathOptions struct {
|
||||
local bool
|
||||
global bool
|
||||
specifiedFile string
|
||||
}
|
||||
|
||||
func NewCmdConfig(out io.Writer) *cobra.Command {
|
||||
pathOptions := &pathOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "config <subcommand>",
|
||||
Short: "config modifies .kubeconfig files",
|
||||
Long: `config modifies .kubeconfig files using subcommands like "kubectl config set current-context my-context"`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
// file paths are common to all sub commands
|
||||
cmd.PersistentFlags().BoolVar(&pathOptions.local, "local", true, "use the .kubeconfig in the currect directory")
|
||||
cmd.PersistentFlags().BoolVar(&pathOptions.global, "global", false, "use the .kubeconfig from "+os.Getenv("HOME"))
|
||||
cmd.PersistentFlags().StringVar(&pathOptions.specifiedFile, "kubeconfig", "", "use a particular .kubeconfig file")
|
||||
|
||||
cmd.AddCommand(NewCmdConfigView(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSetCluster(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSetAuthInfo(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSetContext(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSet(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigUnset(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigUseContext(out, pathOptions))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *pathOptions) getStartingConfig() (*clientcmdapi.Config, string, error) {
|
||||
filename := ""
|
||||
config := clientcmdapi.NewConfig()
|
||||
switch {
|
||||
case o.global:
|
||||
filename = os.Getenv("HOME") + "/.kube/.kubeconfig"
|
||||
config = getConfigFromFileOrDie(filename)
|
||||
|
||||
case len(o.specifiedFile) > 0:
|
||||
filename = o.specifiedFile
|
||||
config = getConfigFromFileOrDie(filename)
|
||||
|
||||
case o.local:
|
||||
filename = ".kubeconfig"
|
||||
config = getConfigFromFileOrDie(filename)
|
||||
}
|
||||
|
||||
return config, filename, nil
|
||||
}
|
||||
|
||||
// getConfigFromFileOrDie tries to read a kubeconfig file and if it can't, it calls exit. One exception, missing files result in empty configs, not an exit
|
||||
func getConfigFromFileOrDie(filename string) *clientcmdapi.Config {
|
||||
var err error
|
||||
config, err := clientcmd.LoadFromFile(filename)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
glog.FatalDepth(1, err)
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config = clientcmdapi.NewConfig()
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func toBool(propertyValue string) (bool, error) {
|
||||
boolValue := false
|
||||
if len(propertyValue) != 0 {
|
||||
var err error
|
||||
boolValue, err = strconv.ParseBool(propertyValue)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return boolValue, nil
|
||||
}
|
374
pkg/kubectl/cmd/config/config_test.go
Normal file
374
pkg/kubectl/cmd/config/config_test.go
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func newRedFederalCowHammerConfig() clientcmdapi.Config {
|
||||
return clientcmdapi.Config{
|
||||
AuthInfos: map[string]clientcmdapi.AuthInfo{
|
||||
"red-user": {Token: "red-token"}},
|
||||
Clusters: map[string]clientcmdapi.Cluster{
|
||||
"cow-cluster": {Server: "http://cow.org:8080"}},
|
||||
Contexts: map[string]clientcmdapi.Context{
|
||||
"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster", Namespace: "hammer-ns"}},
|
||||
}
|
||||
}
|
||||
|
||||
type configCommandTest struct {
|
||||
args []string
|
||||
startingConfig clientcmdapi.Config
|
||||
expectedConfig clientcmdapi.Config
|
||||
}
|
||||
|
||||
func TestSetCurrentContext(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.CurrentContext = "the-new-context"
|
||||
test := configCommandTest{
|
||||
args: []string{"use-context", "the-new-context"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetIntoExistingStruct(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
a := expectedConfig.AuthInfos["red-user"]
|
||||
authInfo := &a
|
||||
authInfo.AuthPath = "new-path-value"
|
||||
expectedConfig.AuthInfos["red-user"] = *authInfo
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "users.red-user.auth-path", "new-path-value"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestUnsetStruct(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
delete(expectedConfig.AuthInfos, "red-user")
|
||||
test := configCommandTest{
|
||||
args: []string{"unset", "users.red-user"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestUnsetField(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.AuthInfos["red-user"] = *clientcmdapi.NewAuthInfo()
|
||||
test := configCommandTest{
|
||||
args: []string{"unset", "users.red-user.token"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetIntoNewStruct(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.Server = "new-server-value"
|
||||
expectedConfig.Clusters["big-cluster"] = *cluster
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "clusters.big-cluster.server", "new-server-value"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetBoolean(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.InsecureSkipTLSVerify = true
|
||||
expectedConfig.Clusters["big-cluster"] = *cluster
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "clusters.big-cluster.insecure-skip-tls-verify", "true"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetIntoNewConfig(t *testing.T) {
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
context := clientcmdapi.NewContext()
|
||||
context.AuthInfo = "fake-user"
|
||||
expectedConfig.Contexts["new-context"] = *context
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "contexts.new-context.user", "fake-user"},
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestNewEmptyAuth(t *testing.T) {
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
expectedConfig.AuthInfos["the-user-name"] = *clientcmdapi.NewAuthInfo()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "the-user-name"},
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestAdditionalAuth(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.AuthPath = "auth-path"
|
||||
authInfo.ClientKey = "client-key"
|
||||
authInfo.Token = "token"
|
||||
expectedConfig.AuthInfos["another-user"] = *authInfo
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagAuthPath + "=auth-path", "--" + clientcmd.FlagKeyFile + "=client-key", "--" + clientcmd.FlagBearerToken + "=token"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestOverwriteExistingAuth(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.AuthPath = "auth-path"
|
||||
expectedConfig.AuthInfos["red-user"] = *authInfo
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "red-user", "--" + clientcmd.FlagAuthPath + "=auth-path"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestNewEmptyCluster(t *testing.T) {
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
expectedConfig.Clusters["new-cluster"] = *clientcmdapi.NewCluster()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "new-cluster"},
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestAdditionalCluster(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
cluster := *clientcmdapi.NewCluster()
|
||||
cluster.APIVersion = "v1beta1"
|
||||
cluster.CertificateAuthority = "ca-location"
|
||||
cluster.InsecureSkipTLSVerify = true
|
||||
cluster.Server = "serverlocation"
|
||||
expectedConfig.Clusters["different-cluster"] = cluster
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "different-cluster", "--" + clientcmd.FlagAPIServer + "=serverlocation", "--" + clientcmd.FlagInsecure + "=true", "--" + clientcmd.FlagCAFile + "=ca-location", "--" + clientcmd.FlagAPIVersion + "=v1beta1"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestOverwriteExistingCluster(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
cluster := *clientcmdapi.NewCluster()
|
||||
cluster.Server = "serverlocation"
|
||||
expectedConfig.Clusters["cow-cluster"] = cluster
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "cow-cluster", "--" + clientcmd.FlagAPIServer + "=serverlocation"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestNewEmptyContext(t *testing.T) {
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
expectedConfig.Contexts["new-context"] = *clientcmdapi.NewContext()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-context", "new-context"},
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestAdditionalContext(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
context := *clientcmdapi.NewContext()
|
||||
context.Cluster = "some-cluster"
|
||||
context.AuthInfo = "some-user"
|
||||
context.Namespace = "different-namespace"
|
||||
expectedConfig.Contexts["different-context"] = context
|
||||
test := configCommandTest{
|
||||
args: []string{"set-context", "different-context", "--" + clientcmd.FlagClusterName + "=some-cluster", "--" + clientcmd.FlagAuthInfoName + "=some-user", "--" + clientcmd.FlagNamespace + "=different-namespace"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestOverwriteExistingContext(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
context := *clientcmdapi.NewContext()
|
||||
context.Cluster = "clustername"
|
||||
expectedConfig.Contexts["federal-context"] = context
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-context", "federal-context", "--" + clientcmd.FlagClusterName + "=clustername"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestToBool(t *testing.T) {
|
||||
type test struct {
|
||||
in string
|
||||
out bool
|
||||
err string
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{"", false, ""},
|
||||
{"true", true, ""},
|
||||
{"on", false, `strconv.ParseBool: parsing "on": invalid syntax`},
|
||||
}
|
||||
|
||||
for _, curr := range tests {
|
||||
b, err := toBool(curr.in)
|
||||
if (len(curr.err) != 0) && err == nil {
|
||||
t.Errorf("Expected error: %v, but got nil", curr.err)
|
||||
}
|
||||
if (len(curr.err) == 0) && err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if (err != nil) && (err.Error() != curr.err) {
|
||||
t.Errorf("Expected %v, got %v", curr.err, err)
|
||||
|
||||
}
|
||||
if b != curr.out {
|
||||
t.Errorf("Expected %v, got %v", curr.out, b)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testConfigCommand(args []string, startingConfig clientcmdapi.Config) (string, clientcmdapi.Config) {
|
||||
fakeKubeFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
clientcmd.WriteToFile(startingConfig, fakeKubeFile.Name())
|
||||
|
||||
argsToUse := make([]string, 0, 2+len(args))
|
||||
argsToUse = append(argsToUse, "--kubeconfig="+fakeKubeFile.Name())
|
||||
argsToUse = append(argsToUse, args...)
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdConfig(buf)
|
||||
cmd.SetArgs(argsToUse)
|
||||
cmd.Execute()
|
||||
|
||||
// outBytes, _ := ioutil.ReadFile(fakeKubeFile.Name())
|
||||
config := getConfigFromFileOrDie(fakeKubeFile.Name())
|
||||
|
||||
return buf.String(), *config
|
||||
}
|
||||
|
||||
func (test configCommandTest) run(t *testing.T) {
|
||||
_, actualConfig := testConfigCommand(test.args, test.startingConfig)
|
||||
|
||||
testSetNilMapsToEmpties(reflect.ValueOf(&test.expectedConfig))
|
||||
testSetNilMapsToEmpties(reflect.ValueOf(&actualConfig))
|
||||
|
||||
if !reflect.DeepEqual(test.expectedConfig, actualConfig) {
|
||||
t.Errorf("diff: %v", util.ObjectDiff(test.expectedConfig, actualConfig))
|
||||
t.Errorf("expected: %#v\n actual: %#v", test.expectedConfig, actualConfig)
|
||||
}
|
||||
}
|
||||
func testSetNilMapsToEmpties(curr reflect.Value) {
|
||||
actualCurrValue := curr
|
||||
if curr.Kind() == reflect.Ptr {
|
||||
actualCurrValue = curr.Elem()
|
||||
}
|
||||
|
||||
switch actualCurrValue.Kind() {
|
||||
case reflect.Map:
|
||||
for _, mapKey := range actualCurrValue.MapKeys() {
|
||||
currMapValue := actualCurrValue.MapIndex(mapKey)
|
||||
|
||||
// our maps do not hold pointers to structs, they hold the structs themselves. This means that MapIndex returns the struct itself
|
||||
// That in turn means that they have kinds of type.Struct, which is not a settable type. Because of this, we need to make new struct of that type
|
||||
// copy all the data from the old value into the new value, then take the .addr of the new value to modify it in the next recursion.
|
||||
// clear as mud
|
||||
modifiableMapValue := reflect.New(currMapValue.Type()).Elem()
|
||||
modifiableMapValue.Set(currMapValue)
|
||||
|
||||
if modifiableMapValue.Kind() == reflect.Struct {
|
||||
modifiableMapValue = modifiableMapValue.Addr()
|
||||
}
|
||||
|
||||
testSetNilMapsToEmpties(modifiableMapValue)
|
||||
actualCurrValue.SetMapIndex(mapKey, reflect.Indirect(modifiableMapValue))
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ {
|
||||
currFieldValue := actualCurrValue.Field(fieldIndex)
|
||||
|
||||
if currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil() {
|
||||
newValue := reflect.MakeMap(currFieldValue.Type())
|
||||
currFieldValue.Set(newValue)
|
||||
} else {
|
||||
testSetNilMapsToEmpties(currFieldValue.Addr())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
120
pkg/kubectl/cmd/config/create_authinfo.go
Normal file
120
pkg/kubectl/cmd/config/create_authinfo.go
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
|
||||
)
|
||||
|
||||
type createAuthInfoOptions struct {
|
||||
pathOptions *pathOptions
|
||||
name string
|
||||
authPath string
|
||||
clientCertificate string
|
||||
clientKey string
|
||||
token string
|
||||
}
|
||||
|
||||
func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *pathOptions) *cobra.Command {
|
||||
options := &createAuthInfoOptions{pathOptions: pathOptions}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "set-credentials name",
|
||||
Short: "Sets a user entry in .kubeconfig",
|
||||
Long: `Sets a user entry in .kubeconfig
|
||||
|
||||
Specifying a name that already exists overwrites that user entry.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !options.complete(cmd) {
|
||||
return
|
||||
}
|
||||
|
||||
err := options.run()
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&options.authPath, clientcmd.FlagAuthPath, "", clientcmd.FlagAuthPath+" for the user entry in .kubeconfig")
|
||||
cmd.Flags().StringVar(&options.clientCertificate, clientcmd.FlagCertFile, "", clientcmd.FlagCertFile+" for the user entry in .kubeconfig")
|
||||
cmd.Flags().StringVar(&options.clientKey, clientcmd.FlagKeyFile, "", clientcmd.FlagKeyFile+" for the user entry in .kubeconfig")
|
||||
cmd.Flags().StringVar(&options.token, clientcmd.FlagBearerToken, "", clientcmd.FlagBearerToken+" for the user entry in .kubeconfig")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o createAuthInfoOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, filename, err := o.pathOptions.getStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authInfo := o.authInfo()
|
||||
config.AuthInfos[o.name] = authInfo
|
||||
|
||||
err = clientcmd.WriteToFile(*config, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// authInfo builds an AuthInfo object from the options
|
||||
func (o *createAuthInfoOptions) authInfo() clientcmdapi.AuthInfo {
|
||||
authInfo := clientcmdapi.AuthInfo{
|
||||
AuthPath: o.authPath,
|
||||
ClientCertificate: o.clientCertificate,
|
||||
ClientKey: o.clientKey,
|
||||
Token: o.token,
|
||||
}
|
||||
|
||||
return authInfo
|
||||
}
|
||||
|
||||
func (o *createAuthInfoOptions) complete(cmd *cobra.Command) bool {
|
||||
args := cmd.Flags().Args()
|
||||
if len(args) != 1 {
|
||||
cmd.Help()
|
||||
return false
|
||||
}
|
||||
|
||||
o.name = args[0]
|
||||
return true
|
||||
}
|
||||
|
||||
func (o createAuthInfoOptions) validate() error {
|
||||
if len(o.name) == 0 {
|
||||
return errors.New("You must specify a non-empty user name")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
124
pkg/kubectl/cmd/config/create_cluster.go
Normal file
124
pkg/kubectl/cmd/config/create_cluster.go
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
|
||||
)
|
||||
|
||||
type createClusterOptions struct {
|
||||
pathOptions *pathOptions
|
||||
name string
|
||||
server string
|
||||
apiVersion string
|
||||
insecureSkipTLSVerify bool
|
||||
certificateAuthority string
|
||||
}
|
||||
|
||||
func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Command {
|
||||
options := &createClusterOptions{pathOptions: pathOptions}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "set-cluster name [server] [insecure-skip-tls-verify] [certificate-authority] [api-version]",
|
||||
Short: "Sets a cluster entry in .kubeconfig",
|
||||
Long: `Sets a cluster entry in .kubeconfig
|
||||
|
||||
Specifying a name that already exists overwrites that cluster entry.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !options.complete(cmd) {
|
||||
return
|
||||
}
|
||||
|
||||
err := options.run()
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&options.server, clientcmd.FlagAPIServer, "", clientcmd.FlagAPIServer+" for the cluster entry in .kubeconfig")
|
||||
cmd.Flags().StringVar(&options.apiVersion, clientcmd.FlagAPIVersion, "", clientcmd.FlagAPIVersion+" for the cluster entry in .kubeconfig")
|
||||
cmd.Flags().BoolVar(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, false, clientcmd.FlagInsecure+" for the cluster entry in .kubeconfig")
|
||||
cmd.Flags().StringVar(&options.certificateAuthority, clientcmd.FlagCAFile, "", clientcmd.FlagCAFile+" for the cluster entry in .kubeconfig")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o createClusterOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, filename, err := o.pathOptions.getStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.Clusters == nil {
|
||||
config.Clusters = make(map[string]clientcmdapi.Cluster)
|
||||
}
|
||||
|
||||
cluster := o.cluster()
|
||||
config.Clusters[o.name] = cluster
|
||||
|
||||
err = clientcmd.WriteToFile(*config, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cluster builds a Cluster object from the options
|
||||
func (o *createClusterOptions) cluster() clientcmdapi.Cluster {
|
||||
cluster := clientcmdapi.Cluster{
|
||||
Server: o.server,
|
||||
APIVersion: o.apiVersion,
|
||||
InsecureSkipTLSVerify: o.insecureSkipTLSVerify,
|
||||
CertificateAuthority: o.certificateAuthority,
|
||||
}
|
||||
|
||||
return cluster
|
||||
}
|
||||
|
||||
func (o *createClusterOptions) complete(cmd *cobra.Command) bool {
|
||||
args := cmd.Flags().Args()
|
||||
if len(args) != 1 {
|
||||
cmd.Help()
|
||||
return false
|
||||
}
|
||||
|
||||
o.name = args[0]
|
||||
return true
|
||||
}
|
||||
|
||||
func (o createClusterOptions) validate() error {
|
||||
if len(o.name) == 0 {
|
||||
return errors.New("You must specify a non-empty cluster name")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
116
pkg/kubectl/cmd/config/create_context.go
Normal file
116
pkg/kubectl/cmd/config/create_context.go
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
|
||||
)
|
||||
|
||||
type createContextOptions struct {
|
||||
pathOptions *pathOptions
|
||||
name string
|
||||
cluster string
|
||||
authInfo string
|
||||
namespace string
|
||||
}
|
||||
|
||||
func NewCmdConfigSetContext(out io.Writer, pathOptions *pathOptions) *cobra.Command {
|
||||
options := &createContextOptions{pathOptions: pathOptions}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "set-context name",
|
||||
Short: "Sets a context entry in .kubeconfig",
|
||||
Long: `Sets a context entry in .kubeconfig
|
||||
|
||||
Specifying a name that already exists overwrites that context entry.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !options.complete(cmd) {
|
||||
return
|
||||
}
|
||||
|
||||
err := options.run()
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&options.cluster, clientcmd.FlagClusterName, "", clientcmd.FlagClusterName+" for the context entry in .kubeconfig")
|
||||
cmd.Flags().StringVar(&options.authInfo, clientcmd.FlagAuthInfoName, "", clientcmd.FlagAuthInfoName+" for the context entry in .kubeconfig")
|
||||
cmd.Flags().StringVar(&options.namespace, clientcmd.FlagNamespace, "", clientcmd.FlagNamespace+" for the context entry in .kubeconfig")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o createContextOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, filename, err := o.pathOptions.getStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context := o.context()
|
||||
config.Contexts[o.name] = context
|
||||
|
||||
err = clientcmd.WriteToFile(*config, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *createContextOptions) context() clientcmdapi.Context {
|
||||
context := clientcmdapi.Context{
|
||||
Cluster: o.cluster,
|
||||
AuthInfo: o.authInfo,
|
||||
Namespace: o.namespace,
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func (o *createContextOptions) complete(cmd *cobra.Command) bool {
|
||||
args := cmd.Flags().Args()
|
||||
if len(args) != 1 {
|
||||
cmd.Help()
|
||||
return false
|
||||
}
|
||||
|
||||
o.name = args[0]
|
||||
return true
|
||||
}
|
||||
|
||||
func (o createContextOptions) validate() error {
|
||||
if len(o.name) == 0 {
|
||||
return errors.New("You must specify a non-empty context name")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
249
pkg/kubectl/cmd/config/set.go
Normal file
249
pkg/kubectl/cmd/config/set.go
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
)
|
||||
|
||||
const (
|
||||
cannotHaveStepsAfterError = "Cannot have steps after %v. %v are remaining"
|
||||
additionStepRequiredUnlessUnsettingError = "Must have additional steps after %v unless you are unsetting it"
|
||||
)
|
||||
|
||||
type navigationSteps []string
|
||||
|
||||
type setOptions struct {
|
||||
pathOptions *pathOptions
|
||||
propertyName string
|
||||
propertyValue string
|
||||
}
|
||||
|
||||
func NewCmdConfigSet(out io.Writer, pathOptions *pathOptions) *cobra.Command {
|
||||
options := &setOptions{pathOptions: pathOptions}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "set property-name property-value",
|
||||
Short: "Sets an individual value in a .kubeconfig file",
|
||||
Long: `Sets an individual value in a .kubeconfig file
|
||||
|
||||
property-name is a dot delimitted name where each token represents either a attribute name or a map key. Map keys may not contain dots.
|
||||
property-value is the new value you wish to set.
|
||||
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !options.complete(cmd) {
|
||||
return
|
||||
}
|
||||
|
||||
err := options.run()
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o setOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, filename, err := o.pathOptions.getStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(filename) == 0 {
|
||||
return errors.New("cannot set property without using a specific file")
|
||||
}
|
||||
|
||||
parts := strings.Split(o.propertyName, ".")
|
||||
err = modifyConfig(reflect.ValueOf(config), parts, o.propertyValue, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = clientcmd.WriteToFile(*config, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *setOptions) complete(cmd *cobra.Command) bool {
|
||||
endingArgs := cmd.Flags().Args()
|
||||
if len(endingArgs) != 2 {
|
||||
cmd.Help()
|
||||
return false
|
||||
}
|
||||
|
||||
o.propertyValue = endingArgs[1]
|
||||
o.propertyName = endingArgs[0]
|
||||
return true
|
||||
}
|
||||
|
||||
func (o setOptions) validate() error {
|
||||
if len(o.propertyValue) == 0 {
|
||||
return errors.New("You cannot use set to unset a property")
|
||||
}
|
||||
|
||||
if len(o.propertyName) == 0 {
|
||||
return errors.New("You must specify a property")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// moreStepsRemaining just makes code read cleaner
|
||||
func moreStepsRemaining(remainder []string) bool {
|
||||
return len(remainder) != 0
|
||||
}
|
||||
|
||||
func (s navigationSteps) nextSteps() navigationSteps {
|
||||
if len(s) < 2 {
|
||||
return make([]string, 0, 0)
|
||||
} else {
|
||||
return s[1:]
|
||||
}
|
||||
}
|
||||
func (s navigationSteps) moreStepsRemaining() bool {
|
||||
return len(s) != 0
|
||||
}
|
||||
func (s navigationSteps) nextStep() string {
|
||||
return s[0]
|
||||
}
|
||||
|
||||
func modifyConfig(curr reflect.Value, steps navigationSteps, propertyValue string, unset bool) error {
|
||||
shouldUnsetNextField := !steps.nextSteps().moreStepsRemaining() && unset
|
||||
shouldSetThisField := !steps.moreStepsRemaining() && !unset
|
||||
|
||||
actualCurrValue := curr
|
||||
if curr.Kind() == reflect.Ptr {
|
||||
actualCurrValue = curr.Elem()
|
||||
}
|
||||
|
||||
switch actualCurrValue.Kind() {
|
||||
case reflect.Map:
|
||||
if shouldSetThisField {
|
||||
return fmt.Errorf("Can't set a map to a value: %v", actualCurrValue)
|
||||
}
|
||||
|
||||
mapKey := reflect.ValueOf(steps.nextStep())
|
||||
mapValueType := curr.Type().Elem().Elem()
|
||||
|
||||
if shouldUnsetNextField {
|
||||
actualCurrValue.SetMapIndex(mapKey, reflect.Value{})
|
||||
return nil
|
||||
}
|
||||
|
||||
currMapValue := actualCurrValue.MapIndex(mapKey)
|
||||
|
||||
needToSetNewMapValue := currMapValue.Kind() == reflect.Invalid
|
||||
if needToSetNewMapValue {
|
||||
currMapValue = reflect.New(mapValueType).Elem()
|
||||
actualCurrValue.SetMapIndex(mapKey, currMapValue)
|
||||
}
|
||||
|
||||
// our maps do not hold pointers to structs, they hold the structs themselves. This means that MapIndex returns the struct itself
|
||||
// That in turn means that they have kinds of type.Struct, which is not a settable type. Because of this, we need to make new struct of that type
|
||||
// copy all the data from the old value into the new value, then take the .addr of the new value to modify it in the next recursion.
|
||||
// clear as mud
|
||||
modifiableMapValue := reflect.New(currMapValue.Type()).Elem()
|
||||
modifiableMapValue.Set(currMapValue)
|
||||
|
||||
if modifiableMapValue.Kind() == reflect.Struct {
|
||||
modifiableMapValue = modifiableMapValue.Addr()
|
||||
}
|
||||
err := modifyConfig(modifiableMapValue, steps.nextSteps(), propertyValue, unset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
actualCurrValue.SetMapIndex(mapKey, reflect.Indirect(modifiableMapValue))
|
||||
return nil
|
||||
|
||||
case reflect.String:
|
||||
if steps.moreStepsRemaining() {
|
||||
return fmt.Errorf("Can't have more steps after a string. %v", steps)
|
||||
}
|
||||
actualCurrValue.SetString(propertyValue)
|
||||
return nil
|
||||
|
||||
case reflect.Bool:
|
||||
if steps.moreStepsRemaining() {
|
||||
return fmt.Errorf("Can't have more steps after a bool. %v", steps)
|
||||
}
|
||||
boolValue, err := toBool(propertyValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
actualCurrValue.SetBool(boolValue)
|
||||
return nil
|
||||
|
||||
case reflect.Struct:
|
||||
if !steps.moreStepsRemaining() {
|
||||
return fmt.Errorf("Can't set a struct to a value: %v", actualCurrValue)
|
||||
}
|
||||
|
||||
for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ {
|
||||
currFieldValue := actualCurrValue.Field(fieldIndex)
|
||||
currFieldType := actualCurrValue.Type().Field(fieldIndex)
|
||||
currYamlTag := currFieldType.Tag.Get("json")
|
||||
currFieldTypeYamlName := strings.Split(currYamlTag, ",")[0]
|
||||
|
||||
if currFieldTypeYamlName == steps.nextStep() {
|
||||
thisMapHasNoValue := (currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil())
|
||||
|
||||
if thisMapHasNoValue {
|
||||
newValue := reflect.MakeMap(currFieldValue.Type())
|
||||
currFieldValue.Set(newValue)
|
||||
|
||||
if shouldUnsetNextField {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if shouldUnsetNextField {
|
||||
// if we're supposed to unset the value or if the value is a map that doesn't exist, create a new value and overwrite
|
||||
newValue := reflect.New(currFieldValue.Type()).Elem()
|
||||
currFieldValue.Set(newValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
return modifyConfig(currFieldValue.Addr(), steps.nextSteps(), propertyValue, unset)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unable to locate path %v under %v", steps, actualCurrValue)
|
||||
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unrecognized type: %v", actualCurrValue)
|
||||
}
|
107
pkg/kubectl/cmd/config/unset.go
Normal file
107
pkg/kubectl/cmd/config/unset.go
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
)
|
||||
|
||||
type unsetOptions struct {
|
||||
pathOptions *pathOptions
|
||||
propertyName string
|
||||
}
|
||||
|
||||
func NewCmdConfigUnset(out io.Writer, pathOptions *pathOptions) *cobra.Command {
|
||||
options := &unsetOptions{pathOptions: pathOptions}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "unset property-name",
|
||||
Short: "Unsets an individual value in a .kubeconfig file",
|
||||
Long: `Unsets an individual value in a .kubeconfig file
|
||||
|
||||
property-name is a dot delimitted name where each token represents either a attribute name or a map key. Map keys may not contain dots.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !options.complete(cmd) {
|
||||
return
|
||||
}
|
||||
|
||||
err := options.run()
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o unsetOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, filename, err := o.pathOptions.getStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(filename) == 0 {
|
||||
return errors.New("cannot set property without using a specific file")
|
||||
}
|
||||
|
||||
parts := strings.Split(o.propertyName, ".")
|
||||
err = modifyConfig(reflect.ValueOf(config), parts, "", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = clientcmd.WriteToFile(*config, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *unsetOptions) complete(cmd *cobra.Command) bool {
|
||||
endingArgs := cmd.Flags().Args()
|
||||
if len(endingArgs) != 1 {
|
||||
cmd.Help()
|
||||
return false
|
||||
}
|
||||
|
||||
o.propertyName = endingArgs[0]
|
||||
return true
|
||||
}
|
||||
|
||||
func (o unsetOptions) validate() error {
|
||||
if len(o.propertyName) == 0 {
|
||||
return errors.New("You must specify a property")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
98
pkg/kubectl/cmd/config/use_context.go
Normal file
98
pkg/kubectl/cmd/config/use_context.go
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
)
|
||||
|
||||
type useContextOptions struct {
|
||||
pathOptions *pathOptions
|
||||
contextName string
|
||||
}
|
||||
|
||||
func NewCmdConfigUseContext(out io.Writer, pathOptions *pathOptions) *cobra.Command {
|
||||
options := &useContextOptions{pathOptions: pathOptions}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "use-context context-name",
|
||||
Short: "Sets the current-context in a .kubeconfig file",
|
||||
Long: `Sets the current-context in a .kubeconfig file`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !options.complete(cmd) {
|
||||
return
|
||||
}
|
||||
|
||||
err := options.run()
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o useContextOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, filename, err := o.pathOptions.getStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(filename) == 0 {
|
||||
return errors.New("cannot set current-context without using a specific file")
|
||||
}
|
||||
|
||||
config.CurrentContext = o.contextName
|
||||
|
||||
err = clientcmd.WriteToFile(*config, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *useContextOptions) complete(cmd *cobra.Command) bool {
|
||||
endingArgs := cmd.Flags().Args()
|
||||
if len(endingArgs) != 1 {
|
||||
cmd.Help()
|
||||
return false
|
||||
}
|
||||
|
||||
o.contextName = endingArgs[0]
|
||||
return true
|
||||
}
|
||||
|
||||
func (o useContextOptions) validate() error {
|
||||
if len(o.contextName) == 0 {
|
||||
return errors.New("You must specify a current-context")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
99
pkg/kubectl/cmd/config/view.go
Normal file
99
pkg/kubectl/cmd/config/view.go
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
|
||||
)
|
||||
|
||||
type viewOptions struct {
|
||||
pathOptions *pathOptions
|
||||
merge bool
|
||||
}
|
||||
|
||||
func NewCmdConfigView(out io.Writer, pathOptions *pathOptions) *cobra.Command {
|
||||
options := &viewOptions{pathOptions: pathOptions}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "view",
|
||||
Short: "displays the specified .kubeconfig file or a merged result",
|
||||
Long: `displays the specified .kubeconfig file or a merged result`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := options.run()
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&options.merge, "merge", false, "merge together the full hierarchy of .kubeconfig files")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o viewOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, _, err := o.getStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%v", string(content))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o viewOptions) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getStartingConfig returns the Config object built from the sources specified by the options, the filename read (only if it was a single file), and an error if something goes wrong
|
||||
func (o *viewOptions) getStartingConfig() (*clientcmdapi.Config, string, error) {
|
||||
switch {
|
||||
case o.merge:
|
||||
loadingRules := clientcmd.NewClientConfigLoadingRules()
|
||||
loadingRules.EnvVarPath = ""
|
||||
loadingRules.CommandLinePath = o.pathOptions.specifiedFile
|
||||
|
||||
overrides := &clientcmd.ConfigOverrides{}
|
||||
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
|
||||
|
||||
config, err := clientConfig.RawConfig()
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("Error getting config: %v", err)
|
||||
}
|
||||
return &config, "", nil
|
||||
default:
|
||||
return o.pathOptions.getStartingConfig()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user