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

cli/cmd: rename --kubeconfig flag to --kubeconfig-file #602

Merged
merged 5 commits into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,27 @@ func Execute() {
}
}

const (
kubeconfigFlag = "kubeconfig-file"
)

func init() {
cobra.OnInitialize(cobraInit)

RootCmd.DisableAutoGenTag = true

// Add kubeconfig flag.
RootCmd.PersistentFlags().String(
"kubeconfig",
kubeconfigFlag,
"", // Special empty default, use getKubeconfig()
"Path to kubeconfig file, taken from the asset dir if not given, and finally falls back to ~/.kube/config")
viper.BindPFlag("kubeconfig", RootCmd.PersistentFlags().Lookup("kubeconfig"))
`Path to a kubeconfig file. If empty, the following precedence order is `+
`used: 1. cluster asset dir when a lokocfg file is present in the `+
`current directory 2. KUBECONFIG environment variable 3. `+
`"~/.kube/config"`)

if err := viper.BindPFlag(kubeconfigFlag, RootCmd.PersistentFlags().Lookup(kubeconfigFlag)); err != nil {
panic("failed registering kubeconfig flag")
}

RootCmd.PersistentFlags().String("lokocfg", "./", "Path to lokocfg directory or file")
viper.BindPFlag("lokocfg", RootCmd.PersistentFlags().Lookup("lokocfg"))
Expand Down
52 changes: 43 additions & 9 deletions cli/cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ import (
"github.com/kinvolk/lokomotive/pkg/platform"
)

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

// getConfiguredBackend loads a backend from the given configuration file.
func getConfiguredBackend(lokoConfig *config.Config) (backend.Backend, hcl.Diagnostics) {
if lokoConfig.RootConfig.Backend == nil {
Expand Down Expand Up @@ -101,26 +106,55 @@ func expandKubeconfigPath(path string) string {
return path
}

// getKubeconfig finds the kubeconfig to be used. Precedence takes a specified
// flag or environment variable. Then the asset directory of the cluster is searched
// and finally the global default value is used. This cannot be done in Viper
// because we need the other values from Viper to find the asset directory.
// getKubeconfig finds the kubeconfig to be used. The precedence is the following:
// - --kubeconfig-file flag OR KUBECONFIG_FILE environent 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 getKubeconfig() (string, error) {
kubeconfig := viper.GetString("kubeconfig")
if kubeconfig != "" {
return expandKubeconfigPath(kubeconfig), nil
assetKubeconfig, err := assetsKubeconfigPath()
if err != nil {
return "", fmt.Errorf("reading kubeconfig path from configuration failed: %w", err)
}

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

return expandKubeconfigPath(pickString(paths...)), nil
}

// pickString returns first non-empty string.
func pickString(options ...string) string {
for _, option := range options {
if option != "" {
return option
}
}

return ""
}

// 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 expandKubeconfigPath(assetsKubeconfig(assetDir)), nil
return assetsKubeconfig(assetDir), nil
}

return expandKubeconfigPath("~/.kube/config"), nil
return "", nil
}

