Skip to content

Commit

Permalink
Split cmd for readability sake
Browse files Browse the repository at this point in the history
Try to ease discovery/readability by splitting cmd (cobra/viper
setup) in various files. The proeminent cmd/execute.go should
hopefuly make the flow clear:
```
main() -> execute -> run.Run() -> {http, recorder, observer, events, git}
```
  • Loading branch information
bpineau committed Apr 20, 2018
1 parent 6fa5415 commit 1311020
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 125 deletions.
36 changes: 36 additions & 0 deletions cmd/configfile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cmd

import (
"os"
"strings"

"github.com/spf13/viper"
"k8s.io/client-go/util/homedir"
)

func loadConfigFile() {
viper.SetConfigType("yaml")
viper.SetConfigName(appName)

// all possible config file paths, by priority
viper.AddConfigPath("/etc/katafygio/")
if home := homedir.HomeDir(); home != "" {
viper.AddConfigPath(home)
}
viper.AddConfigPath(".")

// prefer the config file path provided by cli flag, if any
if _, err := os.Stat(cfgFile); !os.IsNotExist(err) {
viper.SetConfigFile(cfgFile)
}

// allow config params through prefixed env variables
viper.SetEnvPrefix("KF")
replacer := strings.NewReplacer("-", "_", ".", "_DOT_")
viper.SetEnvKeyReplacer(replacer)
viper.AutomaticEnv()

if err := viper.ReadInConfig(); err == nil {
RootCmd.Printf("Using config file: %s", viper.ConfigFileUsed())
}
}
133 changes: 11 additions & 122 deletions cmd/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,51 @@ package cmd

import (
"fmt"
"log"
"os"
"strings"
"time"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/client-go/util/homedir"

"github.com/bpineau/katafygio/config"
klog "github.com/bpineau/katafygio/pkg/log"
"github.com/bpineau/katafygio/pkg/log"
"github.com/bpineau/katafygio/pkg/run"
)

const appName = "katafygio"

var (
version = "0.3.0"

cfgFile string
apiServer string
kubeConf string
dryRun bool
dumpMode bool
logLevel string
logOutput string
logServer string
filter string
localDir string
gitURL string
healthP int
resync int
exclkind []string
exclobj []string

versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number",
Run: func(cmd *cobra.Command, args []string) {
RootCmd.Printf("%s version %s\n", appName, version)
},
}

