Merge pull request #4967 from jlowdermilk/kubeconfig

Simplify generating kubeconfig with embeded cert data
pull/6/head
Eric Tune 2015-03-04 08:38:43 -08:00
commit 662189ebc2
8 changed files with 180 additions and 15 deletions

View File

@ -7,7 +7,7 @@ Sets a cluster entry in .kubeconfig
``` ```
Sets a cluster entry in .kubeconfig Sets a cluster entry in .kubeconfig
Specifying a name that already exists will merge new fields on top of existing values for those fields. Specifying a name that already exists will merge new fields on top of existing values for those fields.
e.g. e.g.
kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/.kubernetes.ca.cert kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/.kubernetes.ca.cert
only sets the certificate-authority field on the e2e cluster entry without touching other values. only sets the certificate-authority field on the e2e cluster entry without touching other values.
@ -15,6 +15,12 @@ Sets a cluster entry in .kubeconfig
kubectl config set-cluster name [--server=server] [--certificate-authority=path/to/certficate/authority] [--api-version=apiversion] [--insecure-skip-tls-verify=true] kubectl config set-cluster name [--server=server] [--certificate-authority=path/to/certficate/authority] [--api-version=apiversion] [--insecure-skip-tls-verify=true]
### Options
```
--embed-certs=false: embed-certs for the cluster entry in .kubeconfig
```
### Options inherrited from parent commands ### Options inherrited from parent commands
``` ```

View File

@ -28,6 +28,12 @@ Sets a user entry in .kubeconfig
kubectl config set-credentials name [--auth-path=authfile] [--client-certificate=certfile] [--client-key=keyfile] [--token=bearer_token] [--username=basic_user] [--password=basic_password] kubectl config set-credentials name [--auth-path=authfile] [--client-certificate=certfile] [--client-key=keyfile] [--token=bearer_token] [--username=basic_user] [--password=basic_password]
### Options
```
--embed-certs=false: embed client cert/key for the user entry in .kubeconfig
```
### Options inherrited from parent commands ### Options inherrited from parent commands
``` ```

View File

@ -20,6 +20,12 @@ Sets a cluster entry in .kubeconfig
only sets the certificate\-authority field on the e2e cluster entry without touching other values. only sets the certificate\-authority field on the e2e cluster entry without touching other values.
.SH OPTIONS
.PP
\fB\-\-embed\-certs\fP=false
embed\-certs for the cluster entry in .kubeconfig
.SH OPTIONS INHERITED FROM PARENT COMMANDS .SH OPTIONS INHERITED FROM PARENT COMMANDS
.PP .PP
\fB\-\-alsologtostderr\fP=false \fB\-\-alsologtostderr\fP=false

View File

@ -45,6 +45,12 @@ Basic auth flags:
Bearer token and basic auth are mutually exclusive. Bearer token and basic auth are mutually exclusive.
.SH OPTIONS
.PP
\fB\-\-embed\-certs\fP=false
embed client cert/key for the user entry in .kubeconfig
.SH OPTIONS INHERITED FROM PARENT COMMANDS .SH OPTIONS INHERITED FROM PARENT COMMANDS
.PP .PP
\fB\-\-alsologtostderr\fP=false \fB\-\-alsologtostderr\fP=false

View File

