Merge branch 'v1.5' into master

This commit is contained in:
Fernandez Ludovic 2018-02-27 14:07:07 +01:00
commit a2db3e0499
18 changed files with 684 additions and 486 deletions

View file

@ -1,5 +1,27 @@
# Change Log
## [v1.5.3](https://github.com/containous/traefik/tree/v1.5.3) (2018-02-27)
[All Commits](https://github.com/containous/traefik/compare/v1.5.2...v1.5.3)
**Bug fixes:**
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates ([#2913](https://github.com/containous/traefik/pull/2913) by [nmengin](https://github.com/nmengin))
- **[docker/swarm]** Empty IP address when use endpoint mode dnsrr ([#2887](https://github.com/containous/traefik/pull/2887) by [mmatur](https://github.com/mmatur))
- **[middleware]** Infinite entry point redirection. ([#2929](https://github.com/containous/traefik/pull/2929) by [ldez](https://github.com/ldez))
- **[provider]** Isolate backend with same name on different provider ([#2862](https://github.com/containous/traefik/pull/2862) by [Juliens](https://github.com/Juliens))
- **[tls]** Starting Træfik even if TLS certificates are in error ([#2909](https://github.com/containous/traefik/pull/2909) by [nmengin](https://github.com/nmengin))
- **[tls]** Add DEBUG log when no provided certificate can check a domain ([#2938](https://github.com/containous/traefik/pull/2938) by [nmengin](https://github.com/nmengin))
- **[webui]** Smooth dashboard refresh. ([#2871](https://github.com/containous/traefik/pull/2871) by [ldez](https://github.com/ldez))
- Fix Duration JSON unmarshal ([#2935](https://github.com/containous/traefik/pull/2935) by [ldez](https://github.com/ldez))
- Default value for lifecycle ([#2934](https://github.com/containous/traefik/pull/2934) by [Juliens](https://github.com/Juliens))
- Check ping configuration. ([#2852](https://github.com/containous/traefik/pull/2852) by [ldez](https://github.com/ldez))
**Documentation:**
- **[docker]** it's -> its ([#2901](https://github.com/containous/traefik/pull/2901) by [piec](https://github.com/piec))
- **[tls]** Fix doc cipher suites ([#2894](https://github.com/containous/traefik/pull/2894) by [emilevauge](https://github.com/emilevauge))
- Add a CLI help command for Docker. ([#2921](https://github.com/containous/traefik/pull/2921) by [ldez](https://github.com/ldez))
- Fix traffic pronounce dead link ([#2870](https://github.com/containous/traefik/pull/2870) by [emilevauge](https://github.com/emilevauge))
- Update documentation on onHostRule, ping examples, and web deprecation ([#2863](https://github.com/containous/traefik/pull/2863) by [Juliens](https://github.com/Juliens))
## [v1.5.2](https://github.com/containous/traefik/tree/v1.5.2) (2018-02-12)
[All Commits](https://github.com/containous/traefik/compare/v1.5.1...v1.5.2)

9
Gopkg.lock generated
View file

@ -225,9 +225,12 @@
[[projects]]
name = "github.com/containous/flaeg"
packages = ["."]
revision = "60c87a513a955ca7225e1b1c772581cea8420cb4"
version = "v1.0.1"
packages = [
".",
"parse"
]
revision = "963366c29a7acc2d6e02f4f9bcf260d5a1cf4968"
version = "v1.1.1"
[[projects]]
branch = "master"

View file

@ -222,6 +222,24 @@ func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate,
return nil, false
}
func (dc *DomainsCertificates) toDomainsMap() map[string]*tls.Certificate {
domainsCertificatesMap := make(map[string]*tls.Certificate)
for _, domainCertificate := range dc.Certs {
certKey := domainCertificate.Domains.Main
if domainCertificate.Domains.SANs != nil {
sort.Strings(domainCertificate.Domains.SANs)
for _, dnsName := range domainCertificate.Domains.SANs {
if dnsName != domainCertificate.Domains.Main {
certKey += fmt.Sprintf(",%s", dnsName)
}
}
}
domainsCertificatesMap[certKey] = domainCertificate.tlsCert
}
return domainsCertificatesMap
}
// DomainsCertificate contains a certificate for multiple domains
type DomainsCertificate struct {
Domains Domain

View file

@ -391,7 +391,7 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat
domain := types.CanonicalDomain(clientHello.ServerName)
account := a.store.Get().(*Account)
if providedCertificate := a.getProvidedCertificate([]string{domain}); providedCertificate != nil {
if providedCertificate := a.getProvidedCertificate(domain); providedCertificate != nil {
return providedCertificate, nil
}
@ -625,11 +625,6 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
domains = fun.Map(types.CanonicalDomain, domains).([]string)
// Check provided certificates
if a.getProvidedCertificate(domains) != nil {
return
}
operation := func() error {
if a.client == nil {
return errors.New("ACME client still not built")
@ -647,32 +642,34 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
return
}
account := a.store.Get().(*Account)
var domain Domain
if len(domains) > 1 {
domain = Domain{Main: domains[0], SANs: domains[1:]}
} else {
domain = Domain{Main: domains[0]}
}
if _, exists := account.DomainsCertificate.exists(domain); exists {
// domain already exists
// Check provided certificates
uncheckedDomains := a.getUncheckedDomains(domains, account)
if len(uncheckedDomains) == 0 {
return
}
certificate, err := a.getDomainsCertificates(domains)
certificate, err := a.getDomainsCertificates(uncheckedDomains)
if err != nil {
log.Errorf("Error getting ACME certificates %+v : %v", domains, err)
log.Errorf("Error getting ACME certificates %+v : %v", uncheckedDomains, err)
return
}
log.Debugf("Got certificate for domains %+v", domains)
log.Debugf("Got certificate for domains %+v", uncheckedDomains)
transaction, object, err := a.store.Begin()
if err != nil {
log.Errorf("Error creating transaction %+v : %v", domains, err)
log.Errorf("Error creating transaction %+v : %v", uncheckedDomains, err)
return
}
var domain Domain
if len(uncheckedDomains) > 1 {
domain = Domain{Main: uncheckedDomains[0], SANs: uncheckedDomains[1:]}
} else {
domain = Domain{Main: uncheckedDomains[0]}
}
account = object.(*Account)
_, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain)
if err != nil {
log.Errorf("Error adding ACME certificates %+v : %v", domains, err)
log.Errorf("Error adding ACME certificates %+v : %v", uncheckedDomains, err)
return
}
if err = transaction.Commit(account); err != nil {
@ -684,36 +681,95 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func (a *ACME) getProvidedCertificate(domains []string) *tls.Certificate {
func (a *ACME) getProvidedCertificate(domains string) *tls.Certificate {
log.Debugf("Looking for provided certificate to validate %s...", domains)
cert := searchProvidedCertificateForDomains(domains, a.TLSConfig.NameToCertificate)
if cert == nil && a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
cert = searchProvidedCertificateForDomains(domains, a.dynamicCerts.Get().(*traefikTls.DomainsCertificates).Get().(map[string]*tls.Certificate))
}
if cert == nil {
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
}
return cert
}
func searchProvidedCertificateForDomains(domains []string, certs map[string]*tls.Certificate) *tls.Certificate {
func searchProvidedCertificateForDomains(domain string, certs map[string]*tls.Certificate) *tls.Certificate {
// Use regex to test for provided certs that might have been added into TLSConfig
providedCertMatch := false
for k := range certs {
selector := "^" + strings.Replace(k, "*.", "[^\\.]*\\.?", -1) + "$"
for _, domainToCheck := range domains {
providedCertMatch, _ = regexp.MatchString(selector, domainToCheck)
if !providedCertMatch {
for certDomains := range certs {
domainCheck := false
for _, certDomain := range strings.Split(certDomains, ",") {
selector := "^" + strings.Replace(certDomain, "*.", "[^\\.]*\\.?", -1) + "$"
domainCheck, _ = regexp.MatchString(selector, domain)
if domainCheck {
break
}
}
if providedCertMatch {
log.Debugf("Got provided certificate for domains %s", domains)
return certs[k]
if domainCheck {
log.Debugf("Domain %q checked by provided certificate %q", domain, certDomains)
return certs[certDomains]
}
}
return nil
}
// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string {
log.Debugf("Looking for provided certificate to validate %s...", domains)
allCerts := make(map[string]*tls.Certificate)
// Get static certificates
for domains, certificate := range a.TLSConfig.NameToCertificate {
allCerts[domains] = certificate
}
// Get dynamic certificates
if a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
for domains, certificate := range a.dynamicCerts.Get().(*traefikTls.DomainsCertificates).Get().(map[string]*tls.Certificate) {
allCerts[domains] = certificate
}
}
// Get ACME certificates
if account != nil {
for domains, certificate := range account.DomainsCertificate.toDomainsMap() {
allCerts[domains] = certificate
}
}
return searchUncheckedDomains(domains, allCerts)
}
func searchUncheckedDomains(domains []string, certs map[string]*tls.Certificate) []string {
uncheckedDomains := []string{}
for _, domainToCheck := range domains {
domainCheck := false
for certDomains := range certs {
domainCheck = false
for _, certDomain := range strings.Split(certDomains, ",") {
// Use regex to test for provided certs that might have been added into TLSConfig
selector := "^" + strings.Replace(certDomain, "*.", "[^\\.]*\\.?", -1) + "$"
domainCheck, _ = regexp.MatchString(selector, domainToCheck)
if domainCheck {
break
}
}
if domainCheck {
break
}
}
if !domainCheck {
uncheckedDomains = append(uncheckedDomains, domainToCheck)
}
}
if len(uncheckedDomains) == 0 {
log.Debugf("No ACME certificate to generate for domains %q.", domains)
} else {
log.Debugf("Domains %q need ACME certificates generation for domains %q.", domains, strings.Join(uncheckedDomains, ","))
}
return uncheckedDomains
}
func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
domains = fun.Map(types.CanonicalDomain, domains).([]string)
log.Debugf("Loading ACME certificates %s...", domains)

View file

@ -281,7 +281,7 @@ cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
}
}
func TestAcme_getProvidedCertificate(t *testing.T) {
func TestAcme_getUncheckedCertificates(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{}
@ -289,9 +289,36 @@ func TestAcme_getProvidedCertificate(t *testing.T) {
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
domains := []string{"traefik.containo.us", "trae.containo.us"}
certificate := a.getProvidedCertificate(domains)
assert.NotNil(t, certificate)
uncheckedDomains := a.getUncheckedDomains(domains, nil)
assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.acme.io", "trae.acme.io"}
certificate = a.getProvidedCertificate(domains)
uncheckedDomains = a.getUncheckedDomains(domains, nil)
assert.Len(t, uncheckedDomains, 1)
domainsCertificates := DomainsCertificates{Certs: []*DomainsCertificate{
{
tlsCert: &tls.Certificate{},
Domains: Domain{
Main: "*.acme.wtf",
SANs: []string{"trae.acme.io"},
},
},
}}
account := Account{DomainsCertificate: domainsCertificates}
uncheckedDomains = a.getUncheckedDomains(domains, &account)
assert.Empty(t, uncheckedDomains)
}
func TestAcme_getProvidedCertificate(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{}
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
domain := "traefik.containo.us"
certificate := a.getProvidedCertificate(domain)
assert.NotNil(t, certificate)
domain = "trae.acme.io"
certificate = a.getProvidedCertificate(domain)
assert.Nil(t, certificate)
}

View file

@ -308,6 +308,9 @@ func NewTraefikConfiguration() *TraefikConfiguration {
HealthCheck: &configuration.HealthCheckConfig{
Interval: flaeg.Duration(configuration.DefaultHealthCheckInterval),
},
LifeCycle: &configuration.LifeCycle{
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
},
CheckNewVersion: true,
},
ConfigFile: "",

View file

@ -569,6 +569,11 @@ Each command is described at the beginning of the help section:
```bash
traefik --help
# or
docker run traefik[:version] --help
# ex: docker run traefik:1.5 --help
```
### Command: bug

View file

@ -69,7 +69,7 @@ networks:
```
As you can see, we're mounting the `traefik.toml` file as well as the (empty) `acme.json` file in the container.
Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Træfik can listen to Docker events and reconfigure it's own internal configuration when containers are created (or shut down).
Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Træfik can listen to Docker events and reconfigure its own internal configuration when containers are created (or shut down).
Also, we're making sure the container is automatically restarted by the Docker engine in case of problems (or: if the server is rebooted).
We're publishing the default HTTP ports `80` and `443` on the host, and making sure the container is placed within the `web` network we've created earlier on.
Finally, we're giving this container a static name called `traefik`.
@ -199,7 +199,7 @@ Since the `traefik` container we've created and started earlier is also attached
As mentioned earlier, we don't want containers exposed automatically by Træfik.
The reason behind this is simple: we want to have control over this process ourselves.
Thanks to Docker labels, we can tell Træfik how to create it's internal routing configuration.
Thanks to Docker labels, we can tell Træfik how to create its internal routing configuration.
Let's take a look at the labels themselves for the `app` service, which is a HTTP webservice listing on port 9000:
@ -222,7 +222,7 @@ We use both `container labels` and `service labels`.
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
We also tell Træfik to use the `web` network to route HTTP traffic to this container.
With the `traefik.enable` label, we tell Træfik to include this container in it's internal configuration.
With the `traefik.enable` label, we tell Træfik to include this container in its internal configuration.
With the `frontend.rule` label, we tell Træfik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
Essentially, this is the actual rule used for Layer-7 load balancing.

View file

@ -1,6 +1,7 @@
package integration
import (
"context"
"crypto/rand"
"crypto/tls"
"crypto/x509"
@ -13,7 +14,6 @@ import (
"github.com/containous/traefik/integration/helloworld"
"github.com/containous/traefik/integration/try"
"github.com/go-check/check"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)

View file

@ -20,7 +20,8 @@ import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
context "context"
grpc "google.golang.org/grpc"
)

View file

@ -32,7 +32,7 @@ func NewEntryPointHandler(dstEntryPoint *configuration.EntryPoint, permanent boo
protocol = "https"
}
replacement := protocol + "://$1" + match[0] + "$2"
replacement := protocol + "://${1}" + match[0] + "${2}"
return NewRegexHandler(defaultRedirectRegex, replacement, permanent)
}

View file

@ -1,6 +1,7 @@
package docker
import (
"context"
"strconv"
"testing"
"time"
@ -11,7 +12,6 @@ import (
"github.com/docker/docker/api/types/swarm"
dockerclient "github.com/docker/docker/client"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
)
type fakeTasksClient struct {

View file

@ -486,6 +486,7 @@ func (s *serverEntryPoint) getCertificate(clientHello *tls.ClientHelloInfo) (*tl
}
}
}
log.Debugf("No certificate provided dynamically can check the domain %q, a per default certificate will be used.", domainToCheck)
}
return nil, nil
}
@ -904,7 +905,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
entryPoint := globalConfiguration.EntryPoints[entryPointName]
n := negroni.New()
if entryPoint.Redirect != nil {
if entryPoint.Redirect != nil && entryPointName != entryPoint.Redirect.EntryPoint {
if redirectHandlers[entryPointName] != nil {
n.Use(redirectHandlers[entryPointName])
} else if handler, err := s.buildRedirectHandler(entryPointName, entryPoint.Redirect); err != nil {
@ -1102,7 +1103,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
log.Infof("Configured IP Whitelists: %s", frontend.WhitelistSourceRange)
}
if frontend.Redirect != nil {
if frontend.Redirect != nil && entryPointName != frontend.Redirect.EntryPoint {
rewrite, err := s.buildRedirectHandler(entryPointName, frontend.Redirect)
if err != nil {
log.Errorf("Error creating Frontend Redirect: %v", err)

View file

@ -101,7 +101,8 @@ func (c *Certificates) CreateTLSConfig(entryPointName string) (*tls.Config, map[
for _, certificate := range *c {
err := certificate.AppendCertificates(domainsCertificates, entryPointName)
if err != nil {
return nil, nil, err
log.Errorf("Unable to add a certificate to the entryPoint %q : %v", entryPointName, err)
continue
}
for _, certDom := range domainsCertificates {
for _, cert := range certDom.Get().(map[string]*tls.Certificate) {
@ -133,16 +134,16 @@ func (c *Certificate) AppendCertificates(certs map[string]*DomainsCertificates,
certContent, err := c.CertFile.Read()
if err != nil {
return err
return fmt.Errorf("unable to read CertFile : %v", err)
}
keyContent, err := c.KeyFile.Read()
if err != nil {
return err
return fmt.Errorf("uUnable to read KeyFile : %v", err)
}
tlsCert, err := tls.X509KeyPair(certContent, keyContent)
if err != nil {
return err
return fmt.Errorf("unable to generate TLS certificate : %v", err)
}
parsedCert, _ := x509.ParseCertificate(tlsCert.Certificate[0])

View file

@ -12,64 +12,63 @@ import (
"strings"
"text/tabwriter"
"text/template"
"time"
"github.com/containous/flaeg/parse"
flag "github.com/ogier/pflag"
)
// ErrParserNotFound is thrown when a field is flaged but not parser match its type
var ErrParserNotFound = errors.New("Parser not found or custom parser missing")
var ErrParserNotFound = errors.New("parser not found or custom parser missing")
// GetTypesRecursive links in flagmap a flag with its reflect.StructField
// GetTypesRecursive links in flagMap a flag with its reflect.StructField
// You can whether provide objValue on a structure or a pointer to structure as first argument
// Flags are genereted from field name or from StructTag
func getTypesRecursive(objValue reflect.Value, flagmap map[string]reflect.StructField, key string) error {
// Flags are generated from field name or from StructTag
func getTypesRecursive(objValue reflect.Value, flagMap map[string]reflect.StructField, key string) error {
name := key
switch objValue.Kind() {
case reflect.Struct:
for i := 0; i < objValue.NumField(); i++ {
if objValue.Type().Field(i).Anonymous {
if err := getTypesRecursive(objValue.Field(i), flagmap, name); err != nil {
if err := getTypesRecursive(objValue.Field(i), flagMap, name); err != nil {
return err
}
} else if len(objValue.Type().Field(i).Tag.Get("description")) > 0 {
fieldName := objValue.Type().Field(i).Name
if !isExported(fieldName) {
return fmt.Errorf("Field %s is an unexported field", fieldName)
return fmt.Errorf("field %s is an unexported field", fieldName)
}
name += objValue.Type().Name()
if tag := objValue.Type().Field(i).Tag.Get("long"); len(tag) > 0 {
fieldName = tag
}
if len(key) == 0 {
//Lower Camel Case
//name = strings.ToLower(string(fieldName[0])) + fieldName[1:]
name = strings.ToLower(fieldName)
} else {
name = key + "." + strings.ToLower(fieldName)
}
if _, ok := flagmap[name]; ok {
return errors.New("Tag already exists: " + name)
}
flagmap[name] = objValue.Type().Field(i)
if err := getTypesRecursive(objValue.Field(i), flagmap, name); err != nil {
if _, ok := flagMap[name]; ok {
return fmt.Errorf("tag already exists: %s", name)
}
flagMap[name] = objValue.Type().Field(i)
if err := getTypesRecursive(objValue.Field(i), flagMap, name); err != nil {
return err
}
}
}
case reflect.Ptr:
if len(key) > 0 {
field := flagmap[name]
field := flagMap[name]
field.Type = reflect.TypeOf(false)
flagmap[name] = field
flagMap[name] = field
}
typ := objValue.Type().Elem()
inst := reflect.New(typ).Elem()
if err := getTypesRecursive(inst, flagmap, name); err != nil {
if err := getTypesRecursive(inst, flagMap, name); err != nil {
return err
}
default:
@ -78,14 +77,15 @@ func getTypesRecursive(objValue reflect.Value, flagmap map[string]reflect.Struct
return nil
}
//GetPointerFlags returns flags on pointers
// GetBoolFlags returns flags on pointers
func GetBoolFlags(config interface{}) ([]string, error) {
flagmap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(config), flagmap, ""); err != nil {
flagMap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(config), flagMap, ""); err != nil {
return []string{}, err
}
flags := make([]string, 0, len(flagmap))
for f, structField := range flagmap {
flags := make([]string, 0, len(flagMap))
for f, structField := range flagMap {
if structField.Type.Kind() == reflect.Bool {
flags = append(flags, f)
}
@ -95,84 +95,40 @@ func GetBoolFlags(config interface{}) ([]string, error) {
// GetFlags returns flags
func GetFlags(config interface{}) ([]string, error) {
flagmap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(config), flagmap, ""); err != nil {
flagMap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(config), flagMap, ""); err != nil {
return []string{}, err
}
flags := make([]string, 0, len(flagmap))
for f := range flagmap {
flags := make([]string, 0, len(flagMap))
for f := range flagMap {
flags = append(flags, f)
}
return flags, nil
}
//loadParsers loads default parsers and custom parsers given as parameter. Return a map [reflect.Type]parsers
// bool, int, int64, uint, uint64, float64,
func loadParsers(customParsers map[reflect.Type]Parser) (map[reflect.Type]Parser, error) {
parsers := map[reflect.Type]Parser{}
var boolParser boolValue
parsers[reflect.TypeOf(true)] = &boolParser
var intParser intValue
parsers[reflect.TypeOf(1)] = &intParser
var int64Parser int64Value
parsers[reflect.TypeOf(int64(1))] = &int64Parser
var uintParser uintValue
parsers[reflect.TypeOf(uint(1))] = &uintParser
var uint64Parser uint64Value
parsers[reflect.TypeOf(uint64(1))] = &uint64Parser
var stringParser stringValue
parsers[reflect.TypeOf("")] = &stringParser
var float64Parser float64Value
parsers[reflect.TypeOf(float64(1.5))] = &float64Parser
var durationParser Duration
parsers[reflect.TypeOf(Duration(time.Second))] = &durationParser
var timeParser timeValue
parsers[reflect.TypeOf(time.Now())] = &timeParser
for rType, parser := range customParsers {
parsers[rType] = parser
}
return parsers, nil
}
//ParseArgs : parses args return valmap map[flag]Getter, using parsers map[type]Getter
//args must be formated as like as flag documentation. See https://golang.org/pkg/flag
func parseArgs(args []string, flagmap map[string]reflect.StructField, parsers map[reflect.Type]Parser) (map[string]Parser, error) {
//Return var
valmap := make(map[string]Parser)
//Visitor in flag.Parse
flagList := []*flag.Flag{}
visitor := func(fl *flag.Flag) {
flagList = append(flagList, fl)
}
newParsers := map[string]Parser{}
// ParseArgs : parses args return a map[flag]Getter, using parsers map[type]Getter
// args must be formatted as like as flag documentation. See https://golang.org/pkg/flag
func parseArgs(args []string, flagMap map[string]reflect.StructField, parsers map[reflect.Type]parse.Parser) (map[string]parse.Parser, error) {
newParsers := map[string]parse.Parser{}
flagSet := flag.NewFlagSet("flaeg.Load", flag.ContinueOnError)
// Disable output
flagSet.SetOutput(ioutil.Discard)
var err error
for flag, structField := range flagmap {
//for _, flag := range flags {
//structField := flagmap[flag]
for flg, structField := range flagMap {
if parser, ok := parsers[structField.Type]; ok {
newparserValue := reflect.New(reflect.TypeOf(parser).Elem())
newparserValue.Elem().Set(reflect.ValueOf(parser).Elem())
newparser := newparserValue.Interface().(Parser)
newParserValue := reflect.New(reflect.TypeOf(parser).Elem())
newParserValue.Elem().Set(reflect.ValueOf(parser).Elem())
newParser := newParserValue.Interface().(parse.Parser)
if short := structField.Tag.Get("short"); len(short) == 1 {
// fmt.Printf("short : %s long : %s\n", short, flag)
flagSet.VarP(newparser, flag, short, structField.Tag.Get("description"))
flagSet.VarP(newParser, flg, short, structField.Tag.Get("description"))
} else {
flagSet.Var(newparser, flag, structField.Tag.Get("description"))
flagSet.Var(newParser, flg, structField.Tag.Get("description"))
}
newParsers[flag] = newparser
newParsers[flg] = newParser
} else {
err = ErrParserNotFound
}
@ -180,24 +136,35 @@ func parseArgs(args []string, flagmap map[string]reflect.StructField, parsers ma
// prevents case sensitivity issue
args = argsToLower(args)
if err := flagSet.Parse(args); err != nil {
return nil, err
if errParse := flagSet.Parse(args); errParse != nil {
return nil, errParse
}
// Visitor in flag.Parse
var flagList []*flag.Flag
visitor := func(fl *flag.Flag) {
flagList = append(flagList, fl)
}
// Fill flagList with parsed flags
flagSet.Visit(visitor)
// Return var
valMap := make(map[string]parse.Parser)
// Return parsers on parsed flag
for _, flag := range flagList {
valmap[flag.Name] = newParsers[flag.Name]
for _, flg := range flagList {
valMap[flg.Name] = newParsers[flg.Name]
}
return valmap, err
return valMap, err
}
func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Value, defaultValmap map[string]reflect.Value, key string) error {
if defaultValue.Type() != defaultPointersValue.Type() {
return fmt.Errorf("Parameters defaultValue and defaultPointersValue must be the same struct. defaultValue type : %s is not defaultPointersValue type : %s", defaultValue.Type().String(), defaultPointersValue.Type().String())
return fmt.Errorf("parameters defaultValue and defaultPointersValue must be the same struct. defaultValue type: %s is not defaultPointersValue type: %s", defaultValue.Type().String(), defaultPointersValue.Type().String())
}
name := key
switch defaultValue.Kind() {
case reflect.Struct:
@ -207,22 +174,19 @@ func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Va
return err
}
} else if len(defaultValue.Type().Field(i).Tag.Get("description")) > 0 {
name += defaultValue.Type().Name()
fieldName := defaultValue.Type().Field(i).Name
if tag := defaultValue.Type().Field(i).Tag.Get("long"); len(tag) > 0 {
fieldName = tag
}
if len(key) == 0 {
name = strings.ToLower(fieldName)
} else {
name = key + "." + strings.ToLower(fieldName)
}
if defaultValue.Field(i).Kind() != reflect.Ptr {
// if _, ok := defaultValmap[name]; ok {
// return errors.New("Tag already exists: " + name)
// }
defaultValmap[name] = defaultValue.Field(i)
// fmt.Printf("%s: got default value %+v\n", name, defaultValue.Field(i))
}
if err := getDefaultValue(defaultValue.Field(i), defaultPointersValue.Field(i), defaultValmap, name); err != nil {
return err
@ -238,8 +202,8 @@ func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Va
return err
}
defaultValmap[name] = defaultPointersNilValue
// fmt.Printf("%s: got default value %+v\n", name, defaultPointersNilValue)
}
if !defaultValue.IsNil() {
if err := getDefaultValue(defaultValue.Elem(), defaultPointersValue.Elem(), defaultValmap, name); err != nil {
return err
@ -253,8 +217,8 @@ func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Va
instValue := reflect.New(defaultPointersValue.Type().Elem())
if len(key) != 0 {
defaultValmap[name] = instValue
// fmt.Printf("%s: got default value %+v\n", name, instValue)
}
if !defaultValue.IsNil() {
if err := getDefaultValue(defaultValue.Elem(), instValue.Elem(), defaultValmap, name); err != nil {
return err
@ -274,13 +238,14 @@ func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Va
// objValue a reflect.Value of a not-nil pointer on a struct
func setPointersNil(objValue reflect.Value) (reflect.Value, error) {
if objValue.Kind() != reflect.Ptr {
return objValue, fmt.Errorf("Parameters objValue must be a not-nil pointer on a struct, not a %s", objValue.Kind().String())
return objValue, fmt.Errorf("parameters objValue must be a not-nil pointer on a struct, not a %s", objValue.Kind())
} else if objValue.IsNil() {
return objValue, fmt.Errorf("Parameters objValue must be a not-nil pointer")
return objValue, errors.New("parameters objValue must be a not-nil pointer")
} else if objValue.Elem().Kind() != reflect.Struct {
// fmt.Printf("Parameters objValue must be a not-nil pointer on a struct, not a pointer on a %s\n", objValue.Elem().Kind().String())
return objValue, nil
}
// Clone
starObjValue := objValue.Elem()
nilPointersObjVal := reflect.New(starObjValue.Type())
@ -295,39 +260,38 @@ func setPointersNil(objValue reflect.Value) (reflect.Value, error) {
return nilPointersObjVal, nil
}
//FillStructRecursive initialize a value of any taged Struct given by reference
func fillStructRecursive(objValue reflect.Value, defaultPointerValmap map[string]reflect.Value, valmap map[string]Parser, key string) error {
// FillStructRecursive initialize a value of any tagged Struct given by reference
func fillStructRecursive(objValue reflect.Value, defaultPointerValMap map[string]reflect.Value, valMap map[string]parse.Parser, key string) error {
name := key
switch objValue.Kind() {
case reflect.Struct:
for i := 0; i < objValue.Type().NumField(); i++ {
if objValue.Type().Field(i).Anonymous {
if err := fillStructRecursive(objValue.Field(i), defaultPointerValmap, valmap, name); err != nil {
if err := fillStructRecursive(objValue.Field(i), defaultPointerValMap, valMap, name); err != nil {
return err
}
} else if len(objValue.Type().Field(i).Tag.Get("description")) > 0 {
name += objValue.Type().Name()
fieldName := objValue.Type().Field(i).Name
if tag := objValue.Type().Field(i).Tag.Get("long"); len(tag) > 0 {
fieldName = tag
}
if len(key) == 0 {
name = strings.ToLower(fieldName)
} else {
name = key + "." + strings.ToLower(fieldName)
}
// fmt.Println(name)
if objValue.Field(i).Kind() != reflect.Ptr {
if val, ok := valmap[name]; ok {
// fmt.Printf("%s : set def val\n", name)
if objValue.Field(i).Kind() != reflect.Ptr {
if val, ok := valMap[name]; ok {
if err := setFields(objValue.Field(i), val); err != nil {
return err
}
}
}
if err := fillStructRecursive(objValue.Field(i), defaultPointerValmap, valmap, name); err != nil {
if err := fillStructRecursive(objValue.Field(i), defaultPointerValMap, valMap, name); err != nil {
return err
}
}
@ -335,39 +299,38 @@ func fillStructRecursive(objValue reflect.Value, defaultPointerValmap map[string
case reflect.Ptr:
if len(key) == 0 && !objValue.IsNil() {
if err := fillStructRecursive(objValue.Elem(), defaultPointerValmap, valmap, name); err != nil {
return err
}
return nil
return fillStructRecursive(objValue.Elem(), defaultPointerValMap, valMap, name)
}
contains := false
for flag := range valmap {
for flg := range valMap {
// TODO replace by regexp
if strings.HasPrefix(flag, name+".") {
if strings.HasPrefix(flg, name+".") {
contains = true
break
}
}
needDefault := false
if _, ok := valmap[name]; ok {
needDefault = valmap[name].Get().(bool)
if _, ok := valMap[name]; ok {
needDefault = valMap[name].Get().(bool)
}
if contains && objValue.IsNil() {
needDefault = true
}
if needDefault {
if defVal, ok := defaultPointerValmap[name]; ok {
if defVal, ok := defaultPointerValMap[name]; ok {
// set default pointer value
// fmt.Printf("%s : set default value %+v\n", name, defVal)
objValue.Set(defVal)
} else {
return fmt.Errorf("flag %s default value not provided", name)
}
}
if !objValue.IsNil() && contains {
if objValue.Type().Elem().Kind() == reflect.Struct {
if err := fillStructRecursive(objValue.Elem(), defaultPointerValmap, valmap, name); err != nil {
if err := fillStructRecursive(objValue.Elem(), defaultPointerValMap, valMap, name); err != nil {
return err
}
}
@ -378,35 +341,35 @@ func fillStructRecursive(objValue reflect.Value, defaultPointerValmap map[string
return nil
}
// SetFields sets value to fieldValue using tag as key in valmap
func setFields(fieldValue reflect.Value, val Parser) error {
// SetFields sets value to fieldValue using tag as key in valMap
func setFields(fieldValue reflect.Value, val parse.Parser) error {
if fieldValue.CanSet() {
fieldValue.Set(reflect.ValueOf(val).Elem().Convert(fieldValue.Type()))
} else {
return errors.New(fieldValue.Type().String() + " is not settable.")
return fmt.Errorf("%s is not settable", fieldValue.Type().String())
}
return nil
}
// PrintHelp generates and prints command line help
func PrintHelp(flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser) error {
return PrintHelpWithCommand(flagmap, defaultValmap, parsers, nil, nil)
func PrintHelp(flagMap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser) error {
return PrintHelpWithCommand(flagMap, defaultValmap, parsers, nil, nil)
}
// PrintError takes a not nil error and prints command line help
func PrintError(err error, flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser) error {
func PrintError(err error, flagMap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser) error {
if err != flag.ErrHelp {
fmt.Printf("Error: %s\n", err)
}
if !strings.Contains(err.Error(), ":No parser for type") {
PrintHelp(flagmap, defaultValmap, parsers)
PrintHelp(flagMap, defaultValmap, parsers)
}
return err
}
// LoadWithParsers initializes config : struct fields given by reference, with args : arguments.
// Some custom parsers may be given.
func LoadWithParsers(config interface{}, defaultValue interface{}, args []string, customParsers map[reflect.Type]Parser) error {
func LoadWithParsers(config interface{}, defaultValue interface{}, args []string, customParsers map[reflect.Type]parse.Parser) error {
cmd := &Command{
Config: config,
DefaultPointersConfig: defaultValue,
@ -437,28 +400,27 @@ type Command struct {
// LoadWithCommand initializes config : struct fields given by reference, with args : arguments.
// Some custom parsers and some subCommand may be given.
func LoadWithCommand(cmd *Command, cmdArgs []string, customParsers map[reflect.Type]Parser, subCommand []*Command) error {
parsers, err := loadParsers(customParsers)
func LoadWithCommand(cmd *Command, cmdArgs []string, customParsers map[reflect.Type]parse.Parser, subCommand []*Command) error {
parsers, err := parse.LoadParsers(customParsers)
if err != nil {
return err
}
tagsmap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(cmd.Config), tagsmap, ""); err != nil {
tagsMap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(cmd.Config), tagsMap, ""); err != nil {
return err
}
defaultValmap := make(map[string]reflect.Value)
if err := getDefaultValue(reflect.ValueOf(cmd.Config), reflect.ValueOf(cmd.DefaultPointersConfig), defaultValmap, ""); err != nil {
defaultValMap := make(map[string]reflect.Value)
if err := getDefaultValue(reflect.ValueOf(cmd.Config), reflect.ValueOf(cmd.DefaultPointersConfig), defaultValMap, ""); err != nil {
return err
}
valmap, errParseArgs := parseArgs(cmdArgs, tagsmap, parsers)
valMap, errParseArgs := parseArgs(cmdArgs, tagsMap, parsers)
if errParseArgs != nil && errParseArgs != ErrParserNotFound {
return PrintErrorWithCommand(errParseArgs, tagsmap, defaultValmap, parsers, cmd, subCommand)
return PrintErrorWithCommand(errParseArgs, tagsMap, defaultValMap, parsers, cmd, subCommand)
}
if err := fillStructRecursive(reflect.ValueOf(cmd.Config), defaultValmap, valmap, ""); err != nil {
if err := fillStructRecursive(reflect.ValueOf(cmd.Config), defaultValMap, valMap, ""); err != nil {
return err
}
@ -470,7 +432,7 @@ func LoadWithCommand(cmd *Command, cmdArgs []string, customParsers map[reflect.T
}
// PrintHelpWithCommand generates and prints command line help for a Command
func PrintHelpWithCommand(flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser, cmd *Command, subCmd []*Command) error {
func PrintHelpWithCommand(flagMap map[string]reflect.StructField, defaultValMap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser, cmd *Command, subCmd []*Command) error {
// Define a templates
// Using POSXE STD : http://pubs.opengroup.org/onlinepubs/9699919799/
const helper = `{{if .ProgDescription}}{{.ProgDescription}}
@ -514,38 +476,38 @@ Flags:
return err
}
return printFlagsDescriptionsDefaultValues(flagmap, defaultValmap, parsers, os.Stdout)
return printFlagsDescriptionsDefaultValues(flagMap, defaultValMap, parsers, os.Stdout)
}
func printFlagsDescriptionsDefaultValues(flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser, output io.Writer) error {
func printFlagsDescriptionsDefaultValues(flagMap map[string]reflect.StructField, defaultValMap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser, output io.Writer) error {
// Sort alphabetically & Delete unparsable flags in a slice
flags := []string{}
for flag, field := range flagmap {
var flags []string
for flg, field := range flagMap {
if _, ok := parsers[field.Type]; ok {
flags = append(flags, flag)
flags = append(flags, flg)
}
}
sort.Strings(flags)
// Process data
descriptions := []string{}
defaultValues := []string{}
flagsWithDashs := []string{}
shortFlagsWithDash := []string{}
for _, flag := range flags {
field := flagmap[flag]
var descriptions []string
var defaultValues []string
var flagsWithDash []string
var shortFlagsWithDash []string
for _, flg := range flags {
field := flagMap[flg]
if short := field.Tag.Get("short"); len(short) == 1 {
shortFlagsWithDash = append(shortFlagsWithDash, "-"+short+",")
} else {
shortFlagsWithDash = append(shortFlagsWithDash, "")
}
flagsWithDashs = append(flagsWithDashs, "--"+flag)
flagsWithDash = append(flagsWithDash, "--"+flg)
// flag on pointer ?
if defVal, ok := defaultValmap[flag]; ok {
if defVal, ok := defaultValMap[flg]; ok {
if defVal.Kind() != reflect.Ptr {
// Set defaultValue on parsers
parsers[field.Type].SetValue(defaultValmap[flag].Interface())
parsers[field.Type].SetValue(defaultValMap[flg].Interface())
}
if defVal := parsers[field.Type].String(); len(defVal) > 0 {
@ -560,17 +522,19 @@ func printFlagsDescriptionsDefaultValues(flagmap map[string]reflect.StructField,
descriptions = append(descriptions, description)
if i != 0 {
defaultValues = append(defaultValues, "")
flagsWithDashs = append(flagsWithDashs, "")
flagsWithDash = append(flagsWithDash, "")
shortFlagsWithDash = append(shortFlagsWithDash, "")
}
}
}
//add help flag
shortFlagsWithDash = append(shortFlagsWithDash, "-h,")
flagsWithDashs = append(flagsWithDashs, "--help")
flagsWithDash = append(flagsWithDash, "--help")
descriptions = append(descriptions, "Print Help (this message) and exit")
defaultValues = append(defaultValues, "")
return displayTab(output, shortFlagsWithDash, flagsWithDashs, descriptions, defaultValues)
return displayTab(output, shortFlagsWithDash, flagsWithDash, descriptions, defaultValues)
}
func split(str string, width int) []string {
if len(str) > width {
@ -578,16 +542,19 @@ func split(str string, width int) []string {
if index == -1 {
index = width
}
return append([]string{strings.TrimSpace(str[:index])}, split(strings.TrimSpace(str[index:]), width)...)
}
return []string{str}
}
func displayTab(output io.Writer, columns ...[]string) error {
nbRow := len(columns[0])
nbCol := len(columns)
w := new(tabwriter.Writer)
w.Init(output, 0, 4, 1, ' ', 0)
nbRow := len(columns[0])
nbCol := len(columns)
for i := 0; i < nbRow; i++ {
row := ""
for j, col := range columns {
@ -598,16 +565,17 @@ func displayTab(output io.Writer, columns ...[]string) error {
}
fmt.Fprintln(w, row)
}
w.Flush()
return nil
return w.Flush()
}
// PrintErrorWithCommand takes a not nil error and prints command line help
func PrintErrorWithCommand(err error, flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser, cmd *Command, subCmd []*Command) error {
func PrintErrorWithCommand(err error, flagMap map[string]reflect.StructField, defaultValMap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser, cmd *Command, subCmd []*Command) error {
if err != flag.ErrHelp {
fmt.Printf("Error here : %s\n", err)
}
PrintHelpWithCommand(flagmap, defaultValmap, parsers, cmd, subCmd)
PrintHelpWithCommand(flagMap, defaultValMap, parsers, cmd, subCmd)
return err
}
@ -618,16 +586,16 @@ type Flaeg struct {
calledCommand *Command
commands []*Command ///rootCommand is th fist one in this slice
args []string
commmandArgs []string
customParsers map[reflect.Type]Parser
commandArgs []string
customParsers map[reflect.Type]parse.Parser
}
//New creats and initialize a pointer on Flaeg
// New creates and initialize a pointer on Flaeg
func New(rootCommand *Command, args []string) *Flaeg {
var f Flaeg
f.commands = []*Command{rootCommand}
f.args = args
f.customParsers = map[reflect.Type]Parser{}
f.customParsers = map[reflect.Type]parse.Parser{}
return &f
}
@ -637,17 +605,18 @@ func (f *Flaeg) AddCommand(command *Command) {
}
// AddParser adds custom parser for a type to the map of custom parsers
func (f *Flaeg) AddParser(typ reflect.Type, parser Parser) {
func (f *Flaeg) AddParser(typ reflect.Type, parser parse.Parser) {
f.customParsers[typ] = parser
}
// Run calls the command with flags given as agruments
// Run calls the command with flags given as arguments
func (f *Flaeg) Run() error {
if f.calledCommand == nil {
if _, _, err := f.findCommandWithCommandArgs(); err != nil {
return err
}
}
if _, err := f.Parse(f.calledCommand); err != nil {
return err
}
@ -658,9 +627,10 @@ func (f *Flaeg) Run() error {
// It returns nil and a not nil error if it fails
func (f *Flaeg) Parse(cmd *Command) (*Command, error) {
if f.calledCommand == nil {
f.commmandArgs = f.args
f.commandArgs = f.args
}
if err := LoadWithCommand(cmd, f.commmandArgs, f.customParsers, f.commands); err != nil {
if err := LoadWithCommand(cmd, f.commandArgs, f.customParsers, f.commands); err != nil {
return cmd, err
}
return cmd, nil
@ -680,20 +650,20 @@ func splitArgs(args []string) (string, []string) {
// findCommandWithCommandArgs returns the called command (by reference) and command's args
// the error returned is not nil if it fails
func (f *Flaeg) findCommandWithCommandArgs() (*Command, []string, error) {
commandName := ""
commandName, f.commmandArgs = splitArgs(f.args)
var commandName string
commandName, f.commandArgs = splitArgs(f.args)
if len(commandName) > 0 {
for _, command := range f.commands {
if commandName == command.Name {
f.calledCommand = command
return f.calledCommand, f.commmandArgs, nil
return f.calledCommand, f.commandArgs, nil
}
}
return nil, []string{}, fmt.Errorf("Command %s not found", commandName)
return nil, []string{}, fmt.Errorf("command %s not found", commandName)
}
f.calledCommand = f.commands[0]
return f.calledCommand, f.commmandArgs, nil
return f.calledCommand, f.commandArgs, nil
}
// GetCommand splits args and returns the called command (by reference)
@ -712,9 +682,11 @@ func isExported(fieldName string) bool {
if len(fieldName) < 1 {
return false
}
if string(fieldName[0]) == strings.ToUpper(string(fieldName[0])) {
return true
}
return false
}
@ -722,6 +694,7 @@ func argToLower(inArg string) string {
if len(inArg) < 2 {
return strings.ToLower(inArg)
}
var outArg string
dashIndex := strings.Index(inArg, "--")
if dashIndex == -1 {
@ -732,6 +705,7 @@ func argToLower(inArg string) string {
outArg = strings.ToLower(inArg[dashIndex:dashIndex+2]) + inArg[dashIndex+2:]
return outArg
}
// --flag
if equalIndex := strings.Index(inArg, "="); equalIndex != -1 {
// --flag=value
@ -745,7 +719,7 @@ func argToLower(inArg string) string {
}
func argsToLower(inArgs []string) []string {
outArgs := make([]string, len(inArgs), len(inArgs))
outArgs := make([]string, len(inArgs))
for i, inArg := range inArgs {
outArgs[i] = argToLower(inArg)
}

7
vendor/github.com/containous/flaeg/flaeg_types.go generated vendored Normal file
View file

@ -0,0 +1,7 @@
package flaeg
import "github.com/containous/flaeg/parse"
// Duration is deprecated use parse.Duration instead
// Deprecated
type Duration = parse.Duration

301
vendor/github.com/containous/flaeg/parse/parse.go generated vendored Normal file
View file

@ -0,0 +1,301 @@
package parse
import (
"flag"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
// Parser is an interface that allows the contents of a flag.Getter to be set.
type Parser interface {
flag.Getter
SetValue(interface{})
}
// BoolValue bool Value type
type BoolValue bool
// Set sets bool value from the given string value.
func (b *BoolValue) Set(s string) error {
v, err := strconv.ParseBool(s)
*b = BoolValue(v)
return err
}
// Get returns the bool value.
func (b *BoolValue) Get() interface{} { return bool(*b) }
func (b *BoolValue) String() string { return fmt.Sprintf("%v", *b) }
// IsBoolFlag return true
func (b *BoolValue) IsBoolFlag() bool { return true }
// SetValue sets the duration from the given bool-asserted value.
func (b *BoolValue) SetValue(val interface{}) {
*b = BoolValue(val.(bool))
}
// BoolFlag optional interface to indicate boolean flags that can be
// supplied without "=value" text
type BoolFlag interface {
flag.Value
IsBoolFlag() bool
}
// IntValue int Value
type IntValue int
// Set sets int value from the given string value.
func (i *IntValue) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
*i = IntValue(v)
return err
}
// Get returns the int value.
func (i *IntValue) Get() interface{} { return int(*i) }
func (i *IntValue) String() string { return fmt.Sprintf("%v", *i) }
// SetValue sets the IntValue from the given int-asserted value.
func (i *IntValue) SetValue(val interface{}) {
*i = IntValue(val.(int))
}
// Int64Value int64 Value
type Int64Value int64
// Set sets int64 value from the given string value.
func (i *Int64Value) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
*i = Int64Value(v)
return err
}
// Get returns the int64 value.
func (i *Int64Value) Get() interface{} { return int64(*i) }
func (i *Int64Value) String() string { return fmt.Sprintf("%v", *i) }
// SetValue sets the Int64Value from the given int64-asserted value.
func (i *Int64Value) SetValue(val interface{}) {
*i = Int64Value(val.(int64))
}
// UintValue uint Value
type UintValue uint
// Set sets uint value from the given string value.
func (i *UintValue) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
*i = UintValue(v)
return err
}
// Get returns the uint value.
func (i *UintValue) Get() interface{} { return uint(*i) }
func (i *UintValue) String() string { return fmt.Sprintf("%v", *i) }
// SetValue sets the UintValue from the given uint-asserted value.
func (i *UintValue) SetValue(val interface{}) {
*i = UintValue(val.(uint))
}
// Uint64Value uint64 Value
type Uint64Value uint64
// Set sets uint64 value from the given string value.
func (i *Uint64Value) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
*i = Uint64Value(v)
return err
}
// Get returns the uint64 value.
func (i *Uint64Value) Get() interface{} { return uint64(*i) }
func (i *Uint64Value) String() string { return fmt.Sprintf("%v", *i) }
// SetValue sets the Uint64Value from the given uint64-asserted value.
func (i *Uint64Value) SetValue(val interface{}) {
*i = Uint64Value(val.(uint64))
}
// StringValue string Value
type StringValue string
// Set sets string value from the given string value.
func (s *StringValue) Set(val string) error {
*s = StringValue(val)
return nil
}
// Get returns the string value.
func (s *StringValue) Get() interface{} { return string(*s) }
func (s *StringValue) String() string { return string(*s) }
// SetValue sets the StringValue from the given string-asserted value.
func (s *StringValue) SetValue(val interface{}) {
*s = StringValue(val.(string))
}
// Float64Value float64 Value
type Float64Value float64
// Set sets float64 value from the given string value.
func (f *Float64Value) Set(s string) error {
v, err := strconv.ParseFloat(s, 64)
*f = Float64Value(v)
return err
}
// Get returns the float64 value.
func (f *Float64Value) Get() interface{} { return float64(*f) }
func (f *Float64Value) String() string { return fmt.Sprintf("%v", *f) }
// SetValue sets the Float64Value from the given float64-asserted value.
func (f *Float64Value) SetValue(val interface{}) {
*f = Float64Value(val.(float64))
}
// Duration is a custom type suitable for parsing duration values.
// It supports `time.ParseDuration`-compatible values and suffix-less digits; in
// the latter case, seconds are assumed.
type Duration time.Duration
// Set sets the duration from the given string value.
func (d *Duration) Set(s string) error {
if v, err := strconv.Atoi(s); err == nil {
*d = Duration(time.Duration(v) * time.Second)
return nil
}
v, err := time.ParseDuration(s)
*d = Duration(v)
return err
}
// Get returns the duration value.
func (d *Duration) Get() interface{} { return time.Duration(*d) }
// String returns a string representation of the duration value.
func (d *Duration) String() string { return (*time.Duration)(d).String() }
// SetValue sets the duration from the given Duration-asserted value.
func (d *Duration) SetValue(val interface{}) {
*d = val.(Duration)
}
// MarshalText serialize the given duration value into a text.
func (d *Duration) MarshalText() ([]byte, error) {
return []byte(d.String()), nil
}
// UnmarshalText deserializes the given text into a duration value.
// It is meant to support TOML decoding of durations.
func (d *Duration) UnmarshalText(text []byte) error {
return d.Set(string(text))
}
// UnmarshalJSON deserializes the given text into a duration value.
func (d *Duration) UnmarshalJSON(text []byte) error {
if v, err := strconv.Atoi(string(text)); err == nil {
*d = Duration(time.Duration(v))
return nil
}
v, err := time.ParseDuration(string(text))
*d = Duration(v)
return err
}
// TimeValue time.Time Value
type TimeValue time.Time
// Set sets time.Time value from the given string value.
func (t *TimeValue) Set(s string) error {
v, err := time.Parse(time.RFC3339, s)
*t = TimeValue(v)
return err
}
// Get returns the time.Time value.
func (t *TimeValue) Get() interface{} { return time.Time(*t) }
func (t *TimeValue) String() string { return (*time.Time)(t).String() }
// SetValue sets the TimeValue from the given time.Time-asserted value.
func (t *TimeValue) SetValue(val interface{}) {
*t = TimeValue(val.(time.Time))
}
// SliceStrings parse slice of strings
type SliceStrings []string
// Set adds strings elem into the the parser.
// It splits str on , and ;
func (s *SliceStrings) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
*s = append(*s, slice...)
return nil
}
// Get []string
func (s *SliceStrings) Get() interface{} { return []string(*s) }
// String return slice in a string
func (s *SliceStrings) String() string { return fmt.Sprintf("%v", *s) }
// SetValue sets []string into the parser
func (s *SliceStrings) SetValue(val interface{}) {
*s = SliceStrings(val.([]string))
}
// LoadParsers loads default parsers and custom parsers given as parameter.
// Return a map [reflect.Type]parsers
// bool, int, int64, uint, uint64, float64,
func LoadParsers(customParsers map[reflect.Type]Parser) (map[reflect.Type]Parser, error) {
parsers := map[reflect.Type]Parser{}
var boolParser BoolValue
parsers[reflect.TypeOf(true)] = &boolParser
var intParser IntValue
parsers[reflect.TypeOf(1)] = &intParser
var int64Parser Int64Value
parsers[reflect.TypeOf(int64(1))] = &int64Parser
var uintParser UintValue
parsers[reflect.TypeOf(uint(1))] = &uintParser
var uint64Parser Uint64Value
parsers[reflect.TypeOf(uint64(1))] = &uint64Parser
var stringParser StringValue
parsers[reflect.TypeOf("")] = &stringParser
var float64Parser Float64Value
parsers[reflect.TypeOf(float64(1.5))] = &float64Parser
var durationParser Duration
parsers[reflect.TypeOf(Duration(time.Second))] = &durationParser
var timeParser TimeValue
parsers[reflect.TypeOf(time.Now())] = &timeParser
for rType, parser := range customParsers {
parsers[rType] = parser
}
return parsers, nil
}

View file

@ -1,221 +0,0 @@
package flaeg
import (
"flag"
"fmt"
"strconv"
"strings"
"time"
)
//TODO : add parsers on all types in https://golang.org/pkg/builtin/
// Parser is an interface that allows the contents of a flag.Getter to be set.
type Parser interface {
flag.Getter
SetValue(interface{})
}
// -- bool Value
type boolValue bool
func (b *boolValue) Set(s string) error {
v, err := strconv.ParseBool(s)
*b = boolValue(v)
return err
}
func (b *boolValue) Get() interface{} { return bool(*b) }
func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
func (b *boolValue) IsBoolFlag() bool { return true }
func (b *boolValue) SetValue(val interface{}) {
*b = boolValue(val.(bool))
}
// optional interface to indicate boolean flags that can be
// supplied without "=value" text
type boolFlag interface {
flag.Value
IsBoolFlag() bool
}
// -- int Value
type intValue int
func (i *intValue) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
*i = intValue(v)
return err
}
func (i *intValue) Get() interface{} { return int(*i) }
func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
func (i *intValue) SetValue(val interface{}) {
*i = intValue(val.(int))
}
// -- int64 Value
type int64Value int64
func (i *int64Value) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
*i = int64Value(v)
return err
}
func (i *int64Value) Get() interface{} { return int64(*i) }
func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
func (i *int64Value) SetValue(val interface{}) {
*i = int64Value(val.(int64))
}
// -- uint Value
type uintValue uint
func (i *uintValue) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
*i = uintValue(v)
return err
}
func (i *uintValue) Get() interface{} { return uint(*i) }
func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
func (i *uintValue) SetValue(val interface{}) {
*i = uintValue(val.(uint))
}
// -- uint64 Value
type uint64Value uint64
func (i *uint64Value) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
*i = uint64Value(v)
return err
}
func (i *uint64Value) Get() interface{} { return uint64(*i) }
func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
func (i *uint64Value) SetValue(val interface{}) {
*i = uint64Value(val.(uint64))
}
// -- string Value
type stringValue string
func (s *stringValue) Set(val string) error {
*s = stringValue(val)
return nil
}
func (s *stringValue) Get() interface{} { return string(*s) }
func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
func (s *stringValue) SetValue(val interface{}) {
*s = stringValue(val.(string))
}
// -- float64 Value
type float64Value float64
func (f *float64Value) Set(s string) error {
v, err := strconv.ParseFloat(s, 64)
*f = float64Value(v)
return err
}
func (f *float64Value) Get() interface{} { return float64(*f) }
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
func (f *float64Value) SetValue(val interface{}) {
*f = float64Value(val.(float64))
}
// Duration is a custom type suitable for parsing duration values.
// It supports `time.ParseDuration`-compatible values and suffix-less digits; in
// the latter case, seconds are assumed.
type Duration time.Duration
// Set sets the duration from the given string value.
func (d *Duration) Set(s string) error {
if v, err := strconv.Atoi(s); err == nil {
*d = Duration(time.Duration(v) * time.Second)
return nil
}
v, err := time.ParseDuration(s)
*d = Duration(v)
return err
}
// Get returns the duration value.
func (d *Duration) Get() interface{} { return time.Duration(*d) }
// String returns a string representation of the duration value.
func (d *Duration) String() string { return (*time.Duration)(d).String() }
// SetValue sets the duration from the given Duration-asserted value.
func (d *Duration) SetValue(val interface{}) {
*d = Duration(val.(Duration))
}
// UnmarshalText deserializes the given text into a duration value.
// It is meant to support TOML decoding of durations.
func (d *Duration) UnmarshalText(text []byte) error {
return d.Set(string(text))
}
// -- time.Time Value
type timeValue time.Time
func (t *timeValue) Set(s string) error {
v, err := time.Parse(time.RFC3339, s)
*t = timeValue(v)
return err
}
func (t *timeValue) Get() interface{} { return time.Time(*t) }
func (t *timeValue) String() string { return (*time.Time)(t).String() }
func (t *timeValue) SetValue(val interface{}) {
*t = timeValue(val.(time.Time))
}
//SliceStrings parse slice of strings
type SliceStrings []string
//Set adds strings elem into the the parser
//it splits str on , and ;
func (s *SliceStrings) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
*s = append(*s, slice...)
return nil
}
//Get []string
func (s *SliceStrings) Get() interface{} { return []string(*s) }
//String return slice in a string
func (s *SliceStrings) String() string { return fmt.Sprintf("%v", *s) }
//SetValue sets []string into the parser
func (s *SliceStrings) SetValue(val interface{}) {
*s = SliceStrings(val.([]string))
}