// RootCmd represents the base command when called without any subcommands
// RootCmd is our main entry point, launching pkg/run.Run()
RootCmd = &cobra.Command{
Use: appName,
Short: "Backup Kubernetes cluster as yaml files",
Long: "Backup Kubernetes cluster as yaml files in a git repository.\n" +
"--exclude-kind (x) and --exclude-object (-y) may be specified several times.",
"--exclude-kind (-x) and --exclude-object (-y) may be specified several times.",

RunE: func(cmd *cobra.Command, args []string) error {
resync := time.Duration(viper.GetInt("resync-interval")) * time.Second
logger := log.New(viper.GetString("log.level"),
viper.GetString("log.server"),
viper.GetString("log.output"))

conf := &config.KfConfig{
DryRun: viper.GetBool("dry-run"),
DumpMode: viper.GetBool("dump-only"),
Logger: klog.New(viper.GetString("log.level"), viper.GetString("log.server"), viper.GetString("log.output")),
Logger: logger,
LocalDir: viper.GetString("local-dir"),
GitURL: viper.GetString("git-url"),
Filter: viper.GetString("filter"),
ExcludeKind: viper.GetStringSlice("exclude-kind"),
ExcludeObject: viper.GetStringSlice("exclude-object"),
HealthPort: viper.GetInt("healthcheck-port"),
ResyncIntv: time.Duration(viper.GetInt("resync-interval")) * time.Second,
ResyncIntv: resync,
}

err := conf.Init(viper.GetString("api-server"), viper.GetString("kube-config"))
if err != nil {
return fmt.Errorf("Failed to initialize the configuration: %v", err)
}

run.Run(conf)
run.Run(conf) // <- this is where things happens
return nil
},
}
Expand All @@ -81,89 +56,3 @@ var (
func Execute() error {
return RootCmd.Execute()
}

func bindPFlag(key string, cmd string) {
if err := viper.BindPFlag(key, RootCmd.PersistentFlags().Lookup(cmd)); err != nil {
log.Fatal("Failed to bind cli argument:", err)
}
}

func init() {
cobra.OnInitialize(initConfig)
RootCmd.AddCommand(versionCmd)

defaultCfg := "/etc/katafygio/" + appName + ".yaml"
RootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", defaultCfg, "Configuration file")

RootCmd.PersistentFlags().StringVarP(&apiServer, "api-server", "s", "", "Kubernetes api-server url")
bindPFlag("api-server", "api-server")

RootCmd.PersistentFlags().StringVarP(&kubeConf, "kube-config", "k", "", "Kubernetes config path")
bindPFlag("kube-config", "kube-config")
if err := viper.BindEnv("kube-config", "KUBECONFIG"); err != nil {
log.Fatal("Failed to bind cli argument:", err)
}

RootCmd.PersistentFlags().BoolVarP(&dryRun, "dry-run", "d", false, "Dry-run mode: don't store anything")
bindPFlag("dry-run", "dry-run")

RootCmd.PersistentFlags().BoolVarP(&dumpMode, "dump-only", "m", false, "Dump mode: dump everything once and exit")
bindPFlag("dump-only", "dump-only")

RootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "v", "info", "Log level")
bindPFlag("log.level", "log-level")

RootCmd.PersistentFlags().StringVarP(&logOutput, "log-output", "o", "stderr", "Log output")
bindPFlag("log.output", "log-output")

RootCmd.PersistentFlags().StringVarP(&logServer, "log-server", "r", "", "Log server (if using syslog)")
bindPFlag("log.server", "log-server")

RootCmd.PersistentFlags().StringVarP(&localDir, "local-dir", "e", "./kubernetes-backup", "Where to dump yaml files")
bindPFlag("local-dir", "local-dir")

RootCmd.PersistentFlags().StringVarP(&gitURL, "git-url", "g", "", "Git repository URL")
bindPFlag("git-url", "git-url")

RootCmd.PersistentFlags().StringSliceVarP(&exclkind, "exclude-kind", "x", nil, "Ressource kind to exclude. Eg. 'deployment'")
bindPFlag("exclude-kind", "exclude-kind")

RootCmd.PersistentFlags().StringSliceVarP(&exclobj, "exclude-object", "y", nil, "Object to exclude. Eg. 'configmap:kube-system/kube-dns'")
bindPFlag("exclude-object", "exclude-object")

RootCmd.PersistentFlags().StringVarP(&filter, "filter", "l", "", "Label filter. Select only objects matching the label.")
bindPFlag("filter", "filter")

RootCmd.PersistentFlags().IntVarP(&healthP, "healthcheck-port", "p", 0, "Port for answering healthchecks on /health url")
bindPFlag("healthcheck-port", "healthcheck-port")

RootCmd.PersistentFlags().IntVarP(&resync, "resync-interval", "i", 900, "Full resync interval in seconds (0 to disable)")
bindPFlag("resync-interval", "resync-interval")
}

func initConfig() {
viper.SetConfigType("yaml")
viper.SetConfigName(appName)

// all possible config file paths, by priority
viper.AddConfigPath("/etc/katafygio/")
if home := homedir.HomeDir(); home != "" {
viper.AddConfigPath(home)
}
viper.AddConfigPath(".")

// prefer the config file path provided by cli flag, if any
if _, err := os.Stat(cfgFile); !os.IsNotExist(err) {
viper.SetConfigFile(cfgFile)
}

// allow config params through prefixed env variables
viper.SetEnvPrefix("KF")
replacer := strings.NewReplacer("-", "_", ".", "_DOT_")
viper.SetEnvKeyReplacer(replacer)
viper.AutomaticEnv()

if err := viper.ReadInConfig(); err == nil {
RootCmd.Printf("Using config file: %s", viper.ConfigFileUsed())
}
}
85 changes: 85 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cmd