func assetsKubeconfig(assetDir string) string {
Expand Down
204 changes: 204 additions & 0 deletions cli/cmd/utils_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright 2020 The Lokomotive Authors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like a test where both the KUBECONFIG flag is set and the lokocfg file is present to make sure it takes the file defined in the lokocfg. This is the most dangerous case IMO.

//
// 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 cmd

import (
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/spf13/viper"
)

type kubeconfigSources struct {
flag string
env string
configFile string
}

func prepareKubeconfigSource(t *testing.T, k *kubeconfigSources) {
// Ensure viper flag is NOT empty.
viper.Set(kubeconfigFlag, k.flag)

if k.env == "" {
// Ensure KUBECONFIG is not set.
if err := os.Unsetenv(kubeconfigEnvVariable); err != nil {
t.Fatalf("unsetting %q environment variable: %v", kubeconfigEnvVariable, err)
}
}

if k.env != "" {
// Ensure KUBECONFIG IS set.
if err := os.Setenv(kubeconfigEnvVariable, k.env); err != nil {
t.Fatalf("setting %q environment variable: %v", kubeconfigEnvVariable, err)
}
}

// Ensure there is no lokocfg configuration in working directory.
tmpDir, err := ioutil.TempDir("", "lokoctl-tests-")
if err != nil {
t.Fatalf("creating tmp dir: %v", err)
}

t.Cleanup(func() {
if err := os.RemoveAll(tmpDir); err != nil {
t.Logf("removing temp dir %q: %v", tmpDir, err)
}
})

if err := os.Chdir(tmpDir); err != nil {
t.Fatalf("changing working directory to %q: %v", tmpDir, err)
}

if k.configFile != "" {
path := filepath.Join(tmpDir, "cluster.lokocfg")
if err := ioutil.WriteFile(path, []byte(k.configFile), 0600); err != nil {
t.Fatalf("writing file %q: %v", path, err)
}
}
}

func TestGetKubeconfigFlag(t *testing.T) {
expectedPath := "/foo"

k := &kubeconfigSources{
configFile: `cluster "packet" {
asset_dir = "/bad"

cluster_name = ""
controller_count = 0
facility = ""
management_cidrs = []
node_private_cidr = ""
project_id = ""
ssh_pubkeys = []
dns {
provider = ""
zone = ""
}
worker_pool "foo" {
count = 0
}
}`,
flag: expectedPath,
iaguis marked this conversation as resolved.
Show resolved Hide resolved
env: "/badpath",
}

prepareKubeconfigSource(t, k)

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

if kubeconfig != expectedPath {
t.Fatalf("expected %q, got %q", expectedPath, kubeconfig)
}
}

func TestGetKubeconfigConfigFile(t *testing.T) {
expectedPath := assetsKubeconfig("/foo")

k := &kubeconfigSources{
configFile: `cluster "packet" {
iaguis marked this conversation as resolved.
Show resolved Hide resolved
asset_dir = "/foo"

cluster_name = ""
controller_count = 0
facility = ""
management_cidrs = []
node_private_cidr = ""
project_id = ""
ssh_pubkeys = []
dns {
provider = ""
zone = ""
}
worker_pool "foo" {
count = 0
}
}`,
env: "/badpath",
}

prepareKubeconfigSource(t, k)

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

if kubeconfig != expectedPath {
t.Fatalf("expected %q, got %q", expectedPath, kubeconfig)
}
}

func TestGetKubeconfigBadConfigFile(t *testing.T) {
expectedPath := ""

k := &kubeconfigSources{
configFile: `cluster "packet" {
asset_dir = "/foo"
}`,
}

prepareKubeconfigSource(t, k)

kubeconfig, err := getKubeconfig()
if err == nil {
t.Errorf("getting kubeconfig with bad configuration should fail")
}

if kubeconfig != expectedPath {
t.Fatalf("if getting kubeconfig fails, empty path should be returned")
}
}

func TestGetKubeconfigEnvVariable(t *testing.T) {
expectedPath := "/foo"

k := &kubeconfigSources{
env: expectedPath,
}

prepareKubeconfigSource(t, k)

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

if kubeconfig != expectedPath {
t.Fatalf("expected %q, got %q", expectedPath, kubeconfig)
}
}

func TestGetKubeconfigDefault(t *testing.T) {
expectedPath := expandKubeconfigPath(defaultKubeconfigPath)

k := &kubeconfigSources{}

prepareKubeconfigSource(t, k)

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

if kubeconfig != expectedPath {
t.Fatalf("expected %q, got %q", expectedPath, kubeconfig)
}
}
8 changes: 4 additions & 4 deletions docs/cli/lokoctl.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ Manage Lokomotive clusters
### Options

```
-h, --help help for lokoctl
--kubeconfig string Path to kubeconfig file, taken from the asset dir if not given, and finally falls back to ~/.kube/config
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
-h, --help help for lokoctl
--kubeconfig-file string Path to a kubeconfig file. If empty, the following precedence order is used: 1. cluster asset dir when a lokocfg file is present in the current directory 2. KUBECONFIG environment variable 3. "~/.kube/config"
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
```

### SEE ALSO
Expand Down
6 changes: 3 additions & 3 deletions docs/cli/lokoctl_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ Manage a cluster
### Options inherited from parent commands

```
--kubeconfig string Path to kubeconfig file, taken from the asset dir if not given, and finally falls back to ~/.kube/config
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
--kubeconfig-file string Path to a kubeconfig file. If empty, the following precedence order is used: 1. cluster asset dir when a lokocfg file is present in the current directory 2. KUBECONFIG environment variable 3. "~/.kube/config"
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
```

### SEE ALSO
Expand Down
6 changes: 3 additions & 3 deletions docs/cli/lokoctl_cluster_apply.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ lokoctl cluster apply [flags]
### Options inherited from parent commands

```
--kubeconfig string Path to kubeconfig file, taken from the asset dir if not given, and finally falls back to ~/.kube/config
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
--kubeconfig-file string Path to a kubeconfig file. If empty, the following precedence order is used: 1. cluster asset dir when a lokocfg file is present in the current directory 2. KUBECONFIG environment variable 3. "~/.kube/config"
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
```

### SEE ALSO
Expand Down
6 changes: 3 additions & 3 deletions docs/cli/lokoctl_cluster_destroy.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ lokoctl cluster destroy [flags]
### Options inherited from parent commands

```
--kubeconfig string Path to kubeconfig file, taken from the asset dir if not given, and finally falls back to ~/.kube/config
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
--kubeconfig-file string Path to a kubeconfig file. If empty, the following precedence order is used: 1. cluster asset dir when a lokocfg file is present in the current directory 2. KUBECONFIG environment variable 3. "~/.kube/config"
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
```

### SEE ALSO
Expand Down
6 changes: 3 additions & 3 deletions docs/cli/lokoctl_component.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ Manage components
### Options inherited from parent commands

```
--kubeconfig string Path to kubeconfig file, taken from the asset dir if not given, and finally falls back to ~/.kube/config
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
--kubeconfig-file string Path to a kubeconfig file. If empty, the following precedence order is used: 1. cluster asset dir when a lokocfg file is present in the current directory 2. KUBECONFIG environment variable 3. "~/.kube/config"
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
```

### SEE ALSO
Expand Down
6 changes: 3 additions & 3 deletions docs/cli/lokoctl_component_apply.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ lokoctl component apply [flags]
### Options inherited from parent commands

```
--kubeconfig string Path to kubeconfig file, taken from the asset dir if not given, and finally falls back to ~/.kube/config
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
--kubeconfig-file string Path to a kubeconfig file. If empty, the following precedence order is used: 1. cluster asset dir when a lokocfg file is present in the current directory 2. KUBECONFIG environment variable 3. "~/.kube/config"
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
```

### SEE ALSO
Expand Down
Loading