Skip to content

Commit

Permalink
Add basic functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
mateimicu committed Apr 25, 2020
1 parent 90c57f7 commit c30971d
Show file tree
Hide file tree
Showing 14 changed files with 1,021 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# kdiscover

A cli application used for discovering all EKS clusters that it can and exporting there kube-config's


## ToDo

- [x] allow for templated alias for the cluster name (give access to region, partition, cluster name, cluster arn)
- [x] investigate maybe it is worth parsing the kubeconfig instead of executing another command
- [ ] CleanUp comments. print statements
- [ ] prepare a ci pipeline and maybe a cd one
- [ ] prepare packages for brew to distribute this project (and maybe others)
- [ ] add documentation to the readme
- [ ] refactor modules (move from cmd to internals and add a better structure to internals)
- [ ] add documentation to modules
- [ ] add tests for important parts and expecially some integration tests
Empty file added cmd/.gitkeep
Empty file.
58 changes: 58 additions & 0 deletions cmd/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cmd

import (
"fmt"

"github.com/jedib0t/go-pretty/table"
"github.com/jedib0t/go-pretty/text"
"github.com/mateimicu/kdiscover/internal"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

func newListCommand() *cobra.Command {
listCommand := &cobra.Command{
Use: "list",
Short: "List all EKS Clusters",
Run: func(cmd *cobra.Command, args []string) {
remoteEKSClusters := internal.GetEKSClusters(awsRegions)
log.Info(remoteEKSClusters)

tw := table.NewWriter()
tw.AppendHeader(table.Row{"Cluster Name", "Region", "Status", "Exported Locally"})
rows := []table.Row{}
for _, cls := range remoteEKSClusters {
rows = append(rows, table.Row{cls.Name, cls.Region, cls.Status, getExportedString(&cls, kubeconfigPath)})
}
tw.AppendRows(rows)

tw.AppendFooter(table.Row{"", "Number of clusters", len(remoteEKSClusters)})

tw.SetAutoIndex(true)
tw.SortBy([]table.SortBy{{Name: "Region", Mode: table.Dsc}})

tw.SetStyle(table.StyleLight)
tw.Style().Format.Header = text.FormatLower
tw.Style().Format.Footer = text.FormatLower
tw.Style().Options.SeparateColumns = false
tw.SetColumnConfigs([]table.ColumnConfig{
{
Name: "Exported Locally",
Align: text.AlignCenter,
AlignHeader: text.AlignCenter,
},
})
// render it
fmt.Println(tw.Render())
},
}

return listCommand
}

func getExportedString(cls *internal.Cluster, kubeconfigPath string) string {
if cls.IsExported(kubeconfigPath) {
return "Yes"
}
return "No"
}
79 changes: 79 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cmd

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

"github.com/mateimicu/kdiscover/internal"
"github.com/spf13/cobra"

log "github.com/sirupsen/logrus"
)

var (
awsPartitions []string
awsRegions []string
kubeconfigPath string
debug bool
rootCmd = &cobra.Command{
Use: "kdiscover",
Short: "Discover all EKS clusters on an account.",
Long: `kdiscover is a simple utility that can search
all regions on an AWS account and try to find all EKS clsuters.
It will try to upgrade the kube-config for each cluster.`,

PersistentPreRun: func(cmd *cobra.Command, args []string) {
if debug {
log.SetLevel(log.DebugLevel)
} else {
log.SetOutput(ioutil.Discard)
}

log.WithFields(log.Fields{
"partitions": awsPartitions,
}).Debug("Search regions for partitions")

awsRegions = internal.GetRegions(awsPartitions)

if len(awsRegions) == 0 {
log.WithFields(log.Fields{
"partitions": awsPartitions,
}).Error("Can't find regions for partitions")
os.Exit(1)
}

log.WithFields(log.Fields{
"regions": awsRegions,
}).Info("Founds regions")
},

Run: func(cmd *cobra.Command, args []string) {
cmd.HelpFunc()(cmd, args)
},
}
)

func init() {
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "set log level to debug")

rootCmd.PersistentFlags().StringSliceVar(
&awsPartitions,
"aws-partitions",
[]string{"aws"},
fmt.Sprintf("In what partitions to search for clusters. Supported %v", internal.AllowedParitions()))