@ -81,6 +81,7 @@ const (
FlagCertFile = "client-certificate" FlagCertFile = "client-certificate"
FlagKeyFile = "client-key" FlagKeyFile = "client-key"
FlagCAFile = "certificate-authority" FlagCAFile = "certificate-authority"
FlagEmbedCerts = "embed-certs"
FlagBearerToken = "token" FlagBearerToken = "token"
FlagUsername = "username" FlagUsername = "username"
FlagPassword = "password" FlagPassword = "password"

View File

@ -167,6 +167,56 @@ func TestAdditionalAuth(t *testing.T) {
test.run(t) test.run(t)
} }
func TestEmbedClientCert(t *testing.T) {
fakeCertFile, _ := ioutil.TempFile("", "")
defer os.Remove(fakeCertFile.Name())
fakeData := []byte("fake-data")
ioutil.WriteFile(fakeCertFile.Name(), fakeData, 0600)
expectedConfig := newRedFederalCowHammerConfig()
authInfo := clientcmdapi.NewAuthInfo()
authInfo.ClientCertificateData = fakeData
expectedConfig.AuthInfos["another-user"] = *authInfo
test := configCommandTest{
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=" + fakeCertFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"},
startingConfig: newRedFederalCowHammerConfig(),
expectedConfig: expectedConfig,
}
test.run(t)
}
func TestEmbedClientKey(t *testing.T) {
fakeKeyFile, _ := ioutil.TempFile("", "")
defer os.Remove(fakeKeyFile.Name())
fakeData := []byte("fake-data")
ioutil.WriteFile(fakeKeyFile.Name(), fakeData, 0600)
expectedConfig := newRedFederalCowHammerConfig()
authInfo := clientcmdapi.NewAuthInfo()
authInfo.ClientKeyData = fakeData
expectedConfig.AuthInfos["another-user"] = *authInfo
test := configCommandTest{
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagKeyFile + "=" + fakeKeyFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"},
startingConfig: newRedFederalCowHammerConfig(),
expectedConfig: expectedConfig,
}
test.run(t)
}
func TestEmbedNoKeyOrCertDisallowed(t *testing.T) {
expectedConfig := newRedFederalCowHammerConfig()
test := configCommandTest{
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagEmbedCerts + "=true"},
startingConfig: newRedFederalCowHammerConfig(),
expectedConfig: expectedConfig,
expectedOutputs: []string{"--client-certificate", "--client-key", "embed"},
}
test.run(t)
}
func TestEmptyTokenAndCertAllowed(t *testing.T) { func TestEmptyTokenAndCertAllowed(t *testing.T) {
expectedConfig := newRedFederalCowHammerConfig() expectedConfig := newRedFederalCowHammerConfig()
authInfo := clientcmdapi.NewAuthInfo() authInfo := clientcmdapi.NewAuthInfo()
@ -375,6 +425,45 @@ func TestInsecureClearsCA(t *testing.T) {
test.run(t) test.run(t)
} }
func TestCADataClearsCA(t *testing.T) {
fakeCAFile, _ := ioutil.TempFile("", "")
defer os.Remove(fakeCAFile.Name())
fakeData := []byte("cadata")
ioutil.WriteFile(fakeCAFile.Name(), fakeData, 0600)
clusterInfoWithCAData := clientcmdapi.NewCluster()
clusterInfoWithCAData.CertificateAuthorityData = fakeData
clusterInfoWithCA := clientcmdapi.NewCluster()
clusterInfoWithCA.CertificateAuthority = "cafile"
startingConfig := newRedFederalCowHammerConfig()
startingConfig.Clusters["another-cluster"] = *clusterInfoWithCA
expectedConfig := newRedFederalCowHammerConfig()
expectedConfig.Clusters["another-cluster"] = *clusterInfoWithCAData
test := configCommandTest{
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=" + fakeCAFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"},
startingConfig: startingConfig,
expectedConfig: expectedConfig,
}
test.run(t)
}
func TestEmbedNoCADisallowed(t *testing.T) {
expectedConfig := newRedFederalCowHammerConfig()
test := configCommandTest{
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagEmbedCerts + "=true"},
startingConfig: newRedFederalCowHammerConfig(),
expectedConfig: expectedConfig,
expectedOutputs: []string{"--certificate-authority", "embed"},
}
test.run(t)
}
func TestCAAndInsecureDisallowed(t *testing.T) { func TestCAAndInsecureDisallowed(t *testing.T) {
test := configCommandTest{ test := configCommandTest{
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=cafile", "--" + clientcmd.FlagInsecure + "=true"}, args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=cafile", "--" + clientcmd.FlagInsecure + "=true"},

View File

@ -20,6 +20,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -38,6 +39,7 @@ type createAuthInfoOptions struct {
token util.StringFlag token util.StringFlag
username util.StringFlag username util.StringFlag
password util.StringFlag password util.StringFlag
embedCertData util.BoolFlag
} }
func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *pathOptions) *cobra.Command { func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *pathOptions) *cobra.Command {
@ -78,11 +80,12 @@ func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *pathOptions) *cobra.Com
} }
cmd.Flags().Var(&options.authPath, clientcmd.FlagAuthPath, clientcmd.FlagAuthPath+" for the user entry in .kubeconfig") cmd.Flags().Var(&options.authPath, clientcmd.FlagAuthPath, clientcmd.FlagAuthPath+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, clientcmd.FlagCertFile+" for the user entry in .kubeconfig") cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, "path to "+clientcmd.FlagCertFile+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, clientcmd.FlagKeyFile+" for the user entry in .kubeconfig") cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, "path to "+clientcmd.FlagKeyFile+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in .kubeconfig") cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.username, clientcmd.FlagUsername, clientcmd.FlagUsername+" for the user entry in .kubeconfig") cmd.Flags().Var(&options.username, clientcmd.FlagUsername, clientcmd.FlagUsername+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in .kubeconfig") cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.embedCertData, clientcmd.FlagEmbedCerts, "embed client cert/key for the user entry in .kubeconfig")
return cmd return cmd
} }
@ -120,15 +123,27 @@ func (o *createAuthInfoOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.Aut
} }
if o.clientCertificate.Provided() { if o.clientCertificate.Provided() {
modifiedAuthInfo.ClientCertificate = o.clientCertificate.Value() certPath := o.clientCertificate.Value()
if len(modifiedAuthInfo.ClientCertificate) > 0 { if o.embedCertData.Value() {
modifiedAuthInfo.ClientCertificateData = nil modifiedAuthInfo.ClientCertificateData, _ = ioutil.ReadFile(certPath)
modifiedAuthInfo.ClientCertificate = ""
} else {
modifiedAuthInfo.ClientCertificate = certPath
if len(modifiedAuthInfo.ClientCertificate) > 0 {
modifiedAuthInfo.ClientCertificateData = nil
}
} }
} }
if o.clientKey.Provided() { if o.clientKey.Provided() {
modifiedAuthInfo.ClientKey = o.clientKey.Value() keyPath := o.clientKey.Value()
if len(modifiedAuthInfo.ClientKey) > 0 { if o.embedCertData.Value() {
modifiedAuthInfo.ClientKeyData = nil modifiedAuthInfo.ClientKeyData, _ = ioutil.ReadFile(keyPath)
modifiedAuthInfo.ClientKey = ""
} else {
modifiedAuthInfo.ClientKey = keyPath
if len(modifiedAuthInfo.ClientKey) > 0 {
modifiedAuthInfo.ClientKeyData = nil
}
} }
} }
@ -185,6 +200,23 @@ func (o createAuthInfoOptions) validate() error {
if len(methods) > 1 { if len(methods) > 1 {
return fmt.Errorf("You cannot specify more than one authentication method at the same time: %v", strings.Join(methods, ", ")) return fmt.Errorf("You cannot specify more than one authentication method at the same time: %v", strings.Join(methods, ", "))
} }
if o.embedCertData.Value() {
certPath := o.clientCertificate.Value()
keyPath := o.clientKey.Value()
if certPath == "" && keyPath == "" {
return fmt.Errorf("You must specify a --%s or --%s to embed", clientcmd.FlagCertFile, clientcmd.FlagKeyFile)
}
if certPath != "" {
if _, err := ioutil.ReadFile(certPath); err != nil {
return fmt.Errorf("Error reading %s data from %s: %v", clientcmd.FlagCertFile, certPath, err)
}
}
if keyPath != "" {
if _, err := ioutil.ReadFile(keyPath); err != nil {
return fmt.Errorf("Error reading %s data from %s: %v", clientcmd.FlagKeyFile, keyPath, err)
}
}
}
return nil return nil
} }

