Exemple #1
0
func writePreferences(configAccess ConfigAccess, newPrefs clientcmdapi.Preferences) error {
	if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
		return err
	} else if reflect.DeepEqual(startingConfig.Preferences, newPrefs) {
		return nil
	}

	if configAccess.IsExplicitFile() {
		file := configAccess.GetExplicitFile()
		currConfig := getConfigFromFileOrDie(file)
		currConfig.Preferences = newPrefs
		if err := clientcmd.WriteToFile(*currConfig, file); err != nil {
			return err
		}

		return nil
	}

	for _, file := range configAccess.GetLoadingPrecedence() {
		currConfig := getConfigFromFileOrDie(file)

		if !reflect.DeepEqual(currConfig.Preferences, newPrefs) {
			currConfig.Preferences = newPrefs
			if err := clientcmd.WriteToFile(*currConfig, file); err != nil {
				return err
			}

			return nil
		}
	}

	return errors.New("no config found to write preferences")
}
Exemple #2
0
// writeCurrentContext takes three possible paths.
// If newCurrentContext is the same as the startingConfig's current context, then we exit.
// If newCurrentContext has a value, then that value is written into the default destination file.
// If newCurrentContext is empty, then we find the config file that is setting the CurrentContext and clear the value from that file
func writeCurrentContext(configAccess ConfigAccess, newCurrentContext string) error {
	if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
		return err
	} else if startingConfig.CurrentContext == newCurrentContext {
		return nil
	}

	if configAccess.IsExplicitFile() {
		file := configAccess.GetExplicitFile()
		currConfig := getConfigFromFileOrDie(file)
		currConfig.CurrentContext = newCurrentContext
		if err := clientcmd.WriteToFile(*currConfig, file); err != nil {
			return err
		}

		return nil
	}

	if len(newCurrentContext) > 0 {
		destinationFile := configAccess.GetDefaultFilename()
		config := getConfigFromFileOrDie(destinationFile)
		config.CurrentContext = newCurrentContext

		if err := clientcmd.WriteToFile(*config, destinationFile); err != nil {
			return err
		}

		return nil
	}

	// we're supposed to be clearing the current context.  We need to find the first spot in the chain that is setting it and clear it
	for _, file := range configAccess.GetLoadingPrecedence() {
		if _, err := os.Stat(file); err == nil {
			currConfig := getConfigFromFileOrDie(file)

			if len(currConfig.CurrentContext) > 0 {
				currConfig.CurrentContext = newCurrentContext
				if err := clientcmd.WriteToFile(*currConfig, file); err != nil {
					return err
				}

				return nil
			}
		}
	}

	return errors.New("no config found to write context")
}
Exemple #3
0
// ChangeUser changes the user used by the current CLI session.
func (c *CLI) ChangeUser(name string) *CLI {
	adminClientConfig, err := testutil.GetClusterAdminClientConfig(c.adminConfigPath)
	if err != nil {
		FatalErr(err)
	}
	_, _, clientConfig, err := testutil.GetClientForUser(*adminClientConfig, name)
	if err != nil {
		FatalErr(err)
	}

	kubeConfig, err := config.CreateConfig(c.Namespace(), clientConfig)
	if err != nil {
		FatalErr(err)
	}

	c.configPath = filepath.Join(c.outputDir, name+".kubeconfig")
	err = clientcmd.WriteToFile(*kubeConfig, c.configPath)
	if err != nil {
		FatalErr(err)
	}

	c.username = name
	e2e.Logf("configPath is now %q", c.configPath)
	return c
}
func (test currentContextTest) run(t *testing.T) {
	fakeKubeFile, _ := ioutil.TempFile("", "")
	defer os.Remove(fakeKubeFile.Name())
	err := clientcmd.WriteToFile(test.startingConfig, fakeKubeFile.Name())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	pathOptions := NewDefaultPathOptions()
	pathOptions.GlobalFile = fakeKubeFile.Name()
	pathOptions.EnvVar = ""
	options := CurrentContextOptions{
		ConfigAccess: pathOptions,
	}

	buf := bytes.NewBuffer([]byte{})
	err = RunCurrentContext(buf, []string{}, &options)
	if len(test.expectedError) != 0 {
		if err == nil {
			t.Errorf("Did not get %v", test.expectedError)
		} else {
			if !strings.Contains(err.Error(), test.expectedError) {
				t.Errorf("Expected %v, but got %v", test.expectedError, err)
			}
		}
		return
	}

	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
}
func (test getContextsTest) run(t *testing.T) {
	fakeKubeFile, _ := ioutil.TempFile("", "")
	defer os.Remove(fakeKubeFile.Name())
	err := clientcmd.WriteToFile(test.startingConfig, fakeKubeFile.Name())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	pathOptions := clientcmd.NewDefaultPathOptions()
	pathOptions.GlobalFile = fakeKubeFile.Name()
	pathOptions.EnvVar = ""
	buf := bytes.NewBuffer([]byte{})
	options := GetContextsOptions{
		configAccess: pathOptions,
	}
	cmd := NewCmdConfigGetContexts(buf, options.configAccess)
	if test.nameOnly {
		cmd.Flags().Set("output", "name")
	}
	if test.noHeader {
		cmd.Flags().Set("no-headers", "true")
	}
	cmd.Run(cmd, test.names)
	if len(test.expectedOut) != 0 {
		if buf.String() != test.expectedOut {
			t.Errorf("Expected %v, but got %v", test.expectedOut, buf.String())
		}
		return
	}

	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
}
func writeClientConfigToKubeConfig(config kclient.Config, path string) error {
	kubeConfig := &clientcmdapi.Config{
		Clusters:       map[string]*clientcmdapi.Cluster{"myserver": {Server: config.Host, CertificateAuthority: config.CAFile, CertificateAuthorityData: config.CAData}},
		AuthInfos:      map[string]*clientcmdapi.AuthInfo{"myuser": {Token: config.BearerToken}},
		Contexts:       map[string]*clientcmdapi.Context{"mycontext": {Cluster: "myserver", AuthInfo: "myuser"}},
		CurrentContext: "mycontext",
	}
	if err := os.MkdirAll(filepath.Dir(path), os.FileMode(0755)); err != nil {
		return err
	}
	if err := clientcmd.WriteToFile(*kubeConfig, path); err != nil {
		return err
	}
	return nil
}
Exemple #7
0
func WriteKubeconfigToDisk(filepath string, kubeconfig *clientcmdapi.Config) error {
	// Make sure the dir exists or can be created
	if err := os.MkdirAll(path.Dir(filepath), KubernetesDirPermissions); err != nil {
		return fmt.Errorf("failed to create directory %q [%v]", path.Dir(filepath), err)
	}

	// If err == nil, the file exists. Oops, we don't allow the file to exist already, fail.
	if _, err := os.Stat(filepath); err == nil {
		return fmt.Errorf("kubeconfig file %s already exists, but must not exist.", filepath)
	}

	if err := clientcmd.WriteToFile(*kubeconfig, filepath); err != nil {
		return fmt.Errorf("failed to write to %q [%v]", filepath, err)
	}

	fmt.Printf("[kubeconfig] Wrote KubeConfig file to disk: %q\n", filepath)
	return nil
}
Exemple #8
0
func WriteKubeconfigIfNotExists(name string, kubeconfig *clientcmdapi.Config) error {
	if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.KubernetesDir, 0700); err != nil {
		return fmt.Errorf("failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.KubernetesDir, err)
	}

	filename := path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", name))
	// Create and open the file, only if it does not already exist.
	f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0600)
	if err != nil {
		return fmt.Errorf("failed to create %q, it already exists [%v]", filename, err)
	}
	f.Close()

	if err := clientcmd.WriteToFile(*kubeconfig, filename); err != nil {
		return fmt.Errorf("failed to write to %q [%v]", filename, err)
	}

	fmt.Printf("[kubeconfig] Wrote KubeConfig file to disk: %q\n", filename)
	return nil
}
func WriteKubeconfigIfNotExists(name string, kubeconfig *clientcmdapi.Config) error {
	envParams := kubeadmapi.GetEnvParams()
	if err := os.MkdirAll(envParams["kubernetes_dir"], 0700); err != nil {
		return fmt.Errorf("<util/kubeconfig> failed to create directory %q [%v]", envParams["kubernetes_dir"], err)
	}

	filename := path.Join(envParams["kubernetes_dir"], fmt.Sprintf("%s.conf", name))
	// Create and open the file, only if it does not already exist.
	f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0600)
	if err != nil {
		return fmt.Errorf("<util/kubeconfig> failed to create %q, it already exists [%v]", filename, err)
	}
	f.Close()

	if err := clientcmd.WriteToFile(*kubeconfig, filename); err != nil {
		return fmt.Errorf("<util/kubeconfig> failed to write to %q [%v]", filename, err)
	}

	fmt.Printf("<util/kubeconfig> created %q\n", filename)
	return nil
}
func (test deleteContextTest) run(t *testing.T) {
	fakeKubeFile, _ := ioutil.TempFile("", "")
	defer os.Remove(fakeKubeFile.Name())
	err := clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	pathOptions := clientcmd.NewDefaultPathOptions()
	pathOptions.GlobalFile = fakeKubeFile.Name()
	pathOptions.EnvVar = ""

	buf := bytes.NewBuffer([]byte{})
	cmd := NewCmdConfigDeleteContext(buf, pathOptions)
	cmd.SetArgs([]string{test.contextToDelete})
	if err := cmd.Execute(); err != nil {
		t.Fatalf("unexpected error executing command: %v", err)
	}

	expectedOutWithFile := fmt.Sprintf(test.expectedOut, fakeKubeFile.Name())
	if expectedOutWithFile != buf.String() {
		t.Errorf("expected output %s, but got %s", expectedOutWithFile, buf.String())
		return
	}

	// Verify context was removed from kubeconfig file
	config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
	if err != nil {
		t.Fatalf("unexpected error loading kubeconfig file: %v", err)
	}

	contexts := make([]string, 0, len(config.Contexts))
	for k := range config.Contexts {
		contexts = append(contexts, k)
	}

	if !reflect.DeepEqual(test.expectedContexts, contexts) {
		t.Errorf("expected contexts %v, but found %v in kubeconfig", test.expectedContexts, contexts)
	}
}
Exemple #11
0
func testConfigCommand(args []string, startingConfig clientcmdapi.Config, t *testing.T) (string, clientcmdapi.Config) {
	fakeKubeFile, _ := ioutil.TempFile("", "")
	defer os.Remove(fakeKubeFile.Name())
	err := clientcmd.WriteToFile(startingConfig, fakeKubeFile.Name())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	argsToUse := make([]string, 0, 2+len(args))
	argsToUse = append(argsToUse, "--kubeconfig="+fakeKubeFile.Name())
	argsToUse = append(argsToUse, args...)

	buf := bytes.NewBuffer([]byte{})

	cmd := NewCmdConfig(clientcmd.NewDefaultPathOptions(), buf, buf)
	cmd.SetArgs(argsToUse)
	cmd.Execute()

	// outBytes, _ := ioutil.ReadFile(fakeKubeFile.Name())
	config := clientcmd.GetConfigFromFileOrDie(fakeKubeFile.Name())

	return buf.String(), *config
}
func (test getClustersTest) run(t *testing.T) {
	fakeKubeFile, _ := ioutil.TempFile("", "")
	defer os.Remove(fakeKubeFile.Name())
	err := clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	pathOptions := clientcmd.NewDefaultPathOptions()
	pathOptions.GlobalFile = fakeKubeFile.Name()
	pathOptions.EnvVar = ""
	buf := bytes.NewBuffer([]byte{})
	cmd := NewCmdConfigGetClusters(buf, pathOptions)
	if err := cmd.Execute(); err != nil {
		t.Fatalf("unexpected error executing command: %v", err)
	}
	if len(test.expected) != 0 {
		if buf.String() != test.expected {
			t.Errorf("expected %v, but got %v", test.expected, buf.String())
		}
		return
	}
}
Exemple #13
0
// ModifyConfig takes a Config object, iterates through Clusters, AuthInfos, and Contexts, uses the LocationOfOrigin if specified or
// uses the default destination file to write the results into.  This results in multiple file reads, but it's very easy to follow.
// Preferences and CurrentContext should always be set in the default destination file.  Since we can't distinguish between empty and missing values
// (no nil strings), we're forced have separate handling for them.  In the kubeconfig cases, newConfig should have at most one difference,
// that means that this code will only write into a single file.  If you want to relativizePaths, you must provide a fully qualified path in any
// modified element.
func ModifyConfig(configAccess ConfigAccess, newConfig clientcmdapi.Config, relativizePaths bool) error {
	startingConfig, err := configAccess.GetStartingConfig()
	if err != nil {
		return err
	}

	// We need to find all differences, locate their original files, read a partial config to modify only that stanza and write out the file.
	// Special case the test for current context and preferences since those always write to the default file.
	if reflect.DeepEqual(*startingConfig, newConfig) {
		// nothing to do
		return nil
	}

	if startingConfig.CurrentContext != newConfig.CurrentContext {
		if err := writeCurrentContext(configAccess, newConfig.CurrentContext); err != nil {
			return err
		}
	}

	if !reflect.DeepEqual(startingConfig.Preferences, newConfig.Preferences) {
		if err := writePreferences(configAccess, newConfig.Preferences); err != nil {
			return err
		}
	}

	// Search every cluster, authInfo, and context.  First from new to old for differences, then from old to new for deletions
	for key, cluster := range newConfig.Clusters {
		startingCluster, exists := startingConfig.Clusters[key]
		if !reflect.DeepEqual(cluster, startingCluster) || !exists {
			destinationFile := cluster.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite := getConfigFromFileOrDie(destinationFile)
			t := *cluster
			configToWrite.Clusters[key] = &t
			configToWrite.Clusters[key].LocationOfOrigin = destinationFile
			if relativizePaths {
				if err := clientcmd.RelativizeClusterLocalPaths(configToWrite.Clusters[key]); err != nil {
					return err
				}
			}

			if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, context := range newConfig.Contexts {
		startingContext, exists := startingConfig.Contexts[key]
		if !reflect.DeepEqual(context, startingContext) || !exists {
			destinationFile := context.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite := getConfigFromFileOrDie(destinationFile)
			configToWrite.Contexts[key] = context

			if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, authInfo := range newConfig.AuthInfos {
		startingAuthInfo, exists := startingConfig.AuthInfos[key]
		if !reflect.DeepEqual(authInfo, startingAuthInfo) || !exists {
			destinationFile := authInfo.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite := getConfigFromFileOrDie(destinationFile)
			t := *authInfo
			configToWrite.AuthInfos[key] = &t
			configToWrite.AuthInfos[key].LocationOfOrigin = destinationFile
			if relativizePaths {
				if err := clientcmd.RelativizeAuthInfoLocalPaths(configToWrite.AuthInfos[key]); err != nil {
					return err
				}
			}

			if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, cluster := range startingConfig.Clusters {
		if _, exists := newConfig.Clusters[key]; !exists {
			destinationFile := cluster.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite := getConfigFromFileOrDie(destinationFile)
			delete(configToWrite.Clusters, key)

			if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, context := range startingConfig.Contexts {
		if _, exists := newConfig.Contexts[key]; !exists {
			destinationFile := context.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite := getConfigFromFileOrDie(destinationFile)
			delete(configToWrite.Contexts, key)

			if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, authInfo := range startingConfig.AuthInfos {
		if _, exists := newConfig.AuthInfos[key]; !exists {
			destinationFile := authInfo.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite := getConfigFromFileOrDie(destinationFile)
			delete(configToWrite.AuthInfos, key)

			if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	return nil
}
Exemple #14
0
// bootstrapClientCert requests a client cert for kubelet if the kubeconfigPath file does not exist.
// The kubeconfig at bootstrapPath is used to request a client certificate from the API server.
// On success, a kubeconfig file referencing the generated key and obtained certificate is written to kubeconfigPath.
// The certificate and key file are stored in certDir.
func bootstrapClientCert(kubeconfigPath string, bootstrapPath string, certDir string, nodeName types.NodeName) error {
	// Short-circuit if the kubeconfig file already exists.
	// TODO: inspect the kubeconfig, ensure a rest client can be built from it, verify client cert expiration, etc.
	_, err := os.Stat(kubeconfigPath)
	if err == nil {
		glog.V(2).Infof("Kubeconfig %s exists, skipping bootstrap", kubeconfigPath)
		return nil
	}
	if !os.IsNotExist(err) {
		glog.Errorf("Error reading kubeconfig %s, skipping bootstrap: %v", kubeconfigPath, err)
		return err
	}

	glog.V(2).Info("Using bootstrap kubeconfig to generate TLS client cert, key and kubeconfig file")

	bootstrapClientConfig, err := loadRESTClientConfig(bootstrapPath)
	if err != nil {
		return fmt.Errorf("unable to load bootstrap kubeconfig: %v", err)
	}
	bootstrapClient, err := unversionedcertificates.NewForConfig(bootstrapClientConfig)
	if err != nil {
		return fmt.Errorf("unable to create certificates signing request client: %v", err)
	}

	success := false

	// Get the private key.
	keyPath, err := filepath.Abs(filepath.Join(certDir, defaultKubeletClientKeyFile))
	if err != nil {
		return fmt.Errorf("unable to build bootstrap key path: %v", err)
	}
	keyData, generatedKeyFile, err := loadOrGenerateKeyFile(keyPath)
	if err != nil {
		return err
	}
	if generatedKeyFile {
		defer func() {
			if !success {
				if err := os.Remove(keyPath); err != nil {
					glog.Warningf("Cannot clean up the key file %q: %v", keyPath, err)
				}
			}
		}()
	}

	// Get the cert.
	certPath, err := filepath.Abs(filepath.Join(certDir, defaultKubeletClientCertificateFile))
	if err != nil {
		return fmt.Errorf("unable to build bootstrap client cert path: %v", err)
	}
	certData, err := csr.RequestNodeCertificate(bootstrapClient.CertificateSigningRequests(), keyData, nodeName)
	if err != nil {
		return err
	}
	if err := certutil.WriteCert(certPath, certData); err != nil {
		return err
	}
	defer func() {
		if !success {
			if err := os.Remove(certPath); err != nil {
				glog.Warningf("Cannot clean up the cert file %q: %v", certPath, err)
			}
		}
	}()

	// Get the CA data from the bootstrap client config.
	caFile, caData := bootstrapClientConfig.CAFile, []byte{}
	if len(caFile) == 0 {
		caData = bootstrapClientConfig.CAData
	}

	// Build resulting kubeconfig.
	kubeconfigData := clientcmdapi.Config{
		// Define a cluster stanza based on the bootstrap kubeconfig.
		Clusters: map[string]*clientcmdapi.Cluster{"default-cluster": {
			Server:                   bootstrapClientConfig.Host,
			InsecureSkipTLSVerify:    bootstrapClientConfig.Insecure,
			CertificateAuthority:     caFile,
			CertificateAuthorityData: caData,
		}},
		// Define auth based on the obtained client cert.
		AuthInfos: map[string]*clientcmdapi.AuthInfo{"default-auth": {
			ClientCertificate: certPath,
			ClientKey:         keyPath,
		}},
		// Define a context that connects the auth info and cluster, and set it as the default
		Contexts: map[string]*clientcmdapi.Context{"default-context": {
			Cluster:   "default-cluster",
			AuthInfo:  "default-auth",
			Namespace: "default",
		}},
		CurrentContext: "default-context",
	}

	// Marshal to disk
	if err := clientcmd.WriteToFile(kubeconfigData, kubeconfigPath); err != nil {
		return err
	}

	success = true
	return nil
}
Exemple #15
0
func FakeKubeconfigFiles() ([]string, error) {
	kubeconfigs := []clientcmdapi.Config{
		{
			Clusters: map[string]*clientcmdapi.Cluster{
				"syndicate": {
					Server: "https://10.20.30.40",
				},
			},
			AuthInfos: map[string]*clientcmdapi.AuthInfo{
				"syndicate": {
					Token: "badge",
				},
			},
			Contexts: map[string]*clientcmdapi.Context{
				"syndicate": {
					Cluster:  "syndicate",
					AuthInfo: "syndicate",
				},
			},
			CurrentContext: "syndicate",
		},
		{
			Clusters: map[string]*clientcmdapi.Cluster{
				"ally": {
					Server: "ally256.example.com:80",
				},
			},
			AuthInfos: map[string]*clientcmdapi.AuthInfo{
				"ally": {
					Token: "souvenir",
				},
			},
			Contexts: map[string]*clientcmdapi.Context{
				"ally": {
					Cluster:  "ally",
					AuthInfo: "ally",
				},
			},
			CurrentContext: "ally",
		},
		{
			Clusters: map[string]*clientcmdapi.Cluster{
				"ally": {
					Server: "https://ally64.example.com",
				},
				"confederate": {
					Server: "10.8.8.8",
				},
			},
			AuthInfos: map[string]*clientcmdapi.AuthInfo{
				"ally": {
					Token: "souvenir",
				},
				"confederate": {
					Token: "totem",
				},
			},
			Contexts: map[string]*clientcmdapi.Context{
				"ally": {
					Cluster:  "ally",
					AuthInfo: "ally",
				},
				"confederate": {
					Cluster:  "confederate",
					AuthInfo: "confederate",
				},
			},
			CurrentContext: "confederate",
		},
	}
	kubefiles := []string{}
	for _, cfg := range kubeconfigs {
		fakeKubeFile, _ := ioutil.TempFile("", "")
		err := clientcmd.WriteToFile(cfg, fakeKubeFile.Name())
		if err != nil {
			return nil, err
		}

		kubefiles = append(kubefiles, fakeKubeFile.Name())
	}
	return kubefiles, nil
}
func (o CreateKubeConfigOptions) CreateKubeConfig() (*clientcmdapi.Config, error) {
	glog.V(4).Infof("creating a .kubeconfig with: %#v", o)

	// read all the referenced filenames
	caData, err := readFiles(o.APIServerCAFiles, []byte("\n"))
	if err != nil {
		return nil, err
	}
	certData, err := ioutil.ReadFile(o.CertFile)
	if err != nil {
		return nil, err
	}
	keyData, err := ioutil.ReadFile(o.KeyFile)
	if err != nil {
		return nil, err
	}
	certConfig, err := crypto.GetTLSCertificateConfig(o.CertFile, o.KeyFile)
	if err != nil {
		return nil, err
	}

	// determine all the nicknames
	clusterNick, err := cliconfig.GetClusterNicknameFromURL(o.APIServerURL)
	if err != nil {
		return nil, err
	}
	userNick, err := cliconfig.GetUserNicknameFromCert(clusterNick, certConfig.Certs...)
	if err != nil {
		return nil, err
	}
	contextNick := cliconfig.GetContextNickname(o.ContextNamespace, clusterNick, userNick)

	credentials := make(map[string]*clientcmdapi.AuthInfo)
	credentials[userNick] = &clientcmdapi.AuthInfo{
		ClientCertificateData: certData,
		ClientKeyData:         keyData,
	}

	clusters := make(map[string]*clientcmdapi.Cluster)
	clusters[clusterNick] = &clientcmdapi.Cluster{
		Server: o.APIServerURL,
		CertificateAuthorityData: caData,
	}

	contexts := make(map[string]*clientcmdapi.Context)
	contexts[contextNick] = &clientcmdapi.Context{Cluster: clusterNick, AuthInfo: userNick, Namespace: o.ContextNamespace}

	createPublic := (len(o.PublicAPIServerURL) > 0) && o.APIServerURL != o.PublicAPIServerURL
	if createPublic {
		publicClusterNick, err := cliconfig.GetClusterNicknameFromURL(o.PublicAPIServerURL)
		if err != nil {
			return nil, err
		}
		publicContextNick := cliconfig.GetContextNickname(o.ContextNamespace, publicClusterNick, userNick)

		clusters[publicClusterNick] = &clientcmdapi.Cluster{
			Server: o.PublicAPIServerURL,
			CertificateAuthorityData: caData,
		}
		contexts[publicContextNick] = &clientcmdapi.Context{Cluster: publicClusterNick, AuthInfo: userNick, Namespace: o.ContextNamespace}
	}

	kubeConfig := &clientcmdapi.Config{
		Clusters:       clusters,
		AuthInfos:      credentials,
		Contexts:       contexts,
		CurrentContext: contextNick,
	}

	glog.V(3).Infof("Generating '%s' API client config as %s\n", userNick, o.KubeConfigFile)
	// Ensure the parent dir exists
	if err := os.MkdirAll(filepath.Dir(o.KubeConfigFile), os.FileMode(0755)); err != nil {
		return nil, err
	}
	if err := clientcmd.WriteToFile(*kubeConfig, o.KubeConfigFile); err != nil {
		return nil, err
	}

	return kubeConfig, nil
}