import (
"log"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var (
cfgFile string
apiServer string
kubeConf string
dryRun bool
dumpMode bool
logLevel string
logOutput string
logServer string
filter string
localDir string
gitURL string
healthP int
resync int
exclkind []string
exclobj []string
)

func bindPFlag(key string, cmd string) {
if err := viper.BindPFlag(key, RootCmd.PersistentFlags().Lookup(cmd)); err != nil {
log.Fatal("Failed to bind cli argument:", err)
}
}

func init() {
cobra.OnInitialize(loadConfigFile)
RootCmd.AddCommand(versionCmd)

defaultCfg := "/etc/katafygio/" + appName + ".yaml"
RootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", defaultCfg, "Configuration file")

RootCmd.PersistentFlags().StringVarP(&apiServer, "api-server", "s", "", "Kubernetes api-server url")
bindPFlag("api-server", "api-server")

RootCmd.PersistentFlags().StringVarP(&kubeConf, "kube-config", "k", "", "Kubernetes config path")
bindPFlag("kube-config", "kube-config")
if err := viper.BindEnv("kube-config", "KUBECONFIG"); err != nil {
log.Fatal("Failed to bind cli argument:", err)
}

RootCmd.PersistentFlags().BoolVarP(&dryRun, "dry-run", "d", false, "Dry-run mode: don't store anything")
bindPFlag("dry-run", "dry-run")

RootCmd.PersistentFlags().BoolVarP(&dumpMode, "dump-only", "m", false, "Dump mode: dump everything once and exit")
bindPFlag("dump-only", "dump-only")

RootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "v", "info", "Log level")
bindPFlag("log.level", "log-level")

RootCmd.PersistentFlags().StringVarP(&logOutput, "log-output", "o", "stderr", "Log output")
bindPFlag("log.output", "log-output")

RootCmd.PersistentFlags().StringVarP(&logServer, "log-server", "r", "", "Log server (if using syslog)")
bindPFlag("log.server", "log-server")

RootCmd.PersistentFlags().StringVarP(&localDir, "local-dir", "e", "./kubernetes-backup", "Where to dump yaml files")
bindPFlag("local-dir", "local-dir")

RootCmd.PersistentFlags().StringVarP(&gitURL, "git-url", "g", "", "Git repository URL")
bindPFlag("git-url", "git-url")

RootCmd.PersistentFlags().StringSliceVarP(&exclkind, "exclude-kind", "x", nil, "Ressource kind to exclude. Eg. 'deployment'")
bindPFlag("exclude-kind", "exclude-kind")

RootCmd.PersistentFlags().StringSliceVarP(&exclobj, "exclude-object", "y", nil, "Object to exclude. Eg. 'configmap:kube-system/kube-dns'")
bindPFlag("exclude-object", "exclude-object")

RootCmd.PersistentFlags().StringVarP(&filter, "filter", "l", "", "Label filter. Select only objects matching the label.")
bindPFlag("filter", "filter")

RootCmd.PersistentFlags().IntVarP(&healthP, "healthcheck-port", "p", 0, "Port for answering healthchecks on /health url")
bindPFlag("healthcheck-port", "healthcheck-port")

RootCmd.PersistentFlags().IntVarP(&resync, "resync-interval", "i", 900, "Full resync interval in seconds (0 to disable)")
bindPFlag("resync-interval", "resync-interval")
}
15 changes: 15 additions & 0 deletions cmd/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cmd

import "github.com/spf13/cobra"

var (
version = "0.3.0"

versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number",
Run: func(cmd *cobra.Command, args []string) {
RootCmd.Printf("%s version %s\n", appName, version)
},
}
)
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import (
"github.com/bpineau/katafygio/cmd"
)

//var privateExitHandler func(code int) = os.Exit
var privateExitHandler = os.Exit

// ExitWrapper allow unit tests on main() exit values
// ExitWrapper allow tests on main() exit values
func ExitWrapper(exit int) {
privateExitHandler(exit)
}

func main() {
if err := cmd.Execute(); err != nil {
err := cmd.Execute()
if err != nil {
fmt.Printf("%+v", err)
ExitWrapper(1)
}
Expand Down

0 comments on commit 1311020

Please sign in to comment.