verdeter

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 20, 2023 License: MPL-2.0 Imports: 11 Imported by: 8

README

Verdeter

Verdeter is a library to write configuration easily with cobra and viper for distributed applications. Verdeter bring the power of cobra and viper in a single library.

It should be consider as a wrapper for cobra and viper that allow developers to code apps that are POSIX compliant by default.

The api is susceptible to change at any point in time until the v1 is released.

Verdeter allow developers to bind a posix compliant flag, an environment variable and a variable in a config file to a viper key with a single line of code. Verdeter also comes with extra features such as:

  • support for normalize function, ex: LowerString (lower the input string)
  • support for key specific checks, ex: StringNotEmpty(check if the input string is empty), CheckIsHighPort(check is the input integer is a high tcp port), or AuthorizedValues(check if the value of a config key is contained in a defined array of authorized values))
  • support for constraints, ex: check for specific arch
  • support for dynamic default values (named Computed values), ex: set time.Now().Unix() as a default for a "time" key

How Verdeter differ from viper in handling configuration value

Verdeter uses the following precedence order. Each item takes precedence over the item below it:

  1. Explicit call to viper.Set:

    viper.Set(key) set the key to a fixed value

    Example: viper.Set("age", 25) will set the key "age" to 25

  2. POSIX flags

    Cli flags are handled by cobra using pflag

    Example: appending the flag --age 25 will set the key "age" to 25

  3. Environment variables

    Environment Variable are handled by viper (read more here)

    Example: running export <APP_NAME>_age will export an environment variable (the <APP_NAME> is set by verdeter). Verdeter will bind automatically the environment variable name to a viper key when the developer will define the key he needs. Then, when the developer retreive a value for the "age" key with a call to viper.Get("age), viper get all the environment variable and find the value of <APP_NAME>_age.

  4. Value in a config file

    Viper support reading from JSON, TOML, YAML, HCL, envfile and Java properties config files. The developer need to set a key named "config_path" to set the path to the config file or the path to the config directory.

    Example: Let's say the "config_path" is set to ./conf.yml and the file looks like below

    # conf.yml
    author:
        name: bob
    age: 25
    

    Then you would use viper.Get("author.name") to access the value bob and viper.Get("age") to access the value 25.

  5. Dynamic default values (computed values)

    Verdeter allow the user of "computed values" as dynamic default values. It means that the developer can values returned by functions as default values.

    Example: The function defaultTime will provide a unix time integer.

    var defaultTime verdeter.models.DefaultValueFunction :=  func () interface{} {
        return time.Now().Unix()
    }
    

    We bind this function to the key time using verdeter.

    (*VerdeterCommand).SetComputedValue("time", defaultTime)
    

    Then the value can be retreived easily using viper.Get("time") as usual

  6. static default

    Static defaults can be set using verdeter

    // of course here the value is static
    (*VerdeterCommand).SetDefault("time", 1661957668)
    

    Alternatively you can use viper directly to do exactly the same thing (please note that we will use (*VerdeterCommand).SetDefault in the rest of the documentation).

    viper.SetDefault("time", 1661957668)
    
  7. type default (0 for an integer)

    If a key is not set and not marked as required (using (*VerdeterCommand).SetRequired(<key_name>)), then a call to viper.Get<Type>(<key_name>) will return the default value for this <Type>.

    Example: let's say thay we did not call (*VerdeterCommand).SetRequired("time") to set the key "time" as required. Then a call to viper.GetInt("time") will return 0. (please note that a call to viper.Get(<key>) returns an interface{} wich has no "defaut value").

Basic Example

Let's create a rootCommand named "myApp"


var rootCommand = verdeter.NewConfigCmd(
	// Name of the app 
    "myApp", 
    
    // A short description
    "myApp is an amazing piece of software",
    
    // A longer description
    `myApp is an amazing piece of software,
that everyone can use thanks to verdeter`,

    // Callback
	func(cfg *verdeter.VerdeterCommand, args []string) {
        key := "author.name"
		fmt.Printf("value for %q is %q\n", key, viper.GetString(key))
	})

You might to receive args on the command line, set the number of args you want. If more are provided, Cobra will throw an error.

// only 2 args please
rootCommand.SetNbArgs(2)

Then I want to add configuration to this command, for example to bind an address and a port to myApp.

// Adding a local key.
rootCommand.LKey("addr", verdeter.IsStr, "a", "bind to IPV4 addr")
rootCommand.LKey("port", verdeter.IsInt, "p", "bind to TCP port")

/* if you want sub commands to inherit this flag, 
   use (*verdeter.VerdeterCommand).GKey instead */

The config types availables are verdeter.IsStr, verdeter.IsInt, verdeter.IsUint and verdeter.IsBool.

A default value can be set for each config key

rootCommand.SetDefault("addr", "127.0.0.1")
rootCommand.SetDefault("port", 7070)

A validator can be bound to a config key.

// creating a validator from scratch 
addrValidator := models.Validator{
    // the name of the validator
    Name: "IPV4 validator",

    // the actual validation function
    Func: func (input interface{}) error {
        valueStr, ok := input.(string)
        if !ok {
            return fmt.Error("wrong input type")
        }
        parts := strings.Split(".")
        if len(parts)!=4 {
            return fmt.Errorf("An IPv4 is composed of four 8bit integers, fount  %d", len(parts))
        }
        for _,p := parts {
            intVal, err := strconv.Atoi(p)
            if err != nil {
                return err
            }
            if intVal<0 || intVal >255 {
                return fmt.Error("one of the part in the string is not a byte")
            }
            
        }
    },
}

// using the validator we just created
rootCommand.SetValidator("addr", addrValidator)

// verdeter comes with some predefined validators
rootCommand.SetValidator("port", verdeter.validators.CheckTCPHighPort)

Config key can be marked as required. The cobra function (* cobra.Command).PreRunE will fail if the designated config key is not provided, preventing the callback to run.

rootCommand.SetRequired("addr")

To actually run the command, use this code in your main.go

func main() {
   

    /*
        YOUR CODE HERE
    */

    // Launch the command
    rootCommand.Execute()

}

Contributing Guidelines

See CONTRIBUTING

Documentation

Overview

Package verdeter provides a config system for distributed programs

Index

Constants

View Source
const (
	IsStr models.ConfigType
	IsInt
	IsBool
	IsUint
)

Variables

View Source
var ErrConfigFileNotFound = errors.New("config file not found")

Functions

func Key

func Key(cmd *cobra.Command, name string, valueType models.ConfigType, short string, usage string, global bool) error

Key defines a flag in cobra bound to env and files

for booleans, the default value will be set to false for strings, the default value will be set to "" for floats, the default value will be set to 0

Types

type ConfigKey added in v0.3.0

type ConfigKey struct {
	// the name of the config key
	Name string
	// contains filtered or unexported fields
}

Represent a Config Key

func (*ConfigKey) CheckRequired added in v0.3.0

func (configKey *ConfigKey) CheckRequired() error

Check if the value is provided if the config key is required Return an error on failure

func (*ConfigKey) CheckValidators added in v0.3.0

func (configKey *ConfigKey) CheckValidators() []error

Return an error on validation failure of one of the validator.

Return on first failure, the remaining validator are not ran.

func (*ConfigKey) ComputeDefaultValue added in v0.3.0

func (configKey *ConfigKey) ComputeDefaultValue()

Compute the default value of the config key using the DefaultValueFunction function if provided

func (*ConfigKey) Normalize added in v0.3.0

func (configKey *ConfigKey) Normalize()

Normalize the config key using the normalization function if provided

func (*ConfigKey) Validate added in v0.3.0

func (configKey *ConfigKey) Validate() []error

Validate the configkey

1. Run the dynamic default function 2. Normalize the value 3. Check the required constraint 4. Check the validators

type VerdeterCommand

type VerdeterCommand struct {
	// contains filtered or unexported fields
}

VerdeterCommand is a wrapper around github.com/spf13/cobra.Command and viper. It provides additional features such as:

VerdeterCommand make the integration between cobra and viper possible.

func BuildVerdeterCommand added in v0.4.0

func BuildVerdeterCommand(config VerdeterConfig) *VerdeterCommand

BuildVerdeterCommand takes a VerdeterConfig and return a *VerdeterCommand

func NewVerdeterCommand

func NewVerdeterCommand(use, shortDesc, longDesc string, runE func(verdeterCmd *VerdeterCommand, args []string) error) *VerdeterCommand

NewVerdeterCommand is the constructor for VerdeterCommand The args "use", "shortDesc" and "longDesc" are string, their role is the same as in github.com/spf13/cobra.Command The arg runE the callback for cobra

func (*VerdeterCommand) AddSubCommand

func (verdeterCmd *VerdeterCommand) AddSubCommand(sub *VerdeterCommand)

Add a sub command

func (*VerdeterCommand) AddValidator added in v0.3.0

func (verdeterCmd *VerdeterCommand) AddValidator(name string, validator models.Validator)

Set the validator for a specific config key

func (*VerdeterCommand) Execute

func (verdeterCmd *VerdeterCommand) Execute()

Execute the VerdeterCommand

(panics if called on a subcommand)

func (*VerdeterCommand) GKey

func (verdeterCmd *VerdeterCommand) GKey(name string, valType models.ConfigType, short string, usage string) error

GKey defines a global flag for cobra bound to env and config file

func (*VerdeterCommand) GetAppName

func (verdeterCmd *VerdeterCommand) GetAppName() string

GetAppName return the name of the root command

func (*VerdeterCommand) LKey

func (verdeterCmd *VerdeterCommand) LKey(name string, valType models.ConfigType, short string, usage string) error

LKey defines a local flag for cobra bound to env and config file

func (*VerdeterCommand) Lookup added in v0.3.0

func (verdeterCmd *VerdeterCommand) Lookup(configKeyName string) *ConfigKey

Lookup returns the ConfigKey structure of the named config key, returning nil if none exists.

func (*VerdeterCommand) SetComputedValue

func (verdeterCmd *VerdeterCommand) SetComputedValue(name string, computedValue models.DefaultValueFunction)

SetComputedValue sets a value dynamically as the default for a key

func (*VerdeterCommand) SetConstraint

func (verdeterCmd *VerdeterCommand) SetConstraint(msg string, constraint func() bool)

SetConstraint sets a constraint

func (*VerdeterCommand) SetDefault

func (verdeterCmd *VerdeterCommand) SetDefault(name string, value interface{})

SetDefault : set default value for a key

func (*VerdeterCommand) SetNormalize

func (verdeterCmd *VerdeterCommand) SetNormalize(name string, normalize models.NormalizationFunction)

SetNormalize : function to normalize the value of a config Key (if set)

func (*VerdeterCommand) SetRequired

func (verdeterCmd *VerdeterCommand) SetRequired(name string)

SetRequired sets a key as required

func (*VerdeterCommand) Validate

func (verdeterCmd *VerdeterCommand) Validate(isTargetCommand bool) error

Validate checks if config keys have valid values

type VerdeterConfig added in v0.4.0

type VerdeterConfig struct {
	// Use is the one-line usage message.
	// Recommended syntax is as follow:
	//   [ ] identifies an optional argument. Arguments that are not enclosed in brackets are required.
	//   ... indicates that you can specify multiple values for the previous argument.
	//   |   indicates mutually exclusive information. You can use the argument to the left of the separator or the
	//       argument to the right of the separator. You cannot use both arguments in a single use of the command.
	//   { } delimits a set of mutually exclusive arguments when one of the arguments is required. If the arguments are
	//       optional, they are enclosed in brackets ([ ]).
	// Example: add [-F file | -D dir]... [-f format] profile
	Use string

	// Aliases is an array of aliases that can be used instead of the first word in Use.
	Aliases []string

	// SuggestFor is an array of command names for which this command will be suggested -
	// similar to aliases but only suggests.
	SuggestFor []string

	// Short is the short description shown in the 'help' output.
	Short string

	// Long is the long message shown in the 'help <this-command>' output.
	Long string

	// Example is examples of how to use the command.
	Example string

	// ValidArgs is list of all valid non-flag arguments that are accepted in shell completions
	ValidArgs []string

	// ValidArgsFunction is an optional function that provides valid non-flag arguments for shell completion.
	// It is a dynamic version of using ValidArgs.
	// Only one of ValidArgs and ValidArgsFunction can be used for a command.
	ValidArgsFunction func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)

	// Expected arguments
	Args cobra.PositionalArgs

	// ArgAliases is List of aliases for ValidArgs.
	// These are not suggested to the user in the shell completion,
	// but accepted if entered manually.
	ArgAliases []string

	// BashCompletionFunction is custom bash functions used by the legacy bash autocompletion generator.
	// For portability with other shells, it is recommended to instead use ValidArgsFunction
	BashCompletionFunction string

	// Deprecated defines, if this command is deprecated and should print this string when used.
	Deprecated string

	// Annotations are key/value pairs that can be used by applications to identify or
	// group commands.
	Annotations map[string]string

	// Version defines the version for this command. If this value is non-empty and the command does not
	// define a "version" flag, a "version" boolean flag will be added to the command and, if specified,
	// will print content of the "Version" variable. A shorthand "v" flag will also be added if the
	// command does not define one.
	Version string

	// The *Run functions are executed in the following order:
	//   * PersistentPreRun()
	//   * PreRun()
	//   * Run()
	//   * PostRun()
	//   * PersistentPostRun()
	// All functions get the same args, the arguments after the command name.
	//
	// PersistentPreRun: children of this command will inherit and execute.
	PersistentPreRun func(cmd *cobra.Command, args []string)
	// PersistentPreRunE: PersistentPreRun but returns an error.
	PersistentPreRunE func(cmd *cobra.Command, args []string) error
	// PreRun: children of this command will not inherit.
	PreRun func(cmd *cobra.Command, args []string)
	// PreRunE: PreRun but returns an error.
	PreRunE func(cmd *cobra.Command, args []string) error
	// Run: Typically the actual work function. Most commands will only implement this.
	Run func(cmd *cobra.Command, args []string)
	// RunE: Run but returns an error.
	RunE func(cmd *cobra.Command, args []string) error
	// PostRun: run after the Run command.
	PostRun func(cmd *cobra.Command, args []string)
	// PostRunE: PostRun but returns an error.
	PostRunE func(cmd *cobra.Command, args []string) error
	// PersistentPostRun: children of this command will inherit and execute after PostRun.
	PersistentPostRun func(cmd *cobra.Command, args []string)
	// PersistentPostRunE: PersistentPostRun but returns an error.
	PersistentPostRunE func(cmd *cobra.Command, args []string) error
}

VerdeterConfig is meant to be passed to BuildVerdeterCommand() to return an initialized VerdeterCommand

Directories

Path Synopsis
docs
testapp module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL