Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added extkingpin module #10

Merged
merged 1 commit into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}