rootCmd.PersistentFlags().StringVar(&kubeconfigPath, "kubeconfig-path", internal.GetDefaultKubeconfigPath(), "Path to the kubeconfig to work with")
}

// Execute will create the tree of commands and will start parsing and execution
func Execute() {

rootCmd.AddCommand(newListCommand())
rootCmd.AddCommand(newUpdateCommand())

if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
140 changes: 140 additions & 0 deletions cmd/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package cmd

import (
"bytes"
"fmt"
"html/template"
"io"
"os"
"path"

"github.com/mateimicu/kdiscover/internal"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var (
backupKubeconfig bool
alias string
)

func newUpdateCommand() *cobra.Command {
updateCommand := &cobra.Command{
Use: "update",
Short: "Update all EKS Clusters",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(cmd.Short)
remoteEKSClusters := internal.GetEKSClusters(awsRegions)
log.Info(remoteEKSClusters)
fmt.Printf("Found %v clusters remote\n", len(remoteEKSClusters))
if backupKubeconfig && fileExists(kubeconfigPath) {
bName, err := generateBackupName(kubeconfigPath)
if err != nil {
log.WithFields(log.Fields{
"kubeconfig-path": kubeconfigPath,
}).Info("Can't generate backup file name ")
}
fmt.Printf("Backup kubeconfig to %v\n", bName)
copy(kubeconfigPath, bName)
}
internal.UpdateKubeconfig(remoteEKSClusters, kubeconfigPath, contextName{templateValue: alias})
},
}
updateCommand.Flags().BoolVar(&backupKubeconfig, "backup-kubeconfig", true, "Backup cubeconfig before update")
updateCommand.Flags().StringVar(&alias, "context-name-alias", "{{.Name}}", "Template for the context name. Has acces to Cluster type")

return updateCommand
}

type contextName struct {
templateValue string
}

func (c contextName) GetContextName(cls internal.Cluster) (string, error) {
tmpl, err := template.New("context-name").Parse(c.templateValue)
if err != nil {
return "", err
}
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, cls)
if err != nil {
return "", err
}
return tpl.String(), nil
}

func copy(src, dst string) error {
sourceFileStat, err := os.Stat(src)
if err != nil {
return err
}

if !sourceFileStat.Mode().IsRegular() {
return fmt.Errorf("%s is not a regular file", src)
}

source, err := os.Open(src)
if err != nil {
return err
}
defer source.Close()

_, err = os.Stat(dst)
if err == nil {
return fmt.Errorf("file %s already exists", dst)
}

destination, err := os.Create(dst)
if err != nil {
return err
}
defer destination.Close()

if err != nil {
panic(err)
}

buf := make([]byte, 1000000)
for {
n, err := source.Read(buf)
if err != nil && err != io.EOF {
return err
}
if n == 0 {
break
}

if _, err := destination.Write(buf[:n]); err != nil {
return err
}
}
return err
}

func generateBackupName(origin string) (string, error) {
fi, err := os.Stat(origin)
if err != nil {
return "", err
}
if !fi.Mode().IsRegular() {
return "", fmt.Errorf("%s is not a regular file", origin)
}
oName := path.Base(origin)
oDir := path.Dir(origin)
for {
if fileExists(path.Join(oDir, oName)) {
oName = oName + ".bak"
} else {
break
}
}
return path.Join(oDir, oName), nil
}

func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
19 changes: 19 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module github.com/mateimicu/kdiscover

go 1.13

require (
github.com/Masterminds/semver v1.5.0
github.com/aws/aws-sdk-go v1.29.29
github.com/google/martian v2.1.0+incompatible
github.com/jedib0t/go-pretty v4.3.0+incompatible
github.com/mateimicu/eks-discover v0.0.0-20200419024348-4eb34377d311
github.com/mitchellh/go-homedir v1.1.0
github.com/sirupsen/logrus v1.2.0
github.com/spf13/cobra v0.0.6
github.com/spf13/viper v1.4.0
google.golang.org/appengine v1.5.0
gopkg.in/yaml.v2 v2.2.8
k8s.io/client-go v0.18.2
k8s.io/utils v0.0.0-20200414100711-2df71ebbae66
)
Loading

0 comments on commit c30971d

Please sign in to comment.