Add Package Config :

- major dependancies are :
  - golib/context config
  - golib/viper
- interface config extend :
  - golib/context config interface
  - component list
- interface component list :
  this interface implement all function to manage a collection of component. All component are registred with they config key.
  A component must implement basic function like start, stop, reload, defaultConfig...
  The main config json is set by calling all component config with the config key attached
  Each component have some status status like isStarted, isRunning, ...
  Each component must also declare his dependencies with other component. As that when start or reload is called,
  the component is sure that dependencies are still started or reloaded.
- They are 4 component for now : log, tls, headers and http server
- The config package will start a new context / cancelfunc on init to be sure to stop cleanly all component and process

Add Package Viper :
- this package is an helper to the config package with the spf13 viper lib
- this package can watch any change of a config file or can be connected to a remote config cluster like ETCD

Add Package Cobra :
- this package is an helper to make a CLI with flag / command
- this package is based on spf13 cobra has all method to be connected to viper golib
This commit is contained in:
Nicolas JUHEL
2022-02-12 14:41:57 +01:00
parent adc69db2d4
commit 49db5e2afb
43 changed files with 4417 additions and 87 deletions

242
cobra/README.md Normal file
View File

@@ -0,0 +1,242 @@
# Cobra pakcage
Help build integration of spf13/Cobra lib.
This package will simplify call and use of flags / command for a CLI Tools.
## Exmaple of implement
Make some folder / file like this:
```
/api
|- root.go
|- one_command.go
/pkg
|- common.go
/main.go
```
### Main init :
This `commong.go` file will make the init of version, logger, cobra, viper and config packages.
```go
import (
"strings"
libcbr "github.com/nabbar/golib/cobra"
libcfg "github.com/nabbar/golib/config"
liblog "github.com/nabbar/golib/logger"
libver "github.com/nabbar/golib/version"
libvpr "github.com/nabbar/golib/viper"
)
```
Some variables :
```go
var(
// Golib Version Package
vrs libver.Version
// Main config / context Golib package
cfg libcfg.Config
// Root logger before config
log liblog.Logger
// Main Cobra / Viper resource
cbr libcbr.Cobra
vpr libvpr.Viper
)
```
This function will init the libs if not set and return the lib resource :
```go
func GetViper() libvpr.Viper {
if vpr == nil {
vpr = libvpr.New()
vpr.SetHomeBaseName(strings.ToLower(GetVersion().GetPackage()))
vpr.SetEnvVarsPrefix(GetVersion().GetPrefix())
vpr.SetDefaultConfig(GetConfig().DefaultConfig)
}
return vpr
}
func GetCobra() libcbr.Cobra {
if cbr == nil {
cbr = libcbr.New()
cbr.SetVersion(GetVersion())
cbr.SetLogger(GetLogger)
cbr.SetViper(GetViper)
}
return cbr
}
func GetConfig() libcfg.Config {
if cfg == nil {
cfg = libcfg.New()
cfg.RegisterFuncViper(GetViper)
}
return cfg
}
func GetVersion() libver.Version {
if vrs == nil {
//vrs = libver.NewVersion(...)
}
return vrs
}
func GetLogger() liblog.Logger {
if log == nil {
log = liblog.New(cfg.Context())
_ = log.SetOptions(&liblog.Options{
DisableStandard: false,
DisableStack: false,
DisableTimestamp: false,
EnableTrace: true,
TraceFilter: GetVersion().GetRootPackagePath(),
DisableColor: false,
LogFile: nil, // TODO
LogSyslog: nil, // TODO
})
log.SetLevel(liblog.InfoLevel)
log.SetSPF13Level(liblog.InfoLevel, nil)
}
return log
}
```
### The root file in command folder
This file will be the global file for cobra. It will config and init the cobra for yours command.
This file will import your `pkg/common.go` file :
```go
import(
"fmt"
"path"
libcns "github.com/nabbar/golib/console"
liberr "github.com/nabbar/golib/errors"
liblog "github.com/nabbar/golib/logger"
compkg "../pkg"
apipkg "../api"
)
```
And will define some flag vars :
```go
var (
// var for config file path to be load by viper
cfgFile string
// var for log verbose
flgVerbose int
// flag for init cobra has error
iniErr liberr.Error
)
```
The `InitCommand` function will initialize the cobra / viper package and will be call by the main init function into you `main/init()` function:
```go
func InitCommand() {
//the initFunc is a function call on init cobra before calling command but after parsing flag / command ...
compkg.GetCobra().SetFuncInit(initConfig)
// must init after the SetFuncinit function
compkg.GetCobra().Init()
// add the Config file flag
compkg.GetLogger().CheckError(liblog.FatalLevel, liblog.DebugLevel, "Add Flag Config File", compkg.GetCobra().SetFlagConfig(true, &cfgFile))
// add the verbose flas
compkg.GetCobra().SetFlagVerbose(true, &flgVerbose)
// Add some generic command
compkg.GetCobra().AddCommandCompletion()
compkg.GetCobra().AddCommandConfigure("", compkg.GetConfig().DefaultConfig)
compkg.GetCobra().AddCommandPrintErrorCode(cmdPrintError)
// Add one custom command. The best is to isolate each command into a specific file
// Each command file, will having a main function to create the cobra command.
// Here we will only call this main function to add each command into the main cobra command like this
compkg.GetCobra().AddCommand(initCustomCommand1())
// Carefully with the `init` function, because you will not manage the order of running each init function.
// To prevent it, the best is to not having init function, but custom init call in the awaiting order into the `main/init` function
}
// this function will be use in the PrintError command to print first ErrorCode => Package
func cmdPrintError(item, value string) {
println(fmt.Sprintf("%s : %s", libcns.PadLeft(item, 15, " "), path.Dir(value)))
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
// Redefine the root logger level with the verbose flag
switch flgVerbose {
case 0:
compkg.GetLogger().SetLevel(liblog.ErrorLevel)
case 1:
compkg.GetLogger().SetLevel(liblog.WarnLevel)
case 2:
compkg.GetLogger().SetLevel(liblog.InfoLevel)
default:
compkg.GetLogger().SetLevel(liblog.DebugLevel)
}
// Define the config file into viper to load it if is set
iniErr = compkg.GetViper().SetConfigFile(cfgFile)
compkg.GetLogger().CheckError(liblog.FatalLevel, liblog.DebugLevel, "define config file", iniErr)
// try to init viper with config (local file or remote)
if iniErr = compkg.GetViper().Config(liblog.NilLevel, liblog.NilLevel); iniErr == nil {
// register the reload config function with the watch FS function
compkg.GetViper().SetRemoteReloadFunc(func() {
_ = compkg.GetLogger().CheckError(liblog.ErrorLevel, liblog.DebugLevel, "config reload", compkg.GetConfig().Reload())
})
compkg.GetViper().WatchFS(liblog.InfoLevel)
}
// Make the config for this app (cf config package)
apipkg.SetConfig(compkg.GetLogger().GetLevel(), apirtr.GetHandlers())
}
// This is called by main.main() to parse and run the app.
func Execute() {
compkg.GetLogger().CheckError(liblog.FatalLevel, liblog.DebugLevel, "RootCmd Executed", compkg.GetCobra().Execute())
}
```
### The main file of your app
The main file of your app, wil implement the `main/init()` function and the `main/main()` function.
```go
import(
liberr "github.com/nabbar/golib/errors"
liblog "github.com/nabbar/golib/logger"
compkg "./pkg"
cmdpkg "./cmd"
)
func init() {
// Configure the golib error package
liberr.SetModeReturnError(liberr.ErrorReturnStringErrorFull)
// Check the go runtime use to build
compkg.GetLogger().CheckError(liblog.FatalLevel, liblog.DebugLevel, "Checking go version", compkg.GetVersion().CheckGo("1.16", ">="))
// Call the `cmd/InitCommand` function
cmdpkg.InitCommand()
}
func main() {
// run the command Execute function
cmdpkg.Execute()
}
```

96
cobra/completion.go Normal file
View File

@@ -0,0 +1,96 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package cobra
import (
"os"
"path/filepath"
"strings"
liblog "github.com/nabbar/golib/logger"
spfcbr "github.com/spf13/cobra"
)
func (c *cobra) AddCommandCompletion() {
pkg := c.getPackageName()
desc := "This command will create a completion shel script for simplify the use of this app.\n" +
"To do this," +
"\n\t 1- generate a completion script for your shell, like this : " +
"\n\t\t" + pkg + " completion bash /etc/bash_completion.d/" + pkg +
"\n\n 2- enable completion into your shell" +
"\n\t\t example to bash, you need to install the package `bash-completion`" +
"\n\n 3- enable completion into your shell profile" +
"\n\t\t example to bash, you need to uncomment the completion section into your /home/<user>/.bashrc" +
"\n\n"
cmd := &spfcbr.Command{
Use: "completion <Bash|Zsh|PowerShell|Fish> <Completion File to be write>",
Example: "completion bash /etc/bash_completion.d/" + pkg,
Short: "Generate a completion scripts for bash, zsh, fish or powershell",
Long: desc,
Run: func(cmd *spfcbr.Command, args []string) {
var file string
if len(args) < 1 {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "missing args", cmd.Usage())
os.Exit(1)
} else if len(args) >= 2 {
file = filepath.Clean(args[1])
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "create file path", os.MkdirAll(filepath.Dir(file), 0755))
}
switch strings.ToLower(args[0]) {
case "bash":
if file == "" {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "generating bash completion", c.c.GenBashCompletionV2(os.Stdout, true))
} else {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "generating bash completion", c.c.GenBashCompletionFileV2(file, true))
}
case "fish":
if file == "" {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "generating bash completion", c.c.GenFishCompletion(os.Stdout, true))
} else {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "generating bash completion", c.c.GenFishCompletionFile(file, true))
}
case "powershell":
if file == "" {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "generating bash completion", c.c.GenPowerShellCompletionWithDesc(os.Stdout))
} else {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "generating bash completion", c.c.GenPowerShellCompletionFileWithDesc(file))
}
case "zsh":
if file == "" {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "generating bash completion", c.c.GenZshCompletion(os.Stdout))
} else {
c.getLog().CheckError(liblog.ErrorLevel, liblog.NilLevel, "generating bash completion", c.c.GenZshCompletionFile(file))
}
}
},
}
c.c.AddCommand(cmd)
}

140
cobra/configure.go Normal file
View File

