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

Pass kubeconfig content around rather than file path #631

Merged
merged 9 commits into from
Aug 3, 2020
26 changes: 12 additions & 14 deletions cli/cmd/cluster-apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package cmd

import (
"fmt"
"io/ioutil"

"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -80,8 +79,12 @@ func runClusterApply(cmd *cobra.Command, args []string) {

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

kubeconfigPath := assetsKubeconfig(assetDir)
if err := verifyCluster(kubeconfigPath, p.Meta().ExpectedNodes); err != nil {
kubeconfig, err := getKubeconfig()
if err != nil {
ctxLogger.Fatalf("Failed to get kubeconfig: %v", err)
}

if err := verifyCluster(kubeconfig, p.Meta().ExpectedNodes); err != nil {
ctxLogger.Fatalf("Verify cluster: %v", err)
}

Expand All @@ -90,10 +93,10 @@ func runClusterApply(cmd *cobra.Command, args []string) {
fmt.Printf("\nEnsuring that cluster controlplane is up to date.\n")

cu := controlplaneUpdater{
kubeconfigPath: kubeconfigPath,
assetDir: assetDir,
ctxLogger: *ctxLogger,
ex: *ex,
kubeconfig: kubeconfig,
invidian marked this conversation as resolved.
Show resolved Hide resolved
assetDir: assetDir,
ctxLogger: *ctxLogger,
ex: *ex,
}

releases := []string{"pod-checkpointer", "kube-apiserver", "kubernetes", "calico"}
Expand All @@ -119,18 +122,13 @@ func runClusterApply(cmd *cobra.Command, args []string) {
ctxLogger.Println("Applying component configuration")

if len(componentsToApply) > 0 {
if err := applyComponents(lokoConfig, kubeconfigPath, componentsToApply...); err != nil {
if err := applyComponents(lokoConfig, kubeconfig, componentsToApply...); err != nil {
ctxLogger.Fatalf("Applying component configuration failed: %v", err)
}
}
}

func verifyCluster(kubeconfigPath string, expectedNodes int) error {
kubeconfig, err := ioutil.ReadFile(kubeconfigPath) // #nosec G304
if err != nil {
return errors.Wrapf(err, "failed to read kubeconfig file")
}

func verifyCluster(kubeconfig []byte, expectedNodes int) error {
cs, err := k8sutil.NewClientset(kubeconfig)
if err != nil {
return errors.Wrapf(err, "failed to set up clientset")
Expand Down
10 changes: 5 additions & 5 deletions cli/cmd/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ func clusterExists(ctxLogger *logrus.Entry, ex *terraform.Executor) bool {
}

type controlplaneUpdater struct {
kubeconfigPath string
assetDir string
ctxLogger logrus.Entry
ex terraform.Executor
kubeconfig []byte
assetDir string
ctxLogger logrus.Entry
ex terraform.Executor
}

func (c controlplaneUpdater) getControlplaneChart(name string) (*chart.Chart, error) {
Expand Down Expand Up @@ -187,7 +187,7 @@ func (c controlplaneUpdater) upgradeComponent(component string) {
"component": component,
})

actionConfig, err := util.HelmActionConfig("kube-system", c.kubeconfigPath)
actionConfig, err := util.HelmActionConfig("kube-system", c.kubeconfig)
if err != nil {
ctxLogger.Fatalf("Failed initializing helm: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/component-apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func runApply(cmd *cobra.Command, args []string) {
}
}

func applyComponents(lokoConfig *config.Config, kubeconfig string, componentNames ...string) error {
func applyComponents(lokoConfig *config.Config, kubeconfig []byte, componentNames ...string) error {
for _, componentName := range componentNames {
fmt.Printf("Applying component '%s'...\n", componentName)

Expand Down
82 changes: 2 additions & 80 deletions cli/cmd/component-delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,14 @@
package cmd

import (
invidian marked this conversation as resolved.
Show resolved Hide resolved
"context"
"fmt"
"io/ioutil"
"strings"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/kinvolk/lokomotive/pkg/components"
"github.com/kinvolk/lokomotive/pkg/components/util"
"github.com/kinvolk/lokomotive/pkg/k8sutil"
)

var componentDeleteCmd = &cobra.Command{
Expand Down Expand Up @@ -102,11 +96,11 @@ func runDelete(cmd *cobra.Command, args []string) {
}
}

func deleteComponents(kubeconfig string, componentObjects ...components.Component) error {
func deleteComponents(kubeconfig []byte, componentObjects ...components.Component) error {
for _, compObj := range componentObjects {
fmt.Printf("Deleting component '%s'...\n", compObj.Metadata().Name)

if err := deleteHelmRelease(compObj, kubeconfig, deleteNamespace); err != nil {
if err := util.UninstallComponent(compObj, kubeconfig, deleteNamespace); err != nil {
return err
}

Expand All @@ -118,75 +112,3 @@ func deleteComponents(kubeconfig string, componentObjects ...components.Componen

return nil
}

// deleteComponent deletes a component.
func deleteHelmRelease(c components.Component, kubeconfig string, deleteNSBool bool) error {
name := c.Metadata().Name
if name == "" {
// This should never fail in real user usage, if this does that means the component was not
// created with all the needed information.
panic(fmt.Errorf("component name is empty"))
}

ns := c.Metadata().Namespace
if ns == "" {
// This should never fail in real user usage, if this does that means the component was not
// created with all the needed information.
panic(fmt.Errorf("component %s namespace is empty", name))
}

cfg, err := util.HelmActionConfig(ns, kubeconfig)
if err != nil {
return fmt.Errorf("failed preparing helm client: %w", err)
}

history := action.NewHistory(cfg)
// Check if the component's release exists. If it does only then proceed to delete.
//
// Note: It is assumed that this call will return error only when the release does not exist.
// The error check is ignored to make `lokoctl component delete ..` idempotent.
// We rely on the fact that the 'component name' == 'release name'. Since component's name is
// hardcoded and unlikely to change release name won't change as well. And they will be
// consistent if installed by lokoctl. So it is highly unlikely that following call will return
// any other error than "release not found".
if _, err := history.Run(name); err == nil {
uninstall := action.NewUninstall(cfg)

// Ignore the err when we have deleted the release already or it does not exist for some reason.
if _, err := uninstall.Run(name); err != nil {
return err
}
}

if deleteNSBool {
if err := deleteNS(ns, kubeconfig); err != nil {
return err
}
}

return nil
}

func deleteNS(ns string, kubeconfig string) error {
kubeconfigContent, err := ioutil.ReadFile(kubeconfig) // #nosec G304
if err != nil {
return fmt.Errorf("failed to read kubeconfig file: %v", err)
}

cs, err := k8sutil.NewClientset(kubeconfigContent)
if err != nil {
return err
}

// Delete the manually created namespace which was not created by helm.
if err = cs.CoreV1().Namespaces().Delete(context.TODO(), ns, metav1.DeleteOptions{}); err != nil {
// Ignore error when the namespace does not exist.
if errors.IsNotFound(err) {
return nil
}

return err
}

return nil
}
15 changes: 4 additions & 11 deletions cli/cmd/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package cmd

import (
"fmt"
"io/ioutil"
"os"
"text/tabwriter"

Expand All @@ -28,10 +27,9 @@ import (
)

var healthCmd = &cobra.Command{
Use: "health",
Short: "Get the health of a cluster",
Run: runHealth,
PersistentPreRunE: doesKubeconfigExist,
Use: "health",
Short: "Get the health of a cluster",
Run: runHealth,
}

func init() {
Expand All @@ -49,12 +47,7 @@ func runHealth(cmd *cobra.Command, args []string) {
contextLogger.Fatalf("Error in finding kubeconfig file: %s", err)
}

kubeconfigContent, err := ioutil.ReadFile(kubeconfig) // #nosec G304
if err != nil {
contextLogger.Fatalf("Failed to read kubeconfig file: %v", err)
}

cs, err := k8sutil.NewClientset(kubeconfigContent)
cs, err := k8sutil.NewClientset(kubeconfig)
if err != nil {
contextLogger.Fatalf("Error in creating setting up Kubernetes client: %q", err)
}
Expand Down
45 changes: 15 additions & 30 deletions cli/cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ package cmd

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

"github.com/hashicorp/hcl/v2"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/kinvolk/lokomotive/pkg/backend"
Expand Down Expand Up @@ -93,17 +93,20 @@ func getAssetDir() (string, error) {
return cfg.Meta().AssetDir, nil
}

// expandKubeconfigPath tries to expand ~ in the given kubeconfig path.
// However, if that fails, it just returns original path as the best effort.
func expandKubeconfigPath(path string) string {
func getKubeconfig() ([]byte, error) {
path, err := getKubeconfigPath()
if err != nil {
return nil, fmt.Errorf("failed getting kubeconfig path: %w", err)
}

if expandedPath, err := homedir.Expand(path); err == nil {
return expandedPath
path = expandedPath
}

// homedir.Expand is too restrictive for the ~ prefix,
// i.e., it errors on "~somepath" which is a valid path,
// so just return the original path.
return path
// so just read from the original path.
return ioutil.ReadFile(path) // #nosec G304
}

// getKubeconfig finds the kubeconfig to be used. The precedence is the following:
Expand All @@ -113,7 +116,7 @@ func expandKubeconfigPath(path string) string {
// - Asset directory from cluster configuration.
// - KUBECONFIG environment variable.
// - ~/.kube/config path, which is the default for kubectl.
func getKubeconfig() (string, error) {
func getKubeconfigPath() (string, error) {
assetKubeconfig, err := assetsKubeconfigPath()
if err != nil {
return "", fmt.Errorf("reading kubeconfig path from configuration failed: %w", err)
Expand All @@ -126,18 +129,13 @@ func getKubeconfig() (string, error) {
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
for _, path := range paths {
if path != "" {
return path, nil
}
}

return ""
return "", nil
}

// assetsKubeconfigPath reads the lokocfg configuration and returns
Expand All @@ -161,19 +159,6 @@ func assetsKubeconfig(assetDir string) string {
return filepath.Join(assetDir, "cluster-assets", "auth", "kubeconfig")
}

// doesKubeconfigExist checks if the kubeconfig provided by user exists
func doesKubeconfigExist(*cobra.Command, []string) error {
var err error
kubeconfig, err := getKubeconfig()
if err != nil {
return err
}
if _, err = os.Stat(kubeconfig); os.IsNotExist(err) {
return fmt.Errorf("Kubeconfig %q not found", kubeconfig)
}
return err
}

func getLokoConfig() (*config.Config, hcl.Diagnostics) {
return config.LoadConfig(viper.GetString("lokocfg"), viper.GetString("lokocfg-vars"))
}
Expand Down
Loading