forked from LaconicNetwork/kompose
Implements a kompose specific docker compose label "kompose.service.expose" which can be used to expose the specified services externally. The accepted values are of type string. If the value is set to "true", the provider sets the endpoint automatically, and for any other value, the value is set as the hostname. If multiple ports are defined in a service, the first one is chosen to be the exposed. Unit tests, functional tests, glide updates and docs have also been added in this commit for the related feature.
547 lines
13 KiB
Go
547 lines
13 KiB
Go
package cli
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// This flag enables bash-completion for all commands and subcommands
|
|
var BashCompletionFlag = BoolFlag{
|
|
Name: "generate-bash-completion",
|
|
}
|
|
|
|
// This flag prints the version for the application
|
|
var VersionFlag = BoolFlag{
|
|
Name: "version, v",
|
|
Usage: "print the version",
|
|
}
|
|
|
|
// This flag prints the help for all commands and subcommands
|
|
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
|
// unless HideHelp is set to true)
|
|
var HelpFlag = BoolFlag{
|
|
Name: "help, h",
|
|
Usage: "show help",
|
|
}
|
|
|
|
// Flag is a common interface related to parsing flags in cli.
|
|
// For more advanced flag parsing techniques, it is recommended that
|
|
// this interface be implemented.
|
|
type Flag interface {
|
|
fmt.Stringer
|
|
// Apply Flag settings to the given flag set
|
|
Apply(*flag.FlagSet)
|
|
GetName() string
|
|
}
|
|
|
|
func flagSet(name string, flags []Flag) *flag.FlagSet {
|
|
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
|
|
|
for _, f := range flags {
|
|
f.Apply(set)
|
|
}
|
|
return set
|
|
}
|
|
|
|
func eachName(longName string, fn func(string)) {
|
|
parts := strings.Split(longName, ",")
|
|
for _, name := range parts {
|
|
name = strings.Trim(name, " ")
|
|
fn(name)
|
|
}
|
|
}
|
|
|
|
// Generic is a generic parseable type identified by a specific flag
|
|
type Generic interface {
|
|
Set(value string) error
|
|
String() string
|
|
}
|
|
|
|
// GenericFlag is the flag type for types implementing Generic
|
|
type GenericFlag struct {
|
|
Name string
|
|
Value Generic
|
|
Usage string
|
|
EnvVar string
|
|
}
|
|
|
|
// String returns the string representation of the generic flag to display the
|
|
// help text to the user (uses the String() method of the generic flag to show
|
|
// the value)
|
|
func (f GenericFlag) String() string {
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
|
|
}
|
|
|
|
func (f GenericFlag) FormatValueHelp() string {
|
|
if f.Value == nil {
|
|
return ""
|
|
}
|
|
s := f.Value.String()
|
|
if len(s) == 0 {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("\"%s\"", s)
|
|
}
|
|
|
|
// Apply takes the flagset and calls Set on the generic flag with the value
|
|
// provided by the user for parsing by the flag
|
|
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
|
val := f.Value
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
val.Set(envVal)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
set.Var(f.Value, name, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f GenericFlag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
// StringSlice is an opaque type for []string to satisfy flag.Value
|
|
type StringSlice []string
|
|
|
|
// Set appends the string value to the list of values
|
|
func (f *StringSlice) Set(value string) error {
|
|
*f = append(*f, value)
|
|
return nil
|
|
}
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
func (f *StringSlice) String() string {
|
|
return fmt.Sprintf("%s", *f)
|
|
}
|
|
|
|
// Value returns the slice of strings set by this flag
|
|
func (f *StringSlice) Value() []string {
|
|
return *f
|
|
}
|
|
|
|
// StringSlice is a string flag that can be specified multiple times on the
|
|
// command-line
|
|
type StringSliceFlag struct {
|
|
Name string
|
|
Value *StringSlice
|
|
Usage string
|
|
EnvVar string
|
|
}
|
|
|
|
// String returns the usage
|
|
func (f StringSliceFlag) String() string {
|
|
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
|
pref := prefixFor(firstName)
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
|
}
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
newVal := &StringSlice{}
|
|
for _, s := range strings.Split(envVal, ",") {
|
|
s = strings.TrimSpace(s)
|
|
newVal.Set(s)
|
|
}
|
|
f.Value = newVal
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
if f.Value == nil {
|
|
f.Value = &StringSlice{}
|
|
}
|
|
set.Var(f.Value, name, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f StringSliceFlag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
// StringSlice is an opaque type for []int to satisfy flag.Value
|
|
type IntSlice []int
|
|
|
|
// Set parses the value into an integer and appends it to the list of values
|
|
func (f *IntSlice) Set(value string) error {
|
|
tmp, err := strconv.Atoi(value)
|
|
if err != nil {
|
|
return err
|
|
} else {
|
|
*f = append(*f, tmp)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
func (f *IntSlice) String() string {
|
|
return fmt.Sprintf("%d", *f)
|
|
}
|
|
|
|
// Value returns the slice of ints set by this flag
|
|
func (f *IntSlice) Value() []int {
|
|
return *f
|
|
}
|
|
|
|
// IntSliceFlag is an int flag that can be specified multiple times on the
|
|
// command-line
|
|
type IntSliceFlag struct {
|
|
Name string
|
|
Value *IntSlice
|
|
Usage string
|
|
EnvVar string
|
|
}
|
|
|
|
// String returns the usage
|
|
func (f IntSliceFlag) String() string {
|
|
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
|
pref := prefixFor(firstName)
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
|
}
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
newVal := &IntSlice{}
|
|
for _, s := range strings.Split(envVal, ",") {
|
|
s = strings.TrimSpace(s)
|
|
err := newVal.Set(s)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, err.Error())
|
|
}
|
|
}
|
|
f.Value = newVal
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
if f.Value == nil {
|
|
f.Value = &IntSlice{}
|
|
}
|
|
set.Var(f.Value, name, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f IntSliceFlag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
// BoolFlag is a switch that defaults to false
|
|
type BoolFlag struct {
|
|
Name string
|
|
Usage string
|
|
EnvVar string
|
|
Destination *bool
|
|
}
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
func (f BoolFlag) String() string {
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
|
}
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
|
val := false
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
envValBool, err := strconv.ParseBool(envVal)
|
|
if err == nil {
|
|
val = envValBool
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
if f.Destination != nil {
|
|
set.BoolVar(f.Destination, name, val, f.Usage)
|
|
return
|
|
}
|
|
set.Bool(name, val, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f BoolFlag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
// BoolTFlag this represents a boolean flag that is true by default, but can
|
|
// still be set to false by --some-flag=false
|
|
type BoolTFlag struct {
|
|
Name string
|
|
Usage string
|
|
EnvVar string
|
|
Destination *bool
|
|
}
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
func (f BoolTFlag) String() string {
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
|
}
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
|
val := true
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
envValBool, err := strconv.ParseBool(envVal)
|
|
if err == nil {
|
|
val = envValBool
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
if f.Destination != nil {
|
|
set.BoolVar(f.Destination, name, val, f.Usage)
|
|
return
|
|
}
|
|
set.Bool(name, val, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f BoolTFlag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
// StringFlag represents a flag that takes as string value
|
|
type StringFlag struct {
|
|
Name string
|
|
Value string
|
|
Usage string
|
|
EnvVar string
|
|
Destination *string
|
|
}
|
|
|
|
// String returns the usage
|
|
func (f StringFlag) String() string {
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
|
|
}
|
|
|
|
func (f StringFlag) FormatValueHelp() string {
|
|
s := f.Value
|
|
if len(s) == 0 {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("\"%s\"", s)
|
|
}
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
func (f StringFlag) Apply(set *flag.FlagSet) {
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
f.Value = envVal
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
if f.Destination != nil {
|
|
set.StringVar(f.Destination, name, f.Value, f.Usage)
|
|
return
|
|
}
|
|
set.String(name, f.Value, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f StringFlag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
// IntFlag is a flag that takes an integer
|
|
// Errors if the value provided cannot be parsed
|
|
type IntFlag struct {
|
|
Name string
|
|
Value int
|
|
Usage string
|
|
EnvVar string
|
|
Destination *int
|
|
}
|
|
|
|
// String returns the usage
|
|
func (f IntFlag) String() string {
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
|
}
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
func (f IntFlag) Apply(set *flag.FlagSet) {
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
|
if err == nil {
|
|
f.Value = int(envValInt)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
if f.Destination != nil {
|
|
set.IntVar(f.Destination, name, f.Value, f.Usage)
|
|
return
|
|
}
|
|
set.Int(name, f.Value, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f IntFlag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
// DurationFlag is a flag that takes a duration specified in Go's duration
|
|
// format: https://golang.org/pkg/time/#ParseDuration
|
|
type DurationFlag struct {
|
|
Name string
|
|
Value time.Duration
|
|
Usage string
|
|
EnvVar string
|
|
Destination *time.Duration
|
|
}
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
func (f DurationFlag) String() string {
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
|
}
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
envValDuration, err := time.ParseDuration(envVal)
|
|
if err == nil {
|
|
f.Value = envValDuration
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
if f.Destination != nil {
|
|
set.DurationVar(f.Destination, name, f.Value, f.Usage)
|
|
return
|
|
}
|
|
set.Duration(name, f.Value, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f DurationFlag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
// Float64Flag is a flag that takes an float value
|
|
// Errors if the value provided cannot be parsed
|
|
type Float64Flag struct {
|
|
Name string
|
|
Value float64
|
|
Usage string
|
|
EnvVar string
|
|
Destination *float64
|
|
}
|
|
|
|
// String returns the usage
|
|
func (f Float64Flag) String() string {
|
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
|
}
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
|
if f.EnvVar != "" {
|
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
envVar = strings.TrimSpace(envVar)
|
|
if envVal := os.Getenv(envVar); envVal != "" {
|
|
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
|
if err == nil {
|
|
f.Value = float64(envValFloat)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
eachName(f.Name, func(name string) {
|
|
if f.Destination != nil {
|
|
set.Float64Var(f.Destination, name, f.Value, f.Usage)
|
|
return
|
|
}
|
|
set.Float64(name, f.Value, f.Usage)
|
|
})
|
|
}
|
|
|
|
func (f Float64Flag) GetName() string {
|
|
return f.Name
|
|
}
|
|
|
|
func prefixFor(name string) (prefix string) {
|
|
if len(name) == 1 {
|
|
prefix = "-"
|
|
} else {
|
|
prefix = "--"
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func prefixedNames(fullName string) (prefixed string) {
|
|
parts := strings.Split(fullName, ",")
|
|
for i, name := range parts {
|
|
name = strings.Trim(name, " ")
|
|
prefixed += prefixFor(name) + name
|
|
if i < len(parts)-1 {
|
|
prefixed += ", "
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func withEnvHint(envVar, str string) string {
|
|
envText := ""
|
|
if envVar != "" {
|
|
prefix := "$"
|
|
suffix := ""
|
|
sep := ", $"
|
|
if runtime.GOOS == "windows" {
|
|
prefix = "%"
|
|
suffix = "%"
|
|
sep = "%, %"
|
|
}
|
|
envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix)
|
|
}
|
|
return str + envText
|
|
}
|