In Go, we often try to use exclusive depend only on the built-in packages as much as possible. It's a sign of pride in Go to avoid adding a dependency by cleverly using a built-in Go package.
The built-in packages however, like Go's flag package, can often be overly simplistic, and do not reach the expectations of most users. If you find yourself looking for a package that can achieve more than the built-in package, and is also trusted enough in the community that it's used by Kubernetes, you have found the right article.
If you simply want to add a flag to your CLI app, then the built-in flag package would suffice. Once you want more though, it starts to show its limitations, so many that someone's even created a POSIX/GNU-style flag drop-in replacement.
With cobra, you could have commands and sub-commands in a single Go file. You'd create the root command with:
rootCmd = &cobra.Command{
Use: "cobra",
Short: "A generator for Cobra based Applications",
}
And then add sub-commands:
rootCmd.AddCommand(&cobra.Command{
Use: "version",
Short: "Print the version number of Hugo",
Long: `All software has versions. This is Hugo's`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")
})
Check their Github page for more examples.
It's not uncommon to need a configuration file, so let's say you store all your configuration in a config.yaml. But then:
So let's configure our flexible configuration with Viper:
viper.SetConfigName("config") // name of config file (without extension)
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
viper.AddConfigPath("/etc/appname/") // path to look for the config file in
viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths
viper.AddConfigPath(".") // optionally look for config in the working directory
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
You could also watch the config, to see if it changes.
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
In a world of microservices, most apps themselves do not generally use much memory. That is because they normally offload the storage to caching solutions like Memcached and Redis. Using an in-memory cache like golang-lru in the right scenarios can greatly improve the simplicity of a service.
Using the package is just as trivial as any other in-memory storage:
cache, _ := lru.New(1)
cache.Add("post", "First blog post")
post, _ := cache.Get("post")
log.Printf("post = %s", post)
// The previous post now gets evicted
cache.Add("post", "Second blog post (will evict second)")
post, _ = cache.Get("post")
log.Printf("post = %s", post)