Skip to content

Commit d787f20

Browse files
committed
Split cmd for readability sake
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} ```
1 parent 6fa5415 commit d787f20

File tree

6 files changed

+166
-141
lines changed

6 files changed

+166
-141
lines changed

README.md

+16-16
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,22 @@ Available Commands:
5858
version Print the version number
5959

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

7979
## Config file and env variables

cmd/configfile.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package cmd
2+
3+
import (
4+
"os"
5+
"strings"
6+
7+
"github.com/spf13/viper"
8+
"k8s.io/client-go/util/homedir"
9+
)
10+
11+
func loadConfigFile() {
12+
viper.SetConfigType("yaml")
13+
viper.SetConfigName(appName)
14+
15+
// all possible config file paths, by priority
16+
viper.AddConfigPath("/etc/katafygio/")
17+
if home := homedir.HomeDir(); home != "" {
18+
viper.AddConfigPath(home)
19+
}
20+
viper.AddConfigPath(".")
21+
22+
// prefer the config file path provided by cli flag, if any
23+
if _, err := os.Stat(cfgFile); !os.IsNotExist(err) {
24+
viper.SetConfigFile(cfgFile)
25+
}
26+
27+
// allow config params through prefixed env variables
28+
viper.SetEnvPrefix("KF")
29+
replacer := strings.NewReplacer("-", "_", ".", "_DOT_")
30+
viper.SetEnvKeyReplacer(replacer)
31+
viper.AutomaticEnv()
32+
33+
if err := viper.ReadInConfig(); err == nil {
34+
RootCmd.Printf("Using config file: %s", viper.ConfigFileUsed())
35+
}
36+
}

cmd/execute.go

+11-122
Original file line numberDiff line numberDiff line change
@@ -2,76 +2,51 @@ package cmd
22

33
import (
44
"fmt"
5-
"log"
6-
"os"
7-
"strings"
85
"time"
96

107
"github.com/spf13/cobra"
118
"github.com/spf13/viper"
12-
"k8s.io/client-go/util/homedir"
139

1410
"github.com/bpineau/katafygio/config"
15-
klog "github.com/bpineau/katafygio/pkg/log"
11+
"github.com/bpineau/katafygio/pkg/log"
1612
"github.com/bpineau/katafygio/pkg/run"
1713
)
1814

1915
const appName = "katafygio"
2016

2117
var (
22-
version = "0.3.0"
23-
24-
cfgFile string
25-
apiServer string
26-
kubeConf string
27-
dryRun bool
28-
dumpMode bool
29-
logLevel string
30-
logOutput string
31-
logServer string
32-
filter string
33-
localDir string
34-
gitURL string
35-
healthP int
36-
resync int
37-
exclkind []string
38-
exclobj []string
39-
40-
versionCmd = &cobra.Command{
41-
Use: "version",
42-
Short: "Print the version number",
43-
Run: func(cmd *cobra.Command, args []string) {
44-
RootCmd.Printf("%s version %s\n", appName, version)
45-
},
46-
}
47-
48-
// RootCmd represents the base command when called without any subcommands
18+
// RootCmd is our main entry point, launching pkg/run.Run()
4919
RootCmd = &cobra.Command{
5020
Use: appName,
5121
Short: "Backup Kubernetes cluster as yaml files",
5222
Long: "Backup Kubernetes cluster as yaml files in a git repository.\n" +
53-
"--exclude-kind (x) and --exclude-object (-y) may be specified several times.",
23+
"--exclude-kind (-x) and --exclude-object (-y) may be specified several times.",
5424

5525
RunE: func(cmd *cobra.Command, args []string) error {
26+
resync := time.Duration(viper.GetInt("resync-interval")) * time.Second
27+
logger := log.New(viper.GetString("log.level"),
28+
viper.GetString("log.server"),
29+
viper.GetString("log.output"))
30+
5631
conf := &config.KfConfig{
5732
DryRun: viper.GetBool("dry-run"),
5833
DumpMode: viper.GetBool("dump-only"),
59-
Logger: klog.New(viper.GetString("log.level"), viper.GetString("log.server"), viper.GetString("log.output")),
34+
Logger: logger,
6035
LocalDir: viper.GetString("local-dir"),
6136
GitURL: viper.GetString("git-url"),
6237
Filter: viper.GetString("filter"),
6338
ExcludeKind: viper.GetStringSlice("exclude-kind"),
6439
ExcludeObject: viper.GetStringSlice("exclude-object"),
6540
HealthPort: viper.GetInt("healthcheck-port"),
66-
ResyncIntv: time.Duration(viper.GetInt("resync-interval")) * time.Second,
41+
ResyncIntv: resync,
6742
}
6843

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

74-
run.Run(conf)
49+
run.Run(conf) // <- this is where things happens
7550
return nil
7651
},
7752
}
@@ -81,89 +56,3 @@ var (
8156
func Execute() error {
8257
return RootCmd.Execute()
8358
}
84-
85-
func bindPFlag(key string, cmd string) {
86-
if err := viper.BindPFlag(key, RootCmd.PersistentFlags().Lookup(cmd)); err != nil {
87-
log.Fatal("Failed to bind cli argument:", err)
88-
}
89-
}
90-
91-
func init() {
92-
cobra.OnInitialize(initConfig)
93-
RootCmd.AddCommand(versionCmd)
94-
95-
defaultCfg := "/etc/katafygio/" + appName + ".yaml"
96-
RootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", defaultCfg, "Configuration file")
97-
98-
RootCmd.PersistentFlags().StringVarP(&apiServer, "api-server", "s", "", "Kubernetes api-server url")
99-
bindPFlag("api-server", "api-server")
100-
101-
RootCmd.PersistentFlags().StringVarP(&kubeConf, "kube-config", "k", "", "Kubernetes config path")
102-
bindPFlag("kube-config", "kube-config")
103-
if err := viper.BindEnv("kube-config", "KUBECONFIG"); err != nil {
104-
log.Fatal("Failed to bind cli argument:", err)
105-
}
106-
107-
RootCmd.PersistentFlags().BoolVarP(&dryRun, "dry-run", "d", false, "Dry-run mode: don't store anything")
108-
bindPFlag("dry-run", "dry-run")
109-
110-
RootCmd.PersistentFlags().BoolVarP(&dumpMode, "dump-only", "m", false, "Dump mode: dump everything once and exit")
111-
bindPFlag("dump-only", "dump-only")
112-
113-
RootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "v", "info", "Log level")
114-
bindPFlag("log.level", "log-level")
115-
116-
RootCmd.PersistentFlags().StringVarP(&logOutput, "log-output", "o", "stderr", "Log output")
117-
bindPFlag("log.output", "log-output")
118-
119-
RootCmd.PersistentFlags().StringVarP(&logServer, "log-server", "r", "", "Log server (if using syslog)")
120-
bindPFlag("log.server", "log-server")
121-
122-
RootCmd.PersistentFlags().StringVarP(&localDir, "local-dir", "e", "./kubernetes-backup", "Where to dump yaml files")
123-
bindPFlag("local-dir", "local-dir")
124-
125-
RootCmd.PersistentFlags().StringVarP(&gitURL, "git-url", "g", "", "Git repository URL")
126-
bindPFlag("git-url", "git-url")
127-
128-
RootCmd.PersistentFlags().StringSliceVarP(&exclkind, "exclude-kind", "x", nil, "Ressource kind to exclude. Eg. 'deployment'")
129-
bindPFlag("exclude-kind", "exclude-kind")
130-
131-
RootCmd.PersistentFlags().StringSliceVarP(&exclobj, "exclude-object", "y", nil, "Object to exclude. Eg. 'configmap:kube-system/kube-dns'")
132-
bindPFlag("exclude-object", "exclude-object")
133-
134-
RootCmd.PersistentFlags().StringVarP(&filter, "filter", "l", "", "Label filter. Select only objects matching the label.")
135-
bindPFlag("filter", "filter")
136-
137-
RootCmd.PersistentFlags().IntVarP(&healthP, "healthcheck-port", "p", 0, "Port for answering healthchecks on /health url")
138-
bindPFlag("healthcheck-port", "healthcheck-port")
139-
140-
RootCmd.PersistentFlags().IntVarP(&resync, "resync-interval", "i", 900, "Full resync interval in seconds (0 to disable)")
141-
bindPFlag("resync-interval", "resync-interval")
142-
}
143-
144-
func initConfig() {
145-
viper.SetConfigType("yaml")
146-
viper.SetConfigName(appName)
147-
148-
// all possible config file paths, by priority
149-
viper.AddConfigPath("/etc/katafygio/")
150-
if home := homedir.HomeDir(); home != "" {
151-
viper.AddConfigPath(home)
152-
}
153-
viper.AddConfigPath(".")
154-
155-
// prefer the config file path provided by cli flag, if any
156-
if _, err := os.Stat(cfgFile); !os.IsNotExist(err) {
157-
viper.SetConfigFile(cfgFile)
158-
}
159-
160-
// allow config params through prefixed env variables
161-
viper.SetEnvPrefix("KF")
162-
replacer := strings.NewReplacer("-", "_", ".", "_DOT_")
163-
viper.SetEnvKeyReplacer(replacer)
164-
viper.AutomaticEnv()
165-
166-
if err := viper.ReadInConfig(); err == nil {
167-
RootCmd.Printf("Using config file: %s", viper.ConfigFileUsed())
168-
}
169-
}

cmd/flags.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package cmd
2+
3+
import (
4+
"log"
5+
6+
"github.com/spf13/cobra"
7+
"github.com/spf13/viper"
8+
)
9+
10+
var (
11+
cfgFile string
12+
apiServer string
13+
kubeConf string
14+
dryRun bool
15+
dumpMode bool
16+
logLevel string
17+
logOutput string
18+
logServer string
19+
filter string
20+
localDir string
21+
gitURL string
22+
healthP int
23+
resync int
24+
exclkind []string
25+
exclobj []string
26+
)
27+
28+
func bindPFlag(key string, cmd string) {
29+
if err := viper.BindPFlag(key, RootCmd.PersistentFlags().Lookup(cmd)); err != nil {
30+
log.Fatal("Failed to bind cli argument:", err)
31+
}
32+
}
33+
34+
func init() {
35+
cobra.OnInitialize(loadConfigFile)
36+
RootCmd.AddCommand(versionCmd)
37+
38+
defaultCfg := "/etc/katafygio/" + appName + ".yaml"
39+
RootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", defaultCfg, "Configuration file")
40+
41+
RootCmd.PersistentFlags().StringVarP(&apiServer, "api-server", "s", "", "Kubernetes api-server url")
42+
bindPFlag("api-server", "api-server")
43+
44+
RootCmd.PersistentFlags().StringVarP(&kubeConf, "kube-config", "k", "", "Kubernetes config path")
45+
bindPFlag("kube-config", "kube-config")
46+
if err := viper.BindEnv("kube-config", "KUBECONFIG"); err != nil {
47+
log.Fatal("Failed to bind cli argument:", err)
48+
}
49+
50+
RootCmd.PersistentFlags().BoolVarP(&dryRun, "dry-run", "d", false, "Dry-run mode: don't store anything")
51+
bindPFlag("dry-run", "dry-run")
52+
53+
RootCmd.PersistentFlags().BoolVarP(&dumpMode, "dump-only", "m", false, "Dump mode: dump everything once and exit")
54+
bindPFlag("dump-only", "dump-only")
55+
56+
RootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "v", "info", "Log level")
57+
bindPFlag("log.level", "log-level")
58+
59+
RootCmd.PersistentFlags().StringVarP(&logOutput, "log-output", "o", "stderr", "Log output")
60+
bindPFlag("log.output", "log-output")
61+
62+
RootCmd.PersistentFlags().StringVarP(&logServer, "log-server", "r", "", "Log server (if using syslog)")
63+
bindPFlag("log.server", "log-server")
64+
65+
RootCmd.PersistentFlags().StringVarP(&localDir, "local-dir", "e", "./kubernetes-backup", "Where to dump yaml files")
66+
bindPFlag("local-dir", "local-dir")
67+
68+
RootCmd.PersistentFlags().StringVarP(&gitURL, "git-url", "g", "", "Git repository URL")
69+
bindPFlag("git-url", "git-url")
70+
71+
RootCmd.PersistentFlags().StringSliceVarP(&exclkind, "exclude-kind", "x", nil, "Ressource kind to exclude. Eg. 'deployment'")
72+
bindPFlag("exclude-kind", "exclude-kind")
73+
74+
RootCmd.PersistentFlags().StringSliceVarP(&exclobj, "exclude-object", "y", nil, "Object to exclude. Eg. 'configmap:kube-system/kube-dns'")
75+
bindPFlag("exclude-object", "exclude-object")
76+
77+
RootCmd.PersistentFlags().StringVarP(&filter, "filter", "l", "", "Label filter. Select only objects matching the label.")
78+
bindPFlag("filter", "filter")
79+
80+
RootCmd.PersistentFlags().IntVarP(&healthP, "healthcheck-port", "p", 0, "Port for answering healthchecks on /health url")
81+
bindPFlag("healthcheck-port", "healthcheck-port")
82+
83+
RootCmd.PersistentFlags().IntVarP(&resync, "resync-interval", "i", 900, "Full resync interval in seconds (0 to disable)")
84+
bindPFlag("resync-interval", "resync-interval")
85+
}

cmd/version.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package cmd
2+
3+
import "github.com/spf13/cobra"
4+
5+
var (
6+
version = "0.3.0"
7+
8+
versionCmd = &cobra.Command{
9+
Use: "version",
10+
Short: "Print the version number",
11+
Run: func(cmd *cobra.Command, args []string) {
12+
RootCmd.Printf("%s version %s\n", appName, version)
13+
},
14+
}
15+
)

main.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ import (
77
"github.com/bpineau/katafygio/cmd"
88
)
99

10-
//var privateExitHandler func(code int) = os.Exit
1110
var privateExitHandler = os.Exit
1211

13-
// ExitWrapper allow unit tests on main() exit values
12+
// ExitWrapper allow tests on main() exit values
1413
func ExitWrapper(exit int) {
1514
privateExitHandler(exit)
1615
}
1716

1817
func main() {
19-
if err := cmd.Execute(); err != nil {
18+
err := cmd.Execute()
19+
if err != nil {
2020
fmt.Printf("%+v", err)
2121
ExitWrapper(1)
2222
}

0 commit comments

Comments
 (0)