Skip to content
This repository has been archived by the owner on Jun 29, 2022. It is now read-only.

Commit

Permalink
cli/cmd: use kubeconfig from Terraform output, not from assets dir
Browse files Browse the repository at this point in the history
Closes #608

Signed-off-by: Mateusz Gozdek <mateusz@kinvolk.io>
  • Loading branch information
invidian committed Aug 4, 2020
1 parent f4db245 commit 9a4c8bc
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 87 deletions.
4 changes: 2 additions & 2 deletions cli/cmd/cluster-apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func runClusterApply(cmd *cobra.Command, args []string) {
"args": args,
})

ex, p, lokoConfig, assetDir := initialize(ctxLogger)
ex, p, lokoConfig, assetDir := initialize(ctxLogger, true)

exists := clusterExists(ctxLogger, ex)
if exists && !confirm {
Expand All @@ -79,7 +79,7 @@ func runClusterApply(cmd *cobra.Command, args []string) {

fmt.Printf("\nYour configurations are stored in %s\n", assetDir)

kubeconfig, err := getKubeconfig()
kubeconfig, err := getKubeconfig(p, ex)
if err != nil {
ctxLogger.Fatalf("Failed to get kubeconfig: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/cluster-destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func runClusterDestroy(cmd *cobra.Command, args []string) {
"args": args,
})

ex, p, _, _ := initialize(ctxLogger)
ex, p, _, _ := initialize(ctxLogger, true)

if !clusterExists(ctxLogger, ex) {
ctxLogger.Println("Cluster already destroyed, nothing to do")
Expand Down
10 changes: 7 additions & 3 deletions cli/cmd/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ func init() {

// initialize does common initialization actions between cluster operations
// and returns created objects to the caller for further use.
func initialize(ctxLogger *logrus.Entry) (*terraform.Executor, platform.Platform, *config.Config, string) {
func initialize(ctxLogger *logrus.Entry, requirePlatform bool) (*terraform.Executor, platform.Platform, *config.Config, string) {
lokoConfig, diags := getLokoConfig()
if len(diags) > 0 {
ctxLogger.Fatal(diags)
}

p, diags := getConfiguredPlatform()
p, diags := getConfiguredPlatform(lokoConfig)
if diags.HasErrors() {
for _, diagnostic := range diags {
ctxLogger.Error(diagnostic.Error())
Expand All @@ -61,7 +61,11 @@ func initialize(ctxLogger *logrus.Entry) (*terraform.Executor, platform.Platform
}

if p == nil {
ctxLogger.Fatal("No cluster configured")
if requirePlatform {
ctxLogger.Fatal("No cluster configured")
}

return nil, nil, nil, ""
}

// Get the configured backend for the cluster. Backend types currently supported: local, s3.
Expand Down
4 changes: 3 additions & 1 deletion cli/cmd/component-apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ func runApply(cmd *cobra.Command, args []string) {
}
}

kubeconfig, err := getKubeconfig()
ex, p, _, _ := initialize(contextLogger, false)

kubeconfig, err := getKubeconfig(p, ex)
if err != nil {
contextLogger.Fatalf("Error in finding kubeconfig file: %s", err)
}
Expand Down
7 changes: 6 additions & 1 deletion cli/cmd/component-delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (

"github.com/kinvolk/lokomotive/pkg/components"
"github.com/kinvolk/lokomotive/pkg/components/util"
"github.com/kinvolk/lokomotive/pkg/platform"
"github.com/kinvolk/lokomotive/pkg/terraform"
)

var componentDeleteCmd = &cobra.Command{
Expand Down Expand Up @@ -86,7 +88,10 @@ func runDelete(cmd *cobra.Command, args []string) {
return
}

kubeconfig, err := getKubeconfig()
var ex *terraform.Executor
var p platform.Platform

kubeconfig, err := getKubeconfig(p, ex)
if err != nil {
contextLogger.Fatalf("Error in finding kubeconfig file: %s", err)
}
Expand Down
16 changes: 3 additions & 13 deletions cli/cmd/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ func runHealth(cmd *cobra.Command, args []string) {
"args": args,
})

kubeconfig, err := getKubeconfig()
ex, p, _, _ := initialize(contextLogger, true)

kubeconfig, err := getKubeconfig(p, ex)
if err != nil {
contextLogger.Fatalf("Error in finding kubeconfig file: %s", err)
}
Expand All @@ -52,18 +54,6 @@ func runHealth(cmd *cobra.Command, args []string) {
contextLogger.Fatalf("Error in creating setting up Kubernetes client: %q", err)
}

p, diags := getConfiguredPlatform()
if diags.HasErrors() {
for _, diagnostic := range diags {
contextLogger.Error(diagnostic.Error())
}
contextLogger.Fatal("Errors found while loading cluster configuration")
}

if p == nil {
contextLogger.Fatal("No cluster configured")
}

cluster, err := lokomotive.NewCluster(cs, p.Meta().ExpectedNodes)
if err != nil {
contextLogger.Fatalf("Error in creating new Lokomotive cluster: %q", err)
Expand Down
102 changes: 39 additions & 63 deletions cli/cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"

"github.com/hashicorp/hcl/v2"
"github.com/mitchellh/go-homedir"
Expand All @@ -27,11 +26,13 @@ import (
"github.com/kinvolk/lokomotive/pkg/backend"
"github.com/kinvolk/lokomotive/pkg/config"
"github.com/kinvolk/lokomotive/pkg/platform"
"github.com/kinvolk/lokomotive/pkg/terraform"
)

const (
kubeconfigEnvVariable = "KUBECONFIG"
defaultKubeconfigPath = "~/.kube/config"
kubeconfigEnvVariable = "KUBECONFIG"
defaultKubeconfigPath = "~/.kube/config"
kubeconfigTerraformOutputKey = "kubeconfig"
)

// getConfiguredBackend loads a backend from the given configuration file.
Expand All @@ -54,12 +55,7 @@ func getConfiguredBackend(lokoConfig *config.Config) (backend.Backend, hcl.Diagn
}

// getConfiguredPlatform loads a platform from the given configuration file.
func getConfiguredPlatform() (platform.Platform, hcl.Diagnostics) {
lokoConfig, diags := getLokoConfig()
if diags.HasErrors() {
return nil, diags
}

func getConfiguredPlatform(lokoConfig *config.Config) (platform.Platform, hcl.Diagnostics) {
if lokoConfig.RootConfig.Cluster == nil {
// No cluster defined and no configuration error
return nil, hcl.Diagnostics{}
Expand All @@ -77,28 +73,16 @@ func getConfiguredPlatform() (platform.Platform, hcl.Diagnostics) {
return platform, platform.LoadConfig(&lokoConfig.RootConfig.Cluster.Config, lokoConfig.EvalContext)
}

// getAssetDir extracts the asset path from the cluster configuration.
// It is empty if there is no cluster defined. An error is returned if the
// cluster configuration has problems.
func getAssetDir() (string, error) {
cfg, diags := getConfiguredPlatform()
if diags.HasErrors() {
return "", fmt.Errorf("cannot load config: %s", diags)
}
if cfg == nil {
// No cluster defined and no configuration error
return "", nil
func readKubeconfigFromTerraformOutput(ex *terraform.Executor, key string) ([]byte, error) {
kubeconfig := ""
if err := ex.Output(key, &kubeconfig); err != nil {
return nil, fmt.Errorf("reading kubeconfig file content from Terraform state: %w", err)
}

return cfg.Meta().AssetDir, nil
return []byte(kubeconfig), nil
}

func getKubeconfig() ([]byte, error) {
path, err := getKubeconfigPath()
if err != nil {
return nil, fmt.Errorf("failed getting kubeconfig path: %w", err)
}

func readKubeconfigFromFile(path string) ([]byte, error) {
if expandedPath, err := homedir.Expand(path); err == nil {
path = expandedPath
}
Expand All @@ -109,54 +93,46 @@ func getKubeconfig() ([]byte, error) {
return ioutil.ReadFile(path) // #nosec G304
}

// getKubeconfig finds the kubeconfig to be used. The precedence is the following:
// - --kubeconfig-file flag OR KUBECONFIG_FILE environment variable (the latter
// is a side-effect of cobra/viper and should NOT be documented because it's
// confusing).
// - Asset directory from cluster configuration.
// - KUBECONFIG environment variable.
// - ~/.kube/config path, which is the default for kubectl.
func getKubeconfigPath() (string, error) {
assetKubeconfig, err := assetsKubeconfigPath()
if err != nil {
return "", fmt.Errorf("reading kubeconfig path from configuration failed: %w", err)
// getKubeconfig returns content of kubeconfig file, based on the cluster configuration, flags and
// environment variables set.
//
// The hierarchy of selecting kubeconfig file to use is the following:
//
// - --kubeconfig-file OR KUBECONFIG_FILE environent variable (the latter
// is a side-effect of cobra/viper and should NOT be documented because it's
// confusing). It always takes precendence if it's not empty.
//
// - If cluster configuration is found and contains platform configuration, kubeconfig from the
// Terraform state will be used.
//
// - Path from KUBECONFIG environment variable.
//
// - Default KUBECONFIG path, which is ~/.kube/config.
func getKubeconfig(p platform.Platform, ex *terraform.Executor) ([]byte, error) {
// TODO: This should probably be passed as an argument, so we don't do global lookups here,
// but for now, it stays here, as it would duplicate the code and require all callers to import
// viper package.
flagPath := viper.GetString(kubeconfigFlag)

// Path from the flag takes precedence over all other source of kubeconfig content.
if flagPath == "" && p != nil {
return readKubeconfigFromTerraformOutput(ex, kubeconfigTerraformOutputKey)
}

paths := []string{
viper.GetString(kubeconfigFlag),
assetKubeconfig,
flagPath,
os.Getenv(kubeconfigEnvVariable),
defaultKubeconfigPath,
}

for _, path := range paths {
if path != "" {
return path, nil
return readKubeconfigFromFile(path)
}
}

return "", nil
}

// assetsKubeconfigPath reads the lokocfg configuration and returns
// the kubeconfig path defined in it.
//
// If no configuration is defined, empty string is returned.
func assetsKubeconfigPath() (string, error) {
assetDir, err := getAssetDir()
if err != nil {
return "", err
}

if assetDir != "" {
return assetsKubeconfig(assetDir), nil
}

return "", nil
}

func assetsKubeconfig(assetDir string) string {
return filepath.Join(assetDir, "cluster-assets", "auth", "kubeconfig")
// As we use defaultKubeconfigPath, this should never be triggered.
return nil, fmt.Errorf("no valid kubeconfig found")
}

func getLokoConfig() (*config.Config, hcl.Diagnostics) {
Expand Down
6 changes: 3 additions & 3 deletions cli/cmd/utils_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func TestGetKubeconfigBadConfig(t *testing.T) {

prepareKubeconfigSource(t, k)

kubeconfig, err := getKubeconfig()
kubeconfig, err := getKubeconfig(nil, nil)
if err == nil {
t.Errorf("getting kubeconfig with bad configuration should fail")
}
Expand Down Expand Up @@ -113,7 +113,7 @@ func TestGetKubeconfig(t *testing.T) {

prepareKubeconfigSource(t, k)

kubeconfig, err := getKubeconfig()
kubeconfig, err := getKubeconfig(nil, nil)
if err != nil {
t.Fatalf("getting kubeconfig: %v", err)
}
Expand Down Expand Up @@ -151,7 +151,7 @@ func TestGetKubeconfigPathFlag(t *testing.T) {

prepareKubeconfigSource(t, k)

kubeconfig, err := getKubeconfigPath()
kubeconfig, err := getKubeconfig(nil, nil)
if err != nil {
t.Fatalf("getting kubeconfig: %v", err)
}
Expand Down

0 comments on commit 9a4c8bc

Please sign in to comment.