@@ -0,0 +1,140 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package cobra
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/mitchellh/go-homedir"
"github.com/pelletier/go-toml"
"gopkg.in/yaml.v2"
liblog "github.com/nabbar/golib/logger"
spfcbr "github.com/spf13/cobra"
)
func (c *cobra) getDefaultPath(baseName string) (string, error) {
path := ""
// Find home directory.
home, err := homedir.Dir()
c.getLog().CheckError(liblog.WarnLevel, liblog.InfoLevel, "Loading home dir", err)
// set configname based on package name
if baseName == "" {
return "", fmt.Errorf("arguments missing: requires the destination file path")
}
path = filepath.Clean(home + string(filepath.Separator) + baseName + ".json")
if path == "." || path == ".json" {
return "", fmt.Errorf("arguments missing: requires the destination file path")
}
return path, nil
}
func (c *cobra) AddCommandConfigure(basename string, defaultConfig func() io.Reader) {
pkg := c.getPackageName()
if basename == "" && pkg != "" {
basename = "." + strings.ToLower(pkg)
}
var cfgFile string
c.c.AddCommand(&spfcbr.Command{
Use: "configure <file path with valid extension (json, yaml, toml, ...) to be generated>",
Example: "configure ~/." + strings.ToLower(pkg) + ".yml",
Short: "Generate config file",
Long: `Generates a configuration file based on giving existing config flag
override by passed flag in command line and completed with default for non existing values.`,
Run: func(cmd *spfcbr.Command, args []string) {
var fs *os.File
defer func() {
if fs != nil {
_ = fs.Close()
}
}()
buf, err := ioutil.ReadAll(defaultConfig())
c.getLog().CheckError(liblog.FatalLevel, liblog.DebugLevel, "reading default config", err)
if len(path.Ext(cfgFile)) > 0 && strings.ToLower(path.Ext(cfgFile)) != ".json" {
var mod = make(map[string]interface{}, 0)
err = json.Unmarshal(buf, &mod)
c.getLog().CheckError(liblog.FatalLevel, liblog.DebugLevel, "transform json default config", err)
switch strings.ToLower(path.Ext(cfgFile)) {
case ".toml":
buf, err = toml.Marshal(mod)
case ".yml", ".yaml":
buf, err = yaml.Marshal(mod)
default:
c.getLog().CheckError(liblog.FatalLevel, liblog.DebugLevel, "get encode for extension file", fmt.Errorf("extension file '%s' not compatible", path.Ext(cfgFile)))
}
}
fs, err = os.OpenFile(cfgFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
c.getLog().CheckError(liblog.FatalLevel, liblog.DebugLevel, "opening destination config file for exclusive write with truncate", err)
_, err = fs.Write(buf)
c.getLog().CheckError(liblog.FatalLevel, liblog.DebugLevel, fmt.Sprintf("writing config to file '%s'", cfgFile), err)
err = os.Chmod(cfgFile, 0600)
if !c.getLog().CheckError(liblog.ErrorLevel, liblog.InfoLevel, fmt.Sprintf("setting permission for config file '%s'", cfgFile), err) {
println(fmt.Sprintf("\n\t>> Config File '%s' has been created and file permission have been set.", cfgFile))
println("\t>> To explicitly specify this config file when you call this tool, use the '-c' flag like this: ")
println(fmt.Sprintf("\t\t\t %s -c %s <cmd>...\n", pkg, cfgFile))
}
},
Args: func(cmd *spfcbr.Command, args []string) error {
if len(args) < 1 {
var err error
cfgFile, err = c.getDefaultPath(basename)
return err
} else if len(args) > 1 {
return fmt.Errorf("arguments error: too many file path specify")
} else {
cfgFile = args[0]
}
return nil
},
})
}

74
cobra/interface.go Normal file
View File

@@ -0,0 +1,74 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package cobra
import (
"io"
liblog "github.com/nabbar/golib/logger"
libver "github.com/nabbar/golib/version"
libvpr "github.com/nabbar/golib/viper"
spfcbr "github.com/spf13/cobra"
)
type FuncInit func()
type FuncLogger func() liblog.Logger
type FuncViper func() libvpr.Viper
type FuncPrintErrorCode func(item, value string)
type Cobra interface {
SetVersion(v libver.Version)
SetFuncInit(fct FuncInit)
SetViper(fct FuncViper)
SetLogger(fct FuncLogger)
SetForceNoInfo(flag bool)
Init()
SetFlagConfig(persistent bool, flagVar *string) error
SetFlagVerbose(persistent bool, flagVar *int)
NewCommand(cmd, short, long, useWithoutCmd, exampleWithoutCmd string) *spfcbr.Command
AddCommand(subCmd ...*spfcbr.Command)
AddCommandCompletion()
AddCommandConfigure(basename string, defaultConfig func() io.Reader)
AddCommandPrintErrorCode(fct FuncPrintErrorCode)
Execute() error
}
func New() Cobra {
return &cobra{
c: nil,
s: nil,
v: nil,
i: nil,
l: nil,
}
}

172
cobra/model.go Normal file
View File

@@ -0,0 +1,172 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package cobra
import (
"fmt"
"os"
"path"
"strings"
liblog "github.com/nabbar/golib/logger"
libver "github.com/nabbar/golib/version"
spfcbr "github.com/spf13/cobra"
)
type cobra struct {
c *spfcbr.Command
s libver.Version
b bool
d string
v FuncViper
i FuncInit
l FuncLogger
}
func (c *cobra) Init() {
c.c = &spfcbr.Command{
TraverseChildren: true,
Use: c.getPackageName(),
Version: c.getPackageVersion(),
Short: c.getPackageDescShort(),
Long: c.getPackageDescLong(),
}
// launch cobra flag parsing
spfcbr.OnInitialize(c.printHeader, c.i)
}
func (c *cobra) printHeader() {
if !c.b {
println(c.s.GetHeader())
}
}
func (c *cobra) SetForceNoInfo(flag bool) {
c.b = flag
}
func (c *cobra) Execute() error {
return c.c.Execute()
}
func (c *cobra) SetVersion(vers libver.Version) {
c.s = vers
}
func (c *cobra) SetFuncInit(fct FuncInit) {
c.i = fct
}
func (c *cobra) SetViper(fct FuncViper) {
c.v = fct
}
func (c *cobra) SetLogger(fct FuncLogger) {
c.l = fct
}
func (c *cobra) SetFlagConfig(persistent bool, flagVar *string) error {
if persistent {
c.c.PersistentFlags().StringVarP(flagVar, "config", "c", "", "specify the config file to load (default is $HOME/."+strings.ToLower(c.getPackageName())+".[yaml|json|toml])")
return c.c.MarkPersistentFlagFilename("config", "json", "toml", "yaml", "yml")
} else {
c.c.Flags().StringVarP(flagVar, "config", "c", "", "specify the config file to load (default is $HOME/."+strings.ToLower(c.getPackageName())+".[yaml|json|toml])")
return c.c.MarkFlagFilename("config", "json", "toml", "yaml", "yml")
}
}
func (c *cobra) SetFlagVerbose(persistent bool, flagVar *int) {
if persistent {
c.c.PersistentFlags().CountVarP(flagVar, "verbose", "v", "enable verbose mode (multi allowed v, vv, vvv)")
} else {
c.c.Flags().CountVarP(flagVar, "verbose", "v", "enable verbose mode (multi allowed v, vv, vvv)")
}
}
func (c *cobra) NewCommand(cmd, short, long, useWithoutCmd, exampleWithoutCmd string) *spfcbr.Command {
return &spfcbr.Command{
Use: fmt.Sprintf("%s %s", cmd, useWithoutCmd),
Short: short,
Long: long,
Example: fmt.Sprintf("%s %s", cmd, exampleWithoutCmd),
}
}
func (c *cobra) AddCommand(subCmd ...*spfcbr.Command) {
c.c.AddCommand(subCmd...)
}
func (c *cobra) getLog() liblog.Logger {
var l liblog.Logger
if c.l != nil {
l = c.l()
}
if l != nil {
return l
}
return liblog.GetDefault()
}
func (c *cobra) getPackageName() string {
pkg := path.Base(os.Args[0])
if pkg == "" {
if f, e := os.Executable(); e == nil {
pkg = path.Base(f)
} else {
pkg = c.s.GetPackage()
}
}
return pkg
}
func (c *cobra) getPackageVersion() string {
if c.s == nil {
return "missing version"
}
return fmt.Sprintf("Version details: \n\tHash: %s\n\tVersion: %s\n\tRuntime: %s\n\tAuthor: %s\n\tDate: %s\n\tLicence: %s\n", c.s.GetBuild(), c.s.GetRelease(), c.s.GetAppId(), c.s.GetAuthor(), c.s.GetDate(), c.s.GetLicenseName())
}
func (c *cobra) getPackageGRootPath() string {
return c.s.GetRootPackagePath()
}
func (c *cobra) getPackageDescShort() string {
return c.d
}
func (c *cobra) getPackageDescLong() string {
return c.s.GetDescription()
}

60
cobra/printError.go Normal file
View File

@@ -0,0 +1,60 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package cobra
import (
"fmt"
"sort"
liberr "github.com/nabbar/golib/errors"
spfcbr "github.com/spf13/cobra"
)
func (c *cobra) AddCommandPrintErrorCode(fct FuncPrintErrorCode) {
c.c.AddCommand(&spfcbr.Command{
Use: "error",
Example: "error",
Short: "Print error code with package path related",
Long: "",
Run: func(cmd *spfcbr.Command, args []string) {
var (
lst = liberr.GetCodePackages(c.getPackageGRootPath())
key = make([]int, 0)
)
for c := range lst {
key = append(key, int(c.GetUint16()))
}
sort.Ints(key)
for _, c := range key {
fct(fmt.Sprintf("%d", c), lst[liberr.CodeError(uint16(c))])
}
},
})
}

119
config/README.md Normal file
View File

@@ -0,0 +1,119 @@
# Config pakcage
This package will make a global config management package for an app.
This package work wil component who's will make the real intelligence of the config.
For example, the main config file is not existing into this config, because is generated by the sum of all component config part.
## Exmaple of implement
You can follow the initialization define into the [`cobra` package](../cobra/README.md)
Here we will define the intialization of the config package.
### Main init of config :
Make a package's file to be called by yours application command or routers
Into this file we will use some import
```go
import (
"fmt"
"net/http"
liberr "github.com/nabbar/golib/errors"
libsts "github.com/nabbar/golib/status"
liblog "github.com/nabbar/golib/logger"
librtr "github.com/nabbar/golib/router"
cmphea "github.com/nabbar/golib/config/components/head"
cmphtp "github.com/nabbar/golib/config/components/http"
cmplog "github.com/nabbar/golib/config/components/log"
cmptls "github.com/nabbar/golib/config/components/tls"
compkg "../pkg"
)
```
Some constant to prevents error on copy/paste :
```go
const (
ConfigKeyHead = "headers"
ConfigKeyHttp = "servers"
ConfigKeyLog = "log"
ConfigKeyTls = "tls"
)
```
This function will init the libs and return the lib resource.
This is an example, but you can create your own component to add it into you config.
The config can have multi instance of same component type but with different config key.
As That, you can for example define a TLS config for HTTP server, a TLS config for a client, ...
```go
func SetConfig(lvl liblog.Level, handler map[string]http.Handler) {
compkg.GetConfig().ComponentSet(ConfigKeyHead, cmphea.New())
compkg.GetConfig().ComponentSet(ConfigKeyLog, cmplog.New(lvl, compkg.GetLogger))
compkg.GetConfig().ComponentSet(ConfigKeyTls, cmptls.New())
compkg.GetConfig().ComponentSet(ConfigKeyHttp, cmphtp.New(_ConfigKeyTls, _ConfigKeyLog, handler))
}
// This function will return the router header based of the component header stored into the config
func GetHeader() librtr.Headers {
if cmp := cmphea.Load(compkg.GetConfig().ComponentGet, _ConfigKeyHead); cmp != nil {
return cmp.GetHeaders()
} else {
GetLog().Fatal("component '%s' is not initialized", _ConfigKeyHead)
return nil
}
}
// This function will return the logger for the application.
// If the component is not started, this function will return the root logger
func GetLog() liblog.Logger {
if !compkg.GetConfig().ComponentIsStarted() {
return nil
}
if cmp := cmplog.Load(compkg.GetConfig().ComponentGet, _ConfigKeyLog); cmp != nil {
return cmp.Log()
} else {
compkg.GetLogger().Error("component '%s' is not initialized", _ConfigKeyLog)
return compkg.GetLogger()
}
}
```
This function will connect the `status` package of golib with the `httpserver` package stored into the config :
```go
// This function can be call by the implementation of Status Package to expose the status of the pool web server
// This function will update the status after each reload the pool web server.
func SetRouteStatus(sts libsts.RouteStatus) {
compkg.GetConfig().RegisterFuncReloadAfter(func() liberr.Error {
var cmp cmphtp.ComponentHttp
// If component has not been started, skill the function
if !compkg.GetConfig().ComponentIsStarted() {
return nil
}
// if cannot access to the Http component, generate an error
if cmp = cmphtp.Load(compkg.GetConfig().ComponentGet, ConfigKeyHttp); cmp == nil {
return ErrorComponentNotInitialized.ErrorParent(fmt.Errorf("component '%s'", ConfigKeyHttp))
}
// Get the Pool Server
pool := cmp.GetPool()
pool.StatusRoute(_ConfigKeyHttp, GetRouteStatusMessage, sts)
// Don't forget to update the component pool
cmp.SetPool(pool)
return nil
})
}
```
To generate the config example, just call this function :
```go
compkg.GetConfig().DefaultConfig()
```
This function will associate the config Key with the component default config into a main buffer to return it.
As that, config can be extended easily with many component and the config is automatically managed.

89
config/component.go Normal file
View File

@@ -0,0 +1,89 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package config
import (
liberr "github.com/nabbar/golib/errors"
)
type Component interface {
// Type return the component type.
Type() string
// RegisterContext is called by Config to register a function to get the main context.
// This function can be used into start / reload function to use context interface.
RegisterContext(fct FuncContext)
// RegisterGet is called by Config to register a function to get a component by his key.
// This function can be used for dependencies into start / reload function.
RegisterGet(fct FuncComponentGet)
// RegisterFuncStartBefore is called to register a function to be called before the start function.
RegisterFuncStartBefore(fct func() liberr.Error)
// RegisterFuncStartAfter is called to register a function to be called after the start function.
RegisterFuncStartAfter(fct func() liberr.Error)
// RegisterFuncReloadBefore is called to register a function to be called before the reload function.
RegisterFuncReloadBefore(fct func() liberr.Error)
// RegisterFuncReloadAfter is called to register a function to be called after the reload function.
RegisterFuncReloadAfter(fct func() liberr.Error)
// Start is called by the Config interface when the global configuration as been started
// This function can be usefull to start server in go routine with a configuration stored
// itself.
Start(getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error
// IsStarted is trigger by the Config interface with function ComponentIsStarted.
// This function can be usefull to know if the start server function is still call.
IsStarted() bool
// Reload is called by the Config interface when the global configuration as been updated
// It receives a func as param to grab a config model by sending a model structure.
// It must configure itself, and stop / start his server if possible or return an error.
Reload(getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error
// Stop is called by the Config interface when global context is done.
// The context done can arrive by stopping the application or by received a signal KILL/TERM.
// This function must stop cleanly the component.
Stop()
// IsRunning is trigger by the Config interface with function ComponentIsRunning.
// This function can be usefully to know if the component server function is still call.
// The atLeast param is used to know if the function must return true on first server is running
// or if all server must be running to return true.
IsRunning(atLeast bool) bool
// DefaultConfig is called by Config.GetDefault.
// It must return a slice of byte containing the default json config for this component.
DefaultConfig() []byte
// Dependencies is called by Config to define if this component need other component.
// Each other component can be call by calling Config.Get
Dependencies() []string
}

View File

@@ -0,0 +1,45 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package head
var _defaultConfig = []byte(`{
"Content-Security-Policy":"default-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data: image/svg+xml*; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'self'; child-src 'none'; frame-src 'none'; worker-src 'none'; frame-ancestors 'none'; form-action 'none'; upgrade-insecure-requests 1; block-all-mixed-content; disown-opener; require-sri-for script style; sandbox allow-same-origin allow-scripts; reflected-xss block; referrer no-referrer",
"Feature-Policy":"geolocation 'self'; midi 'self'; notifications 'self'; push 'self'; sync-xhr 'self'; microphone 'self'; camera 'self'; magnetometer 'self'; gyroscope 'self'; speaker 'self'; vibrate 'self'; fullscreen 'self'; payment 'self';",
"Strict-Transport-Security":"max-age=1; preload; includeSubDomains",
"X-Frame-Options":"DENY",
"X-Xss-Protection":"1; mode=block",
"X-Content-Type-Options":"nosniff",
"Referrer-Policy":"no-referrer"
}`)
func (c *componentHead) DefaultConfig() []byte {
return _defaultConfig
}
func SetDefaultConfig(cfg []byte) {
_defaultConfig = cfg
}

View File

@@ -0,0 +1,71 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package head
import (
libcfg "github.com/nabbar/golib/config"
liberr "github.com/nabbar/golib/errors"
)
const (
ErrorParamsEmpty liberr.CodeError = iota + libcfg.MinErrorComponentHttp
ErrorParamsInvalid
ErrorComponentNotInitialized
ErrorConfigInvalid
ErrorReloadPoolServer
ErrorReloadTLSDefault
)
func init() {
isCodeError = liberr.ExistInMapMessage(ErrorParamsEmpty)
liberr.RegisterIdFctMessage(ErrorParamsEmpty, getMessage)
}
var isCodeError = false
func IsCodeError() bool {
return isCodeError
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case ErrorParamsEmpty:
return "at least one given parameters is empty"
case ErrorParamsInvalid:
return "at least one given parameters is invalid"
case ErrorComponentNotInitialized:
return "this component seems to not be correctly initialized"
case ErrorConfigInvalid:
return "server invalid config"
case ErrorReloadPoolServer:
return "cannot update pool servers with new config"
case ErrorReloadTLSDefault:
return "cannot update default TLS with new config"
}
return ""
}

View File

@@ -0,0 +1,67 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package head
import (
libcfg "github.com/nabbar/golib/config"
librtr "github.com/nabbar/golib/router"
)
const (
ComponentType = "head"
)
type ComponentHead interface {
libcfg.Component
GetHeaders() librtr.Headers
SetHeaders(head librtr.Headers)
}
func New() ComponentHead {
return &componentHead{
head: nil,
}
}
func Register(cfg libcfg.Config, key string, cpt ComponentHead) {
cfg.ComponentSet(key, cpt)
}
func RegisterNew(cfg libcfg.Config, key string) {
cfg.ComponentSet(key, New())
}
func Load(getCpt libcfg.FuncComponentGet, key string) ComponentHead {
if c := getCpt(key); c == nil {
return nil
} else if h, ok := c.(ComponentHead); !ok {
return nil
} else {
return h
}
}

View File

@@ -0,0 +1,171 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package head
import (
libcfg "github.com/nabbar/golib/config"
liberr "github.com/nabbar/golib/errors"
libhts "github.com/nabbar/golib/httpserver"
librtr "github.com/nabbar/golib/router"
)
type DefaultModel struct {
Head librtr.HeadersConfig
Http libhts.PoolServerConfig
}
type componentHead struct {
ctx libcfg.FuncContext
get libcfg.FuncComponentGet
fsa func() liberr.Error
fsb func() liberr.Error
fra func() liberr.Error
frb func() liberr.Error
head librtr.Headers
}
func (c *componentHead) Type() string {
return ComponentType
}
func (c *componentHead) RegisterContext(fct libcfg.FuncContext) {
c.ctx = fct
}
func (c *componentHead) RegisterGet(fct libcfg.FuncComponentGet) {
c.get = fct
}
func (c *componentHead) RegisterFuncStartBefore(fct func() liberr.Error) {
c.fsb = fct
}
func (c *componentHead) RegisterFuncStartAfter(fct func() liberr.Error) {
c.fsa = fct
}
func (c *componentHead) RegisterFuncReloadBefore(fct func() liberr.Error) {
c.frb = fct
}
func (c *componentHead) RegisterFuncReloadAfter(fct func() liberr.Error) {
c.fra = fct
}
func (c *componentHead) _run(getCfg libcfg.FuncComponentConfigGet) liberr.Error {
if c == nil {
return ErrorComponentNotInitialized.Error(nil)
}
cnf := DefaultModel{}
if err := getCfg(&cnf); err != nil {
return ErrorParamsInvalid.Error(err)
}
c.head = cnf.Head.New()
return nil
}
func (c *componentHead) Start(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var err liberr.Error
if c.fsb != nil {
if err = c.fsb(); err != nil {
return err
}
}
if err = c._run(getCfg); err != nil {
return err
}
if c.fsa != nil {
if err = c.fsa(); err != nil {
return err
}
}
return nil
}
func (c *componentHead) Reload(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var (
err liberr.Error
)
if c.frb != nil {
if err = c.frb(); err != nil {
return err
}
}
if err = c._run(getCfg); err != nil {
return err
}
if c.fra != nil {
if err = c.fra(); err != nil {
return err
}
}
return nil
}
func (c *componentHead) Stop() {
}
func (c *componentHead) IsStarted() bool {
return c.head == nil
}
func (c *componentHead) IsRunning(atLeast bool) bool {
return c.head == nil
}
func (c *componentHead) Dependencies() []string {
return []string{}
}
func (c *componentHead) GetHeaders() librtr.Headers {
if c == nil || c.head == nil {
return nil
}
return c.head
}
func (c *componentHead) SetHeaders(head librtr.Headers) {
if c == nil {
return
}
c.head = head
}

View File

@@ -0,0 +1,113 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package http
var _defaultConfig = []byte(`[
{
"disabled":false,
"mandatory":true,
"timeout_cache_info":"30s",
"timeout_cache_health":"30s",
"read_timeout":"0s",
"read_header_timeout":"0s",
"write_timeout":"0s",
"idle_timeout":"0s",
"max_header_bytes":0,
"max_handlers":0,
"max_concurrent_streams":0,
"max_read_frame_size":0,
"permit_prohibited_cipher_suites":false,
"max_upload_buffer_per_connection":0,
"max_upload_buffer_per_stream":0,
"name":"status_http",
"handler_keys":"status",
"tls_mandatory":false,
"listen":"0.0.0.0:6080",
"expose":"http://0.0.0.0",
"tls":{
}
},
{
"disabled":false,
"mandatory":true,
"timeout_cache_info":"30s",
"timeout_cache_health":"30s",
"read_timeout":"0s",
"read_header_timeout":"0s",
"write_timeout":"0s",
"idle_timeout":"0s",
"max_header_bytes":0,
"max_handlers":0,
"max_concurrent_streams":0,
"max_read_frame_size":0,
"permit_prohibited_cipher_suites":false,
"max_upload_buffer_per_connection":0,
"max_upload_buffer_per_stream":0,
"handler_keys":"api",
"tls_mandatory":false,
"name":"api_http",
"listen":"0.0.0.0:7080",
"expose":"http://0.0.0.0",
"tls":{
}
},
{
"disabled":false,
"mandatory":true,
"timeout_cache_info":"30s",
"timeout_cache_health":"30s",
"read_timeout":"0s",
"read_header_timeout":"0s",
"write_timeout":"0s",
"idle_timeout":"0s",
"max_header_bytes":0,
"max_handlers":0,
"max_concurrent_streams":0,
"max_read_frame_size":0,
"permit_prohibited_cipher_suites":false,
"max_upload_buffer_per_connection":0,
"max_upload_buffer_per_stream":0,
"handler_keys":"metrics",
"tls_mandatory":false,
"name":"metrics_http",
"listen":"0.0.0.0:8080",
"expose":"http://0.0.0.0",
"tls":{
}
}
]`)
func (c *componentHttp) DefaultConfig() []byte {
return _defaultConfig
}
func SetDefaultConfig(cfg []byte) {
_defaultConfig = cfg
}

View File

@@ -0,0 +1,77 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package http
import (
libcfg "github.com/nabbar/golib/config"
liberr "github.com/nabbar/golib/errors"
)
const (
ErrorParamsEmpty liberr.CodeError = iota + libcfg.MinErrorComponentHttp
ErrorParamsInvalid
ErrorComponentNotInitialized
ErrorConfigInvalid
ErrorStartPoolServer
ErrorReloadPoolServer
ErrorDependencyTLSDefault
ErrorDependencyLogDefault
)
func init() {
isCodeError = liberr.ExistInMapMessage(ErrorParamsEmpty)
liberr.RegisterIdFctMessage(ErrorParamsEmpty, getMessage)
}
var isCodeError = false
func IsCodeError() bool {
return isCodeError
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case ErrorParamsEmpty:
return "at least one given parameters is empty"
case ErrorParamsInvalid:
return "at least one given parameters is invalid"
case ErrorComponentNotInitialized:
return "this component seems to not be correctly initialized"
case ErrorConfigInvalid:
return "server invalid config"
case ErrorStartPoolServer:
return "cannot start new pool servers with config"
case ErrorReloadPoolServer:
return "cannot update pool servers with new config"
case ErrorDependencyTLSDefault:
return "cannot retrieve default TLS"
case ErrorDependencyLogDefault:
return "cannot retrieve default Logger"
}
return ""
}

View File

@@ -0,0 +1,87 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package http
import (
"net/http"
libcfg "github.com/nabbar/golib/config"
libhts "github.com/nabbar/golib/httpserver"
)
const (
DefaultTlsKey = "tls"
DefaultLogKey = "log"
ComponentType = "http"
)
type ComponentHttp interface {
libcfg.Component
SetTLSKey(tlsKey string)
SetLOGKey(logKey string)
SetHandler(handler map[string]http.Handler)
GetPool() libhts.PoolServer
SetPool(pool libhts.PoolServer)
}
func New(tlsKey, logKey string, handler map[string]http.Handler) ComponentHttp {
if tlsKey == "" {
tlsKey = DefaultTlsKey
}
if logKey == "" {
logKey = DefaultLogKey
}
return &componentHttp{
tls: tlsKey,
log: logKey,
run: false,
hand: handler,
pool: nil,
}
}
func Register(cfg libcfg.Config, key string, cpt ComponentHttp) {
cfg.ComponentSet(key, cpt)
}
func RegisterNew(cfg libcfg.Config, key string, tlsKey, logKey string, handler map[string]http.Handler) {
cfg.ComponentSet(key, New(tlsKey, logKey, handler))
}
func Load(getCpt libcfg.FuncComponentGet, key string) ComponentHttp {
if c := getCpt(key); c == nil {
return nil
} else if h, ok := c.(ComponentHttp); !ok {
return nil
} else {
return h
}
}

View File

@@ -0,0 +1,306 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package http
import (
"fmt"
"net/http"
libtls "github.com/nabbar/golib/certificates"
libcfg "github.com/nabbar/golib/config"
cptlog "github.com/nabbar/golib/config/components/log"
cpttls "github.com/nabbar/golib/config/components/tls"
liberr "github.com/nabbar/golib/errors"
libhts "github.com/nabbar/golib/httpserver"
liblog "github.com/nabbar/golib/logger"
)
type componentHttp struct {
ctx libcfg.FuncContext
get libcfg.FuncComponentGet
fsa func() liberr.Error
fsb func() liberr.Error
fra func() liberr.Error
frb func() liberr.Error
tls string
log string
run bool
hand map[string]http.Handler
pool libhts.PoolServer
}
func (c *componentHttp) _CheckDep() bool {
return c != nil && len(c.hand) > 0 && c.tls != "" && c.log != ""
}
func (c *componentHttp) _CheckInit() bool {
return c != nil && c._CheckDep() && c.pool != nil
}
func (c *componentHttp) Type() string {
return ComponentType
}
func (c *componentHttp) RegisterContext(fct libcfg.FuncContext) {
c.ctx = fct
}
func (c *componentHttp) RegisterGet(fct libcfg.FuncComponentGet) {
c.get = fct
}
func (c *componentHttp) RegisterFuncStartBefore(fct func() liberr.Error) {
c.fsb = fct
}
func (c *componentHttp) RegisterFuncStartAfter(fct func() liberr.Error) {
c.fsa = fct
}
func (c *componentHttp) RegisterFuncReloadBefore(fct func() liberr.Error) {
c.frb = fct
}
func (c *componentHttp) RegisterFuncReloadAfter(fct func() liberr.Error) {
c.fra = fct
}
func (c *componentHttp) getTLS(getCpt libcfg.FuncComponentGet) (libtls.TLSConfig, liberr.Error) {
if !c._CheckDep() {
return nil, ErrorComponentNotInitialized.Error(nil)
}
if i := cpttls.Load(getCpt, c.tls); i == nil {
return nil, ErrorDependencyTLSDefault.Error(nil)
} else if tls := i.GetTLS(); tls == nil {
return nil, ErrorDependencyTLSDefault.Error(nil)
} else {
return tls, nil
}
}
func (c *componentHttp) getLogger(getCpt libcfg.FuncComponentGet) (liblog.Logger, liberr.Error) {
if !c._CheckDep() {
return nil, ErrorComponentNotInitialized.Error(nil)
}
if i := cptlog.Load(getCpt, c.log); i == nil {
return nil, ErrorDependencyLogDefault.Error(nil)
} else if log := i.Log(); log == nil {
return nil, ErrorDependencyLogDefault.Error(nil)
} else {
return log, nil
}
}
func (c *componentHttp) _getPoolServerConfig(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) (libhts.PoolServerConfig, liberr.Error) {
cnf := make(libhts.PoolServerConfig, 0)
if !c._CheckDep() {
return cnf, ErrorComponentNotInitialized.Error(nil)
}
if err := getCfg(&cnf); err != nil {
return cnf, ErrorParamsInvalid.Error(err)
}
if tls, err := c.getTLS(getCpt); err != nil {
return cnf, err
} else {
cnf.MapUpdate(func(sCFG libhts.ServerConfig) libhts.ServerConfig {
sCFG.SetDefaultTLS(func() libtls.TLSConfig {
return tls
})
sCFG.SetParentContext(c.ctx)
return sCFG
})
}
if err := cnf.Validate(); err != nil {
return cnf, ErrorConfigInvalid.Error(err)
} else if len(c.hand) < 1 {
return cnf, ErrorComponentNotInitialized.ErrorParent(fmt.Errorf("missing handler"))
}
return cnf, nil
}
func (c *componentHttp) Start(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var (
err liberr.Error
cnf libhts.PoolServerConfig
)
if c.fsb != nil {
if err = c.fsb(); err != nil {
return err
}
}
if cnf, err = c._getPoolServerConfig(getCpt, getCfg); err != nil {
return err
}
if p, e := cnf.PoolServer(); e != nil {
return ErrorStartPoolServer.Error(e)
} else {
c.pool = p
}
c.pool.SetLogger(func() liblog.Logger {
if log, err := c.getLogger(getCpt); err != nil {
return liblog.GetDefault()
} else {
return log
}
})
if err = c.pool.ListenMultiHandler(c.hand); err != nil {
return ErrorStartPoolServer.Error(err)
}
if c.fsa != nil {
if err = c.fsa(); err != nil {
return err
}
}
return nil
}
func (c *componentHttp) Reload(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var (
err liberr.Error
cnf libhts.PoolServerConfig
)
if c.frb != nil {
if err = c.frb(); err != nil {
return err
}
}
if cnf, err = c._getPoolServerConfig(getCpt, getCfg); err != nil {
return err
}
if c.pool != nil {
if p, e := cnf.UpdatePoolServer(c.pool); e != nil {
return ErrorReloadPoolServer.Error(e)
} else {
c.pool = p
}
} else if p, e := cnf.PoolServer(); e != nil {
return ErrorReloadPoolServer.Error(e)
} else {
c.pool = p
}
c.pool.SetLogger(func() liblog.Logger {
if log, err := c.getLogger(getCpt); err != nil {
return liblog.GetDefault()
} else {
return log
}
})
c.pool.Restart()
if c.fra != nil {
if err = c.fra(); err != nil {
return err
}
}
return nil
}
func (c *componentHttp) Stop() {
c.run = false
if !c._CheckInit() {
return
}
c.pool.Shutdown()
}
func (c *componentHttp) IsStarted() bool {
return c._CheckInit() && c.pool.IsRunning(true)
}
func (c *componentHttp) IsRunning(atLeast bool) bool {
return c._CheckInit() && c.pool.IsRunning(atLeast)
}
func (c *componentHttp) Dependencies() []string {
return []string{cpttls.ComponentType, cptlog.ComponentType}
}
func (c *componentHttp) SetTLSKey(tlsKey string) {
if c == nil {
return
}
c.tls = tlsKey
}
func (c *componentHttp) SetLOGKey(logKey string) {
if c == nil {
return
}
c.log = logKey
}
func (c *componentHttp) SetHandler(handler map[string]http.Handler) {
if c == nil {
return
}
c.hand = handler
}
func (c *componentHttp) GetPool() libhts.PoolServer {
if c == nil || c.pool == nil {
return nil
}
return c.pool
}
func (c *componentHttp) SetPool(pool libhts.PoolServer) {
if c == nil {
return
}
c.pool = pool
}

View File

@@ -0,0 +1,85 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package log
var _defaultConfig = []byte(`
{
"disableStandard":false,
"disableStack":false,
"disableTimestamp":false,
"enableTrace":true,
"traceFilter":"",
"disableColor":false,
"logFile":[
{
"logLevel":[
"Debug",
"Info",
"Warning",
"Error",
"Fatal",
"Critical"
],
"filepath":"",
"create":false,
"createPath":false,
"fileMode":"0644",
"pathMode":"0755",
"disableStack":false,
"disableTimestamp":false,
"enableTrace":true
}
],
"logSyslog":[
{
"logLevel":[
"Debug",
"Info",
"Warning",
"Error",
"Fatal",
"Critical"
],
"network":"tcp",
"host":"",
"severity":"Error",
"facility":"local0",
"tag":"",
"disableStack":false,
"disableTimestamp":false,
"enableTrace":true
}
]
}`)
func (c *componentLog) DefaultConfig() []byte {
return _defaultConfig
}
func SetDefaultConfig(cfg []byte) {
_defaultConfig = cfg
}

View File

@@ -0,0 +1,71 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package log
import (
libcfg "github.com/nabbar/golib/config"
liberr "github.com/nabbar/golib/errors"
)
const (
ErrorParamsEmpty liberr.CodeError = iota + libcfg.MinErrorComponentLog
ErrorParamsInvalid
ErrorConfigInvalid
ErrorComponentNotInitialized
ErrorStartLog
ErrorReloadLog
)
func init() {
isCodeError = liberr.ExistInMapMessage(ErrorParamsEmpty)
liberr.RegisterIdFctMessage(ErrorParamsEmpty, getMessage)
}
var isCodeError = false
func IsCodeError() bool {
return isCodeError
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case ErrorParamsEmpty:
return "at least one given parameters is empty"
case ErrorParamsInvalid:
return "at least one given parameters is invalid"
case ErrorConfigInvalid:
return "server invalid config"
case ErrorComponentNotInitialized:
return "this component seems to not be correctly initialized"
case ErrorStartLog:
return "cannot start Logger"
case ErrorReloadLog:
return "cannot update Logger with new config"
}
return ""
}

