-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Saswata Mukherjee <[email protected]>
- Loading branch information
1 parent
425a09c
commit 10b0347
Showing
5 changed files
with
205 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |