Browse Source

cli generate command

Alexander Belanger 5 years ago
parent
commit
558c04888a

+ 6 - 2
README.md

@@ -1,6 +1,6 @@
 # Porter
 
-For development:
+### Development
 
 ```sh
 docker-compose -f docker-compose.dev.yaml up --build
@@ -16,4 +16,8 @@ From the root directory, run `go test ./...` to run all tests and ensure the bui
 
 From the root directory, run `DOCKER_BUILDKIT=1 docker build . --file ./docker/Dockerfile -t porter`. Then you can run `docker run -p 8080:8080 porter`. 
 
-To build the test container, run `DOCKER_BUILDKIT=1 docker build . --file ./docker/Dockerfile -t porter --target porter-test`. 
+To build the test container, run `DOCKER_BUILDKIT=1 docker build . --file ./docker/Dockerfile -t porter --target porter-test`. 
+
+### Running
+
+`docker run -p 8080:8080 porter1/porter:latest`

+ 36 - 0
cmd/cli/cmd/credstore/credstore.go

@@ -0,0 +1,36 @@
+package credstore
+
+import "github.com/docker/docker-credential-helpers/credentials"
+
+const (
+	url   = "https://github.com/porter-dev/porter"
+	label = "Porter Credentials"
+)
+
+// Set stores a given username/pw with a given credentials label in the OS-specific
+// credentials store
+func Set(username, pw string) error {
+	cr := &credentials.Credentials{
+		ServerURL: url,
+		Username:  username,
+		Secret:    pw,
+	}
+
+	credentials.SetCredsLabel(label)
+
+	return ns.Add(cr)
+}
+
+// Get retrieves a given username/pw with a given credentials label in the OS-specific
+// credentials store
+func Get() (string, string, error) {
+	credentials.SetCredsLabel(label)
+	return ns.Get(url)
+}
+
+// Del removes a given credential that uses a label in the OS-specific
+// credentials store
+func Del() error {
+	credentials.SetCredsLabel(label)
+	return ns.Delete(url)
+}

+ 5 - 0
cmd/cli/cmd/credstore/credstore_darwin.go

@@ -0,0 +1,5 @@
+package credstore
+
+import "github.com/docker/docker-credential-helpers/osxkeychain"
+
+var ns = osxkeychain.Osxkeychain{}

+ 5 - 0
cmd/cli/cmd/credstore/credstore_linux.go

@@ -0,0 +1,5 @@
+package credstore
+
+import "github.com/docker/docker-credential-helpers/pass"
+
+var ns = pass.Pass{}

+ 34 - 0
cmd/cli/cmd/credstore/credstore_test.go

@@ -0,0 +1,34 @@
+package credstore_test
+
+import (
+	"log"
+	"testing"
+
+	"github.com/porter-dev/porter/cmd/cli/cmd/credstore"
+)
+
+func TestSetGet(t *testing.T) {
+	credstore.Set("user", "password")
+
+	user, secret, err := credstore.Get()
+	if err == nil {
+		if user != "user" {
+			t.Errorf("Expecting user, got %s", user)
+		}
+
+		if secret != "password" {
+			t.Errorf("Expecting password, got %s", secret)
+		}
+	} else {
+		log.Println("got error:", err)
+	}
+
+	credstore.Del()
+
+	_, _, err = credstore.Get()
+
+	if err == nil {
+		t.Fatalf("Expecting an error, got nil")
+	}
+
+}

+ 5 - 0
cmd/cli/cmd/credstore/credstore_windows.go

@@ -0,0 +1,5 @@
+package credstore
+
+import "github.com/docker/docker-credential-helpers/wincred"
+
+var ns = wincred.Wincred{}

+ 27 - 0
cmd/cli/cmd/docker/agent.go

@@ -0,0 +1,27 @@
+package docker
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/docker/docker/client"
+)
+
+// Agent is a Docker client for performing operations that interact
+// with the Docker engine over REST
+type Agent struct {
+	client *client.Client
+	ctx    context.Context
+	label  string
+}
+
+// ------------------------- AGENT HELPER FUNCTIONS ------------------------- //
+
+func (a *Agent) handleDockerClientErr(err error, errPrefix string) error {
+	if strings.Contains(err.Error(), "Cannot connect to the Docker daemon") {
+		return fmt.Errorf("The Docker daemon must be running in order to start Porter: connection to %s failed", a.client.DaemonHost())
+	}
+
+	return fmt.Errorf("%s:%s", errPrefix, err.Error())
+}

+ 29 - 0
cmd/cli/cmd/docker/config.go

@@ -0,0 +1,29 @@
+package docker
+
+import (
+	"context"
+
+	"github.com/docker/docker/client"
+)
+
+const label = "CreatedByPorterCLI"
+
+// NewAgentFromEnv creates a new Docker agent using the environment variables set
+// on the host
+func NewAgentFromEnv() (*Agent, error) {
+	ctx := context.Background()
+	cli, err := client.NewClientWithOpts(
+		client.FromEnv,
+		client.WithAPIVersionNegotiation(),
+	)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &Agent{
+		client: cli,
+		ctx:    ctx,
+		label:  label,
+	}, nil
+}

+ 125 - 0
cmd/cli/cmd/docker/porter.go

@@ -0,0 +1,125 @@
+package docker
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/client"
+	"github.com/docker/go-connections/nat"
+)
+
+// PorterStartOpts are the options for starting the Porter container
+type PorterStartOpts struct {
+	Image         string
+	StartCmd      []string
+	HostPort      uint
+	ContainerPort uint
+}
+
+// StartPorterContainerAndWait pulls a specific Porter image and starts a container
+// using the Docker engine. It returns when the container has stopped.
+func (a *Agent) StartPorterContainerAndWait(opts PorterStartOpts) error {
+	// pull the specified image
+	_, err := a.client.ImagePull(a.ctx, opts.Image, types.ImagePullOptions{})
+
+	if err != nil {
+		return a.handleDockerClientErr(err, "Could not pull Porter image")
+	}
+
+	// format the port array for binding to host machine
+	ports := []string{fmt.Sprintf("127.0.0.1:%d:%d/tcp", opts.HostPort, opts.ContainerPort)}
+
+	_, portBindings, err := nat.ParsePortSpecs(ports)
+
+	if err != nil {
+		return fmt.Errorf("Unable to parse port specification %s", ports)
+	}
+
+	// create the container with a label specifying this was created via the CLI
+	resp, err := a.client.ContainerCreate(a.ctx, &container.Config{
+		Image: opts.Image,
+		Cmd:   opts.StartCmd,
+		Tty:   false,
+		Labels: map[string]string{
+			"CreatedByPorterCLI": "true",
+		},
+	}, &container.HostConfig{
+		PortBindings: portBindings,
+	}, nil, "")
+
+	if err != nil {
+		return a.handleDockerClientErr(err, "Could not create Porter container")
+	}
+
+	// start the container and listen until the container is stopped
+	if err := a.client.ContainerStart(a.ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
+		return a.handleDockerClientErr(err, "Could not start Porter container")
+	}
+
+	statusCh, errCh := a.client.ContainerWait(a.ctx, resp.ID, container.WaitConditionNotRunning)
+	select {
+	case err := <-errCh:
+		if err != nil {
+			return a.handleDockerClientErr(err, "Error waiting for stopped container")
+		}
+	case <-statusCh:
+	}
+
+	return nil
+}
+
+// StopPorterContainers finds all containers that were started via the CLI and stops them
+// without removal.
+func (a *Agent) StopPorterContainers() error {
+	ctx := context.Background()
+	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
+
+	if err != nil {
+		panic(err)
+	}
+
+	containers, err := a.getContainersCreatedByStart()
+
+	if err != nil {
+		return err
+	}
+
+	// remove all Porter containers
+	for _, container := range containers {
+		fmt.Println("removing container", container.ID)
+		timeout, _ := time.ParseDuration("15s")
+
+		err := cli.ContainerStop(ctx, container.ID, &timeout)
+
+		if err != nil {
+			return a.handleDockerClientErr(err, "Could not stop container "+container.ID)
+		}
+	}
+
+	return nil
+}
+
+// getContainersCreatedByStart gets all containers that were created by the "porter start"
+// command by looking for the label "CreatedByPorterCLI"
+func (a *Agent) getContainersCreatedByStart() ([]types.Container, error) {
+	containers, err := a.client.ContainerList(a.ctx, types.ContainerListOptions{
+		All: true,
+	})
+
+	if err != nil {
+		return nil, a.handleDockerClientErr(err, "Could not list containers")
+	}
+
+	res := make([]types.Container, 0)
+
+	for _, container := range containers {
+		if contains, ok := container.Labels[a.label]; ok && contains == "true" {
+			res = append(res, container)
+		}
+	}
+
+	return res, nil
+}

+ 86 - 0
cmd/cli/cmd/generate.go

@@ -0,0 +1,86 @@
+package cmd
+
+import (
+	"fmt"
+	"path/filepath"
+
+	"github.com/porter-dev/porter/internal/kubernetes"
+	"k8s.io/client-go/tools/clientcmd"
+	"k8s.io/client-go/util/homedir"
+
+	"github.com/spf13/cobra"
+)
+
+var (
+	outputFile string
+	print      *bool
+	contexts   *[]string
+)
+
+// generateCmd represents the generate command
+var generateCmd = &cobra.Command{
+	Use:   "generate",
+	Short: "Generates a kubeconfig with certificate data added",
+	Run: func(cmd *cobra.Command, args []string) {
+		generate(outputFile, *print, *contexts)
+	},
+}
+
+func init() {
+	home := homedir.HomeDir()
+
+	rootCmd.AddCommand(generateCmd)
+
+	generateCmd.PersistentFlags().StringVarP(
+		&outputFile,
+		"output",
+		"o",
+		filepath.Join(home, ".porter", "porter.kubeconfig"),
+		"output file location",
+	)
+
+	contexts = generateCmd.PersistentFlags().StringArray(
+		"contexts",
+		nil,
+		"the list of contexts to use (defaults to the current context)",
+	)
+
+	print = generateCmd.PersistentFlags().BoolP(
+		"print",
+		"p",
+		false,
+		"print result to stdout",
+	)
+}
+
+func generate(output string, print bool, contexts []string) error {
+	conf, err := kubernetes.GetConfigFromHostWithCertData("", contexts)
+
+	if err != nil {
+		return err
+	}
+
+	rawConf, err := conf.RawConfig()
+
+	if err != nil {
+		return err
+	}
+
+	err = clientcmd.WriteToFile(rawConf, output)
+
+	if err != nil {
+		return err
+	}
+
+	if print {
+		bytes, err := clientcmd.Write(rawConf)
+
+		if err != nil {
+			return err
+		}
+
+		fmt.Printf(string(bytes))
+	}
+
+	return nil
+}

+ 63 - 0
cmd/cli/cmd/helpers.go

@@ -0,0 +1,63 @@
+package cmd
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"os"
+	"os/signal"
+	"strings"
+	"syscall"
+
+	"golang.org/x/crypto/ssh/terminal"
+)
+
+func closeHandler(closer func() error) {
+	sig := make(chan os.Signal)
+	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
+	go func() {
+		<-sig
+		err := closer()
+
+		if err == nil {
+			fmt.Println("shutdown successful")
+			os.Exit(0)
+		}
+
+		fmt.Printf("shutdown unsuccessful: %s\n", err.Error())
+		os.Exit(1)
+	}()
+}
+
+func promptPlaintext(prompt string) (string, error) {
+	reader := bufio.NewReader(os.Stdin)
+
+	fmt.Print(prompt)
+	text, err := reader.ReadString('\n')
+
+	if err != nil {
+		return "", err
+	}
+
+	return strings.TrimSpace(text), nil
+}
+
+func promptPasswordWithConfirmation() (string, error) {
+	fmt.Print("Password: ")
+	pw, err := terminal.ReadPassword(0)
+	fmt.Print("\r")
+
+	if err != nil {
+		return "", err
+	}
+
+	fmt.Print("Confirm password: ")
+	confirmPw, err := terminal.ReadPassword(0)
+	fmt.Print("\n")
+
+	if strings.TrimSpace(string(pw)) != strings.TrimSpace(string(confirmPw)) {
+		return "", errors.New("Passwords do not match")
+	}
+
+	return strings.TrimSpace(string(pw)), nil
+}

+ 24 - 0
cmd/cli/cmd/root.go

@@ -0,0 +1,24 @@
+package cmd
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "porter",
+	Short: "Porter is a dashboard for managing Kubernetes clusters.",
+	Long:  `Porter is a tool for creating, versioning, and updating Kubernetes deployments using a visual dashboard. For more information, visit github.com/porter-dev/porter`,
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	if err := rootCmd.Execute(); err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+}

+ 49 - 0
cmd/cli/cmd/start.go

@@ -0,0 +1,49 @@
+package cmd
+
+import (
+	"fmt"
+
+	"github.com/porter-dev/porter/cmd/cli/cmd/credstore"
+
+	"github.com/spf13/cobra"
+)
+
+// startCmd represents the start command
+var startCmd = &cobra.Command{
+	Args: func(cmd *cobra.Command, args []string) error {
+		return nil
+	},
+	Use:   "start",
+	Short: "Starts a Porter instance using the Docker engine.",
+	Run: func(cmd *cobra.Command, args []string) {
+		var username, pw string
+		var err error
+
+		// if not insecure, or username/pw set incorrectly, prompt for new username/pw
+		if username, pw, err = credstore.Get(); !cmd.Flag("insecure").Changed && err != nil {
+			username, err = promptPlaintext("Email: ")
+
+			if err != nil {
+				fmt.Println(err.Error())
+				return
+			}
+
+			pw, err = promptPasswordWithConfirmation()
+
+			if err != nil {
+				fmt.Println(err.Error())
+				return
+			}
+
+			credstore.Set(username, pw)
+		}
+
+		// start()
+	},
+}
+
+func init() {
+	// closeHandler(stop)
+	rootCmd.AddCommand(startCmd)
+	startCmd.PersistentFlags().Bool("insecure", false, "skip admin setup and authorization")
+}

+ 2 - 41
cmd/cli/main.go

@@ -1,48 +1,9 @@
 package main
 
 import (
-	"context"
-	"fmt"
-	"strings"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/client"
+	"github.com/porter-dev/porter/cmd/cli/cmd"
 )
 
-func handleDockerClientErr(err error, cli *client.Client) {
-	if strings.Contains(err.Error(), "Cannot connect to the Docker daemon") {
-		fmt.Printf("The Docker daemon must be running in order to start Porter: connection to %s failed.\n", cli.DaemonHost())
-		return
-	}
-
-	fmt.Println(err.Error())
-	return
-}
-
 func main() {
-	ctx := context.Background()
-	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
-
-	if err != nil {
-		panic(err)
-	}
-
-	containers, err := cli.ContainerList(ctx, types.ContainerListOptions{})
-	if err != nil {
-		handleDockerClientErr(err, cli)
-		return
-	}
-
-	for _, container := range containers {
-		fmt.Printf("%s %s\n", container.ID[:10], container.Image)
-	}
-
-	// images, err := cli.ImageList(ctx, types.ImageListOptions{})
-	// if err != nil {
-	// 	panic(err)
-	// }
-
-	// for _, image := range images {
-	// 	fmt.Println(image.ID)
-	// }
+	cmd.Execute()
 }

+ 5 - 1
go.mod

@@ -9,7 +9,10 @@ require (
 	github.com/containerd/containerd v1.4.1 // indirect
 	github.com/cosmtrek/air v1.21.2 // indirect
 	github.com/creack/pty v1.1.11 // indirect
+	github.com/danieljoos/wincred v1.1.0 // indirect
 	github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce
+	github.com/docker/docker-credential-helpers v0.6.3
+	github.com/docker/go-connections v0.4.0
 	github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 // indirect
 	github.com/evanphx/json-patch v4.9.0+incompatible // indirect
 	github.com/fatih/color v1.9.0 // indirect
@@ -33,10 +36,11 @@ require (
 	github.com/pkg/errors v0.9.1
 	github.com/rs/zerolog v1.20.0
 	github.com/sirupsen/logrus v1.7.0
+	github.com/spf13/cobra v1.0.0
 	github.com/stretchr/testify v1.6.1
 	golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
 	golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect
-	golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 // indirect
+	golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6
 	golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
 	google.golang.org/genproto v0.0.0-20201014134559-03b6142f0dc9 // indirect
 	google.golang.org/grpc v1.33.0 // indirect

+ 2 - 0
go.sum

@@ -183,6 +183,8 @@ github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
 github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g=
+github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

+ 160 - 1
internal/kubernetes/kubeconfig.go

@@ -1,9 +1,16 @@
 package kubernetes
 
 import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
 	"github.com/porter-dev/porter/internal/models"
 	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/client-go/tools/clientcmd/api"
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+	"k8s.io/client-go/util/homedir"
 )
 
 // GetRestrictedClientConfigFromBytes returns a clientcmd.ClientConfig from a raw kubeconfig,
@@ -90,7 +97,6 @@ func GetContextsFromBytes(bytes []byte, allowedContexts []string) ([]models.Cont
 	contexts := toContexts(&rawConf, allowedContexts)
 
 	return contexts, nil
-
 }
 
 func toContexts(rawConf *api.Config, allowedContexts []string) []models.Context {
@@ -137,3 +143,156 @@ func createAllowedContextMap(contexts []string) map[string]string {
 
 	return aContextMap
 }
+
+// GetConfigFromHostWithCertData gets the kubeconfig using default options set on the host:
+// the kubeconfig can either be retrieved from a specified path or an environment variable.
+// This function only outputs a clientcmd that uses the allowedContexts.
+//
+// This function also populates all of the certificate data that's specified as a filepath.
+func GetConfigFromHostWithCertData(kubeconfigPath string, allowedContexts []string) (clientcmd.ClientConfig, error) {
+	envVarName := clientcmd.RecommendedConfigPathEnvVar
+
+	if kubeconfigPath != "" {
+		if _, err := os.Stat(kubeconfigPath); os.IsNotExist(err) {
+			// the specified kubeconfig does not exist so fallback to other options
+			kubeconfigPath = ""
+		}
+	}
+
+	if kubeconfigPath == "" && os.Getenv(envVarName) == "" {
+		if home := homedir.HomeDir(); home != "" {
+			kubeconfigPath = filepath.Join(home, ".kube", "config")
+		}
+	}
+
+	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
+	loadingRules.ExplicitPath = kubeconfigPath
+
+	clientConf := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
+	rawConf, err := clientConf.RawConfig()
+
+	if err != nil {
+		return nil, err
+	}
+
+	populateCertificateRefs(&rawConf)
+
+	if len(allowedContexts) == 0 {
+		allowedContexts = []string{rawConf.CurrentContext}
+
+		if allowedContexts[0] == "" {
+			return nil, fmt.Errorf("at least one context must be specified")
+		}
+	}
+
+	res, err := stripAndValidateClientContexts(&rawConf, allowedContexts[0], allowedContexts)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return res, nil
+}
+
+// GetRestrictedClientConfigFromBytes returns a clientcmd.ClientConfig from a raw kubeconfig,
+// a context name, and the set of allowed contexts.
+func stripAndValidateClientContexts(
+	rawConf *clientcmdapi.Config,
+	currentContext string,
+	allowedContexts []string,
+) (clientcmd.ClientConfig, error) {
+	// grab a copy to get the pointer and set clusters, authinfos, and contexts to empty
+	copyConf := rawConf.DeepCopy()
+
+	copyConf.Clusters = make(map[string]*api.Cluster)
+	copyConf.AuthInfos = make(map[string]*api.AuthInfo)
+	copyConf.Contexts = make(map[string]*api.Context)
+	copyConf.CurrentContext = currentContext
+
+	// put allowed clusters in a map
+	aContextMap := createAllowedContextMap(allowedContexts)
+
+	for contextName, context := range rawConf.Contexts {
+		userName := context.AuthInfo
+		clusterName := context.Cluster
+		authInfo, userFound := rawConf.AuthInfos[userName]
+		cluster, clusterFound := rawConf.Clusters[clusterName]
+
+		// make sure the cluster is "allowed"
+		_, isAllowed := aContextMap[contextName]
+
+		if userFound && clusterFound && isAllowed {
+			copyConf.Clusters[clusterName] = cluster
+			copyConf.AuthInfos[userName] = authInfo
+			copyConf.Contexts[contextName] = context
+		}
+	}
+
+	// validate the copyConf and create a ClientConfig
+	err := clientcmd.Validate(*copyConf)
+
+	if err != nil {
+		return nil, err
+	}
+
+	clientConf := clientcmd.NewDefaultClientConfig(*copyConf, &clientcmd.ConfigOverrides{})
+
+	return clientConf, nil
+}
+
+func populateCertificateRefs(config *clientcmdapi.Config) {
+	for _, cluster := range config.Clusters {
+		refs := clientcmd.GetClusterFileReferences(cluster)
+		for _, str := range refs {
+			// only write certificate if the file reference is CA
+			if *str != cluster.CertificateAuthority {
+				break
+			}
+
+			fileBytes, err := ioutil.ReadFile(*str)
+
+			if err != nil {
+				continue
+			}
+
+			cluster.CertificateAuthorityData = fileBytes
+			cluster.CertificateAuthority = ""
+		}
+	}
+
+	for _, authInfo := range config.AuthInfos {
+		refs := clientcmd.GetAuthInfoFileReferences(authInfo)
+		for _, str := range refs {
+			if *str == "" {
+				continue
+			}
+
+			var refType int
+
+			if authInfo.ClientCertificate == *str {
+				refType = 0
+			} else if authInfo.ClientKey == *str {
+				refType = 1
+			} else if authInfo.TokenFile == *str {
+				refType = 2
+			}
+
+			fileBytes, err := ioutil.ReadFile(*str)
+
+			if err != nil {
+				continue
+			}
+
+			if refType == 0 {
+				authInfo.ClientCertificateData = fileBytes
+				authInfo.ClientCertificate = ""
+			} else if refType == 1 {
+				authInfo.ClientKeyData = fileBytes
+				authInfo.ClientKey = ""
+			} else if refType == 2 {
+				authInfo.Token = string(fileBytes)
+				authInfo.TokenFile = ""
+			}
+		}
+	}
+}