Skip to content

Commit

Permalink
Added extkingpin module (#10)
Browse files Browse the repository at this point in the history
Signed-off-by: Saswata Mukherjee <[email protected]>
  • Loading branch information
saswatamcode authored Jun 9, 2021
1 parent 425a09c commit d732591
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 3 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# tools

[![copyright module docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/tools/copyright)
[![core module docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/tools/core)
[![e2e module docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/tools/e2e)
[![copyright module docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/tools/copyright) [![core module docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/tools/core) [![e2e module docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/tools/e2e)

Set of lightweight tools, packages and modules that every open-source Go project always needs with almost no dependencies.

Expand Down Expand Up @@ -207,3 +205,25 @@ or via [bingo](https://github.com/bwplotka/bingo) if want to pin it:
go install github.com/bwplotka/bingo
bingo get -u github.com/efficientgo/tools/copyright
```

### Module `github.com/efficientgo/tools/extkingpin`

This module provides the PathOrContent flag type which defines two flags to fetch bytes. Either from file (\*-file flag) or content (\* flag). Also returns the content of a YAML file with substituted environment variables.

```go mdox-gen-exec="sh -c 'tail -n +6 extkingpin/doc.go'"
// PathOrContent is a flag type that defines two flags to fetch bytes. Either from file (*-file flag) or content (* flag).
// Also returns content of YAML file with substituted environment variables.
// Follows K8s convention, i.e $(...), as mentioned here https://kubernetes.io/docs/tasks/inject-data-application/define-interdependent-environment-variables/.

// RegisterPathOrContent registers PathOrContent flag in kingpinCmdClause.

// Content returns the content of the file when given or directly the content that has been passed to the flag.
// It returns an error when:
// * The file and content flags are both not empty.
// * The file flag is not empty but the file can't be read.
// * The content is empty and the flag has been defined as required.

// Option is a functional option type for PathOrContent objects.
// WithRequired allows you to override default required option.
// WithEnvSubstitution allows you to override default envSubstitution option.
```
20 changes: 20 additions & 0 deletions extkingpin/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) The EfficientGo Authors.
// Licensed under the Apache License 2.0.

package extkingpin

// PathOrContent is a flag type that defines two flags to fetch bytes. Either from file (*-file flag) or content (* flag).
// Also returns content of YAML file with substituted environment variables.
// Follows K8s convention, i.e $(...), as mentioned here https://kubernetes.io/docs/tasks/inject-data-application/define-interdependent-environment-variables/.

// RegisterPathOrContent registers PathOrContent flag in kingpinCmdClause.

// Content returns the content of the file when given or directly the content that has been passed to the flag.
// It returns an error when:
// * The file and content flags are both not empty.
// * The file flag is not empty but the file can't be read.
// * The content is empty and the flag has been defined as required.

// Option is a functional option type for PathOrContent objects.
// WithRequired allows you to override default required option.
// WithEnvSubstitution allows you to override default envSubstitution option.
11 changes: 11 additions & 0 deletions extkingpin/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/efficientgo/tools/extkingpin

go 1.15

require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 // indirect
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.0 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6
)
21 changes: 21 additions & 0 deletions extkingpin/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4=
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
130 changes: 130 additions & 0 deletions extkingpin/pathorcontent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) The EfficientGo Authors.
// Licensed under the Apache License 2.0.

// Taken from Thanos project.
//
// Copyright (c) The Thanos Authors.
// Licensed under the Apache License 2.0.
package extkingpin

import (
"fmt"
"io/ioutil"
"os"
"regexp"

"github.com/pkg/errors"
"gopkg.in/alecthomas/kingpin.v2"
)

// PathOrContent is a flag type that defines two flags to fetch bytes. Either from file (*-file flag) or content (* flag).
type PathOrContent struct {
flagName string

envSubstitution bool
required bool

path *string
content *string
}

// Option is a functional option type for PathOrContent objects.
type Option func(*PathOrContent)

type FlagClause interface {
Flag(name, help string) *kingpin.FlagClause
}

// RegisterPathOrContent registers PathOrContent flag in kingpinCmdClause.
func RegisterPathOrContent(cmd FlagClause, flagName string, help string, opts ...Option) *PathOrContent {
fileFlagName := fmt.Sprintf("%s-file", flagName)
contentFlagName := flagName

fileHelp := fmt.Sprintf("Path to %s", help)
fileFlag := cmd.Flag(fileFlagName, fileHelp).PlaceHolder("<file-path>").String()

contentHelp := fmt.Sprintf("Alternative to '%s' flag (mutually exclusive). Content of %s", fileFlagName, help)
contentFlag := cmd.Flag(contentFlagName, contentHelp).PlaceHolder("<content>").String()

p := &PathOrContent{
flagName: flagName,
path: fileFlag,
content: contentFlag,
required: false,
envSubstitution: false,
}
for _, opt := range opts {
opt(p)
}
return p
}

// Content returns the content of the file when given or directly the content that has been passed to the flag.
// It returns an error when:
// * The file and content flags are both not empty.
// * The file flag is not empty but the file can't be read.
// * The content is empty and the flag has been defined as required.
func (p *PathOrContent) Content() ([]byte, error) {
fileFlagName := fmt.Sprintf("%s-file", p.flagName)

if len(*p.path) > 0 && len(*p.content) > 0 {
return nil, errors.Errorf("both %s and %s flags set.", fileFlagName, p.flagName)
}

var content []byte
if len(*p.path) > 0 {
c, err := ioutil.ReadFile(*p.path)
if err != nil {
return nil, errors.Wrapf(err, "loading file %s for %s", *p.path, fileFlagName)
}
content = c
} else {
content = []byte(*p.content)
}

if len(content) == 0 && p.required {
return nil, errors.Errorf("flag %s or %s is required for running this command and content cannot be empty.", fileFlagName, p.flagName)
}
if p.envSubstitution {
replace, err := expandEnv(content)
if err != nil {
return nil, err
}
content = replace
}
return content, nil
}

// WithRequired allows you to override default required option.
func WithRequired() Option {
return func(p *PathOrContent) {
p.required = true
}
}

// WithEnvSubstitution allows you to override default envSubstitution option.
func WithEnvSubstitution() Option {
return func(p *PathOrContent) {
p.envSubstitution = true
}
}

// expandEnv returns content of YAML file with substituted environment variables.
// Follows K8s convention, i.e $(...), as mentioned here https://kubernetes.io/docs/tasks/inject-data-application/define-interdependent-environment-variables/.
func expandEnv(b []byte) (r []byte, err error) {
var envRe = regexp.MustCompile(`\$\(([a-zA-Z_0-9]+)\)`)
r = envRe.ReplaceAllFunc(b, func(n []byte) []byte {
if err != nil {
return nil
}
n = n[2 : len(n)-1]

v, ok := os.LookupEnv(string(n))
if !ok {
err = errors.Errorf("found reference to unset environment variable %q", n)
return nil
}
return []byte(v)
})
return r, err
}

0 comments on commit d732591

Please sign in to comment.