Skip to content

Commit

Permalink
Merge pull request #36 from kris-nova/1.0.0-rc
Browse files Browse the repository at this point in the history
adding kubeconfig feature
  • Loading branch information
krisnova authored Jul 9, 2021
2 parents e31969b + b5b3cec commit aef71c1
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 8 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

all: compile
version=$(shell git rev-parse HEAD)
version="1.0.0"

compile: ## Compile for the local architecture ⚙
@echo "Compiling..."
Expand Down
67 changes: 64 additions & 3 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,65 @@ package naml
import (
"fmt"
"path"
"strings"

"k8s.io/client-go/util/homedir"

"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)

const (
KubeconfigEnvironmentalVariable = "KUBECONFIG"
KubeconfigDefaultDirectory = ".kube"
KubeconfigDefaultFile = "config"
)

// cachedClient is package level state that will cache the Clienset
var cachedClient *kubernetes.Clientset

// kubeConfigPathValue is a very flexible string, so we have to handle it
// carefully. It can be either the environmental variable, or the CLI flag
//
// KUBECONFIG environmental variable
//
// This can be a list of configs such as:
// config:config-sample:config-boops
//
// --kubeconfig
// This can be a single path such as
// ~/.kube/config
// /home/me/.kube/config
// config (which should assume ${HOME}.kube/config)
var kubeConfigPathValue string

// Client is used to authenticate with Kubernetes and build the Kube client
// for the rest of the program.
func Client() (*kubernetes.Clientset, error) {
kubeConfigPath := path.Join(homedir.HomeDir(), ".kube", "config")
return ClientFromPath(kubeConfigPath)
if cachedClient != nil {
return cachedClient, nil
}

// Windows support requires us to split on ";" instead of ":"
// I personally have no use case to care for this. Pull requests accepted.
configs := strings.Split(kubeConfigPathValue, ":")
if len(configs) > 1 {
for _, config := range configs {
client, err := ClientFromPath(path.Join(homedir.HomeDir(), KubeconfigDefaultDirectory, config))
if err == nil {
// Just pick the first one
return client, nil
}
}
// If we get here, we have no valid config
// We can just silently try and fail...
}
client, err := ClientFromPath(kubeConfigPathValue)
if err != nil {
return nil, fmt.Errorf("unable to load kube config: %v", err)
}
cachedClient = client
return cachedClient, nil
}

// ClientFromPath is used to authenticate with Kubernetes and build the Kube client
Expand All @@ -54,3 +102,16 @@ func ClientFromPath(kubeConfigPath string) (*kubernetes.Clientset, error) {
}
return client, nil
}

// ClientFromFlags will plumb well-known command line flags through to the kubeconfig
func ClientFromFlags(apiUrl, kubeConfigPath string) (*kubernetes.Clientset, error) {
config, err := clientcmd.BuildConfigFromFlags(apiUrl, kubeConfigPath)
if err != nil {
return nil, fmt.Errorf("unable to find local kube config [%s]: %v", kubeConfigPath, err)
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("unable to build kube config: %v", err)
}
return client, nil
}
40 changes: 36 additions & 4 deletions cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ package naml
import (
"fmt"
"os"
"strings"

"k8s.io/client-go/util/homedir"

"github.com/kris-nova/logger"
"github.com/urfave/cli/v2"
Expand Down Expand Up @@ -57,6 +60,10 @@ func RunCommandLineWithOptions() error {
// verbose is the logger verbosity
var verbose bool = false

// kubeconfig is the --kubeconfig value
// which is used in our Client() code
var kubeconfig string

// cli assumes "-v" for version.
// override that here
cli.VersionFlag = &cli.BoolFlag{
Expand Down Expand Up @@ -100,6 +107,12 @@ func RunCommandLineWithOptions() error {
Usage: "toggle verbose mode for logger.",
Destination: &verbose,
},
&cli.StringFlag{
Name: "kubeconfig",
Value: "~/.kube/config",
Usage: "Kubeconfig path (default: ~/.kube/config)",
Destination: &kubeconfig,
},
&cli.StringSliceFlag{
Name: "with",
Aliases: []string{"f", "w"}, // use -f to follow kubectl -f syntax trolol
Expand All @@ -121,7 +134,7 @@ func RunCommandLineWithOptions() error {
UsageText: "naml install [app]",
Action: func(c *cli.Context) error {
// ----------------------------------
err := AllInit(verbose, with.Value())
err := AllInit(kubeconfig, verbose, with.Value())
if err != nil {
return err
}
Expand Down Expand Up @@ -156,7 +169,7 @@ func RunCommandLineWithOptions() error {
UsageText: "naml uninstall [app]",
Action: func(c *cli.Context) error {
// ----------------------------------
err := AllInit(verbose, with.Value())
err := AllInit(kubeconfig, verbose, with.Value())
if err != nil {
return err
}
Expand Down Expand Up @@ -190,7 +203,7 @@ func RunCommandLineWithOptions() error {
Usage: "List applications",
Action: func(c *cli.Context) error {
// ----------------------------------
err := AllInit(verbose, with.Value())
err := AllInit(kubeconfig, verbose, with.Value())
if err != nil {
return err
}
Expand Down Expand Up @@ -226,7 +239,7 @@ func RunCommandLineWithOptions() error {

// AllInit is the "constructor" for every command line flag.
// This is how we use naml -w to include sub-namls
func AllInit(verbose bool, with []string) error {
func AllInit(kubeConfigPath string, verbose bool, with []string) error {

// [ Verbosity System ]
if verbose {
Expand All @@ -236,6 +249,25 @@ func AllInit(verbose bool, with []string) error {
logger.BitwiseLevel = logger.LogAlways | logger.LogCritical | logger.LogWarning | logger.LogDeprecated
}

// [ Kubeconfig System ]
// 1. Check if environmental variable is set
// 2. Default to the --kubeconfig flag
// 3. Follow the logic in the Clientcmd (path, masterURL, inCluster, default)

// Format "~" in command line string
kubeConfigPath = strings.ReplaceAll(kubeConfigPath, "~", homedir.HomeDir())

// Here be dragons
// We probably need an entire fucking client package, but for now
// this will get us to 1.0.0
envVarValue := os.Getenv(KubeconfigEnvironmentalVariable)
if envVarValue == "" {
kubeConfigPathValue = kubeConfigPath
} else {
kubeConfigPathValue = envVarValue
}
logger.Debug("Kubeconfig Value: %s", kubeConfigPathValue)

// [ Child Runtime System ]
if len(with) > 0 {
for _, childPath := range with {
Expand Down
2 changes: 1 addition & 1 deletion rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ func AddRPC(path string) error {
break
default:
switch {
case childOut.Len() > 0:
case childOut.Len() > 1:
message := childOut.Bytes()
// According to the naml RPC this should be the TCP port
rpcHello := string(message)
Expand Down

0 comments on commit aef71c1

Please sign in to comment.