mirror of https://github.com/kubernetes/kops.git
Replace use of kubectl with calls to library
In particular, when setting configuration, just set it using the clientcmd functions. Lets us do this in one change. Follow up from #1663
This commit is contained in:
parent
8b847578b4
commit
851ffc4f5a
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"io"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kops/cmd/kops/util"
|
||||
api "k8s.io/kops/pkg/apis/kops"
|
||||
|
@ -182,9 +183,11 @@ func RunDeleteCluster(f *util.Factory, out io.Writer, options *DeleteClusterOpti
|
|||
}
|
||||
}
|
||||
|
||||
err = kutil.DeleteConfig(clusterName)
|
||||
b := kutil.NewKubeconfigBuilder()
|
||||
b.Context = clusterName
|
||||
err = b.DeleteKubeConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting kube config: %v", err)
|
||||
glog.Warningf("error removing kube config: %v", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "\nCluster deleted\n")
|
||||
|
|
|
@ -18,24 +18,16 @@ package kutil
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
)
|
||||
|
||||
// KubeconfigBuilder builds a kubecfg file
|
||||
// This logic previously lives in the bash scripts (create-kubeconfig in cluster/common.sh)
|
||||
type KubeconfigBuilder struct {
|
||||
KubectlPath string
|
||||
KubeconfigPath string
|
||||
|
||||
KubeMasterIP string
|
||||
|
||||
Context string
|
||||
|
@ -48,63 +40,42 @@ type KubeconfigBuilder struct {
|
|||
CACert []byte
|
||||
ClientCert []byte
|
||||
ClientKey []byte
|
||||
}
|
||||
|
||||
const KUBE_CFG_ENV = clientcmd.RecommendedConfigPathEnvVar + "=%s"
|
||||
configAccess clientcmd.ConfigAccess
|
||||
}
|
||||
|
||||
// Create new KubeconfigBuilder
|
||||
func NewKubeconfigBuilder() *KubeconfigBuilder {
|
||||
c := &KubeconfigBuilder{}
|
||||
c.KubectlPath = "kubectl" // default to in-path
|
||||
kubeConfig := os.Getenv(clientcmd.RecommendedConfigPathEnvVar)
|
||||
c.KubeconfigPath = c.getKubectlPath(kubeConfig)
|
||||
c.configAccess = clientcmd.NewDefaultPathOptions()
|
||||
return c
|
||||
}
|
||||
|
||||
func DeleteConfig(name string) error {
|
||||
filename := clientcmd.NewDefaultClientConfigLoadingRules().GetDefaultFilename()
|
||||
config, err := clientcmd.LoadFromFile(filename)
|
||||
func (b *KubeconfigBuilder) DeleteKubeConfig() error {
|
||||
config, err := b.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error loading kube config: %v", err)
|
||||
return fmt.Errorf("error loading kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
if api.IsConfigEmpty(config) {
|
||||
return fmt.Errorf("kube config is empty")
|
||||
if config == nil || clientcmdapi.IsConfigEmpty(config) {
|
||||
glog.V(2).Infof("kubeconfig is empty")
|
||||
return nil
|
||||
}
|
||||
|
||||
_, ok := config.Clusters[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("cluster %s does not exist", name)
|
||||
}
|
||||
delete(config.Clusters, name)
|
||||
delete(config.Clusters, b.Context)
|
||||
delete(config.AuthInfos, b.Context)
|
||||
delete(config.AuthInfos, fmt.Sprintf("%s-basic-auth", b.Context))
|
||||
delete(config.Contexts, b.Context)
|
||||
|
||||
_, ok = config.AuthInfos[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("user %s does not exist", name)
|
||||
}
|
||||
delete(config.AuthInfos, name)
|
||||
|
||||
_, ok = config.AuthInfos[fmt.Sprintf("%s-basic-auth", name)]
|
||||
if !ok {
|
||||
return fmt.Errorf("user %s-basic-auth does not exist", name)
|
||||
}
|
||||
delete(config.AuthInfos, fmt.Sprintf("%s-basic-auth", name))
|
||||
|
||||
_, ok = config.Contexts[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("context %s does not exist", name)
|
||||
}
|
||||
delete(config.Contexts, name)
|
||||
|
||||
if config.CurrentContext == name {
|
||||
if config.CurrentContext == b.Context {
|
||||
config.CurrentContext = ""
|
||||
}
|
||||
|
||||
err = clientcmd.WriteToFile(*config, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
if err := clientcmd.ModifyConfig(b.configAccess, *config, false); err != nil {
|
||||
return fmt.Errorf("error writing kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Deleted kubectl config for %s\n", b.Context)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -129,161 +100,109 @@ func (c *KubeconfigBuilder) BuildRestConfig() (*restclient.Config, error) {
|
|||
}
|
||||
|
||||
// Write out a new kubeconfig
|
||||
func (c *KubeconfigBuilder) WriteKubecfg() error {
|
||||
tmpdir, err := ioutil.TempDir("", "k8s")
|
||||
func (b *KubeconfigBuilder) WriteKubecfg() error {
|
||||
config, err := b.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating temporary directory: %v", err)
|
||||
return fmt.Errorf("error reading kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := os.RemoveAll(tmpdir)
|
||||
if err != nil {
|
||||
glog.Warningf("error deleting tempdir %q: %v", tmpdir, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := os.Stat(c.KubeconfigPath); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(path.Dir(c.KubeconfigPath), 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating directories for %q: %v", c.KubeconfigPath, err)
|
||||
}
|
||||
f, err := os.OpenFile(c.KubeconfigPath, os.O_RDWR|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating config file %q: %v", c.KubeconfigPath, err)
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
var clusterArgs []string
|
||||
|
||||
clusterArgs = append(clusterArgs, "--server=https://"+c.KubeMasterIP)
|
||||
|
||||
if c.CACert == nil {
|
||||
clusterArgs = append(clusterArgs, "--insecure-skip-tls-verify=true")
|
||||
} else {
|
||||
caCert := path.Join(tmpdir, "ca.crt")
|
||||
if err := ioutil.WriteFile(caCert, c.CACert, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
clusterArgs = append(clusterArgs, "--certificate-authority="+caCert)
|
||||
clusterArgs = append(clusterArgs, "--embed-certs=true")
|
||||
}
|
||||
|
||||
var userArgs []string
|
||||
|
||||
if c.KubeBearerToken != "" {
|
||||
userArgs = append(userArgs, "--token="+c.KubeBearerToken)
|
||||
} else if c.KubeUser != "" && c.KubePassword != "" {
|
||||
userArgs = append(userArgs, "--username="+c.KubeUser)
|
||||
userArgs = append(userArgs, "--password="+c.KubePassword)
|
||||
}
|
||||
|
||||
if c.ClientCert != nil && c.ClientKey != nil {
|
||||
clientCert := path.Join(tmpdir, "client.crt")
|
||||
if err := ioutil.WriteFile(clientCert, c.ClientCert, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
clientKey := path.Join(tmpdir, "client.key")
|
||||
if err := ioutil.WriteFile(clientKey, c.ClientKey, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userArgs = append(userArgs, "--client-certificate="+clientCert)
|
||||
userArgs = append(userArgs, "--client-key="+clientKey)
|
||||
userArgs = append(userArgs, "--embed-certs=true")
|
||||
}
|
||||
|
||||
setClusterArgs := []string{"config", "set-cluster", c.Context}
|
||||
setClusterArgs = append(setClusterArgs, clusterArgs...)
|
||||
err = c.execKubectl(setClusterArgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(userArgs) != 0 {
|
||||
setCredentialsArgs := []string{"config", "set-credentials", c.Context}
|
||||
setCredentialsArgs = append(setCredentialsArgs, userArgs...)
|
||||
err := c.execKubectl(setCredentialsArgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config == nil {
|
||||
config = &clientcmdapi.Config{}
|
||||
}
|
||||
|
||||
{
|
||||
args := []string{"config", "set-context", c.Context, "--cluster=" + c.Context, "--user=" + c.Context}
|
||||
if c.Namespace != "" {
|
||||
args = append(args, "--namespace", c.Namespace)
|
||||
cluster := config.Clusters[b.Context]
|
||||
if cluster == nil {
|
||||
cluster = clientcmdapi.NewCluster()
|
||||
}
|
||||
err = c.execKubectl(args...)
|
||||
if err != nil {
|
||||
return err
|
||||
cluster.Server = "https://" + b.KubeMasterIP
|
||||
|
||||
if b.CACert == nil {
|
||||
cluster.InsecureSkipTLSVerify = true
|
||||
cluster.CertificateAuthority = ""
|
||||
cluster.CertificateAuthorityData = nil
|
||||
} else {
|
||||
cluster.InsecureSkipTLSVerify = false
|
||||
cluster.CertificateAuthority = ""
|
||||
cluster.CertificateAuthorityData = b.CACert
|
||||
}
|
||||
|
||||
if config.Clusters == nil {
|
||||
config.Clusters = make(map[string]*clientcmdapi.Cluster)
|
||||
}
|
||||
config.Clusters[b.Context] = cluster
|
||||
}
|
||||
err = c.execKubectl("config", "use-context", c.Context, "--cluster="+c.Context, "--user="+c.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
{
|
||||
authInfo := config.AuthInfos[b.Context]
|
||||
if authInfo == nil {
|
||||
authInfo = clientcmdapi.NewAuthInfo()
|
||||
}
|
||||
|
||||
if b.KubeBearerToken != "" {
|
||||
authInfo.Token = b.KubeBearerToken
|
||||
} else if b.KubeUser != "" && b.KubePassword != "" {
|
||||
authInfo.Username = b.KubeUser
|
||||
authInfo.Password = b.KubePassword
|
||||
}
|
||||
|
||||
if b.ClientCert != nil && b.ClientKey != nil {
|
||||
authInfo.ClientCertificate = ""
|
||||
authInfo.ClientCertificateData = b.ClientCert
|
||||
authInfo.ClientKey = ""
|
||||
authInfo.ClientKeyData = b.ClientKey
|
||||
}
|
||||
|
||||
if config.AuthInfos == nil {
|
||||
config.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
|
||||
}
|
||||
config.AuthInfos[b.Context] = authInfo
|
||||
}
|
||||
|
||||
// If we have a bearer token, also create a credential entry with basic auth
|
||||
// so that it is easy to discover the basic auth password for your cluster
|
||||
// to use in a web browser.
|
||||
if c.KubeUser != "" && c.KubePassword != "" {
|
||||
err := c.execKubectl("config", "set-credentials", c.Context+"-basic-auth", "--username="+c.KubeUser, "--password="+c.KubePassword)
|
||||
if err != nil {
|
||||
return err
|
||||
if b.KubeUser != "" && b.KubePassword != "" {
|
||||
name := b.Context + "-basic-auth"
|
||||
authInfo := config.AuthInfos[name]
|
||||
if authInfo == nil {
|
||||
authInfo = clientcmdapi.NewAuthInfo()
|
||||
}
|
||||
|
||||
authInfo.Username = b.KubeUser
|
||||
authInfo.Password = b.KubePassword
|
||||
|
||||
if config.AuthInfos == nil {
|
||||
config.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
|
||||
}
|
||||
config.AuthInfos[name] = authInfo
|
||||
}
|
||||
fmt.Printf("Wrote config for %s to %q\n", c.Context, c.KubeconfigPath)
|
||||
fmt.Printf("Kops has set your kubectl context to %s\n", c.Context)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *KubeconfigBuilder) DeleteKubeConfig() {
|
||||
c.execKubectl("config", "unset", fmt.Sprintf("clusters.%s", c.Context))
|
||||
c.execKubectl("config", "unset", fmt.Sprintf("users.%s", c.Context))
|
||||
c.execKubectl("config", "unset", fmt.Sprintf("users.%s-basic-auth", c.Context))
|
||||
c.execKubectl("config", "unset", fmt.Sprintf("contexts.%s", c.Context))
|
||||
config, err := clientcmd.LoadFromFile(c.KubeconfigPath)
|
||||
if err != nil {
|
||||
fmt.Printf("kubectl unset current-context failed: %v", err)
|
||||
}
|
||||
if config.CurrentContext == c.Context {
|
||||
c.execKubectl("config", "unset", "current-context")
|
||||
}
|
||||
fmt.Printf("Deleted kubectl config for %s\n", c.Context)
|
||||
}
|
||||
|
||||
// get the correct path. Handle empty and multiple values.
|
||||
func (c *KubeconfigBuilder) getKubectlPath(kubeConfig string) string {
|
||||
|
||||
if kubeConfig == "" {
|
||||
return clientcmd.RecommendedHomeFile
|
||||
}
|
||||
|
||||
split := strings.Split(kubeConfig, ":")
|
||||
if len(split) > 1 {
|
||||
return split[0]
|
||||
}
|
||||
|
||||
return kubeConfig
|
||||
}
|
||||
|
||||
func (c *KubeconfigBuilder) execKubectl(args ...string) error {
|
||||
cmd := exec.Command(c.KubectlPath, args...)
|
||||
env := os.Environ()
|
||||
env = append(env, fmt.Sprintf(KUBE_CFG_ENV, c.KubeconfigPath))
|
||||
cmd.Env = env
|
||||
|
||||
glog.V(2).Infof("Running command: %s", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if len(output) != 0 {
|
||||
glog.Info("error running kubectl. Output follows:")
|
||||
glog.Info(string(output))
|
||||
}
|
||||
return fmt.Errorf("error running kubectl: %v", err)
|
||||
}
|
||||
|
||||
glog.V(2).Info(string(output))
|
||||
|
||||
{
|
||||
context := config.Contexts[b.Context]
|
||||
if context == nil {
|
||||
context = clientcmdapi.NewContext()
|
||||
}
|
||||
|
||||
context.Cluster = b.Context
|
||||
context.AuthInfo = b.Context
|
||||
|
||||
if b.Namespace != "" {
|
||||
context.Namespace = b.Namespace
|
||||
}
|
||||
|
||||
if config.Contexts == nil {
|
||||
config.Contexts = make(map[string]*clientcmdapi.Context)
|
||||
}
|
||||
config.Contexts[b.Context] = context
|
||||
}
|
||||
|
||||
config.CurrentContext = b.Context
|
||||
|
||||
if err := clientcmd.ModifyConfig(b.configAccess, *config, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Kops has set your kubectl context to %s\n", b.Context)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 kutil
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/homedir"
|
||||
)
|
||||
|
||||
const (
|
||||
RecommendedHomeDir = ".kube"
|
||||
RecommendedFileName = "config"
|
||||
)
|
||||
|
||||
func TestGetKubectlMultiplePath(t *testing.T) {
|
||||
c := testCreateKubectlBuilder()
|
||||
path := c.getKubectlPath(c.KubeconfigPath)
|
||||
|
||||
if path != "/tmp/config" {
|
||||
t.Fatalf("Wrong path got: %s, but expected /tmp/config", path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetKubectlSinglePath(t *testing.T) {
|
||||
c := testCreateKubectlBuilder()
|
||||
c.KubeconfigPath = "/bar/config"
|
||||
path := c.getKubectlPath(c.KubeconfigPath)
|
||||
|
||||
if path != "/bar/config" {
|
||||
t.Fatalf("Wrong path got: %s, but expected /bar/config", path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetKubectlDefault(t *testing.T) {
|
||||
c := testCreateKubectlBuilder()
|
||||
c.KubeconfigPath = "/bar/config"
|
||||
recommendedHomeFile := path.Join(homedir.HomeDir(), RecommendedHomeDir, RecommendedFileName)
|
||||
path := c.getKubectlPath("")
|
||||
|
||||
if path != recommendedHomeFile {
|
||||
t.Fatalf("Wrong path got: %s, but expected /bar/config", path)
|
||||
}
|
||||
}
|
||||
|
||||
func testCreateKubectlBuilder() *KubeconfigBuilder {
|
||||
return &KubeconfigBuilder{
|
||||
KubectlPath: "/usr/local/bin/kubectl",
|
||||
KubeconfigPath: "/tmp/config:/config:path2:path3",
|
||||
KubeMasterIP: "127.0.0.1",
|
||||
Context: "my-context",
|
||||
Namespace: "default",
|
||||
KubeBearerToken: "token",
|
||||
KubeUser: "user",
|
||||
KubePassword: "password",
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue