Skip to content

Commit

Permalink
defaultenv command
Browse files Browse the repository at this point in the history
  • Loading branch information
FZambia committed Oct 3, 2024
1 parent 22fbe56 commit 91a5599
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 8 deletions.
104 changes: 104 additions & 0 deletions internal/cli/defaultenv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package cli

import (
"encoding/json"
"fmt"
"os"
"reflect"
"sort"
"strings"

"github.com/centrifugal/centrifugo/v5/internal/config/envconfig"

"github.com/centrifugal/centrifugo/v5/internal/config"
"github.com/spf13/cobra"
)

func DefaultEnvCommand() *cobra.Command {
var baseConfigFile string
var defaultEnvCmd = &cobra.Command{
Use: "defaultenv",
Short: "Generate full environment var list with defaults",
Long: `Generate full Centrifugo environment var list with defaults`,
Run: func(cmd *cobra.Command, args []string) {
DefaultEnv(baseConfigFile)
},
}
defaultEnvCmd.Flags().StringVarP(&baseConfigFile, "base", "b", "", "path to the base config file to use")
return defaultEnvCmd
}

func DefaultEnv(baseFile string) {
conf, meta, err := config.GetConfig(nil, baseFile)
if err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
if err = conf.Validate(); err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
printSortedEnvVars(meta.KnownEnvVars)
}

func printSortedEnvVars(knownEnvVars map[string]envconfig.VarInfo) {
var envKeys []string
for env := range knownEnvVars {
envKeys = append(envKeys, env)
}
sort.Strings(envKeys)
for _, env := range envKeys {
if strings.HasSuffix(env, "-") {
// Hacky way to skip unnecessary struct field, can be based on struct tag.
continue
}
fmt.Printf("%s=%s\n", env, valueToStringReflect(knownEnvVars[env].Field))
}
}

// valueToStringReflect converts a reflect.Value to a string in a way suitable for environment variables.
func valueToStringReflect(v reflect.Value) string {
switch v.Kind() {
case reflect.Slice, reflect.Array:
// Check if the element type of the slice/array is a struct
if v.Type().Elem().Kind() == reflect.Struct {
// Marshal the entire array/slice of structs to JSON
jsonValue, err := json.Marshal(v.Interface())
if err != nil {
panic(err)
}
// Escape double quotes to make the value suitable for environment variables
return fmt.Sprintf("\"%v\"", strings.ReplaceAll(string(jsonValue), `"`, `\"`))
}
var elements []string
for i := 0; i < v.Len(); i++ {
elements = append(elements, valueToStringReflect(v.Index(i)))
}
if len(elements) == 0 {
return "\"\""
}
return fmt.Sprintf("%v", strings.Join(elements, " "))
case reflect.Struct:
// You can customize how structs should be serialized if needed
return fmt.Sprintf("%v", v.Interface()) // Fallback to default formatting (customize if necessary)
case reflect.Map:
jsonValue, err := json.Marshal(v.Interface())
if err != nil {
panic(err)
}
// Escape double quotes to make the value suitable for environment variables
return fmt.Sprintf("\"%v\"", strings.ReplaceAll(string(jsonValue), `"`, `\"`))
case reflect.Ptr:
if v.IsNil() {
return ""
}
return valueToStringReflect(v.Elem()) // Dereference the pointer and recursively process
case reflect.Invalid:
return "" // Handle zero/nil values
case reflect.String:
return fmt.Sprintf("\"%v\"", v.Interface())
default:
// Fallback for other types (int, bool, etc.)
return fmt.Sprintf("%v", v.Interface())
}
}
10 changes: 6 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ type Meta struct {
FileNotFound bool
UnknownKeys []string
UnknownEnvs []string
KnownEnvVars map[string]envconfig.VarInfo
}

func DefineFlags(rootCmd *cobra.Command) {
Expand Down Expand Up @@ -210,7 +211,7 @@ func GetConfig(cmd *cobra.Command, configFile string) (Config, Meta, error) {
return Config{}, Meta{}, fmt.Errorf("error unmarshaling config: %w", err)
}

knownEnvVars := map[string]struct{}{}
knownEnvVars := map[string]envconfig.VarInfo{}
varInfo, err := envconfig.Process("CENTRIFUGO", conf)
if err != nil {
return Config{}, Meta{}, fmt.Errorf("error processing env: %w", err)
Expand Down Expand Up @@ -264,13 +265,14 @@ func GetConfig(cmd *cobra.Command, configFile string) (Config, Meta, error) {

meta.UnknownKeys = findUnknownKeys(v.AllSettings(), conf, "")
meta.UnknownEnvs = checkEnvironmentVars(knownEnvVars)
meta.KnownEnvVars = knownEnvVars

return *conf, meta, nil
}

func extendKnownEnvVars(knownEnvVars map[string]struct{}, varInfo []envconfig.VarInfo) {
func extendKnownEnvVars(knownEnvVars map[string]envconfig.VarInfo, varInfo []envconfig.VarInfo) {
for _, info := range varInfo {
knownEnvVars[info.Key] = struct{}{}
knownEnvVars[info.Key] = info
}
}

Expand Down Expand Up @@ -364,7 +366,7 @@ func appendKeyPath(parent, key string) string {
return parent + "." + key
}

func checkEnvironmentVars(knownEnvVars map[string]struct{}) []string {
func checkEnvironmentVars(knownEnvVars map[string]envconfig.VarInfo) []string {
var unknownEnvs []string
envPrefix := "CENTRIFUGO_"
envVars := os.Environ()
Expand Down
2 changes: 1 addition & 1 deletion internal/configtypes/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,5 +164,5 @@ type ChannelOptions struct {
}

type Compiled struct {
CompiledChannelRegex *regexp.Regexp `json:"-" yaml:"-" toml:"-"`
CompiledChannelRegex *regexp.Regexp `json:"-" yaml:"-" toml:"-" envconfig:"-"`
}
6 changes: 3 additions & 3 deletions internal/configtypes/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ type PingPong struct {
// NatsBroker configuration.
type NatsBroker struct {
// URL is a Nats server URL.
URL string `mapstructure:"url" json:"url" envconfig:"url" yaml:"url" toml:"url"`
URL string `mapstructure:"url" json:"url" envconfig:"url" yaml:"url" toml:"url" default:"nats://localhost:4222"`
// Prefix allows customizing channel prefix in Nats to work with a single Nats from different
// unrelated Centrifugo setups.
Prefix string `mapstructure:"prefix" default:"centrifugo" json:"prefix" envconfig:"prefix" yaml:"prefix" toml:"prefix"`
Expand Down Expand Up @@ -310,7 +310,7 @@ type RPC struct {
type SubscribeToUserPersonalChannel struct {
Enabled bool `mapstructure:"enabled" json:"enabled" envconfig:"enabled" yaml:"enabled" toml:"enabled"`
PersonalChannelNamespace string `mapstructure:"personal_channel_namespace" json:"personal_channel_namespace" envconfig:"personal_channel_namespace" yaml:"personal_channel_namespace" toml:"personal_channel_namespace"`
SingleConnection bool `mapstructure:"single_connection" json:"single_connection" yaml:"single_connection" toml:"single_connection"`
SingleConnection bool `mapstructure:"single_connection" json:"single_connection" yaml:"single_connection" toml:"single_connection" envconfig:"single_connection"`
}

type Node struct {
Expand Down Expand Up @@ -413,7 +413,7 @@ type Proxy struct {

ProxyCommon `mapstructure:",squash" yaml:",inline"`

TestGrpcDialer func(context.Context, string) (net.Conn, error) `json:"-" yaml:"-" toml:"-"`
TestGrpcDialer func(context.Context, string) (net.Conn, error) `json:"-" yaml:"-" toml:"-" envconfig:"-"`
}

type Proxies []Proxy
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func main() {
rootCmd.AddCommand(cli.CheckTokenCommand())
rootCmd.AddCommand(cli.CheckSubTokenCommand())
rootCmd.AddCommand(cli.DefaultConfigCommand())
rootCmd.AddCommand(cli.DefaultEnvCommand())

_ = rootCmd.Execute()
}

0 comments on commit 91a5599

Please sign in to comment.