View File

@ -20,6 +20,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -35,6 +36,7 @@ type createClusterOptions struct {
apiVersion util.StringFlag apiVersion util.StringFlag
insecureSkipTLSVerify util.BoolFlag insecureSkipTLSVerify util.BoolFlag
certificateAuthority util.StringFlag certificateAuthority util.StringFlag
embedCAData util.BoolFlag
} }
func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Command { func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Command {
@ -45,7 +47,7 @@ func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Comm
Short: "Sets a cluster entry in .kubeconfig", Short: "Sets a cluster entry in .kubeconfig",
Long: `Sets a cluster entry in .kubeconfig Long: `Sets a cluster entry in .kubeconfig
Specifying a name that already exists will merge new fields on top of existing values for those fields. Specifying a name that already exists will merge new fields on top of existing values for those fields.
e.g. e.g.
kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/.kubernetes.ca.cert kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/.kubernetes.ca.cert
only sets the certificate-authority field on the e2e cluster entry without touching other values. only sets the certificate-authority field on the e2e cluster entry without touching other values.
`, `,
@ -66,7 +68,8 @@ func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Comm
cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in .kubeconfig") cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in .kubeconfig")
cmd.Flags().Var(&options.apiVersion, clientcmd.FlagAPIVersion, clientcmd.FlagAPIVersion+" for the cluster entry in .kubeconfig") cmd.Flags().Var(&options.apiVersion, clientcmd.FlagAPIVersion, clientcmd.FlagAPIVersion+" for the cluster entry in .kubeconfig")
cmd.Flags().Var(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, clientcmd.FlagInsecure+" for the cluster entry in .kubeconfig") cmd.Flags().Var(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, clientcmd.FlagInsecure+" for the cluster entry in .kubeconfig")
cmd.Flags().Var(&options.certificateAuthority, clientcmd.FlagCAFile, clientcmd.FlagCAFile+" for the cluster entry in .kubeconfig") cmd.Flags().Var(&options.certificateAuthority, clientcmd.FlagCAFile, "path to "+clientcmd.FlagCAFile+" for the cluster entry in .kubeconfig")
cmd.Flags().Var(&options.embedCAData, clientcmd.FlagEmbedCerts, clientcmd.FlagEmbedCerts+" for the cluster entry in .kubeconfig")
return cmd return cmd
} }
@ -116,11 +119,18 @@ func (o *createClusterOptions) modifyCluster(existingCluster clientcmdapi.Cluste
} }
} }
if o.certificateAuthority.Provided() { if o.certificateAuthority.Provided() {
modifiedCluster.CertificateAuthority = o.certificateAuthority.Value() caPath := o.certificateAuthority.Value()
// Specifying a certificate authority file clears certificate authority data and insecure mode if o.embedCAData.Value() {
if modifiedCluster.CertificateAuthority != "" { modifiedCluster.CertificateAuthorityData, _ = ioutil.ReadFile(caPath)
modifiedCluster.CertificateAuthorityData = nil
modifiedCluster.InsecureSkipTLSVerify = false modifiedCluster.InsecureSkipTLSVerify = false
modifiedCluster.CertificateAuthority = ""
} else {
modifiedCluster.CertificateAuthority = caPath
// Specifying a certificate authority file clears certificate authority data and insecure mode
if caPath != "" {
modifiedCluster.InsecureSkipTLSVerify = false
modifiedCluster.CertificateAuthorityData = nil
}
} }
} }
@ -145,6 +155,15 @@ func (o createClusterOptions) validate() error {
if o.insecureSkipTLSVerify.Value() && o.certificateAuthority.Value() != "" { if o.insecureSkipTLSVerify.Value() && o.certificateAuthority.Value() != "" {
return errors.New("You cannot specify a certificate authority and insecure mode at the same time") return errors.New("You cannot specify a certificate authority and insecure mode at the same time")
} }
if o.embedCAData.Value() {
caPath := o.certificateAuthority.Value()
if caPath == "" {
return fmt.Errorf("You must specify a --%s to embed", clientcmd.FlagCAFile)
}
if _, err := ioutil.ReadFile(caPath); err != nil {
return fmt.Errorf("Could not read %s data from %s: %v", clientcmd.FlagCAFile, caPath, err)
}
}
return nil return nil
} }