View File

@@ -0,0 +1,74 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package log
import (
libcfg "github.com/nabbar/golib/config"
liberr "github.com/nabbar/golib/errors"
liblog "github.com/nabbar/golib/logger"
)
const (
DefaultLevel = liblog.InfoLevel
ComponentType = "log"
)
type ComponentLog interface {
libcfg.Component
Log() liblog.Logger
SetLevel(lvl liblog.Level)
SetField(fields liblog.Fields)
SetOptions(opt *liblog.Options) liberr.Error
}
func New(lvl liblog.Level, defLogger func() liblog.Logger) ComponentLog {
return &componentLog{
d: defLogger,
l: nil,
v: lvl,
}
}
func Register(cfg libcfg.Config, key string, cpt ComponentLog) {
cfg.ComponentSet(key, cpt)
}
func RegisterNew(cfg libcfg.Config, key string, lvl liblog.Level, defLogger func() liblog.Logger) {
cfg.ComponentSet(key, New(lvl, defLogger))
}
func Load(getCpt libcfg.FuncComponentGet, key string) ComponentLog {
if c := getCpt(key); c == nil {
return nil
} else if h, ok := c.(ComponentLog); !ok {
return nil
} else {
return h
}
}

View File

@@ -0,0 +1,196 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package log
import (
libcfg "github.com/nabbar/golib/config"
liberr "github.com/nabbar/golib/errors"
liblog "github.com/nabbar/golib/logger"
)
type DefaultModel struct {
liblog.Options
}
type componentLog struct {
ctx libcfg.FuncContext
get libcfg.FuncComponentGet
fsa func() liberr.Error
fsb func() liberr.Error
fra func() liberr.Error
frb func() liberr.Error
d func() liblog.Logger
l liblog.Logger
v liblog.Level
}
func (c *componentLog) Type() string {
return ComponentType
}
func (c *componentLog) RegisterContext(fct libcfg.FuncContext) {
c.ctx = fct
}
func (c *componentLog) RegisterGet(fct libcfg.FuncComponentGet) {
c.get = fct
}
func (c *componentLog) RegisterFuncStartBefore(fct func() liberr.Error) {
c.fsb = fct
}
func (c *componentLog) RegisterFuncStartAfter(fct func() liberr.Error) {
c.fsa = fct
}
func (c *componentLog) RegisterFuncReloadBefore(fct func() liberr.Error) {
c.frb = fct
}
func (c *componentLog) RegisterFuncReloadAfter(fct func() liberr.Error) {
c.fra = fct
}
func (c *componentLog) _run(errCode liberr.CodeError, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
if c.ctx == nil {
return ErrorComponentNotInitialized.Error(nil)
}
if c.l == nil {
c.l = liblog.New(c.ctx())
}
cnf := DefaultModel{}
if err := getCfg(&cnf); err != nil {
return ErrorParamsInvalid.Error(err)
} else if err = cnf.Validate(); err != nil {
return ErrorConfigInvalid.Error(err)
} else if e := c.l.SetOptions(&cnf.Options); e != nil {
return errCode.ErrorParent(e)
}
return nil
}
func (c *componentLog) Start(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var err liberr.Error
if c.fsb != nil {
if err = c.fsb(); err != nil {
return err
}
}
if err = c._run(ErrorStartLog, getCfg); err != nil {
return err
}
if c.fsa != nil {
if err = c.fsa(); err != nil {
return err
}
}
return nil
}
func (c *componentLog) Reload(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var err liberr.Error
if c.frb != nil {
if err = c.frb(); err != nil {
return err
}
}
if err = c._run(ErrorStartLog, getCfg); err != nil {
return err
}
if c.fra != nil {
if err = c.fra(); err != nil {
return err
}
}
return nil
}
func (c *componentLog) Stop() {
}
func (c *componentLog) IsStarted() bool {
return c.l != nil
}
func (c *componentLog) IsRunning(atLeast bool) bool {
return c.l != nil
}
func (c *componentLog) Dependencies() []string {
return make([]string, 0)
}
func (c *componentLog) Log() liblog.Logger {
if c.l != nil {
_l, _ := c.l.Clone()
return _l
}
return c.d()
}
func (c *componentLog) SetLevel(lvl liblog.Level) {
if c.l != nil {
return
}
c.l.SetLevel(lvl)
}
func (c *componentLog) SetField(fields liblog.Fields) {
if c.l != nil {
return
}
c.l.SetFields(fields)
}
func (c *componentLog) SetOptions(opt *liblog.Options) liberr.Error {
if c.l != nil {
return ErrorComponentNotInitialized.Error(nil)
}
if e := c.l.SetOptions(opt); e != nil {
return ErrorConfigInvalid.ErrorParent(e)
}
return nil
}

View File

@@ -0,0 +1,83 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package tls
var _defaultConfig = []byte(`{
"versionMin":"1.2",
"versionMax":"1.2",
"dynamicSizingDisable":false,
"sessionTicketDisable":false,
"authClient":"none",
"curveList":[
"X25519",
"P256",
"P384",
"P521"
],
"cipherList":[
"RSA-AES128-GCM",
"RSA-AES128-CBC",
"RSA-AES256-GCM",
"RSA-CHACHA",
"ECDSA-AES128-GCM",
"ECDSA-AES128-CBC",
"ECDSA-AES256-GCM",
"ECDSA-CHACHA"
],
"rootCA":[
""
],
"rootCAFiles":[
""
],
"clientCA":[
""
],
"clientCAFiles":[
""
],
"certPair":[
{
"key":"",
"pem":""
}
],
"certPairFiles":[
{
"key":"",
"pem":""
}
]
}`)
func (c *componentTls) DefaultConfig() []byte {
return _defaultConfig
}
func SetDefaultConfig(cfg []byte) {
_defaultConfig = cfg
}

View File

@@ -0,0 +1,71 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package tls
import (
libcfg "github.com/nabbar/golib/config"
liberr "github.com/nabbar/golib/errors"
)
const (
ErrorParamsEmpty liberr.CodeError = iota + libcfg.MinErrorComponentTls
ErrorParamsInvalid
ErrorConfigInvalid
ErrorComponentNotInitialized
ErrorStartTLS
ErrorReloadTLS
)
func init() {
isCodeError = liberr.ExistInMapMessage(ErrorParamsEmpty)
liberr.RegisterIdFctMessage(ErrorParamsEmpty, getMessage)
}
var isCodeError = false
func IsCodeError() bool {
return isCodeError
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case ErrorParamsEmpty:
return "at least one given parameters is empty"
case ErrorParamsInvalid:
return "at least one given parameters is invalid"
case ErrorConfigInvalid:
return "server invalid config"
case ErrorComponentNotInitialized:
return "this component seems to not be correctly initialized"
case ErrorStartTLS:
return "cannot create new TLS Config"
case ErrorReloadTLS:
return "cannot update TLS Config"
}
return ""
}

View File

@@ -0,0 +1,66 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package tls
import (
libtls "github.com/nabbar/golib/certificates"
libcfg "github.com/nabbar/golib/config"
)
const (
ComponentType = "tls"
)
type ComponentTlS interface {
libcfg.Component
GetTLS() libtls.TLSConfig
SetTLS(tls libtls.TLSConfig)
}
func New() ComponentTlS {
return &componentTls{
tls: nil,
}
}
func Register(cfg libcfg.Config, key string, cpt ComponentTlS) {
cfg.ComponentSet(key, cpt)
}
func RegisterNew(cfg libcfg.Config, key string) {
cfg.ComponentSet(key, New())
}
func Load(getCpt libcfg.FuncComponentGet, key string) ComponentTlS {
if c := getCpt(key); c == nil {
return nil
} else if h, ok := c.(ComponentTlS); !ok {
return nil
} else {
return h
}
}

View File

@@ -0,0 +1,182 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package tls
import (
libtls "github.com/nabbar/golib/certificates"
libcfg "github.com/nabbar/golib/config"
liberr "github.com/nabbar/golib/errors"
)
type DefaultModel struct {
libtls.Config
}
type componentTls struct {
ctx libcfg.FuncContext
get libcfg.FuncComponentGet
fsa func() liberr.Error
fsb func() liberr.Error
fra func() liberr.Error
frb func() liberr.Error
tls libtls.TLSConfig
}
func (c *componentTls) Type() string {
return ComponentType
}
func (c *componentTls) RegisterContext(fct libcfg.FuncContext) {
c.ctx = fct
}
func (c *componentTls) RegisterGet(fct libcfg.FuncComponentGet) {
c.get = fct
}
func (c *componentTls) RegisterFuncStartBefore(fct func() liberr.Error) {
c.fsb = fct
}
func (c *componentTls) RegisterFuncStartAfter(fct func() liberr.Error) {
c.fsa = fct
}
func (c *componentTls) RegisterFuncReloadBefore(fct func() liberr.Error) {
c.frb = fct
}
func (c *componentTls) RegisterFuncReloadAfter(fct func() liberr.Error) {
c.fra = fct
}
func (c *componentTls) _run(errCode liberr.CodeError, getCfg libcfg.FuncComponentConfigGet) (libtls.TLSConfig, liberr.Error) {
cnf := DefaultModel{}
if c == nil {
return nil, ErrorComponentNotInitialized.Error(nil)
}
if err := getCfg(&cnf); err != nil {
return nil, ErrorParamsInvalid.Error(err)
}
if err := cnf.Validate(); err != nil {
return nil, ErrorConfigInvalid.Error(err)
}
if tls, err := cnf.New(); err != nil {
return nil, errCode.Error(err)
} else {
return tls, nil
}
}
func (c *componentTls) Start(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var (
err liberr.Error
tls libtls.TLSConfig
)
if c.fsb != nil {
if err = c.fsb(); err != nil {
return err
}
}
if tls, err = c._run(ErrorStartTLS, getCfg); err != nil {
return err
} else {
c.tls = tls
}
if c.fsa != nil {
if err = c.fsa(); err != nil {
return err
}
}
return nil
}
func (c *componentTls) Reload(getCpt libcfg.FuncComponentGet, getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var (
err liberr.Error
tls libtls.TLSConfig
)
if c.frb != nil {
if err = c.frb(); err != nil {
return err
}
}
if tls, err = c._run(ErrorReloadTLS, getCfg); err != nil {
return err
} else {
c.tls = tls
}
if c.fra != nil {
if err = c.fra(); err != nil {
return err
}
}
return nil
}
func (c *componentTls) Stop() {}
func (c *componentTls) Dependencies() []string {
return make([]string, 0)
}
func (c *componentTls) IsStarted() bool {
return c.tls != nil
}
func (c *componentTls) IsRunning(atLeast bool) bool {
return c.tls != nil
}
func (c *componentTls) GetTLS() libtls.TLSConfig {
if c == nil || c.tls == nil {
return nil
}
return c.tls
}
func (c *componentTls) SetTLS(tls libtls.TLSConfig) {
if c == nil || tls == nil {
return
}
c.tls = tls
}

355
config/cptList.go Normal file
View File

