Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split cmd for readability sake #40

Merged
merged 1 commit into from
Apr 20, 2018
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
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,22 @@ Available Commands:
version Print the version number

Flags:
-s, --api-server string Kubernetes api-server url
-c, --config string Configuration file (default "/etc/katafygio/katafygio.yaml")
-d, --dry-run Dry-run mode: don't store anything.
-m, --dump-only Dump mode: dump everything and exit
-x, --exclude-kind stringSlice Ressource kind to exclude. Eg. 'deployment'
-y, --exclude-object stringSlice Object to exclude. Eg. 'configmap:kube-system/kube-dns'
-l, --filter string Label filter. Select only objects matching the label.
-g, --git-url string Git repository URL
-p, --healthcheck-port int Port for answering healthchecks on /health url
-h, --help help for katafygio
-k, --kube-config string Kubernetes config path
-e, --local-dir string Where to dump yaml files (default "./kubernetes-backup")
-v, --log-level string Log level (default "info")
-o, --log-output string Log output (default "stderr")
-r, --log-server string Log server (if using syslog)
-i, --resync-interval int Resync interval in seconds (0 to disable) (default 300)
-s, --api-server string Kubernetes api-server url
-c, --config string Configuration file (default "/etc/katafygio/katafygio.yaml")
-d, --dry-run Dry-run mode: don't store anything
-m, --dump-only Dump mode: dump everything once and exit
-x, --exclude-kind strings Ressource kind to exclude. Eg. 'deployment'
-y, --exclude-object strings Object to exclude. Eg. 'configmap:kube-system/kube-dns'
-l, --filter string Label filter. Select only objects matching the label
-g, --git-url string Git repository URL
-p, --healthcheck-port int Port for answering healthchecks on /health url
-h, --help help for katafygio
-k, --kube-config string Kubernetes config path
-e, --local-dir string Where to dump yaml files (default "./kubernetes-backup")
-v, --log-level string Log level (default "info")
-o, --log-output string Log output (default "stderr")
-r, --log-server string Log server (if using syslog)
-i, --resync-interval int Full resync interval in seconds (0 to disable) (default 900)
```

## Config file and env variables
Expand Down
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