@@ -0,0 +1,355 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package config
import (
"bytes"
"encoding/json"
"fmt"
"io"
"sync"
"sync/atomic"
liberr "github.com/nabbar/golib/errors"
)
type ComponentList interface {
// ComponentHas return true if the key is a registered Component
ComponentHas(key string) bool
// ComponentType return the Component Type of the registered key.
ComponentType(key string) string
// ComponentGet return the given component associated with the config Key.
// The component can be transTyped to other interface to be exploited
ComponentGet(key string) Component
// ComponentDel remove the given Component key from the config.
ComponentDel(key string)
// ComponentSet stores the given Component with a key.
ComponentSet(key string, cpt Component)
// ComponentList returns a map of stored couple keyType and Component
ComponentList() map[string]Component
// ComponentKeys returns a slice of stored Component keys
ComponentKeys() []string
// ComponentStart trigger the Start function of each Component.
// This function will keep the dependencies of each Component.
// This function will stop the Start sequence on any error triggered.
ComponentStart(getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error
// ComponentIsStarted will trigger the IsStarted function of all registered component.
// If any component return false, this func return false.
ComponentIsStarted() bool
// ComponentReload trigger the Reload function of each Component.
// This function will keep the dependencies of each Component.
// This function will stop the Reload sequence on any error triggered.
ComponentReload(getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error
// ComponentStop trigger the Stop function of each Component.
// This function will not keep the dependencies of each Component.
ComponentStop()
// ComponentIsRunning will trigger the IsRunning function of all registered component.
// If any component return false, this func return false.
ComponentIsRunning(atLeast bool) bool
// DefaultConfig aggregates all registered components' default config
// Returns a filled buffer with a complete config json model
DefaultConfig() io.Reader
}
func newComponentList() ComponentList {
return &componentList{
m: sync.Mutex{},
l: make(map[string]*atomic.Value, 0),
}
}
type componentList struct {
m sync.Mutex
l map[string]*atomic.Value
}
func (c *componentList) ComponentHas(key string) bool {
c.m.Lock()
defer c.m.Unlock()
_, ok := c.l[key]
return ok
}
func (c *componentList) ComponentType(key string) string {
if !c.ComponentHas(key) {
return ""
} else if o := c.ComponentGet(key); o == nil {
return ""
} else {
return o.Type()
}
}
func (c *componentList) ComponentGet(key string) Component {
if !c.ComponentHas(key) {
return nil
}
c.m.Lock()
defer c.m.Unlock()
if len(c.l) < 1 {
c.l = make(map[string]*atomic.Value, 0)
}
if v := c.l[key]; v == nil {
return nil
} else if i := v.Load(); i == nil {
return nil
} else if o, ok := i.(Component); !ok {
return nil
} else {
return o
}
}
func (c *componentList) ComponentDel(key string) {
if !c.ComponentHas(key) {
return
}
c.m.Lock()
defer c.m.Unlock()
if len(c.l) < 1 {
c.l = make(map[string]*atomic.Value, 0)
}
if v := c.l[key]; v == nil {
return
} else {
c.l[key] = new(atomic.Value)
}
}
func (c *componentList) ComponentSet(key string, cpt Component) {
c.m.Lock()
defer c.m.Unlock()
if len(c.l) < 1 {
c.l = make(map[string]*atomic.Value, 0)
}
if v, ok := c.l[key]; !ok || v == nil {
c.l[key] = new(atomic.Value)
}
c.l[key].Store(cpt)
}
func (c *componentList) ComponentList() map[string]Component {
var res = make(map[string]Component, 0)
for _, k := range c.ComponentKeys() {
res[k] = c.ComponentGet(k)
}
return res
}
func (c *componentList) ComponentKeys() []string {
c.m.Lock()
defer c.m.Unlock()
var res = make([]string, 0)
for k := range c.l {
res = append(res, k)
}
return res
}
func (c *componentList) startOne(key string, getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error {
var cpt Component
if !c.ComponentHas(key) {
return ErrorComponentNotFound.ErrorParent(fmt.Errorf("component: %s", key))
} else if cpt = c.ComponentGet(key); cpt == nil {
return ErrorComponentNotFound.ErrorParent(fmt.Errorf("component: %s", key))
} else if cpt.IsStarted() {
return nil
}
if dep := cpt.Dependencies(); len(dep) > 0 {
for _, k := range dep {
if err := c.startOne(k, getCpt, getCfg); err != nil {
return err
}
}
}
if err := cpt.Start(getCpt, getCfg); err != nil {
return err
} else {
c.ComponentSet(key, cpt)
}
return nil
}
func (c *componentList) ComponentStart(getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error {
for _, key := range c.ComponentKeys() {
if err := c.startOne(key, getCpt, getCfg); err != nil {
return err
}
}
return nil
}
func (c *componentList) ComponentIsStarted() bool {
for _, k := range c.ComponentKeys() {
if cpt := c.ComponentGet(k); cpt == nil {
continue
} else if ok := cpt.IsStarted(); !ok {
return false
}
}
return true
}
func (c *componentList) reloadOne(isReload []string, key string, getCpt FuncComponentGet, getCfg FuncComponentConfigGet) ([]string, liberr.Error) {
var (
err liberr.Error
cpt Component
)
if !c.ComponentHas(key) {
return isReload, ErrorComponentNotFound.ErrorParent(fmt.Errorf("component: %s", key))
} else if cpt = c.ComponentGet(key); cpt == nil {
return isReload, ErrorComponentNotFound.ErrorParent(fmt.Errorf("component: %s", key))
} else if stringIsInSlice(isReload, key) {
return isReload, nil
}
if dep := cpt.Dependencies(); len(dep) > 0 {
for _, k := range dep {
if isReload, err = c.reloadOne(isReload, k, getCpt, getCfg); err != nil {
return isReload, err
}
}
}
if err = cpt.Reload(getCpt, getCfg); err != nil {
return isReload, err
} else {
c.ComponentSet(key, cpt)
isReload = append(isReload, key)
}
return isReload, nil
}
func (c *componentList) ComponentReload(getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error {
var (
err liberr.Error
key string
isReload = make([]string, 0)
)
for _, key = range c.ComponentKeys() {
if isReload, err = c.reloadOne(isReload, key, getCpt, getCfg); err != nil {
return err
}
}
return nil
}
func (c *componentList) ComponentStop() {
for _, key := range c.ComponentKeys() {
if !c.ComponentHas(key) {
continue
}
cpt := c.ComponentGet(key)
if cpt == nil {
continue
}
cpt.Stop()
}
}
func (c *componentList) ComponentIsRunning(atLeast bool) bool {
for _, k := range c.ComponentKeys() {
if cpt := c.ComponentGet(k); cpt == nil {
continue
} else if ok := cpt.IsRunning(atLeast); !ok {
return false
}
}
return true
}
func (c *componentList) DefaultConfig() io.Reader {
var buffer = bytes.NewBuffer(make([]byte, 0))
buffer.WriteString("{")
buffer.WriteString("\n")
n := buffer.Len()
for _, k := range c.ComponentKeys() {
if cpt := c.ComponentGet(k); cpt == nil {
continue
} else if p := cpt.DefaultConfig(); len(p) > 0 {
if buffer.Len() > n {
buffer.WriteString(",")
}
buffer.WriteString(fmt.Sprintf(" \"%s\": ", k))
buffer.Write(p)
}
}
buffer.WriteString("\n")
buffer.WriteString("}")
var res = bytes.NewBuffer(make([]byte, 0))
if err := json.Indent(res, buffer.Bytes(), "", " "); err != nil {
return buffer
}
return res
}

73
config/errors.go Normal file
View File

@@ -0,0 +1,73 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package config
import "github.com/nabbar/golib/errors"
const (
ErrorParamsEmpty errors.CodeError = iota + errors.MinPkgConfig
ErrorConfigMissingViper
ErrorComponentNotFound
ErrorComponentConfigNotFound
ErrorComponentConfigError
)
const (
MinErrorComponentHttp = ErrorParamsEmpty + 10
MinErrorComponentLog = MinErrorComponentHttp + 10
MinErrorComponentTls = MinErrorComponentLog + 10
)
var isCodeError = false
func IsCodeError() bool {
return isCodeError
}
func init() {
isCodeError = errors.ExistInMapMessage(ErrorParamsEmpty)
errors.RegisterIdFctMessage(ErrorParamsEmpty, getMessage)
}
func getMessage(code errors.CodeError) (message string) {
switch code {
case errors.UNK_ERROR:
return ""
case ErrorParamsEmpty:
return "given parameters is empty"
case ErrorConfigMissingViper:
return "missing valid viper function"
case ErrorComponentNotFound:
return "component is not found"
case ErrorComponentConfigNotFound:
return "config keys for component is not found"
case ErrorComponentConfigError:
return "config for component is trigger an error"
}
return ""
}

158
config/interface.go Normal file
View File

@@ -0,0 +1,158 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package config
import (
"context"
"os"
"os/signal"
"sync"
"syscall"
libctx "github.com/nabbar/golib/context"
liberr "github.com/nabbar/golib/errors"
libvpr "github.com/nabbar/golib/viper"
)
type FuncContext func() context.Context
type FuncComponentGet func(key string) Component
type FuncComponentConfigGet func(model interface{}) liberr.Error
type Config interface {
/*
// Section Context : github.com/nabbar/golib/context
*/
// Context return the current context pointer
Context() context.Context
// ContextMerge trigger the golib/context/config interface
// and will merge the stored context value into current context
ContextMerge(ctx libctx.Config) bool
// ContextStore trigger the golib/context/config interface
// and will store a context value into current context
ContextStore(key string, cfg interface{})
// ContextLoad trigger the golib/context/config interface
// and will restore a context value or nil
ContextLoad(key string) interface{}
// ContextSetCancel allow to register a custom function called on cancel context.
// On context cancel event or signal kill, term... this function will be called
// before config stop and main context cancel function
ContextSetCancel(fct func())
/*
// Section Event : github.com/nabbar/golib/config
*/
RegisterFuncViper(fct func() libvpr.Viper)
// Start will trigger the start function of all registered component.
// If any component return an error, this func will stop the start
// process and return the error.
Start() liberr.Error
// RegisterFuncStartBefore allow to register a func to be call when the config Start
// is trigger. This func is call before the start sequence.
RegisterFuncStartBefore(fct func() liberr.Error)
// RegisterFuncStartAfter allow to register a func to be call when the config Start
// is trigger. This func is call after the start sequence.
RegisterFuncStartAfter(fct func() liberr.Error)
// Reload triggers the Reload function of each registered Component.
Reload() liberr.Error
// RegisterFuncReloadBefore allow to register a func to be call when the config Reload
// is trigger. This func is call before the reload sequence.
RegisterFuncReloadBefore(fct func() liberr.Error)
// RegisterFuncReloadAfter allow to register a func to be call when the config Reload
// is trigger. This func is call after the reload sequence.
RegisterFuncReloadAfter(fct func() liberr.Error)
// Stop will trigger the stop function of all registered component.
// All component must stop cleanly.
Stop()
// RegisterFuncStopBefore allow to register a func to be call when the config Stop
// is trigger. This func is call before the stop sequence.
RegisterFuncStopBefore(fct func())
// RegisterFuncStopAfter allow to register a func to be call when the config Stop
// is trigger. This func is call after the stop sequence.
RegisterFuncStopAfter(fct func())
/*
// Section Component : github.com/nabbar/golib/config
*/
ComponentList
}
var (
ctx context.Context
cnl context.CancelFunc
)
func init() {
ctx, cnl = context.WithCancel(context.Background())
go func() {
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds.
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT)
signal.Notify(quit, syscall.SIGTERM)
signal.Notify(quit, syscall.SIGQUIT)
select {
case <-quit:
cnl()
case <-ctx.Done():
cnl()
}
}()
}
func New() Config {
c := &configModel{
m: sync.Mutex{},
ctx: libctx.NewConfig(ctx),
cpt: newComponentList(),
}
go func() {
select {
case <-c.ctx.Done():
c.cancel()
}
}()
return c
}

318
config/model.go Normal file
View File

@@ -0,0 +1,318 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package config
import (
"context"
"fmt"
libctx "github.com/nabbar/golib/context"
liberr "github.com/nabbar/golib/errors"
libsts "github.com/nabbar/golib/status"
libvpr "github.com/nabbar/golib/viper"
spfvpr "github.com/spf13/viper"
"io"
"sync"
)
type configModel struct {
m sync.Mutex
ctx libctx.Config
fcnl func()
cpt ComponentList
fctGolibViper func() libvpr.Viper
fctRouteStatus func() libsts.RouteStatus
fctStartBefore func() liberr.Error
fctStartAfter func() liberr.Error
fctReloadBefore func() liberr.Error
fctReloadAfter func() liberr.Error
fctStopBefore func()
fctStopAfter func()
}
func (c *configModel) _ComponentGetConfig(key string, model interface{}) liberr.Error {
var (
err error
vpr libvpr.Viper
vip *spfvpr.Viper
)
if c.cpt.ComponentHas(key) {
if c.fctGolibViper == nil {
return ErrorConfigMissingViper.Error(nil)
} else if vpr = c.fctGolibViper(); vpr == nil {
return ErrorConfigMissingViper.Error(nil)
} else if vip = vpr.Viper(); vip == nil {
return ErrorConfigMissingViper.Error(nil)
}
err = vip.UnmarshalKey(key, model)
} else {
return ErrorComponentNotFound.ErrorParent(fmt.Errorf("component '%s'", key))
}
return ErrorComponentConfigError.Iferror(err)
}
func (c *configModel) _FuncComponentGetConfig(key string) FuncComponentConfigGet {
return func(model interface{}) liberr.Error {
return c._ComponentGetConfig(key, model)
}
}
func (c *configModel) Context() context.Context {
return c.ctx
}
func (c *configModel) ContextMerge(ctx libctx.Config) bool {
return c.ctx.Merge(ctx)
}
func (c *configModel) ContextStore(key string, cfg interface{}) {
c.ctx.Store(key, cfg)
}
func (c *configModel) ContextLoad(key string) interface{} {
return c.ctx.Load(key)
}
func (c *configModel) ContextSetCancel(fct func()) {
c.m.Lock()
defer c.m.Unlock()
c.fcnl = fct
}
func (c *configModel) cancel() {
c.cancelCustom()
c.Stop()
}
func (c *configModel) cancelCustom() {
c.m.Lock()
defer c.m.Unlock()
if c.fcnl != nil {
c.fcnl()
}
}
func (c *configModel) RegisterFuncViper(fct func() libvpr.Viper) {
c.fctGolibViper = fct
}
func (c *configModel) Start() liberr.Error {
c.m.Lock()
defer c.m.Unlock()
var err liberr.Error
if c.fctStartBefore != nil {
if err = c.fctStartBefore(); err != nil {
return err
}
}
getCpt := func(key string) Component {
return c.ComponentGet(key)
}
for _, k := range c.ComponentKeys() {
cpt := c.ComponentGet(k)
if cpt == nil {
continue
}
if err = cpt.Start(getCpt, c._FuncComponentGetConfig(k)); err != nil {
return err
}
}
if c.fctStartAfter != nil {
if err = c.fctStartAfter(); err != nil {
return err
}
}
return nil
}
func (c *configModel) RegisterFuncStartBefore(fct func() liberr.Error) {
c.m.Lock()
defer c.m.Unlock()
c.fctStartBefore = fct
}
func (c *configModel) RegisterFuncStartAfter(fct func() liberr.Error) {
c.m.Lock()
defer c.m.Unlock()
c.fctStartAfter = fct
}
func (c *configModel) Reload() liberr.Error {
c.m.Lock()
defer c.m.Unlock()
var err liberr.Error
if c.fctReloadBefore != nil {
if err = c.fctReloadBefore(); err != nil {
return err
}
}
getCpt := func(key string) Component {
return c.ComponentGet(key)
}
for _, k := range c.ComponentKeys() {
cpt := c.ComponentGet(k)
if cpt == nil {
continue
}
if err = cpt.Reload(getCpt, c._FuncComponentGetConfig(k)); err != nil {
return err
}
}
if c.fctReloadAfter != nil {
if err = c.fctReloadAfter(); err != nil {
return err
}
}
return nil
}
func (c *configModel) RegisterFuncReloadBefore(fct func() liberr.Error) {
c.m.Lock()
defer c.m.Unlock()
c.fctReloadBefore = fct
}
func (c *configModel) RegisterFuncReloadAfter(fct func() liberr.Error) {
c.m.Lock()
defer c.m.Unlock()
c.fctReloadAfter = fct
}
func (c *configModel) Stop() {
c.m.Lock()
defer c.m.Unlock()
if c.fctStopBefore != nil {
c.fctStopBefore()
}
for _, k := range c.ComponentKeys() {
cpt := c.ComponentGet(k)
if cpt == nil {
continue
}
cpt.Stop()
}
if c.fctStopAfter != nil {
c.fctStopAfter()
}
}
func (c *configModel) RegisterFuncStopBefore(fct func()) {
c.m.Lock()
defer c.m.Unlock()
c.fctStopBefore = fct
}
func (c *configModel) RegisterFuncStopAfter(fct func()) {
c.m.Lock()
defer c.m.Unlock()
c.fctStopAfter = fct
}
func (c *configModel) ComponentHas(key string) bool {
return c.cpt.ComponentHas(key)
}
func (c *configModel) ComponentType(key string) string {
return c.cpt.ComponentType(key)
}
func (c *configModel) ComponentGet(key string) Component {
return c.cpt.ComponentGet(key)
}
func (c *configModel) ComponentDel(key string) {
c.cpt.ComponentDel(key)
}
func (c *configModel) ComponentSet(key string, cpt Component) {
cpt.RegisterContext(c.Context)
cpt.RegisterGet(c.ComponentGet)
c.cpt.ComponentSet(key, cpt)
}
func (c *configModel) ComponentList() map[string]Component {
return c.cpt.ComponentList()
}
func (c *configModel) ComponentKeys() []string {
return c.cpt.ComponentKeys()
}
func (c *configModel) ComponentStart(getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error {
return c.cpt.ComponentStart(getCpt, getCfg)
}
func (c *configModel) ComponentIsStarted() bool {
return c.cpt.ComponentIsStarted()
}
func (c *configModel) ComponentReload(getCpt FuncComponentGet, getCfg FuncComponentConfigGet) liberr.Error {
return c.cpt.ComponentReload(getCpt, getCfg)
}
func (c *configModel) ComponentStop() {
c.cpt.ComponentStop()
}
func (c *configModel) ComponentIsRunning(atLeast bool) bool {
return c.cpt.ComponentIsRunning(atLeast)
}
func (c *configModel) DefaultConfig() io.Reader {
return c.cpt.DefaultConfig()
}

41
config/tools.go Normal file
View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package config
func stringIsInSlice(list []string, key string) bool {
if len(list) < 1 {
return false
}
for _, k := range list {
if k == key {
return true
}
}
return false
}

View File

@@ -26,6 +26,7 @@ package context
import (
"context"
"sync"
"sync/atomic"
)
@@ -41,53 +42,49 @@ type Config interface {
func NewConfig(ctx context.Context) Config {
return &configContext{
Context: ctx,
cfg: new(atomic.Value),
cfg: make(map[string]*atomic.Value, 0),
}
}
type configContext struct {
context.Context
cfg *atomic.Value
m sync.Mutex
cfg map[string]*atomic.Value
}
func (c configContext) getMap() map[string]*atomic.Value {
func (c configContext) Load(key string) interface{} {
c.m.Lock()
defer c.m.Unlock()
var (
v interface{}
s map[string]*atomic.Value
i interface{}
ok bool
)
if c.cfg == nil {
c.cfg = new(atomic.Value)
} else if v = c.cfg.Load(); v == nil {
s = make(map[string]*atomic.Value)
} else if s, ok = v.(map[string]*atomic.Value); !ok {
s = make(map[string]*atomic.Value)
c.cfg = make(map[string]*atomic.Value, 0)
} else if i, ok = c.cfg[key]; ok && i != nil {
return i
}
return s
return nil
}
func (c *configContext) Store(key string, cfg interface{}) {
s := c.getMap()
func (c configContext) Store(key string, cfg interface{}) {
c.m.Lock()
defer c.m.Unlock()
if _, ok := s[key]; !ok {
s[key] = &atomic.Value{}
var ok bool
if c.cfg == nil {
c.cfg = make(map[string]*atomic.Value, 0)
}
s[key].Store(cfg)
c.cfg.Store(s)
}
func (c *configContext) Load(key string) interface{} {
s := c.getMap()
if _, ok := s[key]; !ok {
return nil
} else {
return s[key].Load()
if _, ok = c.cfg[key]; !ok {
c.cfg[key] = new(atomic.Value)
}
c.cfg[key].Store(cfg)
}
func (c *configContext) Merge(cfg Config) bool {
@@ -100,9 +97,10 @@ func (c *configContext) Merge(cfg Config) bool {
return false
}
s := c.getMap()
x.m.Lock()
defer x.m.Unlock()
for k, v := range x.getMap() {
for k, v := range x.cfg {
if k == "" || v == nil {
continue
}
@@ -110,22 +108,9 @@ func (c *configContext) Merge(cfg Config) bool {
if i := v.Load(); i == nil {
continue
} else {
s[k] = &atomic.Value{}
s[k].Store(i)
c.Store(k, i)
}
}
c.cfg.Store(s)
return true
}
//Deprecated: use Store.
func (c *configContext) ObjectStore(key string, obj interface{}) {
c.Store(key, obj)
}
//Deprecated: use Load.
func (c *configContext) ObjectLoad(key string) interface{} {
return c.Load(key)
}

View File

@@ -31,26 +31,28 @@ const (
MinPkgArtifact = 200
MinPkgCertificate = 300
MinPkgCluster = 400
MinPkgConsole = 500
MinPkgCrypt = 600
MinPkgHttpCli = 700
MinPkgHttpServer = 800
MinPkgIOUtils = 900
MinPkgLDAP = 1000
MinPkgLogger = 1100
MinPkgMail = 1200
MinPkgMailer = 1300
MinPkgMailPooler = 1400
MinPkgNetwork = 1500
MinPkgNats = 1600
MinPkgNutsDB = 1700
MinPkgOAuth = 1800
MinPkgAws = 1900
MinPkgRouter = 2000
MinPkgSemaphore = 2100
MinPkgSMTP = 2200
MinPkgStatic = 2300
MinPkgVersion = 2400
MinPkgConfig = 500
MinPkgConsole = 600
MinPkgCrypt = 700
MinPkgHttpCli = 800
MinPkgHttpServer = 900
MinPkgIOUtils = 1000
MinPkgLDAP = 1100
MinPkgLogger = 1200
MinPkgMail = 1300
MinPkgMailer = 1400
MinPkgMailPooler = 1500
MinPkgNetwork = 1600
MinPkgNats = 1700
MinPkgNutsDB = 1800
MinPkgOAuth = 1900
MinPkgAws = 2000
MinPkgRouter = 2100
MinPkgSemaphore = 2200
MinPkgSMTP = 2300
MinPkgStatic = 2400
MinPkgVersion = 2500
MinPkgViper = 2600
MinAvailable = 4000

43
go.mod
View File

@@ -10,9 +10,10 @@ require (
github.com/aws/aws-sdk-go-v2/service/s3 v1.24.1
github.com/c-bata/go-prompt v0.2.6
github.com/fatih/color v1.13.0
github.com/fsnotify/fsnotify v1.5.1
github.com/fxamacker/cbor/v2 v2.4.0
github.com/gin-gonic/gin v1.7.7
github.com/go-ldap/ldap/v3 v3.4.1
github.com/go-ldap/ldap/v3 v3.4.2
github.com/go-playground/validator/v10 v10.10.0
github.com/google/go-github/v33 v33.0.0
github.com/hashicorp/go-hclog v1.1.0
@@ -22,16 +23,20 @@ require (
github.com/lni/dragonboat/v3 v3.3.5
github.com/matcornic/hermes/v2 v2.1.0
github.com/mattn/go-colorable v0.1.12
github.com/mitchellh/go-homedir v1.1.0
github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296
github.com/nats-io/nats-server/v2 v2.7.2
github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d
github.com/onsi/ginkgo/v2 v2.1.1
github.com/onsi/ginkgo/v2 v2.1.3
github.com/onsi/gomega v1.18.1
github.com/pelletier/go-toml v1.9.4
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.3.0
github.com/spf13/jwalterweatherman v1.1.0
github.com/spf13/viper v1.10.1
github.com/vbauerster/mpb/v5 v5.4.0
github.com/xanzy/go-gitlab v0.55.0
github.com/xanzy/go-gitlab v0.55.1
github.com/xhit/go-simple-mail v2.2.2+incompatible
github.com/xujiajun/nutsdb v0.6.0
github.com/xujiajun/utils v0.0.0-20190123093513-8bf096c4f53b
@@ -40,6 +45,7 @@ require (
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220209214540-3681064d5158
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
gopkg.in/yaml.v2 v2.4.0
)
require (
@@ -53,7 +59,7 @@ require (
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/aokoli/goutils v1.1.1 // indirect
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
github.com/armon/go-metrics v0.3.10 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.2.0 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4 // indirect
@@ -76,7 +82,7 @@ require (
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/gogo/protobuf v1.3.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/google/btree v1.0.0 // indirect
@@ -85,28 +91,32 @@ require (
github.com/gorilla/css v1.0.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack v0.5.3 // indirect
github.com/hashicorp/go-multierror v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
github.com/hashicorp/go-sockaddr v1.0.0 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/hashicorp/memberlist v0.2.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/memberlist v0.3.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/ratelimit v1.0.2-0.20191002062651-f60b32039441 // indirect
github.com/klauspost/compress v1.14.2 // indirect
github.com/klauspost/compress v1.14.3 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lni/goutils v1.3.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-tty v0.0.4 // indirect
github.com/miekg/dns v1.1.26 // indirect
github.com/miekg/dns v1.1.41 // indirect
github.com/minio/highwayhash v1.0.2 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -119,8 +129,11 @@ require (
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/spf13/afero v1.8.1 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
github.com/valyala/fastrand v1.0.0 // indirect
github.com/valyala/histogram v1.0.1 // indirect
@@ -129,11 +142,11 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/xujiajun/mmap-go v1.0.1 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220209195652-db638375bc3a // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/exp v0.0.0-20200513190911-00229845015e // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
)

View File

@@ -283,7 +283,7 @@ func (c *ServerConfig) SetParentContext(f func() context.Context) {
c.getParentContext = f
}
func (c ServerConfig) GetTLS() (libtls.TLSConfig, liberr.Error) {
func (c *ServerConfig) GetTLS() (libtls.TLSConfig, liberr.Error) {
var def libtls.TLSConfig
if c.TLS.InheritDefault && c.getTLSDefault != nil {
@@ -293,7 +293,7 @@ func (c ServerConfig) GetTLS() (libtls.TLSConfig, liberr.Error) {
return c.TLS.NewFrom(def)
}
func (c ServerConfig) IsTLS() bool {
func (c *ServerConfig) IsTLS() bool {
if ssl, err := c.GetTLS(); err == nil && ssl != nil && ssl.LenCertificatePair() > 0 {
return true
}
@@ -301,7 +301,7 @@ func (c ServerConfig) IsTLS() bool {
return false
}
func (c ServerConfig) getContext() context.Context {
func (c *ServerConfig) getContext() context.Context {
var ctx context.Context
if c.getParentContext != nil {
@@ -315,7 +315,7 @@ func (c ServerConfig) getContext() context.Context {
return ctx
}
func (c ServerConfig) GetListen() *url.URL {
func (c *ServerConfig) GetListen() *url.URL {
var (
err error
add *url.URL
@@ -342,7 +342,7 @@ func (c ServerConfig) GetListen() *url.URL {
return add
}
func (c ServerConfig) GetExpose() *url.URL {
func (c *ServerConfig) GetExpose() *url.URL {
var (
err error
add *url.URL
@@ -367,14 +367,18 @@ func (c ServerConfig) GetExpose() *url.URL {
return add
}
func (c ServerConfig) GetHandlerKey() string {
func (c *ServerConfig) GetHandlerKey() string {
return c.HandlerKeys
}
func (c ServerConfig) Validate() liberr.Error {
func (c *ServerConfig) Validate() liberr.Error {
val := validator.New()
err := val.Struct(c)
if err == nil {
return nil
}
if e, ok := err.(*validator.InvalidValidationError); ok {
return ErrorServerValidate.ErrorParent(e)
}
@@ -394,6 +398,6 @@ func (c ServerConfig) Validate() liberr.Error {
}
func (c ServerConfig) Server() Server {
return NewServer(&c)
func (c *ServerConfig) Server() Server {
return NewServer(c)
}

View File

@@ -36,6 +36,7 @@ import (
"net/http"
"os"
"os/signal"
"sync"
"sync/atomic"
"syscall"
"time"
@@ -48,6 +49,8 @@ import (
const _TimeoutWaitingPortFreeing = 500 * time.Microsecond
type srvRun struct {
m sync.Mutex
log liblog.FuncLog // return golib logger interface
err *atomic.Value // last err occured
run *atomic.Value // is running
@@ -70,6 +73,7 @@ type run interface {
func newRun(log liblog.FuncLog) run {
return &srvRun{
m: sync.Mutex{},
log: log,
err: new(atomic.Value),
run: new(atomic.Value),
@@ -270,7 +274,10 @@ func (s *srvRun) Listen(cfg *ServerConfig, handler http.Handler) liberr.Error {
}
s.ctx, s.cnl = context.WithCancel(cfg.getContext())
s.m.Lock()
s.srv = srv
s.m.Unlock()
go func(ctx context.Context, cnl context.CancelFunc, name, host string, tlsMandatory bool) {
var _log = s.getLogger()
@@ -284,9 +291,14 @@ func (s *srvRun) Listen(cfg *ServerConfig, handler http.Handler) liberr.Error {
s.setRunning(false)
}()
s.m.Lock()
if s.srv == nil {
return
}
s.srv.BaseContext = func(listener net.Listener) context.Context {
return s.ctx
}
s.m.Unlock()
var er error
_log.Entry(liblog.InfoLevel, "Server is starting").Log()
@@ -337,7 +349,11 @@ func (s *srvRun) srvShutdown() {
cancel()
if s.srv != nil {
err := s.srv.Close()
s.m.Lock()
s.srv = nil
s.m.Unlock()
_log.Entry(liblog.ErrorLevel, "closing server").ErrorAdd(true, err).Check(liblog.InfoLevel)
}
}()

View File

@@ -101,6 +101,35 @@ func (lic license) GetLicense() string {
return ""
}
func (lic license) GetLicenseName() string {
switch lic {
case License_Apache_v2:
return "Apache License - Version 2.0, January 2004"
case License_GNU_Affero_GPL_v3:
return "GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007"
case License_GNU_GPL_v3:
return "GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007"
case License_GNU_Lesser_GPL_v3:
return "GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007"
case License_MIT:
return "MIT License"
case License_Mozilla_PL_v2:
return "Mozilla Public License Version 2.0"
case License_Unlicense:
return "Free and unencumbered software"
case License_Creative_Common_Zero_v1:
return "Creative Commons - CC0 1.0 Universal"
case License_Creative_Common_Attribution_v4_int:
return "Creative Commons - Attribution 4.0 International"
case License_Creative_Common_Attribution_Share_Alike_v4_int:
return "Creative Commons - Attribution-ShareAlike 4.0 International"
case License_SIL_Open_Font_1_1:
return "SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007"
}
return ""
}
func boiler_MIT(Year, Author string) string {
return fmt.Sprintf(`
MIT License

View File

@@ -55,6 +55,7 @@ type Version interface {
GetAppId() string
GetAuthor() string
GetBuild() string
GetDate() string
GetDescription() string
GetHeader() string
GetInfo() string
@@ -63,6 +64,7 @@ type Version interface {
GetPrefix() string
GetRelease() string
GetLicenseName() string
GetLicenseLegal(addMoreLicence ...license) string
GetLicenseFull(addMoreLicence ...license) string
GetLicenseBoiler(addMoreLicence ...license) string
@@ -156,6 +158,19 @@ func (vers versionModel) GetHeader() string {
return fmt.Sprintf("%s (%s)", vers.versionPackage, vers.GetInfo())
}
func (vers versionModel) GetDate() string {
var (
err error
ts time.Time
)
if ts, err = time.Parse(time.RFC3339, vers.versionDate); err != nil {
ts = time.Time{}
}
return ts.Format(time.RFC1123)
}
func (vers versionModel) GetBuild() string {
return vers.versionBuild
}
@@ -176,6 +191,10 @@ func (vers versionModel) GetRelease() string {
return vers.versionRelease
}
func (vers versionModel) GetLicenseName() string {
return vers.licenceType.GetLicenseName()
}
func (vers versionModel) GetLicenseLegal(addMoreLicence ...license) string {
if len(addMoreLicence) == 0 {
return vers.licenceType.GetLicense()

94
viper/cleaner.go Normal file
View File

@@ -0,0 +1,94 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package viper
import (
"bytes"
"encoding/json"
"strings"
spfvpr "github.com/spf13/viper"
)
func (v *viper) Unset(key ...string) error {
if len(key) < 1 {
return nil
}
configMap := v.v.AllSettings()
for _, k := range key {
configMap = unsetSub(configMap, k)
}
_v := &viper{
v: spfvpr.New(),
base: v.base,
prfx: v.prfx,
deft: v.deft,
remote: v.remote,
}
var (
encodedConfig []byte
err error
)
if encodedConfig, err = json.MarshalIndent(configMap, "", " "); err != nil {
return err
}
_v.v.SetConfigType("json")
if err = _v.v.ReadConfig(bytes.NewReader(encodedConfig)); err != nil {
return err
}
v.v = _v.v
return nil
}
func unsetSub(configMap map[string]interface{}, key string) map[string]interface{} {
kkey := strings.Split(key, ".")
if len(kkey) == 1 {
delete(configMap, key)
return configMap
}
for k, v := range configMap {
if k != kkey[0] {
continue
}
configMap[k] = unsetSub(v.(map[string]interface{}), strings.Join(kkey[1:], "."))
break
}
return configMap
}

58
viper/config.go Normal file
View File

@@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package viper
import (
liberr "github.com/nabbar/golib/errors"
liblog "github.com/nabbar/golib/logger"
)
func (v *viper) Config(logLevelRemoteKO, logLevelRemoteOK liblog.Level) liberr.Error {
if err := v.initAddRemote(); err == nil {
v.initWatchRemote(logLevelRemoteKO, logLevelRemoteOK)
return nil
} else if !err.IsCodeError(ErrorParamMissing) {
return err
}
err := v.v.ReadInConfig()
if err == nil {
return nil
}
if v.deft != nil {
v.v.SetConfigType("json")
if err = v.v.ReadConfig(v.deft()); err != nil {
return ErrorConfigReadDefault.ErrorParent(err)
}
return ErrorConfigIsDefault.Error(nil)
}
return ErrorConfigRead.ErrorParent(err)
}

85
viper/errors.go Normal file
View File

@@ -0,0 +1,85 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package viper
import (
liberr "github.com/nabbar/golib/errors"
)
const (
ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgViper
ErrorParamMissing
ErrorHomePathNotFound
ErrorBasePathNotFound
ErrorRemoteProvider
ErrorRemoteProviderSecure
ErrorRemoteProviderRead
ErrorRemoteProviderMarshall
ErrorConfigRead
ErrorConfigReadDefault
ErrorConfigIsDefault
)
var isCodeError = false
func IsCodeError() bool {
return isCodeError
}
func init() {
isCodeError = liberr.ExistInMapMessage(ErrorParamEmpty)
liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage)
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case liberr.UNK_ERROR:
return ""
case ErrorParamMissing:
return "at least one parameter is missing"
case ErrorHomePathNotFound:
return "cannot retrieve user home path"
case ErrorBasePathNotFound:
return "cannot retrieve base config path"
case ErrorRemoteProvider:
return "cannot define remote provider"
case ErrorRemoteProviderSecure:
return "cannot define secure remote provider"
case ErrorRemoteProviderRead:
return "cannot read config from remote provider"
case ErrorRemoteProviderMarshall:
return "cannot marshall config from remote provider"
case ErrorConfigRead:
return "cannot read config from file"
case ErrorConfigReadDefault:
return "cannot read default config"
case ErrorConfigIsDefault:
return "cannot read config, use default config"
}
return ""
}

61
viper/interface.go Normal file
View File

@@ -0,0 +1,61 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package viper
import (
"io"
spfvpr "github.com/spf13/viper"
liberr "github.com/nabbar/golib/errors"
liblog "github.com/nabbar/golib/logger"
)
type Viper interface {
SetRemoteProvider(provider string)
SetRemoteEndpoint(endpoint string)
SetRemotePath(path string)
SetRemoteSecureKey(key string)
SetRemoteModel(model interface{})
SetRemoteReloadFunc(fct func())
SetHomeBaseName(base string)
SetEnvVarsPrefix(prefix string)
SetDefaultConfig(fct func() io.Reader)
SetConfigFile(fileConfig string) liberr.Error
Config(logLevelRemoteKO, logLevelRemoteOK liblog.Level) liberr.Error
Viper() *spfvpr.Viper
WatchFS(logLevelFSInfo liblog.Level)
Unset(key ...string) error
}
func New() Viper {
return &viper{
v: spfvpr.New(),
}
}

127
viper/model.go Normal file
View File

@@ -0,0 +1,127 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package viper
import (
"fmt"
"io"
libhom "github.com/mitchellh/go-homedir"
liberr "github.com/nabbar/golib/errors"
spfvpr "github.com/spf13/viper"
)
const (
RemoteETCD = "etcd"
)
type viperRemote struct {
model interface{}
provider string
endpoint string
path string
secure string
fct func()
}
type viper struct {
v *spfvpr.Viper
base string
prfx string
deft func() io.Reader
remote viperRemote
}
func (v *viper) SetRemoteProvider(provider string) {
v.remote.provider = provider
}
func (v *viper) SetRemoteEndpoint(endpoint string) {
v.remote.endpoint = endpoint
}
func (v *viper) SetRemotePath(path string) {
v.remote.path = path
}
func (v *viper) SetRemoteSecureKey(key string) {
v.remote.secure = key
}
func (v *viper) SetRemoteModel(model interface{}) {
v.remote.model = model
}
func (v *viper) SetRemoteReloadFunc(fct func()) {
v.remote.fct = fct
}
func (v *viper) SetHomeBaseName(base string) {
v.base = base
}
func (v *viper) SetEnvVarsPrefix(prefix string) {
v.prfx = prefix
}
func (v *viper) SetDefaultConfig(fct func() io.Reader) {
v.deft = fct
}
func (v *viper) SetConfigFile(fileConfig string) liberr.Error {
if fileConfig != "" {
v.v.SetConfigFile(fileConfig)
} else {
// Find home directory.
home, err := libhom.Dir()
if err != nil {
return ErrorHomePathNotFound.ErrorParent(err)
}
// Search config in home directory with name defined in SetConfigName (without extension).
v.v.AddConfigPath(home)
if v.base == "" {
return ErrorBasePathNotFound.ErrorParent(fmt.Errorf("base name of config file is empty"))
}
v.v.SetConfigName("." + v.base)
if v.prfx != "" {
v.v.SetEnvPrefix(v.prfx)
v.v.AutomaticEnv()
}
}
return nil
}
func (v *viper) Viper() *spfvpr.Viper {
return v.v
}

61
viper/remote.go Normal file
View File

@@ -0,0 +1,61 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package viper
import (
liberr "github.com/nabbar/golib/errors"
)
func (v *viper) initAddRemote() liberr.Error {
if v.remote.provider == "" || v.remote.endpoint == "" || v.remote.path == "" {
return ErrorParamMissing.Error(nil)
}
if v.remote.secure != "" {
if err := v.v.AddSecureRemoteProvider(v.remote.provider, v.remote.endpoint, v.remote.path, v.remote.secure); err != nil {
return ErrorRemoteProviderSecure.ErrorParent(err)
}
} else if err := v.v.AddRemoteProvider(v.remote.provider, v.remote.endpoint, v.remote.path); err != nil {
return ErrorRemoteProvider.ErrorParent(err)
}
return v.initSetRemote()
}
func (v *viper) initSetRemote() liberr.Error {
v.v.SetConfigType("json")
if err := v.v.ReadRemoteConfig(); err != nil {
return ErrorRemoteProviderRead.ErrorParent(err)
}
if err := v.v.Unmarshal(v.remote.model); err != nil {
return ErrorRemoteProviderMarshall.ErrorParent(err)
}
return nil
}

74
viper/watch.go Normal file
View File

@@ -0,0 +1,74 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package viper
import (
"time"
libnot "github.com/fsnotify/fsnotify"
liblog "github.com/nabbar/golib/logger"
)
func (v *viper) initWatchRemote(logLevelRemoteKO, logLevelRemoteOK liblog.Level) {
// open a goroutine to watch remote changes forever
go func() {
// unstopped for loop
for {
// delay after each request
time.Sleep(time.Second * 5)
if v.remote.provider == RemoteETCD {
if logLevelRemoteKO.LogErrorCtxf(logLevelRemoteOK, "Remote config watching", v.v.WatchRemoteConfig()) {
// skip error and try next time
continue
}
} else {
// reading remote config
if logLevelRemoteKO.LogErrorCtxf(logLevelRemoteOK, "Remote config loading", v.v.ReadRemoteConfig()) {
// skip error and try next time
continue
}
}
// add config model
if logLevelRemoteKO.LogErrorCtxf(logLevelRemoteOK, "Remote config parsing", v.v.Unmarshal(v.remote.model)) {
// skip error and try next time
continue
}
}
}()
}
func (v *viper) WatchFS(logLevelFSInfo liblog.Level) {
v.v.WatchConfig()
v.v.OnConfigChange(func(e libnot.Event) {
if v.remote.fct != nil {
logLevelFSInfo.Logf("reloaded config file '%s'...", e.Name)
v.remote.fct()
}
})
}