Merge pull request #2180 from ibuildthecloud/configfile

Go back to urfave v1
This commit is contained in:
Craig Jellick 2020-09-02 11:05:19 -07:00 committed by GitHub
commit 53b3d0fc56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
108 changed files with 5615 additions and 6223 deletions

View File

@ -5,17 +5,18 @@ import (
"github.com/rancher/k3s/pkg/cli/agent"
"github.com/rancher/k3s/pkg/cli/cmds"
"github.com/rancher/spur/cli"
"github.com/rancher/k3s/pkg/configfilearg"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func main() {
app := cmds.NewApp()
app.Commands = []*cli.Command{
app.Commands = []cli.Command{
cmds.NewAgentCommand(agent.Run),
}
err := app.Run(os.Args)
err := app.Run(configfilearg.MustParse(os.Args))
if err != nil {
logrus.Fatal(err)
}

View File

@ -14,8 +14,8 @@ import (
"github.com/rancher/k3s/pkg/datadir"
"github.com/rancher/k3s/pkg/untar"
"github.com/rancher/k3s/pkg/version"
"github.com/rancher/spur/cli"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func main() {
@ -24,7 +24,7 @@ func main() {
}
app := cmds.NewApp()
app.Commands = []*cli.Command{
app.Commands = []cli.Command{
cmds.NewServerCommand(wrap(version.Program+"-server", os.Args)),
cmds.NewAgentCommand(wrap(version.Program+"-agent", os.Args)),
cmds.NewKubectlCommand(externalCLIAction("kubectl")),
@ -56,7 +56,7 @@ func runCLIs() bool {
func externalCLIAction(cmd string) func(cli *cli.Context) error {
return func(cli *cli.Context) error {
return externalCLI(cmd, cli.String("data-dir"), cli.Args().Slice())
return externalCLI(cmd, cli.String("data-dir"), cli.Args())
}
}
@ -100,6 +100,7 @@ func stageAndRun(dataDir string, cmd string, args []string) error {
if err != nil {
return err
}
logrus.Debugf("Running %s %v", cmd, args)
return syscall.Exec(cmd, args, os.Environ())
}

View File

@ -12,11 +12,12 @@ import (
"github.com/rancher/k3s/pkg/cli/ctr"
"github.com/rancher/k3s/pkg/cli/kubectl"
"github.com/rancher/k3s/pkg/cli/server"
"github.com/rancher/k3s/pkg/configfilearg"
"github.com/rancher/k3s/pkg/containerd"
ctr2 "github.com/rancher/k3s/pkg/ctr"
kubectl2 "github.com/rancher/k3s/pkg/kubectl"
"github.com/rancher/spur/cli"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func init() {
@ -35,7 +36,7 @@ func main() {
os.Args[0] = cmd
app := cmds.NewApp()
app.Commands = []*cli.Command{
app.Commands = []cli.Command{
cmds.NewServerCommand(server.Run),
cmds.NewAgentCommand(agent.Run),
cmds.NewKubectlCommand(kubectl.Run),
@ -43,7 +44,7 @@ func main() {
cmds.NewCtrCommand(ctr.Run),
}
err := app.Run(os.Args)
err := app.Run(configfilearg.MustParse(os.Args))
if err != nil {
logrus.Fatal(err)
}

2
go.mod
View File

@ -86,13 +86,13 @@ require (
github.com/rancher/helm-controller v0.7.2
github.com/rancher/kine v0.4.0
github.com/rancher/remotedialer v0.2.0
github.com/rancher/spur v0.0.0-20200617165101-8702c8e4ce7a
github.com/rancher/wrangler v0.6.1
github.com/rancher/wrangler-api v0.6.0
github.com/robfig/cron/v3 v3.0.1
github.com/rootless-containers/rootlesskit v0.10.0
github.com/sirupsen/logrus v1.6.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.6.1
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
github.com/urfave/cli v1.22.2
go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5

13
go.sum
View File

@ -186,6 +186,7 @@ github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@ -382,6 +383,7 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-containerregistry v0.0.0-20190617215043-876b8855d23c/go.mod h1:yZAFP63pRshzrEYLXLGPmUt0Ay+2zdjmMN1loCnRLUk=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -557,6 +559,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976 h1:aZQToFSLH8ejFeSkTc3r3L4dPImcj7Ib/KgmkQqbGGg=
github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
@ -581,6 +584,7 @@ github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -618,6 +622,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -719,9 +724,8 @@ github.com/rancher/moq v0.0.0-20190404221404-ee5226d43009/go.mod h1:wpITyDPTi/Na
github.com/rancher/nocode v0.0.0-20200630202308-cb097102c09f/go.mod h1:iAAt6Amgbysi6srDJs9SxGSbG2j/JSRb/xCrnEtA69g=
github.com/rancher/remotedialer v0.2.0 h1:xD7t3K6JYwTdAsxmGtTHQMkEkFgKouQ1foLxVW424Dc=
github.com/rancher/remotedialer v0.2.0/go.mod h1:tkU8ZvrR5lRgaKWaX71nAy6daeqvPFx/lJEnbW7tXSI=
github.com/rancher/spur v0.0.0-20200617165101-8702c8e4ce7a h1:MIWeFYPZ/XXnskvcUqV8W89PFs+n/mr4YWMHeZuMpYs=
github.com/rancher/spur v0.0.0-20200617165101-8702c8e4ce7a/go.mod h1:Q6L6c+4FRxY5CF1xG3oEAfH3tSQDf0NAZes7q7wRyPE=
github.com/rancher/wrangler v0.1.4/go.mod h1:EYP7cqpg42YqElaCm+U9ieSrGQKAXxUH5xsr+XGpWyE=
github.com/rancher/wrangler v0.4.0 h1:iLvuJcZkd38E3RGG74dFMMNEju0PeTzfT1PQiv5okVU=
github.com/rancher/wrangler v0.4.0/go.mod h1:1cR91WLhZgkZ+U4fV9nVuXqKurWbgXcIReU4wnQvTN8=
github.com/rancher/wrangler v0.6.0/go.mod h1:L4HtjPeX8iqLgsxfJgz+JjKMcX2q3qbRXSeTlC/CSd4=
github.com/rancher/wrangler v0.6.1 h1:7tyLk/FV2zCQkYg5SEtT4lSlsHNwa5yMOa797/VJhiQ=
@ -1044,6 +1048,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/gonum v0.6.2 h1:4r+yNT0+8SWcOkXP+63H2zQbN+USnC73cjGUxnDF94Q=
gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
@ -1068,7 +1073,6 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 h1:YzfoEYWbODU5Fbt
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@ -1102,6 +1106,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -1119,6 +1124,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e h1:HqlU9dKk5YVs7R84jmq6U3Wo/XslpkxHpBv2iWHLtLc=
k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
@ -1157,6 +1163,7 @@ sigs.k8s.io/structured-merge-diff v0.0.0-20190426204423-ea680f03cc65 h1:xJNnO2qz
sigs.k8s.io/structured-merge-diff v0.0.0-20190426204423-ea680f03cc65/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@ -14,20 +14,21 @@ import (
"github.com/rancher/k3s/pkg/cli/crictl"
"github.com/rancher/k3s/pkg/cli/kubectl"
"github.com/rancher/k3s/pkg/cli/server"
"github.com/rancher/spur/cli"
"github.com/rancher/k3s/pkg/configfilearg"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func main() {
app := cmds.NewApp()
app.Commands = []*cli.Command{
app.Commands = []cli.Command{
cmds.NewServerCommand(server.Run),
cmds.NewAgentCommand(agent.Run),
cmds.NewKubectlCommand(kubectl.Run),
cmds.NewCRICTL(crictl.Run),
}
if err := app.Run(os.Args); err != nil {
if err := app.Run(configfilearg.MustParse(os.Args)); err != nil {
logrus.Fatal(err)
}
}

View File

@ -13,9 +13,9 @@ import (
"github.com/rancher/k3s/pkg/netutil"
"github.com/rancher/k3s/pkg/token"
"github.com/rancher/k3s/pkg/version"
"github.com/rancher/spur/cli"
"github.com/rancher/wrangler/pkg/signals"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func Run(ctx *cli.Context) error {
@ -23,6 +23,9 @@ func Run(ctx *cli.Context) error {
// database credentials or other secrets.
gspt.SetProcTitle(os.Args[0] + " agent")
if err := cmds.InitLogging(); err != nil {
return err
}
if os.Getuid() != 0 && runtime.GOOS != "windows" {
return fmt.Errorf("agent must be ran as root")
}
@ -59,7 +62,7 @@ func Run(ctx *cli.Context) error {
}
cfg := cmds.AgentConfig
cfg.Debug = ctx.Bool("debug")
cfg.Debug = ctx.GlobalBool("debug")
cfg.DataDir = dataDir
contextCtx := signals.SetupSignalHandler(context.Background())

View File

@ -6,8 +6,7 @@ import (
"github.com/pkg/errors"
"github.com/rancher/k3s/pkg/version"
"github.com/rancher/spur/cli"
"github.com/rancher/spur/cli/altsrc"
"github.com/urfave/cli"
)
type Agent struct {
@ -33,13 +32,13 @@ type Agent struct {
RootlessAlreadyUnshared bool
WithNodeID bool
EnableSELinux bool
ExtraKubeletArgs []string
ExtraKubeProxyArgs []string
Labels []string
Taints []string
PrivateRegistry string
ProtectKernelDefaults bool
AgentShared
ExtraKubeletArgs cli.StringSlice
ExtraKubeProxyArgs cli.StringSlice
Labels cli.StringSlice
Taints cli.StringSlice
PrivateRegistry string
}
type AgentShared struct {
@ -62,7 +61,7 @@ var (
NodeNameFlag = cli.StringFlag{
Name: "node-name",
Usage: "(agent/node) Node name",
EnvVars: []string{version.ProgramUpper + "_NODE_NAME"},
EnvVar: version.ProgramUpper + "_NODE_NAME",
Destination: &AgentConfig.NodeName,
}
WithNodeIDFlag = cli.BoolFlag{
@ -116,34 +115,33 @@ var (
ResolvConfFlag = cli.StringFlag{
Name: "resolv-conf",
Usage: "(agent/networking) Kubelet resolv.conf file",
EnvVars: []string{version.ProgramUpper + "_RESOLV_CONF"},
EnvVar: version.ProgramUpper + "_RESOLV_CONF",
Destination: &AgentConfig.ResolvConf,
}
ExtraKubeletArgs = cli.StringSliceFlag{
Name: "kubelet-arg",
Usage: "(agent/flags) Customized flag for kubelet process",
Destination: &AgentConfig.ExtraKubeletArgs,
Name: "kubelet-arg",
Usage: "(agent/flags) Customized flag for kubelet process",
Value: &AgentConfig.ExtraKubeletArgs,
}
ExtraKubeProxyArgs = cli.StringSliceFlag{
Name: "kube-proxy-arg",
Usage: "(agent/flags) Customized flag for kube-proxy process",
Destination: &AgentConfig.ExtraKubeProxyArgs,
Name: "kube-proxy-arg",
Usage: "(agent/flags) Customized flag for kube-proxy process",
Value: &AgentConfig.ExtraKubeProxyArgs,
}
NodeTaints = cli.StringSliceFlag{
Name: "node-taint",
Usage: "(agent/node) Registering kubelet with set of taints",
Destination: &AgentConfig.Taints,
Name: "node-taint",
Usage: "(agent/node) Registering kubelet with set of taints",
Value: &AgentConfig.Taints,
}
NodeLabels = cli.StringSliceFlag{
Name: "node-label",
Usage: "(agent/node) Registering and starting kubelet with set of labels",
Destination: &AgentConfig.Labels,
Name: "node-label",
Usage: "(agent/node) Registering and starting kubelet with set of labels",
Value: &AgentConfig.Labels,
}
DisableSELinuxFlag = cli.BoolFlag{
DisableSELinuxFlag = cli.BoolTFlag{
Name: "disable-selinux",
Usage: "(deprecated) Use --selinux to explicitly enable SELinux",
Hidden: true,
Value: true, // disabled by default
}
ProtectKernelDefaultsFlag = cli.BoolFlag{
Name: "protect-kernel-defaults",
@ -155,7 +153,7 @@ var (
Usage: "(agent/node) Enable SELinux in containerd",
Hidden: false,
Destination: &AgentConfig.EnableSELinux,
EnvVars: []string{version.ProgramUpper + "_SELINUX"},
EnvVar: version.ProgramUpper + "_SELINUX",
}
)
@ -169,67 +167,62 @@ func CheckSELinuxFlags(ctx *cli.Context) error {
}
return nil
}
func NewAgentCommand(action func(ctx *cli.Context) error) *cli.Command {
return &cli.Command{
func NewAgentCommand(action func(ctx *cli.Context) error) cli.Command {
return cli.Command{
Name: "agent",
Usage: "Run node agent",
UsageText: appName + " agent [OPTIONS]",
Before: func(ctx *cli.Context) error {
if err := CheckSELinuxFlags(ctx); err != nil {
return err
}
return DebugContext(cli.InitAllInputSource(altsrc.NewConfigFromFlag(ConfigFlag.Name)))(ctx)
},
Action: InitLogging(action),
Before: SetupDebug(CheckSELinuxFlags),
Action: action,
Flags: []cli.Flag{
&ConfigFlag,
&DebugFlag,
&VLevel,
&VModule,
&LogFile,
&AlsoLogToStderr,
&cli.StringFlag{
ConfigFlag,
DebugFlag,
VLevel,
VModule,
LogFile,
AlsoLogToStderr,
cli.StringFlag{
Name: "token,t",
Usage: "(cluster) Token to use for authentication",
EnvVars: []string{version.ProgramUpper + "_TOKEN"},
EnvVar: version.ProgramUpper + "_TOKEN",
Destination: &AgentConfig.Token,
},
&cli.StringFlag{
cli.StringFlag{
Name: "token-file",
Usage: "(cluster) Token file to use for authentication",
EnvVars: []string{version.ProgramUpper + "_TOKEN_FILE"},
EnvVar: version.ProgramUpper + "_TOKEN_FILE",
Destination: &AgentConfig.TokenFile,
},
&cli.StringFlag{
cli.StringFlag{
Name: "server,s",
Usage: "(cluster) Server to connect to",
EnvVars: []string{version.ProgramUpper + "_URL"},
EnvVar: version.ProgramUpper + "_URL",
Destination: &AgentConfig.ServerURL,
},
&cli.StringFlag{
cli.StringFlag{
Name: "data-dir,d",
Usage: "(agent/data) Folder to hold state",
Destination: &AgentConfig.DataDir,
Value: "/var/lib/rancher/" + version.Program + "",
},
&NodeNameFlag,
&WithNodeIDFlag,
&NodeLabels,
&NodeTaints,
&DockerFlag,
&CRIEndpointFlag,
&PauseImageFlag,
&SnapshotterFlag,
&PrivateRegistryFlag,
&NodeIPFlag,
&NodeExternalIPFlag,
&ResolvConfFlag,
&FlannelIfaceFlag,
&FlannelConfFlag,
&ExtraKubeletArgs,
&ExtraKubeProxyArgs,
&ProtectKernelDefaultsFlag,
&cli.BoolFlag{
NodeNameFlag,
WithNodeIDFlag,
NodeLabels,
NodeTaints,
DockerFlag,
CRIEndpointFlag,
PauseImageFlag,
SnapshotterFlag,
PrivateRegistryFlag,
NodeIPFlag,
NodeExternalIPFlag,
ResolvConfFlag,
FlannelIfaceFlag,
FlannelConfFlag,
ExtraKubeletArgs,
ExtraKubeProxyArgs,
ProtectKernelDefaultsFlag,
cli.BoolFlag{
Name: "rootless",
Usage: "(experimental) Run rootless",
Destination: &AgentConfig.Rootless,
@ -239,12 +232,12 @@ func NewAgentCommand(action func(ctx *cli.Context) error) *cli.Command {
// Deprecated/hidden below
&DisableSELinuxFlag,
&FlannelFlag,
&cli.StringFlag{
FlannelFlag,
cli.StringFlag{
Name: "cluster-secret",
Usage: "(deprecated) use --token",
Destination: &AgentConfig.ClusterSecret,
EnvVars: []string{version.ProgramUpper + "_CLUSTER_SECRET"},
EnvVar: version.ProgramUpper + "_CLUSTER_SECRET",
},
},
}

View File

@ -1,14 +1,15 @@
package cmds
import (
"github.com/rancher/spur/cli"
"github.com/urfave/cli"
)
func NewCheckConfigCommand(action func(*cli.Context) error) *cli.Command {
return &cli.Command{
func NewCheckConfigCommand(action func(*cli.Context) error) cli.Command {
return cli.Command{
Name: "check-config",
Usage: "Run config check",
SkipFlagParsing: true,
SkipArgReorder: true,
Action: action,
}
}

View File

@ -1,16 +1,17 @@
package cmds
import (
"github.com/rancher/spur/cli"
"github.com/rancher/k3s/pkg/version"
"github.com/urfave/cli"
)
var (
DefaultConfig = "/etc/rancher/k3s/config.yaml"
ConfigFlag = cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "(config) Load configuration from `FILE`",
EnvVars: []string{"K3S_CONFIG_FILE"},
Value: DefaultConfig,
// ConfigFlag is here to show to the user, but the actually processing is done by configfileargs before
// call urfave
ConfigFlag = cli.StringFlag{
Name: "config,c",
Usage: "(config) Load configuration from `FILE`",
EnvVar: version.ProgramUpper + "_CONFIG_FILE",
Value: "/etc/rancher/" + version.Program + "/config.yaml",
}
)

View File

@ -1,14 +1,15 @@
package cmds
import (
"github.com/rancher/spur/cli"
"github.com/urfave/cli"
)
func NewCRICTL(action func(*cli.Context) error) *cli.Command {
return &cli.Command{
func NewCRICTL(action func(*cli.Context) error) cli.Command {
return cli.Command{
Name: "crictl",
Usage: "Run crictl",
SkipFlagParsing: true,
SkipArgReorder: true,
Action: action,
}
}

View File

@ -1,14 +1,15 @@
package cmds
import (
"github.com/rancher/spur/cli"
"github.com/urfave/cli"
)
func NewCtrCommand(action func(*cli.Context) error) *cli.Command {
return &cli.Command{
func NewCtrCommand(action func(*cli.Context) error) cli.Command {
return cli.Command{
Name: "ctr",
Usage: "Run ctr",
SkipFlagParsing: true,
SkipArgReorder: true,
Action: action,
}
}

View File

@ -1,31 +0,0 @@
package cmds
import (
"github.com/rancher/k3s/pkg/version"
"github.com/rancher/spur/cli"
"github.com/sirupsen/logrus"
)
var (
Debug = false
DebugFlag = cli.BoolFlag{
Name: "debug",
Usage: "(logging) Turn on debug logs",
Destination: &Debug,
EnvVars: []string{version.ProgramUpper + "_DEBUG"},
}
)
func DebugContext(f func(*cli.Context) error) func(ctx *cli.Context) error {
return func(ctx *cli.Context) error {
if f != nil {
if err := f(ctx); err != nil {
return err
}
}
if Debug {
logrus.SetLevel(logrus.DebugLevel)
}
return nil
}
}

View File

@ -1,14 +1,15 @@
package cmds
import (
"github.com/rancher/spur/cli"
"github.com/urfave/cli"
)
func NewKubectlCommand(action func(*cli.Context) error) *cli.Command {
return &cli.Command{
func NewKubectlCommand(action func(*cli.Context) error) cli.Command {
return cli.Command{
Name: "kubectl",
Usage: "Run kubectl",
SkipFlagParsing: true,
SkipArgReorder: true,
Action: action,
}
}

View File

@ -12,7 +12,7 @@ import (
"github.com/docker/docker/pkg/reexec"
"github.com/natefinch/lumberjack"
"github.com/rancher/k3s/pkg/version"
"github.com/rancher/spur/cli"
"github.com/urfave/cli"
)
type Log struct {
@ -49,31 +49,22 @@ var (
logSetupOnce sync.Once
)
func InitLogging(action func(*cli.Context) error) func(*cli.Context) error {
return func(ctx *cli.Context) error {
var (
err error
reExec bool
)
logSetupOnce.Do(func() {
if LogConfig.LogFile != "" && os.Getenv("_K3S_LOG_REEXEC_") == "" {
reExec = true
err = runWithLogging()
return
}
if err = checkUnixTimestamp(); err != nil {
return
}
setupLogging()
})
if reExec || err != nil {
return err
func InitLogging() error {
var rErr error
logSetupOnce.Do(func() {
if LogConfig.LogFile != "" && os.Getenv("_K3S_LOG_REEXEC_") == "" {
rErr = runWithLogging()
return
}
if action != nil {
return action(ctx)
if err := checkUnixTimestamp(); err != nil {
rErr = err
return
}
return nil
}
setupLogging()
})
return rErr
}
func checkUnixTimestamp() error {

View File

@ -5,7 +5,18 @@ import (
"os"
"github.com/rancher/k3s/pkg/version"
"github.com/rancher/spur/cli"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var (
Debug bool
DebugFlag = cli.BoolFlag{
Name: "debug",
Usage: "Turn on debug logs",
Destination: &Debug,
EnvVar: version.ProgramUpper + "_DEBUG",
}
)
func init() {
@ -23,8 +34,22 @@ func NewApp() *cli.App {
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("%s version %s\n", app.Name, app.Version)
}
app.Flags = []cli.Flag{&DebugFlag}
app.Before = DebugContext(nil)
app.Flags = []cli.Flag{
DebugFlag,
}
app.Before = SetupDebug(nil)
return app
}
func SetupDebug(next func(ctx *cli.Context) error) func(ctx *cli.Context) error {
return func(ctx *cli.Context) error {
if Debug {
logrus.SetLevel(logrus.DebugLevel)
}
if next != nil {
return next(ctx)
}
return nil
}
}

View File

@ -5,8 +5,7 @@ import (
"github.com/rancher/k3s/pkg/daemons/config"
"github.com/rancher/k3s/pkg/version"
"github.com/rancher/spur/cli"
"github.com/rancher/spur/cli/altsrc"
"github.com/urfave/cli"
)
const (
@ -37,12 +36,12 @@ type Server struct {
DisableAgent bool
KubeConfigOutput string
KubeConfigMode string
TLSSan []string
TLSSan cli.StringSlice
BindAddress string
ExtraAPIArgs []string
ExtraSchedulerArgs []string
ExtraControllerArgs []string
ExtraCloudControllerArgs []string
ExtraAPIArgs cli.StringSlice
ExtraSchedulerArgs cli.StringSlice
ExtraControllerArgs cli.StringSlice
ExtraCloudControllerArgs cli.StringSlice
Rootless bool
DatastoreEndpoint string
DatastoreCAFile string
@ -70,153 +69,146 @@ type Server struct {
var ServerConfig Server
func NewServerCommand(action func(*cli.Context) error) *cli.Command {
return &cli.Command{
func NewServerCommand(action func(*cli.Context) error) cli.Command {
return cli.Command{
Name: "server",
Usage: "Run management server",
UsageText: appName + " server [OPTIONS]",
Before: func(ctx *cli.Context) error {
if err := CheckSELinuxFlags(ctx); err != nil {
return err
}
return DebugContext(cli.InitAllInputSource(altsrc.NewConfigFromFlag(ConfigFlag.Name)))(ctx)
},
Action: InitLogging(action),
Before: SetupDebug(CheckSELinuxFlags),
Action: action,
Flags: []cli.Flag{
&ConfigFlag,
&DebugFlag,
&VLevel,
&VModule,
&LogFile,
&AlsoLogToStderr,
&cli.StringFlag{
VLevel,
VModule,
LogFile,
AlsoLogToStderr,
cli.StringFlag{
Name: "bind-address",
Usage: "(listener) " + version.Program + " bind address (default: 0.0.0.0)",
Destination: &ServerConfig.BindAddress,
},
&cli.IntFlag{
cli.IntFlag{
Name: "https-listen-port",
Usage: "(listener) HTTPS listen port",
Value: 6443,
Destination: &ServerConfig.HTTPSPort,
},
&cli.StringFlag{
cli.StringFlag{
Name: "advertise-address",
Usage: "(listener) IP address that apiserver uses to advertise to members of the cluster (default: node-external-ip/node-ip)",
Destination: &ServerConfig.AdvertiseIP,
},
&cli.IntFlag{
cli.IntFlag{
Name: "advertise-port",
Usage: "(listener) Port that apiserver uses to advertise to members of the cluster (default: listen-port)",
Destination: &ServerConfig.AdvertisePort,
},
&cli.StringSliceFlag{
Name: "tls-san",
Usage: "(listener) Add additional hostname or IP as a Subject Alternative Name in the TLS cert",
Destination: &ServerConfig.TLSSan,
cli.StringSliceFlag{
Name: "tls-san",
Usage: "(listener) Add additional hostname or IP as a Subject Alternative Name in the TLS cert",
Value: &ServerConfig.TLSSan,
},
&cli.StringFlag{
cli.StringFlag{
Name: "data-dir,d",
Usage: "(data) Folder to hold state default /var/lib/rancher/" + version.Program + " or ${HOME}/.rancher/" + version.Program + " if not root",
Destination: &ServerConfig.DataDir,
},
&cli.StringFlag{
cli.StringFlag{
Name: "cluster-cidr",
Usage: "(networking) Network CIDR to use for pod IPs",
Destination: &ServerConfig.ClusterCIDR,
Value: "10.42.0.0/16",
},
&cli.StringFlag{
cli.StringFlag{
Name: "service-cidr",
Usage: "(networking) Network CIDR to use for services IPs",
Destination: &ServerConfig.ServiceCIDR,
Value: "10.43.0.0/16",
},
&cli.StringFlag{
cli.StringFlag{
Name: "cluster-dns",
Usage: "(networking) Cluster IP for coredns service. Should be in your service-cidr range (default: 10.43.0.10)",
Destination: &ServerConfig.ClusterDNS,
Value: "",
},
&cli.StringFlag{
cli.StringFlag{
Name: "cluster-domain",
Usage: "(networking) Cluster Domain",
Destination: &ServerConfig.ClusterDomain,
Value: "cluster.local",
},
&cli.StringFlag{
cli.StringFlag{
Name: "flannel-backend",
Usage: "(networking) One of 'none', 'vxlan', 'ipsec', 'host-gw', or 'wireguard'",
Destination: &ServerConfig.FlannelBackend,
Value: "vxlan",
},
&cli.StringFlag{
cli.StringFlag{
Name: "token,t",
Usage: "(cluster) Shared secret used to join a server or agent to a cluster",
Destination: &ServerConfig.Token,
EnvVars: []string{version.ProgramUpper + "_TOKEN"},
EnvVar: version.ProgramUpper + "_TOKEN",
},
&cli.StringFlag{
cli.StringFlag{
Name: "token-file",
Usage: "(cluster) File containing the cluster-secret/token",
Destination: &ServerConfig.TokenFile,
EnvVars: []string{version.ProgramUpper + "_TOKEN_FILE"},
EnvVar: version.ProgramUpper + "_TOKEN_FILE",
},
&cli.StringFlag{
cli.StringFlag{
Name: "write-kubeconfig,o",
Usage: "(client) Write kubeconfig for admin client to this file",
Destination: &ServerConfig.KubeConfigOutput,
EnvVars: []string{version.ProgramUpper + "_KUBECONFIG_OUTPUT"},
EnvVar: version.ProgramUpper + "_KUBECONFIG_OUTPUT",
},
&cli.StringFlag{
cli.StringFlag{
Name: "write-kubeconfig-mode",
Usage: "(client) Write kubeconfig with this mode",
Destination: &ServerConfig.KubeConfigMode,
EnvVars: []string{version.ProgramUpper + "_KUBECONFIG_MODE"},
EnvVar: version.ProgramUpper + "_KUBECONFIG_MODE",
},
&cli.StringSliceFlag{
Name: "kube-apiserver-arg",
Usage: "(flags) Customized flag for kube-apiserver process",
Destination: &ServerConfig.ExtraAPIArgs,
cli.StringSliceFlag{
Name: "kube-apiserver-arg",
Usage: "(flags) Customized flag for kube-apiserver process",
Value: &ServerConfig.ExtraAPIArgs,
},
&cli.StringSliceFlag{
Name: "kube-scheduler-arg",
Usage: "(flags) Customized flag for kube-scheduler process",
Destination: &ServerConfig.ExtraSchedulerArgs,
cli.StringSliceFlag{
Name: "kube-scheduler-arg",
Usage: "(flags) Customized flag for kube-scheduler process",
Value: &ServerConfig.ExtraSchedulerArgs,
},
&cli.StringSliceFlag{
Name: "kube-controller-manager-arg",
Usage: "(flags) Customized flag for kube-controller-manager process",
Destination: &ServerConfig.ExtraControllerArgs,
cli.StringSliceFlag{
Name: "kube-controller-manager-arg",
Usage: "(flags) Customized flag for kube-controller-manager process",
Value: &ServerConfig.ExtraControllerArgs,
},
&cli.StringSliceFlag{
Name: "kube-cloud-controller-manager-arg",
Usage: "(flags) Customized flag for kube-cloud-controller-manager process",
Destination: &ServerConfig.ExtraCloudControllerArgs,
cli.StringSliceFlag{
Name: "kube-cloud-controller-manager-arg",
Usage: "(flags) Customized flag for kube-cloud-controller-manager process",
Value: &ServerConfig.ExtraCloudControllerArgs,
},
&cli.StringFlag{
cli.StringFlag{
Name: "datastore-endpoint",
Usage: "(db) Specify etcd, Mysql, Postgres, or Sqlite (default) data source name",
Destination: &ServerConfig.DatastoreEndpoint,
EnvVars: []string{version.ProgramUpper + "_DATASTORE_ENDPOINT"},
EnvVar: version.ProgramUpper + "_DATASTORE_ENDPOINT",
},
&cli.StringFlag{
cli.StringFlag{
Name: "datastore-cafile",
Usage: "(db) TLS Certificate Authority file used to secure datastore backend communication",
Destination: &ServerConfig.DatastoreCAFile,
EnvVars: []string{version.ProgramUpper + "_DATASTORE_CAFILE"},
EnvVar: version.ProgramUpper + "_DATASTORE_CAFILE",
},
&cli.StringFlag{
cli.StringFlag{
Name: "datastore-certfile",
Usage: "(db) TLS certification file used to secure datastore backend communication",
Destination: &ServerConfig.DatastoreCertFile,
EnvVars: []string{version.ProgramUpper + "_DATASTORE_CERTFILE"},
EnvVar: version.ProgramUpper + "_DATASTORE_CERTFILE",
},
&cli.StringFlag{
cli.StringFlag{
Name: "datastore-keyfile",
Usage: "(db) TLS key file used to secure datastore backend communication",
Destination: &ServerConfig.DatastoreKeyFile,
EnvVars: []string{version.ProgramUpper + "_DATASTORE_KEYFILE"},
EnvVar: version.ProgramUpper + "_DATASTORE_KEYFILE",
},
&cli.BoolFlag{
Name: "etcd-disable-snapshots",
@ -240,88 +232,88 @@ func NewServerCommand(action func(*cli.Context) error) *cli.Command {
Usage: "(db) Directory to save db snapshots. (Default location: ${data-dir}/db/snapshots)",
Destination: &ServerConfig.EtcdSnapshotDir,
},
&cli.StringFlag{
cli.StringFlag{
Name: "default-local-storage-path",
Usage: "(storage) Default local storage path for local provisioner storage class",
Destination: &ServerConfig.DefaultLocalStoragePath,
},
&cli.StringSliceFlag{
cli.StringSliceFlag{
Name: "disable",
Usage: "(components) Do not deploy packaged components and delete any deployed components (valid items: " + DisableItems + ")",
},
&cli.BoolFlag{
cli.BoolFlag{
Name: "disable-scheduler",
Usage: "(components) Disable Kubernetes default scheduler",
Destination: &ServerConfig.DisableScheduler,
},
&cli.BoolFlag{
cli.BoolFlag{
Name: "disable-cloud-controller",
Usage: "(components) Disable " + version.Program + " default cloud controller manager",
Destination: &ServerConfig.DisableCCM,
},
&cli.BoolFlag{
cli.BoolFlag{
Name: "disable-kube-proxy",
Usage: "(components) Disable running kube-proxy",
Destination: &ServerConfig.DisableKubeProxy,
},
&cli.BoolFlag{
cli.BoolFlag{
Name: "disable-network-policy",
Usage: "(components) Disable " + version.Program + " default network policy controller",
Destination: &ServerConfig.DisableNPC,
},
&NodeNameFlag,
&WithNodeIDFlag,
&NodeLabels,
&NodeTaints,
&DockerFlag,
&CRIEndpointFlag,
&PauseImageFlag,
&SnapshotterFlag,
&PrivateRegistryFlag,
&NodeIPFlag,
&NodeExternalIPFlag,
&ResolvConfFlag,
&FlannelIfaceFlag,
&FlannelConfFlag,
&ExtraKubeletArgs,
&ExtraKubeProxyArgs,
&ProtectKernelDefaultsFlag,
&cli.BoolFlag{
NodeNameFlag,
WithNodeIDFlag,
NodeLabels,
NodeTaints,
DockerFlag,
CRIEndpointFlag,
PauseImageFlag,
SnapshotterFlag,
PrivateRegistryFlag,
NodeIPFlag,
NodeExternalIPFlag,
ResolvConfFlag,
FlannelIfaceFlag,
FlannelConfFlag,
ExtraKubeletArgs,
ExtraKubeProxyArgs,
ProtectKernelDefaultsFlag,
cli.BoolFlag{
Name: "rootless",
Usage: "(experimental) Run rootless",
Destination: &ServerConfig.Rootless,
},
&cli.StringFlag{
cli.StringFlag{
Name: "agent-token",
Usage: "(experimental/cluster) Shared secret used to join agents to the cluster, but not servers",
Destination: &ServerConfig.AgentToken,
EnvVars: []string{version.ProgramUpper + "_AGENT_TOKEN"},
EnvVar: version.ProgramUpper + "_AGENT_TOKEN",
},
&cli.StringFlag{
cli.StringFlag{
Name: "agent-token-file",
Usage: "(experimental/cluster) File containing the agent secret",
Destination: &ServerConfig.AgentTokenFile,
EnvVars: []string{version.ProgramUpper + "_AGENT_TOKEN_FILE"},
EnvVar: version.ProgramUpper + "_AGENT_TOKEN_FILE",
},
&cli.StringFlag{
cli.StringFlag{
Name: "server,s",
Hidden: hideClusterFlags,
Usage: "(experimental/cluster) Server to connect to, used to join a cluster",
EnvVars: []string{version.ProgramUpper + "_URL"},
EnvVar: version.ProgramUpper + "_URL",
Destination: &ServerConfig.ServerURL,
},
&cli.BoolFlag{
cli.BoolFlag{
Name: "cluster-init",
Hidden: hideClusterFlags,
Usage: "(experimental/cluster) Initialize a new cluster",
EnvVars: []string{version.ProgramUpper + "_CLUSTER_INIT"},
EnvVar: version.ProgramUpper + "_CLUSTER_INIT",
Destination: &ServerConfig.ClusterInit,
},
&cli.BoolFlag{
cli.BoolFlag{
Name: "cluster-reset",
Hidden: hideClusterFlags,
Usage: "(experimental/cluster) Forget all peers and become sole member of a new cluster",
EnvVars: []string{version.ProgramUpper + "_CLUSTER_RESET"},
EnvVar: version.ProgramUpper + "_CLUSTER_RESET",
Destination: &ServerConfig.ClusterReset,
},
&cli.StringFlag{
@ -329,7 +321,7 @@ func NewServerCommand(action func(*cli.Context) error) *cli.Command {
Usage: "(db) Path to snapshot file to be restored",
Destination: &ServerConfig.ClusterResetRestorePath,
},
&cli.BoolFlag{
cli.BoolFlag{
Name: "secrets-encryption",
Usage: "(experimental) Enable Secret encryption at rest",
Destination: &ServerConfig.EncryptSecrets,
@ -339,34 +331,34 @@ func NewServerCommand(action func(*cli.Context) error) *cli.Command {
// Hidden/Deprecated flags below
&DisableSELinuxFlag,
&FlannelFlag,
&cli.StringSliceFlag{
FlannelFlag,
cli.StringSliceFlag{
Name: "no-deploy",
Usage: "(deprecated) Do not deploy packaged components (valid items: " + DisableItems + ")",
},
&cli.StringFlag{
cli.StringFlag{
Name: "cluster-secret",
Usage: "(deprecated) use --token",
Destination: &ServerConfig.ClusterSecret,
EnvVars: []string{version.ProgramUpper + "_CLUSTER_SECRET"},
EnvVar: version.ProgramUpper + "_CLUSTER_SECRET",
},
&cli.BoolFlag{
cli.BoolFlag{
Name: "disable-agent",
Usage: "Do not run a local agent and register a local kubelet",
Hidden: true,
Destination: &ServerConfig.DisableAgent,
},
&cli.StringSliceFlag{
Hidden: true,
Name: "kube-controller-arg",
Usage: "(flags) Customized flag for kube-controller-manager process",
Destination: &ServerConfig.ExtraControllerArgs,
cli.StringSliceFlag{
Hidden: true,
Name: "kube-controller-arg",
Usage: "(flags) Customized flag for kube-controller-manager process",
Value: &ServerConfig.ExtraControllerArgs,
},
&cli.StringSliceFlag{
Hidden: true,
Name: "kube-cloud-controller-arg",
Usage: "(flags) Customized flag for kube-cloud-controller-manager process",
Destination: &ServerConfig.ExtraCloudControllerArgs,
cli.StringSliceFlag{
Hidden: true,
Name: "kube-cloud-controller-arg",
Usage: "(flags) Customized flag for kube-cloud-controller-manager process",
Value: &ServerConfig.ExtraCloudControllerArgs,
},
},
}

View File

@ -2,7 +2,7 @@ package crictl
import (
"github.com/kubernetes-sigs/cri-tools/cmd/crictl"
"github.com/rancher/spur/cli"
"github.com/urfave/cli"
)
func Run(ctx *cli.Context) error {

View File

@ -2,7 +2,7 @@ package ctr
import (
"github.com/rancher/k3s/pkg/ctr"
"github.com/rancher/spur/cli"
"github.com/urfave/cli"
)
func Run(ctx *cli.Context) error {

View File

@ -2,7 +2,7 @@ package kubectl
import (
"github.com/rancher/k3s/pkg/kubectl"
"github.com/rancher/spur/cli"
"github.com/urfave/cli"
)
func Run(ctx *cli.Context) error {

View File

@ -19,9 +19,9 @@ import (
"github.com/rancher/k3s/pkg/server"
"github.com/rancher/k3s/pkg/token"
"github.com/rancher/k3s/pkg/version"
"github.com/rancher/spur/cli"
"github.com/rancher/wrangler/pkg/signals"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"k8s.io/apimachinery/pkg/util/net"
kubeapiserverflag "k8s.io/component-base/cli/flag"
"k8s.io/kubernetes/pkg/master"
@ -32,6 +32,9 @@ import (
)
func Run(app *cli.Context) error {
if err := cmds.InitLogging(); err != nil {
return err
}
return run(app, &cmds.ServerConfig)
}
@ -263,7 +266,7 @@ func run(app *cli.Context, cfg *cmds.Server) error {
}
agentConfig := cmds.AgentConfig
agentConfig.Debug = app.Bool("debug")
agentConfig.Debug = app.GlobalBool("debug")
agentConfig.DataDir = filepath.Dir(serverConfig.ControlConfig.DataDir)
agentConfig.ServerURL = url
agentConfig.Token = token

View File

@ -0,0 +1,20 @@
package configfilearg
import (
"github.com/rancher/k3s/pkg/version"
"github.com/sirupsen/logrus"
)
func MustParse(args []string) []string {
parser := &Parser{
After: []string{"server", "agent"},
FlagNames: []string{"--config", "-c"},
EnvName: version.ProgramUpper + "_CONFIG_FILE",
DefaultConfig: "/etc/rancher/" + version.Program + "/config.yaml",
}
result, err := parser.Parse(args)
if err != nil {
logrus.Fatal(err)
}
return result
}

139
pkg/configfilearg/parser.go Normal file
View File

@ -0,0 +1,139 @@
package configfilearg
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"github.com/rancher/wrangler/pkg/data/convert"
"gopkg.in/yaml.v2"
)
type Parser struct {
After []string
FlagNames []string
EnvName string
DefaultConfig string
}
// Parser will parse an os.Args style slice looking for Parser.FlagNames after Parse.After.
// It will read the parameter value of Parse.FlagNames and read the file, appending all flags directly after
// the Parser.After value. This means a the non-config file flags will override, or if a slice append to, the config
// file values.
// If Parser.DefaultConfig is set, the existence of the config file is optional if not set in the os.Args. This means
// if Parser.DefaultConfig is set we will always try to read the config file but only fail if it's not found if the
// args contains Parser.FlagNames
func (p *Parser) Parse(args []string) ([]string, error) {
prefix, suffix, found := p.findStart(args)
if !found {
return args, nil
}
configFile, isSet := p.findConfigFileFlag(args)
if configFile != "" {
values, err := readConfigFile(configFile)
if !isSet && os.IsNotExist(err) {
return args, nil
} else if err != nil {
return nil, err
}
return append(prefix, append(values, suffix...)...), nil
}
return args, nil
}
func (p *Parser) findConfigFileFlag(args []string) (string, bool) {
if envVal := os.Getenv(p.EnvName); p.EnvName != "" && envVal != "" {
return envVal, true
}
for i, arg := range args {
for _, flagName := range p.FlagNames {
if flagName == arg {
if len(args) > i+1 {
return args[i+1], true
}
// This is actually invalid, so we rely on the CLI parser after the fact flagging it as bad
return "", false
} else if strings.HasPrefix(arg, flagName+"=") {
return arg[len(flagName)+1:], true
}
}
}
return p.DefaultConfig, false
}
func (p *Parser) findStart(args []string) ([]string, []string, bool) {
if len(p.After) == 0 {
return []string{}, args, true
}
for i, val := range args {
for _, test := range p.After {
if val == test {
return args[0 : i+1], args[i+1:], true
}
}
}
return args, nil, false
}
func readConfigFile(file string) (result []string, _ error) {
bytes, err := readConfigFileData(file)
if err != nil {
return nil, err
}
data := yaml.MapSlice{}
if err := yaml.Unmarshal(bytes, &data); err != nil {
return nil, err
}
for _, i := range data {
k, v := convert.ToString(i.Key), i.Value
prefix := "--"
if len(k) == 1 {
prefix = "-"
}
if slice, ok := v.([]interface{}); ok {
for _, v := range slice {
result = append(result, prefix+k, convert.ToString(v))
result = append(result)
}
} else {
str := convert.ToString(v)
result = append(result, prefix+k)
if str != "" {
result = append(result, str)
}
}
}
return
}
func readConfigFileData(file string) ([]byte, error) {
u, err := url.Parse(file)
if err != nil {
return nil, fmt.Errorf("failed to parse config location %s: %w", file, err)
}
switch u.Scheme {
case "http", "https":
resp, err := http.Get(file)
if err != nil {
return nil, fmt.Errorf("failed to read http config %s: %w", file, err)
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
default:
return ioutil.ReadFile(file)
}
}

View File

@ -0,0 +1,233 @@
package configfilearg
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFindStart(t *testing.T) {
testCases := []struct {
input []string
prefix []string
suffix []string
found bool
what string
}{
{
input: nil,
prefix: nil,
suffix: nil,
found: false,
what: "default case",
},
{
input: []string{"server"},
prefix: []string{"server"},
suffix: []string{},
found: true,
what: "simple case",
},
{
input: []string{"server", "foo"},
prefix: []string{"server"},
suffix: []string{"foo"},
found: true,
what: "also simple case",
},
{
input: []string{"server", "foo", "bar"},
prefix: []string{"server"},
suffix: []string{"foo", "bar"},
found: true,
what: "longer simple case",
},
{
input: []string{"not-server", "foo", "bar"},
prefix: []string{"not-server", "foo", "bar"},
found: false,
what: "not found",
},
}
p := Parser{
After: []string{"server", "agent"},
}
for _, testCase := range testCases {
prefix, suffix, found := p.findStart(testCase.input)
assert.Equal(t, testCase.prefix, prefix)
assert.Equal(t, testCase.suffix, suffix)
assert.Equal(t, testCase.found, found)
}
}
func TestConfigFile(t *testing.T) {
testCases := []struct {
input []string
env string
def string
configFile string
found bool
what string
}{
{
input: nil,
found: false,
what: "default case",
},
{
input: []string{"asdf", "-c", "value"},
configFile: "value",
found: true,
what: "simple case",
},
{
input: []string{"-c"},
found: false,
what: "invalid args string",
},
{
input: []string{"-c="},
found: true,
what: "empty arg value",
},
{
def: "def",
input: []string{"-c="},
found: true,
what: "empty arg value override default",
},
{
def: "def",
input: []string{"-c"},
found: false,
what: "invalid args always return no value",
},
{
def: "def",
input: []string{"-c", "value"},
configFile: "value",
found: true,
what: "value override default",
},
{
def: "def",
configFile: "def",
found: false,
what: "default gets used when nothing is passed",
},
{
def: "def",
input: []string{"-c", "value"},
env: "env",
configFile: "env",
found: true,
what: "env override args",
},
{
def: "def",
input: []string{"before", "-c", "value", "after"},
configFile: "value",
found: true,
what: "garbage in start and end",
},
}
for _, testCase := range testCases {
p := Parser{
FlagNames: []string{"--config", "-c"},
EnvName: "_TEST_FLAG_ENV",
DefaultConfig: testCase.def,
}
os.Setenv(p.EnvName, testCase.env)
configFile, found := p.findConfigFileFlag(testCase.input)
assert.Equal(t, testCase.configFile, configFile, testCase.what)
assert.Equal(t, testCase.found, found, testCase.what)
}
}
func TestParse(t *testing.T) {
testDataOutput := []string{
"--foo-bar", "baz",
"--a-slice", "1",
"--a-slice", "2",
"--a-slice", "",
"--a-slice", "three",
"--isempty",
"-c", "b",
"--islast", "true",
}
defParser := Parser{
After: []string{"server", "agent"},
FlagNames: []string{"-c", "--config"},
EnvName: "_TEST_ENV",
DefaultConfig: "./testdata/data.yaml",
}
testCases := []struct {
parser Parser
env string
input []string
output []string
err string
what string
}{
{
parser: defParser,
what: "default case",
},
{
parser: defParser,
input: []string{"server"},
output: append([]string{"server"}, testDataOutput...),
what: "read config file when not specified",
},
{
parser: Parser{
After: []string{"server", "agent"},
FlagNames: []string{"-c", "--config"},
DefaultConfig: "missing",
},
input: []string{"server"},
output: []string{"server"},
what: "ignore missing config when not set",
},
{
parser: Parser{
After: []string{"server", "agent"},
FlagNames: []string{"-c", "--config"},
DefaultConfig: "missing",
},
input: []string{"server", "-c=missing"},
output: []string{"server", "-c=missing"},
what: "fail when missing config",
err: "open missing: no such file or directory",
},
{
parser: Parser{
After: []string{"server", "agent"},
FlagNames: []string{"-c", "--config"},
DefaultConfig: "missing",
},
input: []string{"before", "server", "before", "-c", "./testdata/data.yaml", "after"},
output: append(append([]string{"before", "server"}, testDataOutput...), "before", "-c", "./testdata/data.yaml", "after"),
what: "read config file",
},
}
for _, testCase := range testCases {
os.Setenv(testCase.parser.EnvName, testCase.env)
output, err := testCase.parser.Parse(testCase.input)
if err == nil {
assert.Equal(t, testCase.err, "", testCase.what)
} else {
assert.Equal(t, testCase.err, err.Error(), testCase.what)
}
if testCase.err == "" {
assert.Equal(t, testCase.output, output, testCase.what)
}
}
}

9
pkg/configfilearg/testdata/data.yaml vendored Normal file
View File

@ -0,0 +1,9 @@
foo-bar: baz
a-slice:
- 1
- "2"
- ""
- three
isempty:
c: b
islast: true

27
vendor/github.com/pmezard/go-difflib/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2013, Patrick Mezard
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

772
vendor/github.com/pmezard/go-difflib/difflib/difflib.go generated vendored Normal file
View File

@ -0,0 +1,772 @@
// Package difflib is a partial port of Python difflib module.
//
// It provides tools to compare sequences of strings and generate textual diffs.
//
// The following class and functions have been ported:
//
// - SequenceMatcher
//
// - unified_diff
//
// - context_diff
//
// Getting unified diffs was the main goal of the port. Keep in mind this code
// is mostly suitable to output text differences in a human friendly way, there
// are no guarantees generated diffs are consumable by patch(1).
package difflib
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func calculateRatio(matches, length int) float64 {
if length > 0 {
return 2.0 * float64(matches) / float64(length)
}
return 1.0
}
type Match struct {
A int
B int
Size int
}
type OpCode struct {
Tag byte
I1 int
I2 int
J1 int
J2 int
}
// SequenceMatcher compares sequence of strings. The basic
// algorithm predates, and is a little fancier than, an algorithm
// published in the late 1980's by Ratcliff and Obershelp under the
// hyperbolic name "gestalt pattern matching". The basic idea is to find
// the longest contiguous matching subsequence that contains no "junk"
// elements (R-O doesn't address junk). The same idea is then applied
// recursively to the pieces of the sequences to the left and to the right
// of the matching subsequence. This does not yield minimal edit
// sequences, but does tend to yield matches that "look right" to people.
//
// SequenceMatcher tries to compute a "human-friendly diff" between two
// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
// longest *contiguous* & junk-free matching subsequence. That's what
// catches peoples' eyes. The Windows(tm) windiff has another interesting
// notion, pairing up elements that appear uniquely in each sequence.
// That, and the method here, appear to yield more intuitive difference
// reports than does diff. This method appears to be the least vulnerable
// to synching up on blocks of "junk lines", though (like blank lines in
// ordinary text files, or maybe "<P>" lines in HTML files). That may be
// because this is the only method of the 3 that has a *concept* of
// "junk" <wink>.
//
// Timing: Basic R-O is cubic time worst case and quadratic time expected
// case. SequenceMatcher is quadratic time for the worst case and has
// expected-case behavior dependent in a complicated way on how many
// elements the sequences have in common; best case time is linear.
type SequenceMatcher struct {
a []string
b []string
b2j map[string][]int
IsJunk func(string) bool
autoJunk bool
bJunk map[string]struct{}
matchingBlocks []Match
fullBCount map[string]int
bPopular map[string]struct{}
opCodes []OpCode
}
func NewMatcher(a, b []string) *SequenceMatcher {
m := SequenceMatcher{autoJunk: true}
m.SetSeqs(a, b)
return &m
}
func NewMatcherWithJunk(a, b []string, autoJunk bool,
isJunk func(string) bool) *SequenceMatcher {
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
m.SetSeqs(a, b)
return &m
}
// Set two sequences to be compared.
func (m *SequenceMatcher) SetSeqs(a, b []string) {
m.SetSeq1(a)
m.SetSeq2(b)
}
// Set the first sequence to be compared. The second sequence to be compared is
// not changed.
//
// SequenceMatcher computes and caches detailed information about the second
// sequence, so if you want to compare one sequence S against many sequences,
// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
// sequences.
//
// See also SetSeqs() and SetSeq2().
func (m *SequenceMatcher) SetSeq1(a []string) {
if &a == &m.a {
return
}
m.a = a
m.matchingBlocks = nil
m.opCodes = nil
}
// Set the second sequence to be compared. The first sequence to be compared is
// not changed.
func (m *SequenceMatcher) SetSeq2(b []string) {
if &b == &m.b {
return
}
m.b = b
m.matchingBlocks = nil
m.opCodes = nil
m.fullBCount = nil
m.chainB()
}
func (m *SequenceMatcher) chainB() {
// Populate line -> index mapping
b2j := map[string][]int{}
for i, s := range m.b {
indices := b2j[s]
indices = append(indices, i)
b2j[s] = indices
}
// Purge junk elements
m.bJunk = map[string]struct{}{}
if m.IsJunk != nil {
junk := m.bJunk
for s, _ := range b2j {
if m.IsJunk(s) {
junk[s] = struct{}{}
}
}
for s, _ := range junk {
delete(b2j, s)
}
}
// Purge remaining popular elements
popular := map[string]struct{}{}
n := len(m.b)
if m.autoJunk && n >= 200 {
ntest := n/100 + 1
for s, indices := range b2j {
if len(indices) > ntest {
popular[s] = struct{}{}
}
}
for s, _ := range popular {
delete(b2j, s)
}
}
m.bPopular = popular
m.b2j = b2j
}
func (m *SequenceMatcher) isBJunk(s string) bool {
_, ok := m.bJunk[s]
return ok
}
// Find longest matching block in a[alo:ahi] and b[blo:bhi].
//
// If IsJunk is not defined:
//
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
// alo <= i <= i+k <= ahi
// blo <= j <= j+k <= bhi
// and for all (i',j',k') meeting those conditions,
// k >= k'
// i <= i'
// and if i == i', j <= j'
//
// In other words, of all maximal matching blocks, return one that
// starts earliest in a, and of all those maximal matching blocks that
// start earliest in a, return the one that starts earliest in b.
//
// If IsJunk is defined, first the longest matching block is
// determined as above, but with the additional restriction that no
// junk element appears in the block. Then that block is extended as
// far as possible by matching (only) junk elements on both sides. So
// the resulting block never matches on junk except as identical junk
// happens to be adjacent to an "interesting" match.
//
// If no blocks match, return (alo, blo, 0).
func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
// CAUTION: stripping common prefix or suffix would be incorrect.
// E.g.,
// ab
// acab
// Longest matching block is "ab", but if common prefix is
// stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
// strip, so ends up claiming that ab is changed to acab by
// inserting "ca" in the middle. That's minimal but unintuitive:
// "it's obvious" that someone inserted "ac" at the front.
// Windiff ends up at the same place as diff, but by pairing up
// the unique 'b's and then matching the first two 'a's.
besti, bestj, bestsize := alo, blo, 0
// find longest junk-free match
// during an iteration of the loop, j2len[j] = length of longest
// junk-free match ending with a[i-1] and b[j]
j2len := map[int]int{}
for i := alo; i != ahi; i++ {
// look at all instances of a[i] in b; note that because
// b2j has no junk keys, the loop is skipped if a[i] is junk
newj2len := map[int]int{}
for _, j := range m.b2j[m.a[i]] {
// a[i] matches b[j]
if j < blo {
continue
}
if j >= bhi {
break
}
k := j2len[j-1] + 1
newj2len[j] = k
if k > bestsize {
besti, bestj, bestsize = i-k+1, j-k+1, k
}
}
j2len = newj2len
}
// Extend the best by non-junk elements on each end. In particular,
// "popular" non-junk elements aren't in b2j, which greatly speeds
// the inner loop above, but also means "the best" match so far
// doesn't contain any junk *or* popular non-junk elements.
for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
m.a[besti-1] == m.b[bestj-1] {
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
}
for besti+bestsize < ahi && bestj+bestsize < bhi &&
!m.isBJunk(m.b[bestj+bestsize]) &&
m.a[besti+bestsize] == m.b[bestj+bestsize] {
bestsize += 1
}
// Now that we have a wholly interesting match (albeit possibly
// empty!), we may as well suck up the matching junk on each
// side of it too. Can't think of a good reason not to, and it
// saves post-processing the (possibly considerable) expense of
// figuring out what to do with it. In the case of an empty
// interesting match, this is clearly the right thing to do,
// because no other kind of match is possible in the regions.
for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
m.a[besti-1] == m.b[bestj-1] {
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
}
for besti+bestsize < ahi && bestj+bestsize < bhi &&
m.isBJunk(m.b[bestj+bestsize]) &&
m.a[besti+bestsize] == m.b[bestj+bestsize] {
bestsize += 1
}
return Match{A: besti, B: bestj, Size: bestsize}
}
// Return list of triples describing matching subsequences.
//
// Each triple is of the form (i, j, n), and means that
// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
// adjacent triples in the list, and the second is not the last triple in the
// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
// adjacent equal blocks.
//
// The last triple is a dummy, (len(a), len(b), 0), and is the only
// triple with n==0.
func (m *SequenceMatcher) GetMatchingBlocks() []Match {
if m.matchingBlocks != nil {
return m.matchingBlocks
}
var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
match := m.findLongestMatch(alo, ahi, blo, bhi)
i, j, k := match.A, match.B, match.Size
if match.Size > 0 {
if alo < i && blo < j {
matched = matchBlocks(alo, i, blo, j, matched)
}
matched = append(matched, match)
if i+k < ahi && j+k < bhi {
matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
}
}
return matched
}
matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
// It's possible that we have adjacent equal blocks in the
// matching_blocks list now.
nonAdjacent := []Match{}
i1, j1, k1 := 0, 0, 0
for _, b := range matched {
// Is this block adjacent to i1, j1, k1?
i2, j2, k2 := b.A, b.B, b.Size
if i1+k1 == i2 && j1+k1 == j2 {
// Yes, so collapse them -- this just increases the length of
// the first block by the length of the second, and the first
// block so lengthened remains the block to compare against.
k1 += k2
} else {
// Not adjacent. Remember the first block (k1==0 means it's
// the dummy we started with), and make the second block the
// new block to compare against.
if k1 > 0 {
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
}
i1, j1, k1 = i2, j2, k2
}
}
if k1 > 0 {
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
}
nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
m.matchingBlocks = nonAdjacent
return m.matchingBlocks
}
// Return list of 5-tuples describing how to turn a into b.
//
// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
// tuple preceding it, and likewise for j1 == the previous j2.
//
// The tags are characters, with these meanings:
//
// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
//
// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
//
// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
//
// 'e' (equal): a[i1:i2] == b[j1:j2]
func (m *SequenceMatcher) GetOpCodes() []OpCode {
if m.opCodes != nil {
return m.opCodes
}
i, j := 0, 0
matching := m.GetMatchingBlocks()
opCodes := make([]OpCode, 0, len(matching))
for _, m := range matching {
// invariant: we've pumped out correct diffs to change
// a[:i] into b[:j], and the next matching block is
// a[ai:ai+size] == b[bj:bj+size]. So we need to pump
// out a diff to change a[i:ai] into b[j:bj], pump out
// the matching block, and move (i,j) beyond the match
ai, bj, size := m.A, m.B, m.Size
tag := byte(0)
if i < ai && j < bj {
tag = 'r'
} else if i < ai {
tag = 'd'
} else if j < bj {
tag = 'i'
}
if tag > 0 {
opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
}
i, j = ai+size, bj+size
// the list of matching blocks is terminated by a
// sentinel with size 0
if size > 0 {
opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
}
}
m.opCodes = opCodes
return m.opCodes
}
// Isolate change clusters by eliminating ranges with no changes.
//
// Return a generator of groups with up to n lines of context.
// Each group is in the same format as returned by GetOpCodes().
func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
if n < 0 {
n = 3
}
codes := m.GetOpCodes()
if len(codes) == 0 {
codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
}
// Fixup leading and trailing groups if they show no changes.
if codes[0].Tag == 'e' {
c := codes[0]
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
}
if codes[len(codes)-1].Tag == 'e' {
c := codes[len(codes)-1]
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
}
nn := n + n
groups := [][]OpCode{}
group := []OpCode{}
for _, c := range codes {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
// End the current group and start a new one whenever
// there is a large range with no changes.
if c.Tag == 'e' && i2-i1 > nn {
group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
j1, min(j2, j1+n)})
groups = append(groups, group)
group = []OpCode{}
i1, j1 = max(i1, i2-n), max(j1, j2-n)
}
group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
}
if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
groups = append(groups, group)
}
return groups
}
// Return a measure of the sequences' similarity (float in [0,1]).
//
// Where T is the total number of elements in both sequences, and
// M is the number of matches, this is 2.0*M / T.
// Note that this is 1 if the sequences are identical, and 0 if
// they have nothing in common.
//
// .Ratio() is expensive to compute if you haven't already computed
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
// want to try .QuickRatio() or .RealQuickRation() first to get an
// upper bound.
func (m *SequenceMatcher) Ratio() float64 {
matches := 0
for _, m := range m.GetMatchingBlocks() {
matches += m.Size
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() relatively quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute.
func (m *SequenceMatcher) QuickRatio() float64 {
// viewing a and b as multisets, set matches to the cardinality
// of their intersection; this counts the number of matches
// without regard to order, so is clearly an upper bound
if m.fullBCount == nil {
m.fullBCount = map[string]int{}
for _, s := range m.b {
m.fullBCount[s] = m.fullBCount[s] + 1
}
}
// avail[x] is the number of times x appears in 'b' less the
// number of times we've seen it in 'a' so far ... kinda
avail := map[string]int{}
matches := 0
for _, s := range m.a {
n, ok := avail[s]
if !ok {
n = m.fullBCount[s]
}
avail[s] = n - 1
if n > 0 {
matches += 1
}
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() very quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute than either .Ratio() or .QuickRatio().
func (m *SequenceMatcher) RealQuickRatio() float64 {
la, lb := len(m.a), len(m.b)
return calculateRatio(min(la, lb), la+lb)
}
// Convert range to the "ed" format
func formatRangeUnified(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 1 {
return fmt.Sprintf("%d", beginning)
}
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
return fmt.Sprintf("%d,%d", beginning, length)
}
// Unified diff parameters
type UnifiedDiff struct {
A []string // First sequence lines
FromFile string // First file name
FromDate string // First file time
B []string // Second sequence lines
ToFile string // Second file name
ToDate string // Second file time
Eol string // Headers end of line, defaults to LF
Context int // Number of context lines
}
// Compare two sequences of lines; generate the delta as a unified diff.
//
// Unified diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by 'n' which
// defaults to three.
//
// By default, the diff control lines (those with ---, +++, or @@) are
// created with a trailing newline. This is helpful so that inputs
// created from file.readlines() result in diffs that are suitable for
// file.writelines() since both the inputs and outputs have trailing
// newlines.
//
// For inputs that do not have trailing newlines, set the lineterm
// argument to "" so that the output will be uniformly newline free.
//
// The unidiff format normally has a header for filenames and modification
// times. Any or all of these may be specified using strings for
// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
// The modification times are normally expressed in the ISO 8601 format.
func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
wf := func(format string, args ...interface{}) error {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
return err
}
ws := func(s string) error {
_, err := buf.WriteString(s)
return err
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
if err != nil {
return err
}
err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
if err != nil {
return err
}
}
}
first, last := g[0], g[len(g)-1]
range1 := formatRangeUnified(first.I1, last.I2)
range2 := formatRangeUnified(first.J1, last.J2)
if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
return err
}
for _, c := range g {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
if c.Tag == 'e' {
for _, line := range diff.A[i1:i2] {
if err := ws(" " + line); err != nil {
return err
}
}
continue
}
if c.Tag == 'r' || c.Tag == 'd' {
for _, line := range diff.A[i1:i2] {
if err := ws("-" + line); err != nil {
return err
}
}
}
if c.Tag == 'r' || c.Tag == 'i' {
for _, line := range diff.B[j1:j2] {
if err := ws("+" + line); err != nil {
return err
}
}
}
}
}
return nil
}
// Like WriteUnifiedDiff but returns the diff a string.
func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteUnifiedDiff(w, diff)
return string(w.Bytes()), err
}
// Convert range to the "ed" format.
func formatRangeContext(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
if length <= 1 {
return fmt.Sprintf("%d", beginning)
}
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
}
type ContextDiff UnifiedDiff
// Compare two sequences of lines; generate the delta as a context diff.
//
// Context diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by diff.Context
// which defaults to three.
//
// By default, the diff control lines (those with *** or ---) are
// created with a trailing newline.
//
// For inputs that do not have trailing newlines, set the diff.Eol
// argument to "" so that the output will be uniformly newline free.
//
// The context diff format normally has a header for filenames and
// modification times. Any or all of these may be specified using
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
// The modification times are normally expressed in the ISO 8601 format.
// If not specified, the strings default to blanks.
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
var diffErr error
wf := func(format string, args ...interface{}) {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
if diffErr == nil && err != nil {
diffErr = err
}
}
ws := func(s string) {
_, err := buf.WriteString(s)
if diffErr == nil && err != nil {
diffErr = err
}
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
prefix := map[byte]string{
'i': "+ ",
'd': "- ",
'r': "! ",
'e': " ",
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
}
}
first, last := g[0], g[len(g)-1]
ws("***************" + diff.Eol)
range1 := formatRangeContext(first.I1, last.I2)
wf("*** %s ****%s", range1, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'd' {
for _, cc := range g {
if cc.Tag == 'i' {
continue
}
for _, line := range diff.A[cc.I1:cc.I2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
range2 := formatRangeContext(first.J1, last.J2)
wf("--- %s ----%s", range2, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'i' {
for _, cc := range g {
if cc.Tag == 'd' {
continue
}
for _, line := range diff.B[cc.J1:cc.J2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
}
return diffErr
}
// Like WriteContextDiff but returns the diff a string.
func GetContextDiffString(diff ContextDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteContextDiff(w, diff)
return string(w.Bytes()), err
}
// Split a string on "\n" while preserving them. The output can be used
// as input for UnifiedDiff and ContextDiff structures.
func SplitLines(s string) []string {
lines := strings.SplitAfter(s, "\n")
lines[len(lines)-1] += "\n"
return lines
}

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,2 +0,0 @@
[flake8]
max-line-length = 120

View File

@ -1,7 +0,0 @@
*.coverprofile
*.orig
node_modules/
vendor
.idea
internal/*/built-example
coverage.txt

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Title__ is a type alias for Type__
type Title__ = Type__
// Title__Flag is a flag with type Type__
type Title__Flag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Title__
Destination *Title__
}
// Apply populates the flag given the flag set and environment
func (f *Title__Flag) Apply(set *flag.FlagSet) error {
return Apply(f, "LongName__", set)
}
// Title__ looks up the value of a local Title__Flag, returns
// an empty value if not found
func (c *Context) Title__(name string) Type__ {
return c.Lookup(name, *new(Title__)).(Type__)
}

View File

@ -1,23 +0,0 @@
package altsrc
import (
"fmt"
"os"
"github.com/rancher/spur/cli"
)
// NewConfigFromFlag creates a new Yaml cli.InputSourceContext from a provided flag name and source context.
// If the flag is not set and the default config does not exist then returns an empty input and no error.
func NewConfigFromFlag(flagFileName string) func(*cli.Context) (cli.InputSourceContext, error) {
return func(ctx *cli.Context) (cli.InputSourceContext, error) {
filePath := ctx.String(flagFileName)
if isc, err := NewYamlSourceFromFile(filePath); ctx.IsSet(flagFileName) || !os.IsNotExist(err) {
if err != nil {
err = fmt.Errorf("unable to load config file '%s': %s", filePath, err)
}
return isc, err
}
return &MapInputSource{}, nil
}
}

View File

@ -1,49 +0,0 @@
package altsrc
import (
"strings"
)
// MapInputSource implements InputSourceContext to return
// data from the map that is loaded.
type MapInputSource struct {
file string
valueMap map[interface{}]interface{}
}
// nestedVal checks if the name has '.' delimiters.
// If so, it tries to traverse the tree by the '.' delimited sections to find
// a nested value for the key.
func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool) {
if sections := strings.Split(name, "."); len(sections) > 1 {
node := tree
for _, section := range sections[:len(sections)-1] {
child, ok := node[section]
if !ok {
return nil, false
}
ctype, ok := child.(map[interface{}]interface{})
if !ok {
return nil, false
}
node = ctype
}
if val, ok := node[sections[len(sections)-1]]; ok {
return val, true
}
}
return nil, false
}
// Get returns the named value
func (fsm *MapInputSource) Get(name string) (interface{}, bool) {
if value, exists := fsm.valueMap[name]; exists {
return value, true
}
return nestedVal(name, fsm.valueMap)
}
// Source returns the path of the source file
func (fsm *MapInputSource) Source() string {
return fsm.file
}

View File

@ -1,56 +0,0 @@
package altsrc
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"github.com/rancher/spur/cli"
"gopkg.in/yaml.v2"
)
type yamlSourceContext struct {
FilePath string
}
// NewYamlSourceFromFile creates a new Yaml cli.InputSourceContext from a filepath.
func NewYamlSourceFromFile(file string) (cli.InputSourceContext, error) {
ysc := &yamlSourceContext{FilePath: file}
var results map[interface{}]interface{}
err := readCommandYaml(ysc.FilePath, &results)
return &MapInputSource{file: file, valueMap: results}, err
}
func readCommandYaml(filePath string, container interface{}) error {
b, err := loadDataFrom(filePath)
if err != nil {
return err
}
return yaml.Unmarshal(b, container)
}
func loadDataFrom(filePath string) ([]byte, error) {
u, err := url.Parse(filePath)
if err != nil {
return nil, err
}
if u.Host != "" { // i have a host, now do i support the scheme?
switch u.Scheme {
case "http", "https":
res, err := http.Get(filePath)
if err != nil {
return nil, err
}
return ioutil.ReadAll(res.Body)
default:
return nil, fmt.Errorf("scheme of %s is unsupported", filePath)
}
}
if _, err := os.Stat(filePath); err != nil {
return nil, os.ErrNotExist
}
return ioutil.ReadFile(filePath)
}

View File

@ -1,540 +0,0 @@
package cli
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"time"
"github.com/rancher/spur/flag"
)
// App is the main structure of a cli application. It is recommended that
// an app be created with the cli.NewApp() function
type App struct {
// The name of the program. Defaults to path.Base(os.Args[0])
Name string
// Full name of command for help, defaults to Name
HelpName string
// Description of the program.
Usage string
// Text to override the USAGE section of help
UsageText string
// Description of the program argument format.
ArgsUsage string
// Version of the program
Version string
// Description of the program
Description string
// List of commands to execute
Commands []*Command
// List of flags to parse
Flags []Flag
// Boolean to enable bash completion commands
EnableBashCompletion bool
// Boolean to hide built-in help command and help flag
HideHelp bool
// Boolean to hide built-in help command but keep help flag.
// Ignored if HideHelp is true.
HideHelpCommand bool
// Boolean to hide built-in version flag and the VERSION section of help
HideVersion bool
// categories contains the categorized commands and is populated on app startup
categories CommandCategories
// An action to execute when the shell completion flag is set
BashComplete BashCompleteFunc
// An action to execute before any subcommands are run, but after the context is ready
// If a non-nil error is returned, no subcommands are run
Before BeforeFunc
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
After AfterFunc
// The action to execute when no subcommands are specified
Action ActionFunc
// Execute this function if the proper command cannot be found
CommandNotFound CommandNotFoundFunc
// Execute this function if an usage error occurs
OnUsageError OnUsageErrorFunc
// Compilation date
Compiled time.Time
// List of all authors who contributed
Authors []*Author
// Copyright of the binary if any
Copyright string
// Writer writer to write output to
Writer io.Writer
// ErrWriter writes error output
ErrWriter io.Writer
// ExitErrHandler processes any error encountered while running an App before
// it is returned to the caller. If no function is provided, HandleExitCoder
// is used as the default behavior.
ExitErrHandler ExitErrHandlerFunc
// Other custom info
Metadata map[string]interface{}
// Carries a function which returns app specific info.
ExtraInfo func() map[string]string
// CustomAppHelpTemplate the text template for app help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
CustomAppHelpTemplate string
// Boolean to enable short-option handling so user can combine several
// single-character bool arguments into one
// i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool
didSetup bool
}
type showHelpFunc = func(context *Context) error
type showHelpError struct {
error
}
// ShowHelpOnError will take a BeforeFunc and show command usage on error
func ShowHelpOnError(someFunc BeforeFunc) BeforeFunc {
return func(context *Context) error {
if err := someFunc(context); err != nil {
return showHelpError{err}
}
return nil
}
}
// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
info, err := os.Stat(os.Args[0])
if err != nil {
return time.Now()
}
return info.ModTime()
}
// NewApp creates a new cli Application with some reasonable defaults for Name,
// Usage, Version and Action.
func NewApp() *App {
return &App{
Name: filepath.Base(os.Args[0]),
HelpName: filepath.Base(os.Args[0]),
Usage: "A new cli application",
UsageText: "",
BashComplete: DefaultAppComplete,
Action: helpCommand.Action,
Compiled: compileTime(),
Writer: os.Stdout,
ErrWriter: os.Stderr,
}
}
// Setup runs initialization code to ensure all data structures are ready for
// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
// will return early if setup has already happened.
func (a *App) Setup() {
if a.didSetup {
return
}
a.didSetup = true
if a.Name == "" {
a.Name = filepath.Base(os.Args[0])
}
if a.HelpName == "" {
a.HelpName = filepath.Base(os.Args[0])
}
if a.Usage == "" {
a.Usage = "A new cli application"
}
if a.Version == "" {
a.HideVersion = true
}
if a.BashComplete == nil {
a.BashComplete = DefaultAppComplete
}
if a.Action == nil {
a.Action = helpCommand.Action
}
if a.Compiled == (time.Time{}) {
a.Compiled = compileTime()
}
if a.Writer == nil {
a.Writer = os.Stdout
}
if a.ErrWriter == nil {
a.ErrWriter = os.Stderr
}
var newCommands []*Command
for _, c := range a.Commands {
if c.HelpName == "" {
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
}
newCommands = append(newCommands, c)
}
a.Commands = newCommands
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
if !a.HideHelpCommand {
a.appendCommand(helpCommand)
}
if HelpFlag != nil {
a.appendFlag(HelpFlag)
}
}
if !a.HideVersion {
a.appendFlag(VersionFlag)
}
a.categories = newCommandCategories()
for _, command := range a.Commands {
a.categories.AddCommand(command.Category, command)
}
sort.Sort(a.categories.(*commandCategories))
if a.Metadata == nil {
a.Metadata = make(map[string]interface{})
}
}
func (a *App) newFlagSet() (*flag.FlagSet, error) {
return flagSet(a.Name, a.Flags)
}
func (a *App) useShortOptionHandling() bool {
return a.UseShortOptionHandling
}
// Run is the entry point to the cli app. Parses the arguments slice and routes
// to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
return a.RunContext(context.Background(), arguments)
}
// RunContext is like Run except it takes a Context that will be
// passed to its commands and sub-commands. Through this, you can
// propagate timeouts and cancellation requests
func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
if len(arguments) == 0 {
return fmt.Errorf("arguments not provided")
}
a.Setup()
// handle the completion flag separately from the flagset since
// completion could be attempted after a flag, but before its value was put
// on the command line. this causes the flagset to interpret the completion
// flag name as the value of the flag before it which is undesirable
// note that we can only do this because the shell autocomplete function
// always appends the completion flag at the end of the command
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
set, err := a.newFlagSet()
if err != nil {
return err
}
err = parseIter(set, a, arguments[1:], shellComplete)
nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, &Context{Context: ctx})
if nerr != nil {
fmt.Fprintln(a.Writer, nerr)
ShowAppHelp(context)
return nerr
}
context.shellComplete = shellComplete
if checkCompletions(context) {
return nil
}
if err != nil {
return a.helpOnError(ShowAppHelp, context, showHelpError{err})
}
if !a.HideHelp && checkHelp(context) {
ShowAppHelp(context)
return nil
}
if !a.HideVersion && checkVersion(context) {
ShowVersion(context)
return nil
}
cerr := checkRequiredFlags(a.Flags, context)
if cerr != nil {
ShowAppHelp(context)
return cerr
}
if a.After != nil {
defer func() {
if afterErr := a.After(context); afterErr != nil {
a.handleExitCoder(context, err)
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
if a.Before != nil {
if err := a.Before(context); err != nil {
return a.helpOnError(ShowAppHelp, context, err)
}
}
args := context.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(context)
}
}
if a.Action == nil {
a.Action = helpCommand.Action
}
// Run default Action
err = a.Action(context)
a.handleExitCoder(context, err)
return err
}
func (a *App) helpOnError(showHelp showHelpFunc, context *Context, err error) error {
if err == nil {
return nil
}
if e, ok := err.(showHelpError); ok {
err = e.error
if a.OnUsageError != nil {
err = a.OnUsageError(context, err, false)
} else {
fmt.Fprintf(a.Writer, "%s:\n %s\n\n", "Incorrect Usage", err.Error())
showHelp(context)
}
}
a.handleExitCoder(context, err)
return err
}
// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
//
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
// to cli.App.Run. This will cause the application to exit with the given error
// code in the cli.ExitCoder
func (a *App) RunAndExitOnError() {
if err := a.Run(os.Args); err != nil {
fmt.Fprintf(a.ErrWriter, "\nFatal: %s\n", err)
OsExiter(1)
}
}
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
// generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// Setup also handles HideHelp and HideHelpCommand
a.Setup()
var newCmds []*Command
for _, c := range a.Commands {
if c.HelpName == "" {
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
}
newCmds = append(newCmds, c)
}
a.Commands = newCmds
set, err := a.newFlagSet()
if err != nil {
return err
}
err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)
nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, ctx)
if nerr != nil {
fmt.Fprintln(a.Writer, nerr)
fmt.Fprintln(a.Writer)
if len(a.Commands) > 0 {
ShowSubcommandHelp(context)
} else {
ShowCommandHelp(ctx, context.Args().First())
}
return nerr
}
if checkCompletions(context) {
return nil
}
if err != nil {
return a.helpOnError(ShowSubcommandHelp, context, showHelpError{err})
}
if len(a.Commands) > 0 {
if checkSubcommandHelp(context) {
return nil
}
} else {
if checkCommandHelp(ctx, context.Args().First()) {
return nil
}
}
cerr := checkRequiredFlags(a.Flags, context)
if cerr != nil {
ShowSubcommandHelp(context)
return cerr
}
if a.After != nil {
defer func() {
if afterErr := a.After(context); afterErr != nil {
a.handleExitCoder(context, err)
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
if a.Before != nil {
if err := a.Before(context); err != nil {
return a.helpOnError(ShowSubcommandHelp, context, err)
}
}
args := context.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(context)
}
}
// Run default Action
err = a.Action(context)
a.handleExitCoder(context, err)
return err
}
// Command returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
for _, c := range a.Commands {
if c.HasName(name) {
return c
}
}
return nil
}
// VisibleCategories returns a slice of categories and commands that are
// Hidden=false
func (a *App) VisibleCategories() []CommandCategory {
ret := []CommandCategory{}
for _, category := range a.categories.Categories() {
if visible := func() CommandCategory {
if len(category.VisibleCommands()) > 0 {
return category
}
return nil
}(); visible != nil {
ret = append(ret, visible)
}
}
return ret
}
// VisibleCommands returns a slice of the Commands with Hidden=false
func (a *App) VisibleCommands() []*Command {
var ret []*Command
for _, command := range a.Commands {
if !command.Hidden {
ret = append(ret, command)
}
}
return ret
}
// VisibleFlags returns a slice of the Flags with Hidden=false
func (a *App) VisibleFlags() []Flag {
return visibleFlags(a.Flags)
}
func (a *App) appendFlag(fl Flag) {
if !hasFlag(a.Flags, fl) {
a.Flags = append(a.Flags, fl)
}
}
func (a *App) appendCommand(c *Command) {
if !hasCommand(a.Commands, c) {
a.Commands = append(a.Commands, c)
}
}
func (a *App) handleExitCoder(context *Context, err error) {
if a.ExitErrHandler != nil {
a.ExitErrHandler(context, err)
} else {
HandleExitCoder(err)
}
}
// Author represents someone who has contributed to a cli project.
type Author struct {
Name string // The Authors name
Email string // The Authors email
}
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
func (a *Author) String() string {
e := ""
if a.Email != "" {
e = " <" + a.Email + ">"
}
return fmt.Sprintf("%v%v", a.Name, e)
}
// HandleAction attempts to figure out which Action signature was used. If
// it's an ActionFunc or a func with the legacy signature for Action, the func
// is run! Panics on invalid function signature.
func HandleAction(action interface{}, context *Context) (err error) {
switch a := action.(type) {
case ActionFunc:
return a(context)
case func(*Context) error:
return a(context)
case func(*Context): // deprecated function signature
a(context)
return nil
}
panic(fmt.Sprintf("invalid Action type '%T', should be 'func(*Context) error'", action))
}

View File

@ -1,54 +0,0 @@
package cli
type Args interface {
// Get returns the nth argument, or else a blank string
Get(n int) string
// First returns the first argument, or else a blank string
First() string
// Tail returns the rest of the arguments (not the first one)
// or else an empty string slice
Tail() []string
// Len returns the length of the wrapped slice
Len() int
// Present checks if there are any arguments present
Present() bool
// Slice returns a copy of the internal slice
Slice() []string
}
type args []string
func (a *args) Get(n int) string {
if len(*a) > n {
return (*a)[n]
}
return ""
}
func (a *args) First() string {
return a.Get(0)
}
func (a *args) Tail() []string {
if a.Len() >= 2 {
tail := []string((*a)[1:])
ret := make([]string, len(tail))
copy(ret, tail)
return ret
}
return []string{}
}
func (a *args) Len() int {
return len(*a)
}
func (a *args) Present() bool {
return a.Len() != 0
}
func (a *args) Slice() []string {
ret := make([]string, len(*a))
copy(ret, *a)
return ret
}

View File

@ -1,79 +0,0 @@
package cli
// CommandCategories interface allows for category manipulation
type CommandCategories interface {
// AddCommand adds a command to a category, creating a new category if necessary.
AddCommand(category string, command *Command)
// categories returns a copy of the category slice
Categories() []CommandCategory
}
type commandCategories []*commandCategory
func newCommandCategories() CommandCategories {
ret := commandCategories([]*commandCategory{})
return &ret
}
func (c *commandCategories) Less(i, j int) bool {
return lexicographicLess((*c)[i].Name(), (*c)[j].Name())
}
func (c *commandCategories) Len() int {
return len(*c)
}
func (c *commandCategories) Swap(i, j int) {
(*c)[i], (*c)[j] = (*c)[j], (*c)[i]
}
func (c *commandCategories) AddCommand(category string, command *Command) {
for _, commandCategory := range []*commandCategory(*c) {
if commandCategory.name == category {
commandCategory.commands = append(commandCategory.commands, command)
return
}
}
newVal := append(*c,
&commandCategory{name: category, commands: []*Command{command}})
*c = newVal
}
func (c *commandCategories) Categories() []CommandCategory {
ret := make([]CommandCategory, len(*c))
for i, cat := range *c {
ret[i] = cat
}
return ret
}
// CommandCategory is a category containing commands.
type CommandCategory interface {
// Name returns the category name string
Name() string
// VisibleCommands returns a slice of the Commands with Hidden=false
VisibleCommands() []*Command
}
type commandCategory struct {
name string
commands []*Command
}
func (c *commandCategory) Name() string {
return c.name
}
func (c *commandCategory) VisibleCommands() []*Command {
if c.commands == nil {
c.commands = []*Command{}
}
var ret []*Command
for _, command := range c.commands {
if !command.Hidden {
ret = append(ret, command)
}
}
return ret
}

View File

@ -1,301 +0,0 @@
package cli
import (
"fmt"
"sort"
"strings"
"github.com/rancher/spur/flag"
)
// Command is a subcommand for a cli.App.
type Command struct {
// The name of the command
Name string
// A list of aliases for the command
Aliases []string
// A short description of the usage of this command
Usage string
// Custom text to show on USAGE section of help
UsageText string
// A longer explanation of how the command works
Description string
// A short description of the arguments of this command
ArgsUsage string
// The category the command is part of
Category string
// The function to call when checking for bash command completions
BashComplete BashCompleteFunc
// An action to execute before any sub-subcommands are run, but after the context is ready
// If a non-nil error is returned, no sub-subcommands are run
Before BeforeFunc
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
After AfterFunc
// The function to call when this command is invoked
Action ActionFunc
// Execute this function if a usage error occurs.
OnUsageError OnUsageErrorFunc
// List of child commands
Subcommands []*Command
// List of flags to parse
Flags []Flag
// Treat all flags as normal arguments if true
SkipFlagParsing bool
// Boolean to hide built-in help command and help flag
HideHelp bool
// Boolean to hide built-in help command but keep help flag
// Ignored if HideHelp is true.
HideHelpCommand bool
// Boolean to hide this command from help or completion
Hidden bool
// Boolean to enable short-option handling so user can combine several
// single-character bool arguments into one
// i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool
// Full name of command for help, defaults to full command name, including parent commands.
HelpName string
commandNamePath []string
// CustomHelpTemplate the text template for the command help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
CustomHelpTemplate string
}
type Commands []*Command
type CommandsByName []*Command
func (c CommandsByName) Len() int {
return len(c)
}
func (c CommandsByName) Less(i, j int) bool {
return lexicographicLess(c[i].Name, c[j].Name)
}
func (c CommandsByName) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
// FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path
func (c *Command) FullName() string {
if c.commandNamePath == nil {
return c.Name
}
return strings.Join(c.commandNamePath, " ")
}
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c *Command) Run(ctx *Context) (err error) {
if len(c.Subcommands) > 0 {
return c.startApp(ctx)
}
if !c.HideHelp && HelpFlag != nil {
// append help to flags
c.appendFlag(HelpFlag)
}
if ctx.App.UseShortOptionHandling {
c.UseShortOptionHandling = true
}
set, err := c.parseFlags(ctx.Args(), ctx.shellComplete)
context := NewContext(ctx.App, set, ctx)
context.Command = c
if checkCommandCompletions(context, c.Name) {
return nil
}
if err != nil {
if c.OnUsageError != nil {
err = c.OnUsageError(context, err, false)
context.App.handleExitCoder(context, err)
return err
}
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
fmt.Fprintln(context.App.Writer)
ShowCommandHelp(context, c.Name)
return err
}
if checkCommandHelp(context, c.Name) {
return nil
}
cerr := checkRequiredFlags(c.Flags, context)
if cerr != nil {
ShowCommandHelp(context, c.Name)
return cerr
}
if c.After != nil {
defer func() {
afterErr := c.After(context)
if afterErr != nil {
context.App.handleExitCoder(context, err)
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
if c.Before != nil {
err = c.Before(context)
if err != nil {
context.App.handleExitCoder(context, err)
return err
}
}
if c.Action == nil {
c.Action = helpSubcommand.Action
}
context.Command = c
err = c.Action(context)
if err != nil {
context.App.handleExitCoder(context, err)
}
return err
}
func (c *Command) newFlagSet() (*flag.FlagSet, error) {
return flagSet(c.Name, c.Flags)
}
func (c *Command) useShortOptionHandling() bool {
return c.UseShortOptionHandling
}
func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) {
set, err := c.newFlagSet()
if err != nil {
return nil, err
}
if c.SkipFlagParsing {
return set, set.Parse(append([]string{"--"}, args.Tail()...))
}
err = parseIter(set, c, args.Tail(), shellComplete)
if err != nil {
return nil, err
}
err = normalizeFlags(c.Flags, set)
if err != nil {
return nil, err
}
return set, nil
}
// Names returns the names including short names and aliases.
func (c *Command) Names() []string {
return append([]string{c.Name}, c.Aliases...)
}
// HasName returns true if Command.Name matches given name
func (c *Command) HasName(name string) bool {
for _, n := range c.Names() {
if n == name {
return true
}
}
return false
}
func (c *Command) startApp(ctx *Context) error {
app := &App{
Metadata: ctx.App.Metadata,
Name: fmt.Sprintf("%s %s", ctx.App.Name, c.Name),
}
if c.HelpName == "" {
app.HelpName = c.HelpName
} else {
app.HelpName = app.Name
}
app.Usage = c.Usage
app.Description = c.Description
app.ArgsUsage = c.ArgsUsage
// set CommandNotFound
app.CommandNotFound = ctx.App.CommandNotFound
app.CustomAppHelpTemplate = c.CustomHelpTemplate
// set the flags and commands
app.Commands = c.Subcommands
app.Flags = c.Flags
app.HideHelp = c.HideHelp
app.HideHelpCommand = c.HideHelpCommand
app.Version = ctx.App.Version
app.HideVersion = ctx.App.HideVersion
app.Compiled = ctx.App.Compiled
app.Writer = ctx.App.Writer
app.ErrWriter = ctx.App.ErrWriter
app.ExitErrHandler = ctx.App.ExitErrHandler
app.UseShortOptionHandling = ctx.App.UseShortOptionHandling
app.categories = newCommandCategories()
for _, command := range c.Subcommands {
app.categories.AddCommand(command.Category, command)
}
sort.Sort(app.categories.(*commandCategories))
// bash completion
app.EnableBashCompletion = ctx.App.EnableBashCompletion
if c.BashComplete != nil {
app.BashComplete = c.BashComplete
}
// set the actions
app.Before = c.Before
app.After = c.After
if c.Action != nil {
app.Action = c.Action
} else {
app.Action = helpSubcommand.Action
}
app.OnUsageError = c.OnUsageError
for index, cc := range app.Commands {
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
}
return app.RunAsSubcommand(ctx)
}
// VisibleFlags returns a slice of the Flags with Hidden=false
func (c *Command) VisibleFlags() []Flag {
return visibleFlags(c.Flags)
}
func (c *Command) appendFlag(fl Flag) {
if !hasFlag(c.Flags, fl) {
c.Flags = append(c.Flags, fl)
}
}
func hasCommand(commands []*Command, command *Command) bool {
for _, existing := range commands {
if command == existing {
return true
}
}
return false
}

View File

@ -1,275 +0,0 @@
package cli
import (
"context"
"fmt"
"reflect"
"strings"
"github.com/rancher/spur/flag"
)
// Context is a type that is passed through to
// each Handler action in a cli application. Context
// can be used to retrieve context-specific args and
// parsed command-line options.
type Context struct {
context.Context
App *App
Command *Command
shellComplete bool
flagSet *flag.FlagSet
parentContext *Context
}
// NewContext creates a new context. For use in when invoking an App or Command action.
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
c := &Context{App: app, flagSet: set, parentContext: parentCtx}
if parentCtx != nil {
c.Context = parentCtx.Context
c.shellComplete = parentCtx.shellComplete
if parentCtx.flagSet == nil {
parentCtx.flagSet = &flag.FlagSet{}
}
}
c.Command = &Command{}
if c.Context == nil {
c.Context = context.Background()
}
return c
}
// NumFlags returns the number of flags set
func (c *Context) NumFlags() int {
return c.flagSet.NFlag()
}
// Set sets a context flag to a value.
func (c *Context) Set(name string, value interface{}) error {
return c.flagSet.Set(name, value)
}
// IsSet determines if the flag was actually set
func (c *Context) IsSet(name string) bool {
if fs := lookupFlagSet(name, c); fs != nil {
isSet := false
fs.Visit(func(f *flag.Flag) {
if f.Name == name {
isSet = true
}
})
if isSet {
return true
}
}
return false
}
// LocalFlagNames returns a slice of flag names used in this context.
func (c *Context) LocalFlagNames() []string {
var names []string
c.flagSet.Visit(makeFlagNameVisitor(&names))
return names
}
// FlagNames returns a slice of flag names used by the this context and all of
// its parent contexts.
func (c *Context) FlagNames() []string {
var names []string
for _, ctx := range c.Lineage() {
ctx.flagSet.Visit(makeFlagNameVisitor(&names))
}
return names
}
// Lineage returns *this* context and all of its ancestor contexts in order from
// child to parent
func (c *Context) Lineage() []*Context {
var lineage []*Context
for cur := c; cur != nil; cur = cur.parentContext {
lineage = append(lineage, cur)
}
return lineage
}
// Value returns the value of the flag corresponding to `name`
func (c *Context) Value(name string) interface{} {
return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
}
// Args returns the command line arguments associated with the context.
func (c *Context) Args() Args {
ret := args(c.flagSet.Args())
return &ret
}
// NArg returns the number of the command line arguments.
func (c *Context) NArg() int {
return c.Args().Len()
}
// Lookup will return the value for a flag, or the default value if
// the flag value does not exist or is not of the same type
func (c *Context) Lookup(name string, defaultVal interface{}) interface{} {
var result interface{}
if fs := lookupFlagSet(name, c); fs != nil {
if f := fs.Lookup(name); f != nil {
result = f.Value
}
}
if result == nil {
return defaultVal
}
// if we don't have a default value assume they want they flag.Value
if defaultVal != nil {
result = result.(flag.Getter).Get()
}
if defaultVal == nil || reflect.TypeOf(result) == reflect.TypeOf(defaultVal) {
return result
}
return defaultVal
}
// GetFlags will return all of the flags found for this context
func (c *Context) GetFlags() []Flag {
flags := []Flag{}
for _, ctx := range c.Lineage() {
if ctx.Command != nil {
flags = append(flags, ctx.Command.Flags...)
}
}
if c.App != nil {
flags = append(flags, c.App.Flags...)
}
return flags
}
func lookupFlag(name string, ctx *Context) Flag {
for _, f := range ctx.GetFlags() {
for _, n := range FlagNames(f) {
if n == name {
return f
}
}
}
return nil
}
func lookupFlagSet(name string, ctx *Context) *flag.FlagSet {
for _, c := range ctx.Lineage() {
if f := c.flagSet.Lookup(name); f != nil {
return c.flagSet
}
}
return nil
}
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
set.Set(name, ff.Value.String())
}
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
visited := make(map[string]bool)
set.Visit(func(f *flag.Flag) {
visited[f.Name] = true
})
for _, f := range flags {
parts := FlagNames(f)
if len(parts) == 1 {
continue
}
var ff *flag.Flag
for _, name := range parts {
name = strings.Trim(name, " ")
if visited[name] {
ff = set.Lookup(name)
}
}
if ff == nil {
continue
}
for _, name := range parts {
name = strings.Trim(name, " ")
if !visited[name] {
copyFlag(name, ff, set)
}
}
}
return nil
}
func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
return func(f *flag.Flag) {
nameParts := strings.Split(f.Name, ",")
name := strings.TrimSpace(nameParts[0])
for _, part := range nameParts {
part = strings.TrimSpace(part)
if len(part) > len(name) {
name = part
}
}
if name != "" {
*names = append(*names, name)
}
}
}
type requiredFlagsErr interface {
error
getMissingFlags() []string
}
type errRequiredFlags struct {
missingFlags []string
}
func (e *errRequiredFlags) Error() string {
numberOfMissingFlags := len(e.missingFlags)
if numberOfMissingFlags == 1 {
return fmt.Sprintf("Required flag %q not set", e.missingFlags[0])
}
joinedMissingFlags := strings.Join(e.missingFlags, ", ")
return fmt.Sprintf("Required flags %q not set", joinedMissingFlags)
}
func (e *errRequiredFlags) getMissingFlags() []string {
return e.missingFlags
}
func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr {
var missingFlags []string
for _, f := range flags {
if required, ok := getFlagRequired(f); ok && required {
var flagPresent bool
var flagName string
for _, key := range FlagNames(f) {
if len(key) > 1 {
flagName = key
}
if context.IsSet(strings.TrimSpace(key)) {
flagPresent = true
}
}
if !flagPresent && flagName != "" {
missingFlags = append(missingFlags, flagName)
}
}
}
if len(missingFlags) != 0 {
return &errRequiredFlags{missingFlags: missingFlags}
}
return nil
}

View File

@ -1,160 +0,0 @@
// +build docgen
package cli
import (
"bytes"
"fmt"
"io"
"sort"
"strings"
"text/template"
"github.com/cpuguy83/go-md2man/v2/md2man"
"github.com/rancher/spur/flag"
"github.com/rancher/spur/generic"
)
// ToMarkdown creates a markdown string for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToMarkdown() (string, error) {
var w bytes.Buffer
if err := a.writeDocTemplate(&w); err != nil {
return "", err
}
return w.String(), nil
}
// ToMan creates a man page string for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToMan() (string, error) {
var w bytes.Buffer
if err := a.writeDocTemplate(&w); err != nil {
return "", err
}
man := md2man.Render(w.Bytes())
return string(man), nil
}
type cliTemplate struct {
App *App
Commands []string
GlobalArgs []string
SynopsisArgs []string
}
func (a *App) writeDocTemplate(w io.Writer) error {
const name = "cli"
t, err := template.New(name).Parse(MarkdownDocTemplate)
if err != nil {
return err
}
return t.ExecuteTemplate(w, name, &cliTemplate{
App: a,
Commands: prepareCommands(a.Commands, 0),
GlobalArgs: prepareArgsWithValues(a.VisibleFlags()),
SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()),
})
}
func prepareCommands(commands []*Command, level int) []string {
var coms []string
for _, command := range commands {
if command.Hidden {
continue
}
usage := ""
if command.Usage != "" {
usage = command.Usage
}
prepared := fmt.Sprintf("%s %s\n\n%s\n",
strings.Repeat("#", level+2),
strings.Join(command.Names(), ", "),
usage,
)
flags := prepareArgsWithValues(command.Flags)
if len(flags) > 0 {
prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n"))
}
coms = append(coms, prepared)
// recursevly iterate subcommands
if len(command.Subcommands) > 0 {
coms = append(
coms,
prepareCommands(command.Subcommands, level+1)...,
)
}
}
return coms
}
func prepareArgsWithValues(flags []Flag) []string {
return prepareFlags(flags, ", ", "**", "**", `""`, true)
}
func prepareArgsSynopsis(flags []Flag) []string {
return prepareFlags(flags, "|", "[", "]", "[value]", false)
}
func prepareFlags(
flags []Flag,
sep, opener, closer, value string,
addDetails bool,
) []string {
args := []string{}
for _, f := range flags {
modifiedArg := opener
for _, s := range FlagNames(f) {
trimmed := strings.TrimSpace(s)
if len(modifiedArg) > len(opener) {
modifiedArg += sep
}
if len(trimmed) > 1 {
modifiedArg += fmt.Sprintf("--%s", trimmed)
} else {
modifiedArg += fmt.Sprintf("-%s", trimmed)
}
}
modifiedArg += closer
if v, ok := getFlagValue(f); ok && !flag.IsBoolValue(v) {
modifiedArg += fmt.Sprintf("=%s", value)
}
if addDetails {
modifiedArg += flagDetails(f)
}
args = append(args, modifiedArg+"\n")
}
sort.Strings(args)
return args
}
// flagDetails returns a string containing the flags metadata
func flagDetails(f Flag) string {
description, _ := getFlagUsage(f)
value, _ := getFlagValue(f)
valStr := ""
if !flag.IsBoolValue(value) {
if v, ok := value.(Generic); ok {
valStr = v.String()
} else if s, ok := generic.ToString(value); ok {
valStr = s
} else {
valStr = fmt.Sprintf("%v", value)
}
}
if valStr != "" {
description += " (default: " + valStr + ")"
}
return ": " + description
}

View File

@ -1,141 +0,0 @@
package cli
import (
"fmt"
"io"
"os"
"strings"
)
// OsExiter is the function used when the app exits. If not set defaults to os.Exit.
var OsExiter = os.Exit
// ErrWriter is used to write errors to the user. This can be anything
// implementing the io.Writer interface and defaults to os.Stderr.
var ErrWriter io.Writer = os.Stderr
// MultiError is an error that wraps multiple errors.
type MultiError interface {
error
Errors() []error
}
// newMultiError creates a new MultiError. Pass in one or more errors.
func newMultiError(err ...error) MultiError {
ret := multiError(err)
return &ret
}
type multiError []error
// Error implements the error interface.
func (m *multiError) Error() string {
errs := make([]string, len(*m))
for i, err := range *m {
errs[i] = err.Error()
}
return strings.Join(errs, "\n")
}
// Errors returns a copy of the errors slice
func (m *multiError) Errors() []error {
errs := make([]error, len(*m))
for _, err := range *m {
errs = append(errs, err)
}
return errs
}
// ErrorFormatter is the interface that will suitably format the error output
type ErrorFormatter interface {
Format(s fmt.State, verb rune)
}
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
// code
type ExitCoder interface {
error
ExitCode() int
}
type exitError struct {
exitCode int
message interface{}
}
// NewExitError calls Exit to create a new ExitCoder.
//
// Deprecated: This function is a duplicate of Exit and will eventually be removed.
func NewExitError(message interface{}, exitCode int) ExitCoder {
return Exit(message, exitCode)
}
// Exit wraps a message and exit code into an error, which by default is
// handled with a call to os.Exit during default error handling.
//
// This is the simplest way to trigger a non-zero exit code for an App without
// having to call os.Exit manually. During testing, this behavior can be avoided
// by overiding the ExitErrHandler function on an App or the package-global
// OsExiter function.
func Exit(message interface{}, exitCode int) ExitCoder {
return &exitError{
message: message,
exitCode: exitCode,
}
}
func (ee *exitError) Error() string {
return fmt.Sprintf("%v", ee.message)
}
func (ee *exitError) ExitCode() int {
return ee.exitCode
}
// HandleExitCoder handles errors implementing ExitCoder by printing their
// message and calling OsExiter with the given exit code.
//
// If the given error instead implements MultiError, each error will be checked
// for the ExitCoder interface, and OsExiter will be called with the last exit
// code found, or exit code 1 if no ExitCoder is found.
//
// This function is the default error-handling behavior for an App.
func HandleExitCoder(err error) {
if err == nil {
return
}
if exitErr, ok := err.(ExitCoder); ok {
if err.Error() != "" {
if _, ok := exitErr.(ErrorFormatter); ok {
fmt.Fprintf(ErrWriter, "%+v\n", err)
} else {
fmt.Fprintln(ErrWriter, err)
}
}
OsExiter(exitErr.ExitCode())
return
}
if multiErr, ok := err.(MultiError); ok {
code := handleMultiError(multiErr)
OsExiter(code)
return
}
}
func handleMultiError(multiErr MultiError) int {
code := 1
for _, merr := range multiErr.Errors() {
if multiErr2, ok := merr.(MultiError); ok {
code = handleMultiError(multiErr2)
} else if merr != nil {
fmt.Fprintln(ErrWriter, merr)
if exitErr, ok := merr.(ExitCoder); ok {
code = exitErr.ExitCode()
}
}
}
return code
}

View File

@ -1,178 +0,0 @@
package cli
import (
"bytes"
"fmt"
"io"
"strings"
"text/template"
"github.com/rancher/spur/flag"
)
// ToFishCompletion creates a fish completion string for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToFishCompletion() (string, error) {
var w bytes.Buffer
if err := a.writeFishCompletionTemplate(&w); err != nil {
return "", err
}
return w.String(), nil
}
type fishCompletionTemplate struct {
App *App
Completions []string
AllCommands []string
}
func (a *App) writeFishCompletionTemplate(w io.Writer) error {
const name = "cli"
t, err := template.New(name).Parse(FishCompletionTemplate)
if err != nil {
return err
}
allCommands := []string{}
// Add global flags
completions := a.prepareFishFlags(a.VisibleFlags(), allCommands)
// Add help flag
if !a.HideHelp {
completions = append(
completions,
a.prepareFishFlags([]Flag{HelpFlag}, allCommands)...,
)
}
// Add version flag
if !a.HideVersion {
completions = append(
completions,
a.prepareFishFlags([]Flag{VersionFlag}, allCommands)...,
)
}
// Add commands and their flags
completions = append(
completions,
a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})...,
)
return t.ExecuteTemplate(w, name, &fishCompletionTemplate{
App: a,
Completions: completions,
AllCommands: allCommands,
})
}
func (a *App) prepareFishCommands(commands []*Command, allCommands *[]string, previousCommands []string) []string {
completions := []string{}
for _, command := range commands {
if command.Hidden {
continue
}
var completion strings.Builder
completion.WriteString(fmt.Sprintf(
"complete -r -c %s -n '%s' -a '%s'",
a.Name,
a.fishSubcommandHelper(previousCommands),
strings.Join(command.Names(), " "),
))
if command.Usage != "" {
completion.WriteString(fmt.Sprintf(" -d '%s'",
escapeSingleQuotes(command.Usage)))
}
if !command.HideHelp {
completions = append(
completions,
a.prepareFishFlags([]Flag{HelpFlag}, command.Names())...,
)
}
*allCommands = append(*allCommands, command.Names()...)
completions = append(completions, completion.String())
completions = append(
completions,
a.prepareFishFlags(command.Flags, command.Names())...,
)
// recursevly iterate subcommands
if len(command.Subcommands) > 0 {
completions = append(
completions,
a.prepareFishCommands(
command.Subcommands, allCommands, command.Names(),
)...,
)
}
}
return completions
}
func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string {
completions := []string{}
for _, f := range flags {
completion := &strings.Builder{}
completion.WriteString(fmt.Sprintf(
"complete -c %s -n '%s'",
a.Name,
a.fishSubcommandHelper(previousCommands),
))
fishAddFileFlag(f, completion)
for idx, opt := range FlagNames(f) {
if idx == 0 {
completion.WriteString(fmt.Sprintf(
" -l %s", strings.TrimSpace(opt),
))
} else {
completion.WriteString(fmt.Sprintf(
" -s %s", strings.TrimSpace(opt),
))
}
}
if v, ok := getFlagValue(f); ok && !flag.IsBoolValue(v) {
completion.WriteString(" -r")
}
if usage, ok := getFlagUsage(f); ok && usage != "" {
completion.WriteString(fmt.Sprintf(" -d '%s'",
escapeSingleQuotes(usage)))
}
completions = append(completions, completion.String())
}
return completions
}
func fishAddFileFlag(flag Flag, completion *strings.Builder) {
if takesFile, ok := getFlagTakesFile(flag); ok && takesFile {
return
}
completion.WriteString(" -f")
}
func (a *App) fishSubcommandHelper(allCommands []string) string {
fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name)
if len(allCommands) > 0 {
fishHelper = fmt.Sprintf(
"__fish_seen_subcommand_from %s",
strings.Join(allCommands, " "),
)
}
return fishHelper
}
func escapeSingleQuotes(input string) string {
return strings.Replace(input, `'`, `\'`, -1)
}

View File

@ -1,318 +0,0 @@
package cli
import (
"fmt"
"io/ioutil"
"reflect"
"regexp"
"runtime"
"strings"
"github.com/rancher/spur/flag"
"github.com/rancher/spur/generic"
)
// Flag is a common interface related to parsing flags in cli.
type Flag interface {
// Apply Flag settings to the given flag set
Apply(*flag.FlagSet) error
}
// BashCompletionFlag enables bash-completion for all commands and subcommands
var BashCompletionFlag Flag = &BoolFlag{
Name: "generate-bash-completion",
Hidden: true,
}
// VersionFlag prints the version for the application
var VersionFlag Flag = &BoolFlag{
Name: "version",
Aliases: []string{"v"},
Usage: "print the version",
}
// HelpFlag prints the help for all commands and subcommands.
// Set to nil to disable the flag. The subcommand
// will still be added unless HideHelp or HideHelpCommand is set to true.
var HelpFlag Flag = &BoolFlag{
Name: "help",
Aliases: []string{"h"},
Usage: "show help",
}
// FlagStringer converts a flag definition to a string. This is used by help
// to display a flag.
var FlagStringer FlagStringFunc = stringifyFlag
// FlagNamePrefixer converts a full flag name and its placeholder into the help
// message flag prefix. This is used by the default FlagStringer.
var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames
// FlagEnvHinter annotates flag help message with the environment variable
// details. This is used by the default FlagStringer.
var FlagEnvHinter FlagEnvHintFunc = withEnvHint
// FlagFileHinter annotates flag help message with the environment variable
// details. This is used by the default FlagStringer.
var FlagFileHinter FlagFileHintFunc = withFileHint
// FlagsByName is a slice of Flag.
type FlagsByName []Flag
const defaultPlaceholder = "value"
func (f FlagsByName) Len() int {
return len(f)
}
func (f FlagsByName) Less(i, j int) bool {
namesI := FlagNames(f[i])
namesJ := FlagNames(f[j])
if len(namesJ) == 0 {
return false
} else if len(namesI) == 0 {
return true
}
return lexicographicLess(namesI[0], namesJ[0])
}
func (f FlagsByName) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
set := flag.NewFlagSet(name, flag.ContinueOnError)
for _, f := range flags {
if err := f.Apply(set); err != nil {
return nil, err
}
}
set.SetOutput(ioutil.Discard)
return set, nil
}
func visibleFlags(fl []Flag) []Flag {
var visible []Flag
for _, f := range fl {
if hidden, ok := getFlagHidden(f); !hidden || !ok {
visible = append(visible, f)
}
}
return visible
}
func prefixFor(name string) (prefix string) {
if len(name) == 1 {
prefix = "-"
} else {
prefix = "--"
}
return
}
// Returns the placeholder, if any, and the unquoted usage string.
func unquoteUsage(usage string) (string, string) {
for i := 0; i < len(usage); i++ {
if usage[i] == '`' {
for j := i + 1; j < len(usage); j++ {
if usage[j] == '`' {
name := usage[i+1 : j]
usage = usage[:i] + name + usage[j+1:]
return name, usage
}
}
break
}
}
return "", usage
}
func prefixedNames(names []string, placeholder string) string {
var prefixed string
for i, name := range names {
if name == "" {
continue
}
prefixed += prefixFor(name) + name
if placeholder != "" {
prefixed += " " + placeholder
}
if i < len(names)-1 {
prefixed += ", "
}
}
return prefixed
}
func withEnvHint(envVars []string, str string) string {
envText := ""
if envVars != nil && len(envVars) > 0 {
prefix := "$"
suffix := ""
sep := ", $"
if runtime.GOOS == "windows" {
prefix = "%"
suffix = "%"
sep = "%, %"
}
envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(envVars, sep), suffix)
}
return str + envText
}
// FlagNames returns the name and aliases for a given flag, and panics
// if any of the values are invalid
func FlagNames(f Flag) []string {
name, ok := getFlagName(f)
if !ok {
panic("flag is missing name field")
}
aliases, _ := getFlagAliases(f)
var ret []string
for _, part := range strings.Split(name, ",") {
// urfave/cli v1 -> v2 migration warning zone:
// split name as per v1 standard
ret = append(ret, strings.TrimSpace(part))
}
// add the aliases to our names
ret = append(ret, aliases...)
// validate the names and panic on failure
for _, part := range ret {
if strings.Contains(part, ",") {
panic(fmt.Errorf("flag name contains a comma: %q", part))
}
if regexp.MustCompile(`\s`).Match([]byte(part)) {
panic(fmt.Errorf("flag name contains whitespace: %q", part))
}
if part == "" {
panic("flag has an empty name")
}
}
return ret
}
func flagStringSliceField(f Flag, name string) []string {
fv := flagValue(f)
field := fv.FieldByName(name)
if field.IsValid() {
return field.Interface().([]string)
}
return []string{}
}
func withFileHint(filePath, str string) string {
fileText := ""
if filePath != "" {
fileText = fmt.Sprintf(" [%s]", filePath)
}
return str + fileText
}
func flagValue(f Flag) reflect.Value {
fv := reflect.ValueOf(f)
for fv.Kind() == reflect.Ptr {
fv = reflect.Indirect(fv)
}
return fv
}
func formatDefault(format string) string {
return " (default: " + format + ")"
}
func stringifyFlag(f Flag) string {
value, _ := getFlagValue(f)
usage, _ := getFlagUsage(f)
if generic.IsSlice(value) {
return withEnvHint(flagStringSliceField(f, "EnvVars"),
stringifySlice(usage, FlagNames(f), value))
}
placeholder, usage := unquoteUsage(usage)
needsPlaceholder := false
defaultValueString := ""
var valKind reflect.Kind
if valType := generic.TypeOf(value); valType != nil {
valKind = valType.Kind()
needsPlaceholder = valKind != reflect.Bool
}
defaultValueString = fmt.Sprintf(formatDefault("%v"), value)
if valKind == reflect.String && value.(string) != "" {
defaultValueString = fmt.Sprintf(formatDefault("%q"), value)
}
if helpText, ok := getFlagDefaultText(f); ok && helpText != "" {
defaultValueString = fmt.Sprintf(formatDefault("%s"), helpText)
}
if defaultValueString == formatDefault("") {
defaultValueString = ""
}
if needsPlaceholder && placeholder == "" {
placeholder = defaultPlaceholder
}
usageWithDefault := strings.TrimSpace(usage + defaultValueString)
return withEnvHint(flagStringSliceField(f, "EnvVars"),
fmt.Sprintf("%s\t%s", prefixedNames(FlagNames(f), placeholder), usageWithDefault))
}
func stringifySlice(usage string, names []string, value interface{}) string {
var defaults []string
for i := 0; i < generic.Len(value); i++ {
v := generic.Index(value, i)
s, ok := v.(string)
if ok && s == "" {
continue
}
if ok {
s = fmt.Sprintf("%q", s)
} else {
s, _ = generic.ToString(v)
}
defaults = append(defaults, s)
}
return stringifySliceFlag(usage, names, defaults)
}
func stringifySliceFlag(usage string, names, defaultVals []string) string {
placeholder, usage := unquoteUsage(usage)
if placeholder == "" {
placeholder = defaultPlaceholder
}
defaultVal := ""
if len(defaultVals) > 0 {
defaultVal = fmt.Sprintf(formatDefault("%s"), strings.Join(defaultVals, ", "))
}
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
return fmt.Sprintf("%s\t%s", prefixedNames(names, placeholder), usageWithDefault)
}
func hasFlag(flags []Flag, fl Flag) bool {
for _, existing := range flags {
if fl == existing {
return true
}
}
return false
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Bool is a type alias for bool
type Bool = bool
// BoolFlag is a flag with type bool
type BoolFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Bool
Destination *Bool
}
// Apply populates the flag given the flag set and environment
func (f *BoolFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "bool", set)
}
// Bool looks up the value of a local BoolFlag, returns
// an empty value if not found
func (c *Context) Bool(name string) bool {
return c.Lookup(name, *new(Bool)).(bool)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// BoolSlice is a type alias for []bool
type BoolSlice = []bool
// BoolSliceFlag is a flag with type []bool
type BoolSliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value BoolSlice
Destination *BoolSlice
}
// Apply populates the flag given the flag set and environment
func (f *BoolSliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "bool slice", set)
}
// BoolSlice looks up the value of a local BoolSliceFlag, returns
// an empty value if not found
func (c *Context) BoolSlice(name string) []bool {
return c.Lookup(name, *new(BoolSlice)).([]bool)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Duration is a type alias for time.Duration
type Duration = time.Duration
// DurationFlag is a flag with type time.Duration
type DurationFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Duration
Destination *Duration
}
// Apply populates the flag given the flag set and environment
func (f *DurationFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "duration", set)
}
// Duration looks up the value of a local DurationFlag, returns
// an empty value if not found
func (c *Context) Duration(name string) time.Duration {
return c.Lookup(name, *new(Duration)).(time.Duration)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// DurationSlice is a type alias for []time.Duration
type DurationSlice = []time.Duration
// DurationSliceFlag is a flag with type []time.Duration
type DurationSliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value DurationSlice
Destination *DurationSlice
}
// Apply populates the flag given the flag set and environment
func (f *DurationSliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "duration slice", set)
}
// DurationSlice looks up the value of a local DurationSliceFlag, returns
// an empty value if not found
func (c *Context) DurationSlice(name string) []time.Duration {
return c.Lookup(name, *new(DurationSlice)).([]time.Duration)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Float64 is a type alias for float64
type Float64 = float64
// Float64Flag is a flag with type float64
type Float64Flag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Float64
Destination *Float64
}
// Apply populates the flag given the flag set and environment
func (f *Float64Flag) Apply(set *flag.FlagSet) error {
return Apply(f, "float64", set)
}
// Float64 looks up the value of a local Float64Flag, returns
// an empty value if not found
func (c *Context) Float64(name string) float64 {
return c.Lookup(name, *new(Float64)).(float64)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Float64Slice is a type alias for []float64
type Float64Slice = []float64
// Float64SliceFlag is a flag with type []float64
type Float64SliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Float64Slice
Destination *Float64Slice
}
// Apply populates the flag given the flag set and environment
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "float64 slice", set)
}
// Float64Slice looks up the value of a local Float64SliceFlag, returns
// an empty value if not found
func (c *Context) Float64Slice(name string) []float64 {
return c.Lookup(name, *new(Float64Slice)).([]float64)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Int is a type alias for int
type Int = int
// IntFlag is a flag with type int
type IntFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Int
Destination *Int
}
// Apply populates the flag given the flag set and environment
func (f *IntFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "int", set)
}
// Int looks up the value of a local IntFlag, returns
// an empty value if not found
func (c *Context) Int(name string) int {
return c.Lookup(name, *new(Int)).(int)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Int64 is a type alias for int64
type Int64 = int64
// Int64Flag is a flag with type int64
type Int64Flag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Int64
Destination *Int64
}
// Apply populates the flag given the flag set and environment
func (f *Int64Flag) Apply(set *flag.FlagSet) error {
return Apply(f, "int64", set)
}
// Int64 looks up the value of a local Int64Flag, returns
// an empty value if not found
func (c *Context) Int64(name string) int64 {
return c.Lookup(name, *new(Int64)).(int64)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Int64Slice is a type alias for []int64
type Int64Slice = []int64
// Int64SliceFlag is a flag with type []int64
type Int64SliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Int64Slice
Destination *Int64Slice
}
// Apply populates the flag given the flag set and environment
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "int64 slice", set)
}
// Int64Slice looks up the value of a local Int64SliceFlag, returns
// an empty value if not found
func (c *Context) Int64Slice(name string) []int64 {
return c.Lookup(name, *new(Int64Slice)).([]int64)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// IntSlice is a type alias for []int
type IntSlice = []int
// IntSliceFlag is a flag with type []int
type IntSliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value IntSlice
Destination *IntSlice
}
// Apply populates the flag given the flag set and environment
func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "int slice", set)
}
// IntSlice looks up the value of a local IntSliceFlag, returns
// an empty value if not found
func (c *Context) IntSlice(name string) []int {
return c.Lookup(name, *new(IntSlice)).([]int)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// String is a type alias for string
type String = string
// StringFlag is a flag with type string
type StringFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value String
Destination *String
}
// Apply populates the flag given the flag set and environment
func (f *StringFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "string", set)
}
// String looks up the value of a local StringFlag, returns
// an empty value if not found
func (c *Context) String(name string) string {
return c.Lookup(name, *new(String)).(string)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// StringSlice is a type alias for []string
type StringSlice = []string
// StringSliceFlag is a flag with type []string
type StringSliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value StringSlice
Destination *StringSlice
}
// Apply populates the flag given the flag set and environment
func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "string slice", set)
}
// StringSlice looks up the value of a local StringSliceFlag, returns
// an empty value if not found
func (c *Context) StringSlice(name string) []string {
return c.Lookup(name, *new(StringSlice)).([]string)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Time is a type alias for time.Time
type Time = time.Time
// TimeFlag is a flag with type time.Time
type TimeFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Time
Destination *Time
}
// Apply populates the flag given the flag set and environment
func (f *TimeFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "time", set)
}
// Time looks up the value of a local TimeFlag, returns
// an empty value if not found
func (c *Context) Time(name string) time.Time {
return c.Lookup(name, *new(Time)).(time.Time)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// TimeSlice is a type alias for []time.Time
type TimeSlice = []time.Time
// TimeSliceFlag is a flag with type []time.Time
type TimeSliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value TimeSlice
Destination *TimeSlice
}
// Apply populates the flag given the flag set and environment
func (f *TimeSliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "time slice", set)
}
// TimeSlice looks up the value of a local TimeSliceFlag, returns
// an empty value if not found
func (c *Context) TimeSlice(name string) []time.Time {
return c.Lookup(name, *new(TimeSlice)).([]time.Time)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Uint is a type alias for uint
type Uint = uint
// UintFlag is a flag with type uint
type UintFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Uint
Destination *Uint
}
// Apply populates the flag given the flag set and environment
func (f *UintFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "uint", set)
}
// Uint looks up the value of a local UintFlag, returns
// an empty value if not found
func (c *Context) Uint(name string) uint {
return c.Lookup(name, *new(Uint)).(uint)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Uint64 is a type alias for uint64
type Uint64 = uint64
// Uint64Flag is a flag with type uint64
type Uint64Flag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Uint64
Destination *Uint64
}
// Apply populates the flag given the flag set and environment
func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
return Apply(f, "uint64", set)
}
// Uint64 looks up the value of a local Uint64Flag, returns
// an empty value if not found
func (c *Context) Uint64(name string) uint64 {
return c.Lookup(name, *new(Uint64)).(uint64)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// Uint64Slice is a type alias for []uint64
type Uint64Slice = []uint64
// Uint64SliceFlag is a flag with type []uint64
type Uint64SliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Uint64Slice
Destination *Uint64Slice
}
// Apply populates the flag given the flag set and environment
func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "uint64 slice", set)
}
// Uint64Slice looks up the value of a local Uint64SliceFlag, returns
// an empty value if not found
func (c *Context) Uint64Slice(name string) []uint64 {
return c.Lookup(name, *new(Uint64Slice)).([]uint64)
}

View File

@ -1,40 +0,0 @@
package cli
import (
"time"
"github.com/rancher/spur/flag"
)
var _ = time.Time{}
// UintSlice is a type alias for []uint
type UintSlice = []uint
// UintSliceFlag is a flag with type []uint
type UintSliceFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value UintSlice
Destination *UintSlice
}
// Apply populates the flag given the flag set and environment
func (f *UintSliceFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "uint slice", set)
}
// UintSlice looks up the value of a local UintSliceFlag, returns
// an empty value if not found
func (c *Context) UintSlice(name string) []uint {
return c.Lookup(name, *new(UintSlice)).([]uint)
}

View File

@ -1,105 +0,0 @@
package cli
import (
"fmt"
"io/ioutil"
"strings"
"syscall"
"github.com/rancher/spur/flag"
"github.com/rancher/spur/generic"
)
// Apply will attempt to apply generic flag values to a flagset
func Apply(f Flag, typ string, set *flag.FlagSet) error {
name := FlagNames(f)[0]
value, _ := getFlagValue(f)
usage, _ := getFlagUsage(f)
envVars, _ := getFlagEnvVars(f)
filePath, _ := getFlagFilePath(f)
// make sure we have a pointer to value (for non-generic values)
if !generic.IsPtr(value) {
value, _ = getFlagValuePtr(f)
}
destination, _ := getFlagDestination(f)
// create new destination if not defined
if destination == nil || generic.ValueOfPtr(destination) == nil {
destination = generic.New(value)
}
// create new value if not defined (for generic flag.Value)
if value == nil || generic.ValueOfPtr(value) == nil {
value = generic.New(destination)
}
wasSet := false
// load flags from environment or file
if val, ok := flagFromEnvOrFile(envVars, filePath); ok {
newValue := generic.New(value)
if err := applyValue(newValue, val); err != nil {
return fmt.Errorf("could not parse %q as %s value for flag %s: %s", val, typ, name, err)
}
value = newValue
wasSet = true
}
// copy value to destination
generic.Set(destination, generic.ValueOfPtr(value))
dest, ok := destination.(flag.Value)
if !ok {
dest = flag.NewGenericValue(destination)
}
// for all of the names set the flag variable
for _, name := range FlagNames(f) {
set.Var(dest, name, usage)
}
// if value is not default mark as needs visit
if wasSet {
set.NeedsVisit(name)
}
return nil
}
func applyValue(ptr interface{}, val string) error {
if !generic.IsSlice(ptr) {
// if we are a slice just return the applied elem
return applyElem(ptr, val)
}
// otherwise create a new slice and apply the split values
values := generic.Zero(ptr)
for _, val := range strings.Split(val, ",") {
value := generic.NewElem(ptr)
if err := generic.FromString(val, value); err != nil {
return err
}
values = generic.Append(values, generic.ValueOfPtr(value))
}
generic.Set(ptr, values)
return nil
}
func applyElem(ptr interface{}, val string) error {
if gen, ok := ptr.(flag.Value); ok {
// if we are a generic flag.Value then apply Set
return gen.Set(val)
}
// otherwise create a new value and convert it
value := generic.NewElem(ptr)
if err := generic.FromString(val, value); err != nil {
return err
}
generic.Set(ptr, generic.ValueOfPtr(value))
return nil
}
func flagFromEnvOrFile(envVars []string, filePath string) (val string, ok bool) {
for _, envVar := range envVars {
envVar = strings.TrimSpace(envVar)
if val, ok := syscall.Getenv(envVar); ok {
return val, true
}
}
for _, fileVar := range strings.Split(filePath, ",") {
if data, err := ioutil.ReadFile(fileVar); err == nil {
return string(data), true
}
}
return "", false
}

View File

@ -1,92 +0,0 @@
package cli
func getFlagName(f Flag) (result string, ok bool) {
if v := flagValue(f).FieldByName("Name"); v.IsValid() {
return v.Interface().(string), true
}
return
}
func getFlagAliases(f Flag) (result []string, ok bool) {
if v := flagValue(f).FieldByName("Aliases"); v.IsValid() {
return v.Interface().([]string), true
}
return
}
func getFlagEnvVars(f Flag) (result []string, ok bool) {
if v := flagValue(f).FieldByName("EnvVars"); v.IsValid() {
return v.Interface().([]string), true
}
return
}
func getFlagUsage(f Flag) (result string, ok bool) {
if v := flagValue(f).FieldByName("Usage"); v.IsValid() {
return v.Interface().(string), true
}
return
}
func getFlagDefaultText(f Flag) (result string, ok bool) {
if v := flagValue(f).FieldByName("DefaultText"); v.IsValid() {
return v.Interface().(string), true
}
return
}
func getFlagFilePath(f Flag) (result string, ok bool) {
if v := flagValue(f).FieldByName("FilePath"); v.IsValid() {
return v.Interface().(string), true
}
return
}
func getFlagRequired(f Flag) (result bool, ok bool) {
if v := flagValue(f).FieldByName("Required"); v.IsValid() {
return v.Interface().(bool), true
}
return
}
func getFlagHidden(f Flag) (result bool, ok bool) {
if v := flagValue(f).FieldByName("Hidden"); v.IsValid() {
return v.Interface().(bool), true
}
return
}
func getFlagTakesFile(f Flag) (result bool, ok bool) {
if v := flagValue(f).FieldByName("TakesFile"); v.IsValid() {
return v.Interface().(bool), true
}
return
}
func getFlagSkipAltSrc(f Flag) (result bool, ok bool) {
if v := flagValue(f).FieldByName("SkipAltSrc"); v.IsValid() {
return v.Interface().(bool), true
}
return
}
func getFlagValue(f Flag) (result interface{}, ok bool) {
if v := flagValue(f).FieldByName("Value"); v.IsValid() {
return v.Interface(), true
}
return
}
func getFlagValuePtr(f Flag) (result interface{}, ok bool) {
if v := flagValue(f).FieldByName("Value"); v.IsValid() {
return v.Addr().Interface(), true
}
return
}
func getFlagDestination(f Flag) (result interface{}, ok bool) {
if v := flagValue(f).FieldByName("Destination"); v.IsValid() {
return v.Interface(), true
}
return
}

View File

@ -1,36 +0,0 @@
package cli
import (
"github.com/rancher/spur/flag"
)
// Generic is a type alias for flag.Value
type Generic = flag.Value
// GenericFlag is a flag with type flag.Value
type GenericFlag struct {
Name string
Aliases []string
EnvVars []string
Usage string
DefaultText string
FilePath string
Required bool
Hidden bool
TakesFile bool
SkipAltSrc bool
Value Generic
Destination Generic
}
// Apply populates the flag given the flag set and environment
func (f *GenericFlag) Apply(set *flag.FlagSet) error {
return Apply(f, "generic", set)
}
// Generic looks up the value of a local GenericFlag, returns
// an empty value if not found
func (c *Context) Generic(name string) interface{} {
return c.Lookup(name, nil)
}

View File

@ -1,44 +0,0 @@
package cli
// BashCompleteFunc is an action to execute when the shell completion flag is set
type BashCompleteFunc func(*Context)
// BeforeFunc is an action to execute before any subcommands are run, but after
// the context is ready if a non-nil error is returned, no subcommands are run
type BeforeFunc func(*Context) error
// AfterFunc is an action to execute after any subcommands are run, but after the
// subcommand has finished it is run even if Action() panics
type AfterFunc func(*Context) error
// ActionFunc is the action to execute when no subcommands are specified
type ActionFunc func(*Context) error
// CommandNotFoundFunc is executed if the proper command cannot be found
type CommandNotFoundFunc func(*Context, string)
// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying
// customized usage error messages. This function is able to replace the
// original error messages. If this function is not set, the "Incorrect usage"
// is displayed and the execution is interrupted.
type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error
// ExitErrHandlerFunc is executed if provided in order to handle exitError values
// returned by Actions and Before/After functions.
type ExitErrHandlerFunc func(context *Context, err error)
// FlagStringFunc is used by the help generation to display a flag, which is
// expected to be a single line.
type FlagStringFunc func(Flag) string
// FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix
// text for a flag's full name.
type FlagNamePrefixFunc func(fullName []string, placeholder string) string
// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help
// with the environment variable details.
type FlagEnvHintFunc func(envVars []string, str string) string
// FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help
// with the file path details.
type FlagFileHintFunc func(filePath, str string) string

View File

@ -1,378 +0,0 @@
package cli
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"text/template"
"unicode/utf8"
)
var helpCommand = &Command{
Name: "help",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *Context) error {
args := c.Args()
if args.Present() {
return ShowCommandHelp(c, args.First())
}
ShowAppHelp(c)
return nil
},
}
var helpSubcommand = &Command{
Name: "help",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *Context) error {
args := c.Args()
if args.Present() {
return ShowCommandHelp(c, args.First())
}
return ShowSubcommandHelp(c)
},
}
// Prints help for the App or Command
type helpPrinter func(w io.Writer, templ string, data interface{})
// Prints help for the App or Command with custom template function.
type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{})
// HelpPrinter is a function that writes the help output. If not set explicitly,
// this calls HelpPrinterCustom using only the default template functions.
//
// If custom logic for printing help is required, this function can be
// overridden. If the ExtraInfo field is defined on an App, this function
// should not be modified, as HelpPrinterCustom will be used directly in order
// to capture the extra information.
var HelpPrinter helpPrinter = printHelp
// HelpPrinterCustom is a function that writes the help output. It is used as
// the default implementation of HelpPrinter, and may be called directly if
// the ExtraInfo field is set on an App.
var HelpPrinterCustom helpPrinterCustom = printHelpCustom
// VersionPrinter prints the version for the App
var VersionPrinter = printVersion
// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
func ShowAppHelpAndExit(c *Context, exitCode int) {
ShowAppHelp(c)
os.Exit(exitCode)
}
// ShowAppHelp is an action that displays the help.
func ShowAppHelp(c *Context) error {
template := c.App.CustomAppHelpTemplate
if template == "" {
template = AppHelpTemplate
}
if c.App.ExtraInfo == nil {
HelpPrinter(c.App.Writer, template, c.App)
return nil
}
customAppData := func() map[string]interface{} {
return map[string]interface{}{
"ExtraInfo": c.App.ExtraInfo,
}
}
HelpPrinterCustom(c.App.Writer, template, c.App, customAppData())
return nil
}
// DefaultAppComplete prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) {
DefaultCompleteWithFlags(nil)(c)
}
func printCommandSuggestions(commands []*Command, writer io.Writer) {
for _, command := range commands {
if command.Hidden {
continue
}
if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" {
for _, name := range command.Names() {
fmt.Fprintf(writer, "%s:%s\n", name, command.Usage)
}
} else {
for _, name := range command.Names() {
fmt.Fprintf(writer, "%s\n", name)
}
}
}
}
func cliArgContains(flagName string) bool {
for _, name := range strings.Split(flagName, ",") {
name = strings.TrimSpace(name)
count := utf8.RuneCountInString(name)
if count > 2 {
count = 2
}
flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name)
for _, a := range os.Args {
if a == flag {
return true
}
}
}
return false
}
func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) {
cur := strings.TrimPrefix(lastArg, "-")
cur = strings.TrimPrefix(cur, "-")
for _, flag := range flags {
if bflag, ok := flag.(*BoolFlag); ok && bflag.Hidden {
continue
}
for _, name := range FlagNames(flag) {
name = strings.TrimSpace(name)
// this will get total count utf8 letters in flag name
count := utf8.RuneCountInString(name)
if count > 2 {
count = 2 // resuse this count to generate single - or -- in flag completion
}
// if flag name has more than one utf8 letter and last argument in cli has -- prefix then
// skip flag completion for short flags example -v or -x
if strings.HasPrefix(lastArg, "--") && count == 1 {
continue
}
// match if last argument matches this flag and it is not repeated
if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(name) {
flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name)
fmt.Fprintln(writer, flagCompletion)
}
}
}
}
func DefaultCompleteWithFlags(cmd *Command) func(c *Context) {
return func(c *Context) {
if len(os.Args) > 2 {
lastArg := os.Args[len(os.Args)-2]
if strings.HasPrefix(lastArg, "-") {
printFlagSuggestions(lastArg, c.App.Flags, c.App.Writer)
if cmd != nil {
printFlagSuggestions(lastArg, cmd.Flags, c.App.Writer)
}
return
}
}
if cmd != nil {
printCommandSuggestions(cmd.Subcommands, c.App.Writer)
} else {
printCommandSuggestions(c.App.Commands, c.App.Writer)
}
}
}
// ShowCommandHelpAndExit - exits with code after showing help
func ShowCommandHelpAndExit(c *Context, command string, code int) {
ShowCommandHelp(c, command)
os.Exit(code)
}
// ShowCommandHelp prints help for the given command
func ShowCommandHelp(ctx *Context, command string) error {
// show the subcommand help for a command with subcommands
if command == "" {
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
return nil
}
for _, c := range ctx.App.Commands {
if c.HasName(command) {
templ := c.CustomHelpTemplate
if templ == "" {
templ = CommandHelpTemplate
}
HelpPrinter(ctx.App.Writer, templ, c)
return nil
}
}
if ctx.App.CommandNotFound == nil {
return Exit(fmt.Sprintf("No help topic for '%v'", command), 3)
}
ctx.App.CommandNotFound(ctx, command)
return nil
}
// ShowSubcommandHelp prints help for the given subcommand
func ShowSubcommandHelp(c *Context) error {
if c == nil {
return nil
}
if c.Command != nil {
return ShowCommandHelp(c, c.Command.Name)
}
return ShowCommandHelp(c, "")
}
// ShowVersion prints the version number of the App
func ShowVersion(c *Context) {
VersionPrinter(c)
}
func printVersion(c *Context) {
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
}
// ShowCompletions prints the lists of commands within a given context
func ShowCompletions(c *Context) {
a := c.App
if a != nil && a.BashComplete != nil {
a.BashComplete(c)
}
}
// ShowCommandCompletions prints the custom completions for a given command
func ShowCommandCompletions(ctx *Context, command string) {
c := ctx.App.Command(command)
if c != nil {
if c.BashComplete != nil {
c.BashComplete(ctx)
} else {
DefaultCompleteWithFlags(c)(ctx)
}
}
}
// FlagToString will convert a flag to a string, using either it's String()
// function, or FlagStringer if String() is not defined
func FlagToString(f Flag) string {
if stringer, ok := f.(fmt.Stringer); ok {
return stringer.String()
}
return FlagStringer(f)
}
// printHelpCustom is the default implementation of HelpPrinterCustom.
//
// The customFuncs map will be combined with a default template.FuncMap to
// allow using arbitrary functions in template rendering.
func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs map[string]interface{}) {
funcMap := template.FuncMap{
"join": strings.Join,
"FlagToString": FlagToString,
}
for key, value := range customFuncs {
funcMap[key] = value
}
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
err := t.Execute(w, data)
if err != nil {
// If the writer is closed, t.Execute will fail, and there's nothing
// we can do to recover.
if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" {
fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
}
return
}
w.Flush()
}
func printHelp(out io.Writer, templ string, data interface{}) {
HelpPrinterCustom(out, templ, data, nil)
}
func checkVersion(c *Context) bool {
found := false
for _, name := range FlagNames(VersionFlag) {
if c.Bool(name) {
found = true
}
}
return found
}
func checkHelp(c *Context) bool {
found := false
for _, name := range FlagNames(HelpFlag) {
if c.Bool(name) {
found = true
}
}
return found
}
func checkCommandHelp(c *Context, name string) bool {
if c.Bool("h") || c.Bool("help") {
ShowCommandHelp(c, name)
return true
}
return false
}
func checkSubcommandHelp(c *Context) bool {
if c.Bool("h") || c.Bool("help") {
ShowSubcommandHelp(c)
return true
}
return false
}
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
if !a.EnableBashCompletion {
return false, arguments
}
pos := len(arguments) - 1
lastArg := arguments[pos]
if lastArg != "--generate-bash-completion" {
return false, arguments
}
return true, arguments[:pos]
}
func checkCompletions(c *Context) bool {
if !c.shellComplete {
return false
}
if args := c.Args(); args.Present() {
name := args.First()
if cmd := c.App.Command(name); cmd != nil {
// let the command handle the completion
return false
}
}
ShowCompletions(c)
return true
}
func checkCommandCompletions(c *Context, name string) bool {
if !c.shellComplete {
return false
}
ShowCommandCompletions(c, name)
return true
}

View File

@ -1,79 +0,0 @@
package cli
import (
"fmt"
"github.com/rancher/spur/flag"
)
// InputSourceContext is an interface used to allow
// other input sources to be implemented as needed.
//
// Source returns an identifier for the input source. In case of file source
// it should return path to the file.
type InputSourceContext interface {
Source() string
Get(name string) (interface{}, bool)
}
// ApplyInputSourceValue will attempt to apply an input source to a generic flag
func ApplyInputSourceValue(f Flag, context *Context, isc InputSourceContext) error {
name := FlagNames(f)[0]
skipAltSrc, _ := getFlagSkipAltSrc(f)
if !skipAltSrc && context.flagSet != nil {
if !context.IsSet(name) {
// only checks the first name of this flag
value, ok := isc.Get(name)
if !ok || value == nil {
return nil
}
// if a generic flag.Value get the string representation
if v, ok := value.(flag.Value); ok {
value = v.String()
}
// sets the new value from some source
if err := context.Set(name, value); err != nil {
return fmt.Errorf("unable to apply input source '%s': %s", isc.Source(), err)
}
}
}
return nil
}
// ApplyInputSourceValues iterates over all provided flags and executes ApplyInputSourceValue
// on each flag to apply an alternate input source.
func ApplyInputSourceValues(context *Context, inputSourceContext InputSourceContext, flags []Flag) (err error) {
for _, f := range flags {
if err = ApplyInputSourceValue(f, context, inputSourceContext); err != nil {
return err
}
}
return
}
// InitInputSource is used to to setup an InputSourceContext on a Command Before method. It will create a new
// input source based on the func provided with potentially using existing Context values to initialize itself. If there is
// no error it will then apply the new input source to any flags that are supported by the input source
func InitInputSource(flags []Flag, createInputSource func(context *Context) (InputSourceContext, error)) BeforeFunc {
return func(context *Context) error {
inputSource, err := createInputSource(context)
if err != nil {
return err
}
return ApplyInputSourceValues(context, inputSource, flags)
}
}
// InitAllInputSource is used to to setup an InputSourceContext on a Command Before method. It will create a new
// input source based on the func provided with potentially using existing Context values to initialize itself. If there is
// no error it will then apply the new input source to all flags that are supported by the input source
func InitAllInputSource(createInputSource func(context *Context) (InputSourceContext, error)) BeforeFunc {
return func(context *Context) error {
inputSource, err := createInputSource(context)
if err != nil {
return err
}
return ApplyInputSourceValues(context, inputSource, context.GetFlags())
}
}

View File

@ -1,95 +0,0 @@
package cli
import (
"strings"
"github.com/rancher/spur/flag"
)
type iterativeParser interface {
newFlagSet() (*flag.FlagSet, error)
useShortOptionHandling() bool
}
// To enable short-option handling (e.g., "-it" vs "-i -t") we have to
// iteratively catch parsing errors. This way we achieve LR parsing without
// transforming any arguments. Otherwise, there is no way we can discriminate
// combined short options from common arguments that should be left untouched.
// Pass `shellComplete` to continue parsing options on failure during shell
// completion when, the user-supplied options may be incomplete.
func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComplete bool) error {
for {
err := set.Parse(args)
if !ip.useShortOptionHandling() || err == nil {
if shellComplete {
return nil
}
return err
}
errStr := err.Error()
trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: -")
if errStr == trimmed {
return err
}
// regenerate the initial args with the split short opts
argsWereSplit := false
for i, arg := range args {
// skip args that are not part of the error message
if name := strings.TrimLeft(arg, "-"); name != trimmed {
continue
}
// if we can't split, the error was accurate
shortOpts := splitShortOptions(set, arg)
if len(shortOpts) == 1 {
return err
}
// swap current argument with the split version
args = append(args[:i], append(shortOpts, args[i+1:]...)...)
argsWereSplit = true
break
}
// This should be an impossible to reach code path, but in case the arg
// splitting failed to happen, this will prevent infinite loops
if !argsWereSplit {
return err
}
// Since custom parsing failed, replace the flag set before retrying
newSet, err := ip.newFlagSet()
if err != nil {
return err
}
*set = *newSet
}
}
func splitShortOptions(set *flag.FlagSet, arg string) []string {
shortFlagsExist := func(s string) bool {
for _, c := range s[1:] {
if f := set.Lookup(string(c)); f == nil {
return false
}
}
return true
}
if !isSplittable(arg) || !shortFlagsExist(arg) {
return []string{arg}
}
separated := make([]string, 0, len(arg)-1)
for _, flagChar := range arg[1:] {
separated = append(separated, "-"+string(flagChar))
}
return separated
}
func isSplittable(flagArg string) bool {
return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2
}

View File

@ -1,29 +0,0 @@
package cli
import "unicode"
// lexicographicLess compares strings alphabetically considering case.
func lexicographicLess(i, j string) bool {
iRunes := []rune(i)
jRunes := []rune(j)
lenShared := len(iRunes)
if lenShared > len(jRunes) {
lenShared = len(jRunes)
}
for index := 0; index < lenShared; index++ {
ir := iRunes[index]
jr := jRunes[index]
if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr {
return lir < ljr
}
if ir != jr {
return ir < jr
}
}
return i < j
}

View File

@ -1,120 +0,0 @@
package cli
// AppHelpTemplate is the text template for the Default help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var AppHelpTemplate = `NAME:
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
USAGE:
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
VERSION:
{{.Version}}{{end}}{{end}}{{if .Description}}
DESCRIPTION:
{{.Description}}{{end}}{{if len .Authors}}
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS:
{{range $index, $option := .VisibleFlags}}{{if $index}}
{{end}}{{FlagToString $option}}{{end}}{{end}}{{if .Copyright}}
COPYRIGHT:
{{.Copyright}}{{end}}
`
// CommandHelpTemplate is the text template for the command help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var CommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
CATEGORY:
{{.Category}}{{end}}{{if .Description}}
DESCRIPTION:
{{.Description}}{{end}}{{if .VisibleFlags}}
OPTIONS:
{{range .VisibleFlags}}{{FlagToString .}}
{{end}}{{end}}
`
// SubcommandHelpTemplate is the text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
DESCRIPTION:
{{.Description}}{{end}}
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
OPTIONS:
{{range .VisibleFlags}}{{FlagToString .}}
{{end}}{{end}}
`
var MarkdownDocTemplate = `% {{ .App.Name }} 8
# NAME
{{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }}
# SYNOPSIS
{{ .App.Name }}
{{ if .SynopsisArgs }}
` + "```" + `
{{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + `
{{ end }}{{ if .App.UsageText }}
# DESCRIPTION
{{ .App.UsageText }}
{{ end }}
**Usage**:
` + "```" + `
{{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
` + "```" + `
{{ if .GlobalArgs }}
# GLOBAL OPTIONS
{{ range $v := .GlobalArgs }}
{{ $v }}{{ end }}
{{ end }}{{ if .Commands }}
# COMMANDS
{{ range $v := .Commands }}
{{ $v }}{{ end }}{{ end }}`
var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion
function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet'
for i in (commandline -opc)
if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }}
return 1
end
end
return 0
end
{{ range $v := .Completions }}{{ $v }}
{{ end }}`

View File

@ -1,27 +0,0 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// Title__Var defines a Type__ flag with specified name, default value, and usage string.
// The argument p points to a Type__ variable in which to store the value of the flag.
func (f *FlagSet) Title__Var(ptr *Type__, name string, value Type__, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Title__ defines a Type__ flag with specified name, default value, and usage string.
// The return value is the address of a Type__ variable that stores the value of the flag.
func (f *FlagSet) Title__(name string, value Type__, usage string) *Type__ {
return f.Generic(name, value, usage).(*Type__)
}
// Title__Var defines a Type__ flag with specified name, default value, and usage string.
// The argument p points to a Type__ variable in which to store the value of the flag.
func Title__Var(ptr *Type__, name string, value Type__, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Title__ defines a Type__ flag with specified name, default value, and usage string.
// The return value is the address of a Type__ variable that stores the value of the flag.
func Title__(name string, value Type__, usage string) *Type__ {
return CommandLine.Generic(name, value, usage).(*Type__)
}

View File

@ -1,661 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package flag implements command-line flag parsing.
Usage
Define flags using flag.String(), Bool(), Int(), etc.
This declares an integer flag, -n, stored in the pointer nFlag, with type *int:
import "flag"
var nFlag = flag.Int("n", 1234, "help message for flag n")
If you like, you can bind the flag to a variable using the Var() functions.
var flagvar int
func init() {
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
}
Or you can create custom flags that satisfy the Value interface (with
pointer receivers) and couple them to flag parsing by
flag.Var(&flagVal, "name", "help message for flagname")
For such flags, the default value is just the initial value of the variable.
After all flags are defined, call
flag.Parse()
to parse the command line into the defined flags.
Flags may then be used directly. If you're using the flags themselves,
they are all pointers; if you bind to variables, they're values.
fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)
After parsing, the arguments following the flags are available as the
slice flag.Args() or individually as flag.Arg(i).
The arguments are indexed from 0 through flag.NArg()-1.
Command line flag syntax
The following forms are permitted:
-flag
-flag=x
-flag x // non-boolean flags only
One or two minus signs may be used; they are equivalent.
The last form is not permitted for boolean flags because the
meaning of the command
cmd -x *
where * is a Unix shell wildcard, will change if there is a file
called 0, false, etc. You must use the -flag=false form to turn
off a boolean flag.
Flag parsing stops just before the first non-flag argument
("-" is a non-flag argument) or after the terminator "--".
Integer flags accept 1234, 0664, 0x1234 and may be negative.
Boolean flags may be:
1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
Duration flags accept any input valid for time.ParseDuration.
The default set of command-line flags is controlled by
top-level functions. The FlagSet type allows one to define
independent sets of flags, such as to implement subcommands
in a command-line interface. The methods of FlagSet are
analogous to the top-level functions for the command-line
flag set.
*/
package flag
import (
"errors"
"fmt"
"io"
"os"
"reflect"
"sort"
"strings"
"github.com/rancher/spur/generic"
)
// ErrHelp is the error returned if the -help or -h flag is invoked
// but no such flag is defined.
var ErrHelp = errors.New("flag: help requested")
// Value is the interface to the dynamic value stored in a flag.
// (The default value is represented as a string.)
//
// If a Value has an IsBoolFlag() bool method returning true,
// the command-line parser makes -name equivalent to -name=true
// rather than using the next command-line argument.
//
// Set is called once, in command line order, for each flag present.
// The flag package may call the String method with a zero-valued receiver,
// such as a nil pointer.
type Value interface {
String() string
Set(interface{}) error
}
// Getter is an interface that allows the contents of a Value to be retrieved.
// It wraps the Value interface, rather than being part of it, because it
// appeared after Go 1 and its compatibility rules. All Value types provided
// by this package satisfy the Getter interface.
type Getter interface {
Value
Get() interface{}
}
// BoolFlag is an interface for determining if the value of a flag is needed.
type BoolFlag interface {
IsBoolFlag() bool
}
// IsBoolValue returns true for data types which don't require a flag value
func IsBoolValue(value interface{}) bool {
if v, ok := value.(BoolFlag); ok {
return v.IsBoolFlag()
}
var t reflect.Type
if v, ok := value.(Getter); ok {
t = generic.ElemTypeOf(v.Get())
} else {
t = generic.ElemTypeOf(value)
}
return t != nil && t.String() == "bool"
}
// ErrorHandling defines how FlagSet.Parse behaves if the parse fails.
type ErrorHandling int
// These constants cause FlagSet.Parse to behave as described if the parse fails.
const (
ContinueOnError ErrorHandling = iota // Return a descriptive error.
ExitOnError // Call os.Exit(2) or for -h/-help Exit(0).
PanicOnError // Call panic with a descriptive error.
)
// A FlagSet represents a set of defined flags. The zero value of a FlagSet
// has no name and has ContinueOnError error handling.
//
// Flag names must be unique within a FlagSet. An attempt to define a flag whose
// name is already in use will cause a panic.
type FlagSet struct {
// Usage is the function called when an error occurs while parsing flags.
// The field is a function (not a method) that may be changed to point to
// a custom error handler. What happens after Usage is called depends
// on the ErrorHandling setting; for the command line, this defaults
// to ExitOnError, which exits the program after calling Usage.
Usage func()
name string
parsed bool
actual map[string]*Flag
formal map[string]*Flag
args []string // arguments after flags
errorHandling ErrorHandling
output io.Writer // nil means stderr; use Output() accessor
}
// A Flag represents the state of a flag.
type Flag struct {
Name string // name as it appears on command line
Usage string // help message
Value Value // value as set
DefValue string // default value (as text); for usage message
}
// sortFlags returns the flags as a slice in lexicographical sorted order.
func sortFlags(flags map[string]*Flag) []*Flag {
result := make([]*Flag, len(flags))
i := 0
for _, f := range flags {
result[i] = f
i++
}
sort.Slice(result, func(i, j int) bool {
return result[i].Name < result[j].Name
})
return result
}
const invalidValueTemplate = "invalid value %q for flag -%s: %v"
func (f *FlagSet) addActual(name string, flag *Flag) {
if f.actual == nil {
f.actual = make(map[string]*Flag)
}
f.actual[name] = flag
}
// Output returns the destination for usage and error messages. os.Stderr is returned if
// output was not set or was set to nil.
func (f *FlagSet) Output() io.Writer {
if f.output == nil {
return os.Stderr
}
return f.output
}
// Name returns the name of the flag set.
func (f *FlagSet) Name() string {
return f.name
}
// ErrorHandling returns the error handling behavior of the flag set.
func (f *FlagSet) ErrorHandling() ErrorHandling {
return f.errorHandling
}
// SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used.
func (f *FlagSet) SetOutput(output io.Writer) {
f.output = output
}
// VisitAll visits the flags in lexicographical order, calling fn for each.
// It visits all flags, even those not set.
func (f *FlagSet) VisitAll(fn func(*Flag)) {
for _, flag := range sortFlags(f.formal) {
fn(flag)
}
}
// VisitAll visits the command-line flags in lexicographical order, calling
// fn for each. It visits all flags, even those not set.
func VisitAll(fn func(*Flag)) {
CommandLine.VisitAll(fn)
}
// Visit visits the flags in lexicographical order, calling fn for each.
// It visits only those flags that have been set.
func (f *FlagSet) Visit(fn func(*Flag)) {
for _, flag := range sortFlags(f.actual) {
fn(flag)
}
}
// NeedsVisit marks the named flags for visit.
func (f *FlagSet) NeedsVisit(names ...string) {
for _, name := range names {
if flag := f.Lookup(name); flag != nil {
f.addActual(name, flag)
}
}
}
// Visit visits the command-line flags in lexicographical order, calling fn
// for each. It visits only those flags that have been set.
func Visit(fn func(*Flag)) {
CommandLine.Visit(fn)
}
// Lookup returns the Flag structure of the named flag, returning nil if none exists.
func (f *FlagSet) Lookup(name string) *Flag {
return f.formal[name]
}
// Lookup returns the Flag structure of the named command-line flag,
// returning nil if none exists.
func Lookup(name string) *Flag {
return CommandLine.formal[name]
}
// Set sets the value of the named flag.
func (f *FlagSet) Set(name string, value interface{}) error {
flag, ok := f.formal[name]
if !ok {
return fmt.Errorf("no such flag -%v", name)
}
err := flag.Value.Set(value)
if err != nil {
return fmt.Errorf(invalidValueTemplate, value, name, err)
}
f.addActual(name, flag)
return nil
}
// Set sets the value of the named command-line flag.
func Set(name string, value interface{}) error {
return CommandLine.Set(name, value)
}
// isZeroValue determines whether the string represents the zero
// value for a flag.
func isZeroValue(flag *Flag, value string) bool {
// Build a zero value of the flag's Value type, and see if the
// result of calling its String method equals the value passed in.
if val, ok := flag.Value.(Getter); ok {
if s, ok := generic.ToString(generic.Zero(val.Get())); ok {
return value == s
}
}
return false
}
// UnquoteUsage extracts a back-quoted name from the usage
// string for a flag and returns it and the un-quoted usage.
// Given "a `name` to show" it returns ("name", "a name to show").
// If there are no back quotes, the name is an educated guess of the
// type of the flag's value, or the empty string if the flag is boolean.
func UnquoteUsage(flag *Flag) (name string, usage string) {
// Look for a back-quoted name, but avoid the strings package.
usage = flag.Usage
for i := 0; i < len(usage); i++ {
if usage[i] == '`' {
for j := i + 1; j < len(usage); j++ {
if usage[j] == '`' {
name = usage[i+1 : j]
usage = usage[:i] + name + usage[j+1:]
return name, usage
}
}
break // Only one back quote; use type name.
}
}
// No explicit name, so use type if we can find one.
name = "value"
if v, ok := flag.Value.(Getter); ok {
name = generic.TypeOf(v.Get()).String()
}
if IsBoolValue(flag.Value) {
name = ""
}
return
}
// PrintDefaults prints, to standard error unless configured otherwise, the
// default values of all defined command-line flags in the set. See the
// documentation for the global function PrintDefaults for more information.
func (f *FlagSet) PrintDefaults() {
f.VisitAll(func(flag *Flag) {
s := fmt.Sprintf(" -%s", flag.Name) // Two spaces before -; see next two comments.
name, usage := UnquoteUsage(flag)
if len(name) > 0 {
s += " " + name
}
// Boolean flags of one ASCII letter are so common we
// treat them specially, putting their usage on the same line.
if len(s) <= 4 { // space, space, '-', 'x'.
s += "\t"
} else {
// Four spaces before the tab triggers good alignment
// for both 4- and 8-space tab stops.
s += "\n \t"
}
s += strings.ReplaceAll(usage, "\n", "\n \t")
if !isZeroValue(flag, flag.DefValue) {
if v, ok := flag.Value.(Getter); ok && generic.TypeOf(v.Get()).String() == "string" {
// put quotes on the value
s += fmt.Sprintf(" (default %q)", flag.DefValue)
} else {
s += fmt.Sprintf(" (default %v)", flag.DefValue)
}
}
fmt.Fprint(f.Output(), s, "\n")
})
}
// PrintDefaults prints, to standard error unless configured otherwise,
// a usage message showing the default settings of all defined
// command-line flags.
// For an integer valued flag x, the default output has the form
// -x int
// usage-message-for-x (default 7)
// The usage message will appear on a separate line for anything but
// a bool flag with a one-byte name. For bool flags, the type is
// omitted and if the flag name is one byte the usage message appears
// on the same line. The parenthetical default is omitted if the
// default is the zero value for the type. The listed type, here int,
// can be changed by placing a back-quoted name in the flag's usage
// string; the first such item in the message is taken to be a parameter
// name to show in the message and the back quotes are stripped from
// the message when displayed. For instance, given
// flag.String("I", "", "search `directory` for include files")
// the output will be
// -I directory
// search directory for include files.
//
// To change the destination for flag messages, call CommandLine.SetOutput.
func PrintDefaults() {
CommandLine.PrintDefaults()
}
// defaultUsage is the default function to print a usage message.
func (f *FlagSet) defaultUsage() {
if f.name == "" {
fmt.Fprintf(f.Output(), "Usage:\n")
} else {
fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name)
}
f.PrintDefaults()
}
// NOTE: Usage is not just defaultUsage(CommandLine)
// because it serves (via godoc flag Usage) as the example
// for how to write your own usage function.
// Usage prints a usage message documenting all defined command-line flags
// to CommandLine's output, which by default is os.Stderr.
// It is called when an error occurs while parsing flags.
// The function is a variable that may be changed to point to a custom function.
// By default it prints a simple header and calls PrintDefaults; for details about the
// format of the output and how to control it, see the documentation for PrintDefaults.
// Custom usage functions may choose to exit the program; by default exiting
// happens anyway as the command line's error handling strategy is set to
// ExitOnError.
var Usage = func() {
fmt.Fprintf(CommandLine.Output(), "Usage of %s:\n", os.Args[0])
PrintDefaults()
}
// NFlag returns the number of flags that have been set.
func (f *FlagSet) NFlag() int { return len(f.actual) }
// NFlag returns the number of command-line flags that have been set.
func NFlag() int { return len(CommandLine.actual) }
// Arg returns the i'th argument. Arg(0) is the first remaining argument
// after flags have been processed. Arg returns an empty string if the
// requested element does not exist.
func (f *FlagSet) Arg(i int) string {
if i < 0 || i >= len(f.args) {
return ""
}
return f.args[i]
}
// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument
// after flags have been processed. Arg returns an empty string if the
// requested element does not exist.
func Arg(i int) string {
return CommandLine.Arg(i)
}
// NArg is the number of arguments remaining after flags have been processed.
func (f *FlagSet) NArg() int { return len(f.args) }
// NArg is the number of arguments remaining after flags have been processed.
func NArg() int { return len(CommandLine.args) }
// Args returns the non-flag arguments.
func (f *FlagSet) Args() []string { return f.args }
// Args returns the non-flag command-line arguments.
func Args() []string { return CommandLine.args }
// Var defines a flag with the specified name and usage string. The type and
// value of the flag are represented by the first argument, of type Value, which
// typically holds a user-defined implementation of Value. For instance, the
// caller could create a flag that turns a comma-separated string into a slice
// of strings by giving the slice the methods of Value; in particular, Set would
// decompose the comma-separated string into the slice.
func (f *FlagSet) Var(value Value, name string, usage string) {
// Remember the default value as a string; it won't change.
flag := &Flag{name, usage, value, value.String()}
_, alreadythere := f.formal[name]
if alreadythere {
var msg string
if f.name == "" {
msg = fmt.Sprintf("flag redefined: %s", name)
} else {
msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)
}
fmt.Fprintln(f.Output(), msg)
panic(msg) // Happens only if flags are declared with identical names
}
if f.formal == nil {
f.formal = make(map[string]*Flag)
}
f.formal[name] = flag
}
// Var defines a flag with the specified name and usage string. The type and
// value of the flag are represented by the first argument, of type Value, which
// typically holds a user-defined implementation of Value. For instance, the
// caller could create a flag that turns a comma-separated string into a slice
// of strings by giving the slice the methods of Value; in particular, Set would
// decompose the comma-separated string into the slice.
func Var(value Value, name string, usage string) {
CommandLine.Var(value, name, usage)
}
// failf prints to standard error a formatted error and usage message and
// returns the error.
func (f *FlagSet) failf(format string, a ...interface{}) error {
err := fmt.Errorf(format, a...)
fmt.Fprintln(f.Output(), err)
f.usage()
return err
}
// usage calls the Usage method for the flag set if one is specified,
// or the appropriate default usage function otherwise.
func (f *FlagSet) usage() {
if f.Usage == nil {
f.defaultUsage()
} else {
f.Usage()
}
}
// parseOne parses one flag. It reports whether a flag was seen.
func (f *FlagSet) parseOne() (bool, error) {
if len(f.args) == 0 {
return false, nil
}
s := f.args[0]
if len(s) < 2 || s[0] != '-' {
return false, nil
}
numMinuses := 1
if s[1] == '-' {
numMinuses++
if len(s) == 2 { // "--" terminates the flags
f.args = f.args[1:]
return false, nil
}
}
name := s[numMinuses:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
return false, f.failf("bad flag syntax: %s", s)
}
// it's a flag. does it have an argument?
f.args = f.args[1:]
hasValue := false
value := ""
for i := 1; i < len(name); i++ { // equals cannot be first
if name[i] == '=' {
value = name[i+1:]
hasValue = true
name = name[0:i]
break
}
}
m := f.formal
flag, alreadythere := m[name] // BUG
if !alreadythere {
if name == "help" || name == "h" { // special case for nice help message.
f.usage()
return false, ErrHelp
}
return false, f.failf("flag provided but not defined: -%s", name)
}
if IsBoolValue(flag.Value) { // special case: doesn't need an arg
if hasValue {
if err := flag.Value.Set(value); err != nil {
return false, f.failf(invalidValueTemplate, value, name, err)
}
} else {
if err := flag.Value.Set("true"); err != nil {
return false, f.failf("invalid boolean flag %s: %v", name, err)
}
}
} else {
// It must have a value, which might be the next argument.
if !hasValue && len(f.args) > 0 {
// value is the next arg
hasValue = true
value, f.args = f.args[0], f.args[1:]
}
if !hasValue {
return false, f.failf("flag needs an argument: -%s", name)
}
if err := flag.Value.Set(value); err != nil {
return false, f.failf(invalidValueTemplate, value, name, err)
}
}
if f.actual == nil {
f.actual = make(map[string]*Flag)
}
f.actual[name] = flag
return true, nil
}
// Parse parses flag definitions from the argument list, which should not
// include the command name. Must be called after all flags in the FlagSet
// are defined and before flags are accessed by the program.
// The return value will be ErrHelp if -help or -h were set but not defined.
func (f *FlagSet) Parse(arguments []string) error {
f.parsed = true
f.args = arguments
for {
seen, err := f.parseOne()
if seen {
continue
}
if err == nil {
break
}
switch f.errorHandling {
case ContinueOnError:
return err
case ExitOnError:
if err == ErrHelp {
os.Exit(0)
}
os.Exit(2)
case PanicOnError:
panic(err)
}
}
return nil
}
// Parsed reports whether f.Parse has been called.
func (f *FlagSet) Parsed() bool {
return f.parsed
}
// Parse parses the command-line flags from os.Args[1:]. Must be called
// after all flags are defined and before flags are accessed by the program.
func Parse() {
// Ignore errors; CommandLine is set for ExitOnError.
CommandLine.Parse(os.Args[1:])
}
// Parsed reports whether the command-line flags have been parsed.
func Parsed() bool {
return CommandLine.Parsed()
}
// CommandLine is the default set of command-line flags, parsed from os.Args.
// The top-level functions such as BoolVar, Arg, and so on are wrappers for the
// methods of CommandLine.
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
func init() {
// Override generic FlagSet default Usage with call to global Usage.
// Note: This is not CommandLine.Usage = Usage,
// because we want any eventual call to use any updated value of Usage,
// not the value it has when this line is run.
CommandLine.Usage = commandLineUsage
}
func commandLineUsage() {
Usage()
}
// NewFlagSet returns a new, empty flag set with the specified name and
// error handling property. If the name is not empty, it will be printed
// in the default usage message and in error messages.
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
f := &FlagSet{
name: name,
errorHandling: errorHandling,
}
f.Usage = f.defaultUsage
return f
}
// Init sets the name and error handling property for a flag set.
// By default, the zero FlagSet uses an empty name and the
// ContinueOnError error handling policy.
func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
f.name = name
f.errorHandling = errorHandling
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// BoolVar defines a bool flag with specified name, default value, and usage string.
// The argument p points to a bool variable in which to store the value of the flag.
func (f *FlagSet) BoolVar(ptr *bool, name string, value bool, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
return f.Generic(name, value, usage).(*bool)
}
// BoolVar defines a bool flag with specified name, default value, and usage string.
// The argument p points to a bool variable in which to store the value of the flag.
func BoolVar(ptr *bool, name string, value bool, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func Bool(name string, value bool, usage string) *bool {
return CommandLine.Generic(name, value, usage).(*bool)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// BoolSliceVar defines a []bool flag with specified name, default value, and usage string.
// The argument p points to a []bool variable in which to store the value of the flag.
func (f *FlagSet) BoolSliceVar(ptr *[]bool, name string, value []bool, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// BoolSlice defines a []bool flag with specified name, default value, and usage string.
// The return value is the address of a []bool variable that stores the value of the flag.
func (f *FlagSet) BoolSlice(name string, value []bool, usage string) *[]bool {
return f.Generic(name, value, usage).(*[]bool)
}
// BoolSliceVar defines a []bool flag with specified name, default value, and usage string.
// The argument p points to a []bool variable in which to store the value of the flag.
func BoolSliceVar(ptr *[]bool, name string, value []bool, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// BoolSlice defines a []bool flag with specified name, default value, and usage string.
// The return value is the address of a []bool variable that stores the value of the flag.
func BoolSlice(name string, value []bool, usage string) *[]bool {
return CommandLine.Generic(name, value, usage).(*[]bool)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
// The argument p points to a time.Duration variable in which to store the value of the flag.
func (f *FlagSet) DurationVar(ptr *time.Duration, name string, value time.Duration, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Duration defines a time.Duration flag with specified name, default value, and usage string.
// The return value is the address of a time.Duration variable that stores the value of the flag.
func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration {
return f.Generic(name, value, usage).(*time.Duration)
}
// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
// The argument p points to a time.Duration variable in which to store the value of the flag.
func DurationVar(ptr *time.Duration, name string, value time.Duration, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Duration defines a time.Duration flag with specified name, default value, and usage string.
// The return value is the address of a time.Duration variable that stores the value of the flag.
func Duration(name string, value time.Duration, usage string) *time.Duration {
return CommandLine.Generic(name, value, usage).(*time.Duration)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// DurationSliceVar defines a []time.Duration flag with specified name, default value, and usage string.
// The argument p points to a []time.Duration variable in which to store the value of the flag.
func (f *FlagSet) DurationSliceVar(ptr *[]time.Duration, name string, value []time.Duration, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string.
// The return value is the address of a []time.Duration variable that stores the value of the flag.
func (f *FlagSet) DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration {
return f.Generic(name, value, usage).(*[]time.Duration)
}
// DurationSliceVar defines a []time.Duration flag with specified name, default value, and usage string.
// The argument p points to a []time.Duration variable in which to store the value of the flag.
func DurationSliceVar(ptr *[]time.Duration, name string, value []time.Duration, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string.
// The return value is the address of a []time.Duration variable that stores the value of the flag.
func DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration {
return CommandLine.Generic(name, value, usage).(*[]time.Duration)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// Float64Var defines a float64 flag with specified name, default value, and usage string.
// The argument p points to a float64 variable in which to store the value of the flag.
func (f *FlagSet) Float64Var(ptr *float64, name string, value float64, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Float64 defines a float64 flag with specified name, default value, and usage string.
// The return value is the address of a float64 variable that stores the value of the flag.
func (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
return f.Generic(name, value, usage).(*float64)
}
// Float64Var defines a float64 flag with specified name, default value, and usage string.
// The argument p points to a float64 variable in which to store the value of the flag.
func Float64Var(ptr *float64, name string, value float64, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Float64 defines a float64 flag with specified name, default value, and usage string.
// The return value is the address of a float64 variable that stores the value of the flag.
func Float64(name string, value float64, usage string) *float64 {
return CommandLine.Generic(name, value, usage).(*float64)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// Float64SliceVar defines a []float64 flag with specified name, default value, and usage string.
// The argument p points to a []float64 variable in which to store the value of the flag.
func (f *FlagSet) Float64SliceVar(ptr *[]float64, name string, value []float64, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Float64Slice defines a []float64 flag with specified name, default value, and usage string.
// The return value is the address of a []float64 variable that stores the value of the flag.
func (f *FlagSet) Float64Slice(name string, value []float64, usage string) *[]float64 {
return f.Generic(name, value, usage).(*[]float64)
}
// Float64SliceVar defines a []float64 flag with specified name, default value, and usage string.
// The argument p points to a []float64 variable in which to store the value of the flag.
func Float64SliceVar(ptr *[]float64, name string, value []float64, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Float64Slice defines a []float64 flag with specified name, default value, and usage string.
// The return value is the address of a []float64 variable that stores the value of the flag.
func Float64Slice(name string, value []float64, usage string) *[]float64 {
return CommandLine.Generic(name, value, usage).(*[]float64)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// IntVar defines a int flag with specified name, default value, and usage string.
// The argument p points to a int variable in which to store the value of the flag.
func (f *FlagSet) IntVar(ptr *int, name string, value int, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Int defines a int flag with specified name, default value, and usage string.
// The return value is the address of a int variable that stores the value of the flag.
func (f *FlagSet) Int(name string, value int, usage string) *int {
return f.Generic(name, value, usage).(*int)
}
// IntVar defines a int flag with specified name, default value, and usage string.
// The argument p points to a int variable in which to store the value of the flag.
func IntVar(ptr *int, name string, value int, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Int defines a int flag with specified name, default value, and usage string.
// The return value is the address of a int variable that stores the value of the flag.
func Int(name string, value int, usage string) *int {
return CommandLine.Generic(name, value, usage).(*int)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// Int64Var defines a int64 flag with specified name, default value, and usage string.
// The argument p points to a int64 variable in which to store the value of the flag.
func (f *FlagSet) Int64Var(ptr *int64, name string, value int64, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Int64 defines a int64 flag with specified name, default value, and usage string.
// The return value is the address of a int64 variable that stores the value of the flag.
func (f *FlagSet) Int64(name string, value int64, usage string) *int64 {
return f.Generic(name, value, usage).(*int64)
}
// Int64Var defines a int64 flag with specified name, default value, and usage string.
// The argument p points to a int64 variable in which to store the value of the flag.
func Int64Var(ptr *int64, name string, value int64, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Int64 defines a int64 flag with specified name, default value, and usage string.
// The return value is the address of a int64 variable that stores the value of the flag.
func Int64(name string, value int64, usage string) *int64 {
return CommandLine.Generic(name, value, usage).(*int64)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// Int64SliceVar defines a []int64 flag with specified name, default value, and usage string.
// The argument p points to a []int64 variable in which to store the value of the flag.
func (f *FlagSet) Int64SliceVar(ptr *[]int64, name string, value []int64, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Int64Slice defines a []int64 flag with specified name, default value, and usage string.
// The return value is the address of a []int64 variable that stores the value of the flag.
func (f *FlagSet) Int64Slice(name string, value []int64, usage string) *[]int64 {
return f.Generic(name, value, usage).(*[]int64)
}
// Int64SliceVar defines a []int64 flag with specified name, default value, and usage string.
// The argument p points to a []int64 variable in which to store the value of the flag.
func Int64SliceVar(ptr *[]int64, name string, value []int64, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Int64Slice defines a []int64 flag with specified name, default value, and usage string.
// The return value is the address of a []int64 variable that stores the value of the flag.
func Int64Slice(name string, value []int64, usage string) *[]int64 {
return CommandLine.Generic(name, value, usage).(*[]int64)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// IntSliceVar defines a []int flag with specified name, default value, and usage string.
// The argument p points to a []int variable in which to store the value of the flag.
func (f *FlagSet) IntSliceVar(ptr *[]int, name string, value []int, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// IntSlice defines a []int flag with specified name, default value, and usage string.
// The return value is the address of a []int variable that stores the value of the flag.
func (f *FlagSet) IntSlice(name string, value []int, usage string) *[]int {
return f.Generic(name, value, usage).(*[]int)
}
// IntSliceVar defines a []int flag with specified name, default value, and usage string.
// The argument p points to a []int variable in which to store the value of the flag.
func IntSliceVar(ptr *[]int, name string, value []int, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// IntSlice defines a []int flag with specified name, default value, and usage string.
// The return value is the address of a []int variable that stores the value of the flag.
func IntSlice(name string, value []int, usage string) *[]int {
return CommandLine.Generic(name, value, usage).(*[]int)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
func (f *FlagSet) StringVar(ptr *string, name string, value string, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// String defines a string flag with specified name, default value, and usage string.
// The return value is the address of a string variable that stores the value of the flag.
func (f *FlagSet) String(name string, value string, usage string) *string {
return f.Generic(name, value, usage).(*string)
}
// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
func StringVar(ptr *string, name string, value string, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// String defines a string flag with specified name, default value, and usage string.
// The return value is the address of a string variable that stores the value of the flag.
func String(name string, value string, usage string) *string {
return CommandLine.Generic(name, value, usage).(*string)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// StringSliceVar defines a []string flag with specified name, default value, and usage string.
// The argument p points to a []string variable in which to store the value of the flag.
func (f *FlagSet) StringSliceVar(ptr *[]string, name string, value []string, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// StringSlice defines a []string flag with specified name, default value, and usage string.
// The return value is the address of a []string variable that stores the value of the flag.
func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string {
return f.Generic(name, value, usage).(*[]string)
}
// StringSliceVar defines a []string flag with specified name, default value, and usage string.
// The argument p points to a []string variable in which to store the value of the flag.
func StringSliceVar(ptr *[]string, name string, value []string, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// StringSlice defines a []string flag with specified name, default value, and usage string.
// The return value is the address of a []string variable that stores the value of the flag.
func StringSlice(name string, value []string, usage string) *[]string {
return CommandLine.Generic(name, value, usage).(*[]string)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// TimeVar defines a time.Time flag with specified name, default value, and usage string.
// The argument p points to a time.Time variable in which to store the value of the flag.
func (f *FlagSet) TimeVar(ptr *time.Time, name string, value time.Time, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Time defines a time.Time flag with specified name, default value, and usage string.
// The return value is the address of a time.Time variable that stores the value of the flag.
func (f *FlagSet) Time(name string, value time.Time, usage string) *time.Time {
return f.Generic(name, value, usage).(*time.Time)
}
// TimeVar defines a time.Time flag with specified name, default value, and usage string.
// The argument p points to a time.Time variable in which to store the value of the flag.
func TimeVar(ptr *time.Time, name string, value time.Time, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Time defines a time.Time flag with specified name, default value, and usage string.
// The return value is the address of a time.Time variable that stores the value of the flag.
func Time(name string, value time.Time, usage string) *time.Time {
return CommandLine.Generic(name, value, usage).(*time.Time)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// TimeSliceVar defines a []time.Time flag with specified name, default value, and usage string.
// The argument p points to a []time.Time variable in which to store the value of the flag.
func (f *FlagSet) TimeSliceVar(ptr *[]time.Time, name string, value []time.Time, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// TimeSlice defines a []time.Time flag with specified name, default value, and usage string.
// The return value is the address of a []time.Time variable that stores the value of the flag.
func (f *FlagSet) TimeSlice(name string, value []time.Time, usage string) *[]time.Time {
return f.Generic(name, value, usage).(*[]time.Time)
}
// TimeSliceVar defines a []time.Time flag with specified name, default value, and usage string.
// The argument p points to a []time.Time variable in which to store the value of the flag.
func TimeSliceVar(ptr *[]time.Time, name string, value []time.Time, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// TimeSlice defines a []time.Time flag with specified name, default value, and usage string.
// The return value is the address of a []time.Time variable that stores the value of the flag.
func TimeSlice(name string, value []time.Time, usage string) *[]time.Time {
return CommandLine.Generic(name, value, usage).(*[]time.Time)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// UintVar defines a uint flag with specified name, default value, and usage string.
// The argument p points to a uint variable in which to store the value of the flag.
func (f *FlagSet) UintVar(ptr *uint, name string, value uint, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Uint defines a uint flag with specified name, default value, and usage string.
// The return value is the address of a uint variable that stores the value of the flag.
func (f *FlagSet) Uint(name string, value uint, usage string) *uint {
return f.Generic(name, value, usage).(*uint)
}
// UintVar defines a uint flag with specified name, default value, and usage string.
// The argument p points to a uint variable in which to store the value of the flag.
func UintVar(ptr *uint, name string, value uint, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Uint defines a uint flag with specified name, default value, and usage string.
// The return value is the address of a uint variable that stores the value of the flag.
func Uint(name string, value uint, usage string) *uint {
return CommandLine.Generic(name, value, usage).(*uint)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
// The argument p points to a uint64 variable in which to store the value of the flag.
func (f *FlagSet) Uint64Var(ptr *uint64, name string, value uint64, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Uint64 defines a uint64 flag with specified name, default value, and usage string.
// The return value is the address of a uint64 variable that stores the value of the flag.
func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 {
return f.Generic(name, value, usage).(*uint64)
}
// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
// The argument p points to a uint64 variable in which to store the value of the flag.
func Uint64Var(ptr *uint64, name string, value uint64, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Uint64 defines a uint64 flag with specified name, default value, and usage string.
// The return value is the address of a uint64 variable that stores the value of the flag.
func Uint64(name string, value uint64, usage string) *uint64 {
return CommandLine.Generic(name, value, usage).(*uint64)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// Uint64SliceVar defines a []uint64 flag with specified name, default value, and usage string.
// The argument p points to a []uint64 variable in which to store the value of the flag.
func (f *FlagSet) Uint64SliceVar(ptr *[]uint64, name string, value []uint64, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// Uint64Slice defines a []uint64 flag with specified name, default value, and usage string.
// The return value is the address of a []uint64 variable that stores the value of the flag.
func (f *FlagSet) Uint64Slice(name string, value []uint64, usage string) *[]uint64 {
return f.Generic(name, value, usage).(*[]uint64)
}
// Uint64SliceVar defines a []uint64 flag with specified name, default value, and usage string.
// The argument p points to a []uint64 variable in which to store the value of the flag.
func Uint64SliceVar(ptr *[]uint64, name string, value []uint64, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Uint64Slice defines a []uint64 flag with specified name, default value, and usage string.
// The return value is the address of a []uint64 variable that stores the value of the flag.
func Uint64Slice(name string, value []uint64, usage string) *[]uint64 {
return CommandLine.Generic(name, value, usage).(*[]uint64)
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"time"
)
var _ = time.Time{}
// UintSliceVar defines a []uint flag with specified name, default value, and usage string.
// The argument p points to a []uint variable in which to store the value of the flag.
func (f *FlagSet) UintSliceVar(ptr *[]uint, name string, value []uint, usage string) {
f.GenericVar(ptr, name, value, usage)
}
// UintSlice defines a []uint flag with specified name, default value, and usage string.
// The return value is the address of a []uint variable that stores the value of the flag.
func (f *FlagSet) UintSlice(name string, value []uint, usage string) *[]uint {
return f.Generic(name, value, usage).(*[]uint)
}
// UintSliceVar defines a []uint flag with specified name, default value, and usage string.
// The argument p points to a []uint variable in which to store the value of the flag.
func UintSliceVar(ptr *[]uint, name string, value []uint, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// UintSlice defines a []uint flag with specified name, default value, and usage string.
// The return value is the address of a []uint variable that stores the value of the flag.
func UintSlice(name string, value []uint, usage string) *[]uint {
return CommandLine.Generic(name, value, usage).(*[]uint)
}

View File

@ -1,83 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"fmt"
"time"
"github.com/rancher/spur/generic"
)
var _ = time.Time{}
// GenericValue takes a pointer to a generic type
type GenericValue struct {
ptr interface{}
set bool
}
// NewGenericValue returns a flag.Value given a pointer
func NewGenericValue(ptr interface{}) Value {
generic.PtrPanic(ptr)
return &GenericValue{ptr: ptr}
}
// Get returns the contents of the stored pointer
func (v *GenericValue) Get() interface{} {
return generic.ValueOfPtr(v.ptr)
}
// Set will convert a given value to the type of our pointer
// and store the new value
func (v *GenericValue) Set(value interface{}) error {
if generic.IsSlice(v.Get()) && !v.set {
// If this is a slice and has not already been set then
// clear any existing value
generic.Set(v.ptr, generic.Zero(v.Get()))
v.set = true
}
val, err := generic.Convert(v.Get(), value)
if err != nil {
return err
}
generic.Set(v.ptr, val)
return nil
}
// String returns a string representation of our generic value
func (v *GenericValue) String() string {
return generic.Stringify(v.Get())
}
// GenericVar defines a generic flag with specified name, default value, and usage string.
// The argument p points to a generic variable in which to store the value of the flag.
func (f *FlagSet) GenericVar(ptr interface{}, name string, value interface{}, usage string) {
generic.Set(ptr, value)
f.Var(NewGenericValue(ptr), name, usage)
}
// Generic defines a generic flag with specified name, default value, and usage string.
// The return value is the address of a generic variable that stores the value of the flag.
func (f *FlagSet) Generic(name string, value interface{}, usage string) interface{} {
if value == nil {
panic(fmt.Errorf("creating generic from nil interface %s", name))
}
ptr := generic.New(value)
f.GenericVar(ptr, name, value, usage)
return ptr
}
// GenericVar defines a generic flag with specified name, default value, and usage string.
// The argument p points to a generic variable in which to store the value of the flag.
func GenericVar(ptr interface{}, name string, value interface{}, usage string) {
CommandLine.GenericVar(ptr, name, value, usage)
}
// Generic defines a generic flag with specified name, default value, and usage string.
// The return value is the address of a generic variable that stores the value of the flag.
func Generic(name string, value interface{}, usage string) interface{} {
return CommandLine.Generic(name, value, usage)
}

View File

@ -1,32 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package generic
import (
"errors"
"strconv"
)
// errParse is returned by Set if a flag's value fails to parse, such as with an invalid integer for Int.
// It then gets wrapped through failf to provide more information.
var errParse = errors.New("parse error")
// errRange is returned by Set if a flag's value is out of range.
// It then gets wrapped through failf to provide more information.
var errRange = errors.New("value out of range")
func numError(err error) error {
ne, ok := err.(*strconv.NumError)
if !ok {
return err
}
if ne.Err == strconv.ErrSyntax {
return errParse
}
if ne.Err == strconv.ErrRange {
return errRange
}
return err
}

View File

@ -1,241 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package generic
import (
"encoding/json"
"fmt"
"reflect"
"time"
"gopkg.in/yaml.v2"
)
// Marshal is the function used for marshaling slices
var Marshal = json.Marshal
// Unmarshal is the function used for un-marshaling slices
var Unmarshal = yaml.Unmarshal
// ToStringFunc is the function definition for converting types to strings
type ToStringFunc = func(interface{}) (string, bool)
// FromStringFunc is the function definition for converting strings to types
type FromStringFunc = func(string) (interface{}, error)
// ToStringMap provides a mapping of type to string conversion function
var ToStringMap = map[string]ToStringFunc{}
// FromStringMap provides a mapping of string to type conversion function
var FromStringMap = map[string]FromStringFunc{}
// TimeLayouts provides a list of layouts to attempt when converting time strings
var TimeLayouts = []string{
time.RFC3339Nano,
time.RFC3339,
time.UnixDate,
time.RubyDate,
time.ANSIC,
time.RFC822,
time.RFC822Z,
time.RFC850,
time.RFC1123,
time.RFC1123Z,
time.StampNano,
time.StampMicro,
time.StampMilli,
time.Stamp,
time.Kitchen,
}
// ToString is a convenience function for converting types to strings as defined in ToStringMap
func ToString(value interface{}) (string, bool) {
if value == nil {
return "", false
}
if toString := ToStringMap[TypeOf(value).String()]; toString != nil {
return toString(value)
}
return "", false
}
// FromString is a convenience function for converting strings to types as defined in FromStringMap
func FromString(value string, ptr interface{}) error {
PtrPanic(ptr)
typ := reflect.TypeOf(ptr).Elem().String()
fromString := FromStringMap[typ]
if fromString == nil {
return errParse
}
val, err := fromString(value)
if err != nil {
return numError(err)
}
Set(ptr, val)
return nil
}
// TypeOf returns the dereferenced value's type
func TypeOf(value interface{}) reflect.Type {
typ := reflect.TypeOf(value)
if typ != nil && typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
return typ
}
// ElemTypeOf returns the dereferenced value's type or TypeOf is not an Elem
func ElemTypeOf(value interface{}) reflect.Type {
typ := TypeOf(value)
if typ.Kind() == reflect.Slice {
return typ.Elem()
}
return typ
}
// New returns a new reflection with TypeOf value
func New(value interface{}) interface{} {
return reflect.New(TypeOf(value)).Interface()
}
// NewElem returns a new reflection with ElemTypeOf value
func NewElem(value interface{}) interface{} {
return reflect.New(ElemTypeOf(value)).Interface()
}
// Zero returns a zero reflection with TypeOf value
func Zero(value interface{}) interface{} {
return reflect.Zero(TypeOf(value)).Interface()
}
// IsSlice return true if the TypeOf value is a slice
func IsSlice(value interface{}) bool {
if value == nil {
return false
}
return TypeOf(value).Kind() == reflect.Slice
}
// PtrPanic halts execution if the passed ptr is not a pointer
func PtrPanic(ptr interface{}) {
if !IsPtr(ptr) {
panic(fmt.Errorf("expected pointer type, got %s", reflect.TypeOf(ptr).String()))
}
}
// Set will assign the contents of ptr to value
func Set(ptr interface{}, value interface{}) {
PtrPanic(ptr)
if value == nil {
return
}
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(value))
}
// Len returns the length of a slice, or -1 if not a slice
func Len(value interface{}) int {
if !IsSlice(value) {
return -1
}
return reflect.ValueOf(value).Len()
}
// Index will return the value of a slice at a given index
func Index(value interface{}, i int) interface{} {
if !IsSlice(value) {
return nil
}
return reflect.ValueOf(value).Index(i).Interface()
}
// Append will append an element onto a generic slice
func Append(slice interface{}, elem interface{}) interface{} {
return reflect.Append(reflect.ValueOf(slice), reflect.ValueOf(elem)).Interface()
}
// IsPtr returns true if the given value is of kind reflect.Ptr
func IsPtr(value interface{}) bool {
if value == nil {
return false
}
return reflect.TypeOf(value).Kind() == reflect.Ptr
}
// ValueOfPtr returns the contents of a pointer, or the given value if not a pointer
func ValueOfPtr(value interface{}) interface{} {
if !IsPtr(value) {
return value
}
elem := reflect.ValueOf(value).Elem()
if !elem.IsValid() {
return nil
}
return elem.Interface()
}
// Convert will return a new result of type src, where value is converted to the type
// of src or appended if src is a slice and value is an element
func Convert(src interface{}, value interface{}) (interface{}, error) {
// Convert an element
elem, err := ConvertElem(src, value)
if !IsSlice(src) {
// Return value and error if not a slice
return elem, err
}
// Try deserializing as string
if s, ok := value.(string); ok {
val := New(src)
if err := Unmarshal([]byte(s), val); err == nil {
return ValueOfPtr(val), nil
}
}
// If no error from converting element return appended value
if err == nil {
return Append(src, elem), nil
}
// Try evaluating value as a slice of interfaces
otherValue, ok := value.([]interface{})
if !ok {
return nil, errParse
}
// Create a new slice and append each converted element
slice := Zero(src)
for _, other := range otherValue {
elem, err := ConvertElem(src, other)
if err != nil {
return nil, err
}
slice = Append(slice, elem)
}
return slice, nil
}
// ConvertElem will return a new result, where value is converted to the type
// of src or returned as an element if src is a slice
func ConvertElem(src interface{}, value interface{}) (interface{}, error) {
// Get our value as a string
s, ok := value.(string)
if !ok {
if s, ok = ToString(value); !ok {
return nil, errParse
}
}
// Return a new value from the string
ptr := NewElem(src)
err := FromString(s, ptr)
return ValueOfPtr(ptr), err
}
// Stringify returns the ToString version of the value, or the Marshaled version
// in the case of slices, otherwise panic if cannot be converted to string
func Stringify(value interface{}) string {
if s, ok := ToString(value); ok {
return s
}
if b, err := Marshal(value); err == nil {
return string(b)
}
panic(errParse)
}

View File

@ -1,57 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package generic
import (
"strconv"
"time"
)
func init() {
FromStringMap["string"] = func(s string) (interface{}, error) {
return s, nil
}
FromStringMap["bool"] = func(s string) (interface{}, error) {
if s == "" {
s = "false"
}
v, err := strconv.ParseBool(s)
return bool(v), err
}
FromStringMap["int"] = func(s string) (interface{}, error) {
v, err := strconv.ParseInt(s, 0, strconv.IntSize)
return int(v), err
}
FromStringMap["int64"] = func(s string) (interface{}, error) {
v, err := strconv.ParseInt(s, 0, 64)
return int64(v), err
}
FromStringMap["uint"] = func(s string) (interface{}, error) {
v, err := strconv.ParseUint(s, 0, strconv.IntSize)
return uint(v), err
}
FromStringMap["uint64"] = func(s string) (interface{}, error) {
v, err := strconv.ParseUint(s, 0, 64)
return uint64(v), err
}
FromStringMap["float64"] = func(s string) (interface{}, error) {
v, err := strconv.ParseFloat(s, 64)
return float64(v), err
}
FromStringMap["time.Duration"] = func(s string) (interface{}, error) {
if v, err := time.ParseDuration(s); err == nil {
return time.Duration(v), nil
}
return nil, errParse
}
FromStringMap["time.Time"] = func(s string) (interface{}, error) {
for _, layout := range TimeLayouts {
if v, err := time.Parse(layout, s); err == nil {
return time.Time(v), nil
}
}
return nil, errParse
}
}

View File

@ -1,43 +0,0 @@
// Copyright 2020 Rancher Labs, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package generic
import (
"strconv"
"time"
)
func init() {
ToStringMap["string"] = func(value interface{}) (string, bool) {
return value.(string), true
}
ToStringMap["bool"] = func(value interface{}) (string, bool) {
return strconv.FormatBool(value.(bool)), true
}
ToStringMap["int"] = func(value interface{}) (string, bool) {
return strconv.Itoa(value.(int)), true
}
ToStringMap["int64"] = func(value interface{}) (string, bool) {
return strconv.FormatInt(value.(int64), 10), true
}
ToStringMap["uint"] = func(value interface{}) (string, bool) {
return strconv.FormatUint(uint64(value.(uint)), 10), true
}
ToStringMap["uint64"] = func(value interface{}) (string, bool) {
return strconv.FormatUint(value.(uint64), 10), true
}
ToStringMap["float64"] = func(value interface{}) (string, bool) {
return strconv.FormatFloat(value.(float64), 'g', -1, 64), true
}
ToStringMap["time.Duration"] = func(value interface{}) (string, bool) {
return value.(time.Duration).String(), true
}
ToStringMap["time.Time"] = func(value interface{}) (string, bool) {
if len(TimeLayouts) > 0 {
return value.(time.Time).Format(TimeLayouts[0]), true
}
return "", false
}
}

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016 Jeremy Saenz & Contributors
Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -0,0 +1,274 @@
package assert
import (
"fmt"
"reflect"
)
type CompareType int
const (
compareLess CompareType = iota - 1
compareEqual
compareGreater
)
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
switch kind {
case reflect.Int:
{
intobj1 := obj1.(int)
intobj2 := obj2.(int)
if intobj1 > intobj2 {
return compareGreater, true
}
if intobj1 == intobj2 {
return compareEqual, true
}
if intobj1 < intobj2 {
return compareLess, true
}
}
case reflect.Int8:
{
int8obj1 := obj1.(int8)
int8obj2 := obj2.(int8)
if int8obj1 > int8obj2 {
return compareGreater, true
}
if int8obj1 == int8obj2 {
return compareEqual, true
}
if int8obj1 < int8obj2 {
return compareLess, true
}
}
case reflect.Int16:
{
int16obj1 := obj1.(int16)
int16obj2 := obj2.(int16)
if int16obj1 > int16obj2 {
return compareGreater, true
}
if int16obj1 == int16obj2 {
return compareEqual, true
}
if int16obj1 < int16obj2 {
return compareLess, true
}
}
case reflect.Int32:
{
int32obj1 := obj1.(int32)
int32obj2 := obj2.(int32)
if int32obj1 > int32obj2 {
return compareGreater, true
}
if int32obj1 == int32obj2 {
return compareEqual, true
}
if int32obj1 < int32obj2 {
return compareLess, true
}
}
case reflect.Int64:
{
int64obj1 := obj1.(int64)
int64obj2 := obj2.(int64)
if int64obj1 > int64obj2 {
return compareGreater, true
}
if int64obj1 == int64obj2 {
return compareEqual, true
}
if int64obj1 < int64obj2 {
return compareLess, true
}
}
case reflect.Uint:
{
uintobj1 := obj1.(uint)
uintobj2 := obj2.(uint)
if uintobj1 > uintobj2 {
return compareGreater, true
}
if uintobj1 == uintobj2 {
return compareEqual, true
}
if uintobj1 < uintobj2 {
return compareLess, true
}
}
case reflect.Uint8:
{
uint8obj1 := obj1.(uint8)
uint8obj2 := obj2.(uint8)
if uint8obj1 > uint8obj2 {
return compareGreater, true
}
if uint8obj1 == uint8obj2 {
return compareEqual, true
}
if uint8obj1 < uint8obj2 {
return compareLess, true
}
}
case reflect.Uint16:
{
uint16obj1 := obj1.(uint16)
uint16obj2 := obj2.(uint16)
if uint16obj1 > uint16obj2 {
return compareGreater, true
}
if uint16obj1 == uint16obj2 {
return compareEqual, true
}
if uint16obj1 < uint16obj2 {
return compareLess, true
}
}
case reflect.Uint32:
{
uint32obj1 := obj1.(uint32)
uint32obj2 := obj2.(uint32)
if uint32obj1 > uint32obj2 {
return compareGreater, true
}
if uint32obj1 == uint32obj2 {
return compareEqual, true
}
if uint32obj1 < uint32obj2 {
return compareLess, true
}
}
case reflect.Uint64:
{
uint64obj1 := obj1.(uint64)
uint64obj2 := obj2.(uint64)
if uint64obj1 > uint64obj2 {
return compareGreater, true
}
if uint64obj1 == uint64obj2 {
return compareEqual, true
}
if uint64obj1 < uint64obj2 {
return compareLess, true
}
}
case reflect.Float32:
{
float32obj1 := obj1.(float32)
float32obj2 := obj2.(float32)
if float32obj1 > float32obj2 {
return compareGreater, true
}
if float32obj1 == float32obj2 {
return compareEqual, true
}
if float32obj1 < float32obj2 {
return compareLess, true
}
}
case reflect.Float64:
{
float64obj1 := obj1.(float64)
float64obj2 := obj2.(float64)
if float64obj1 > float64obj2 {
return compareGreater, true
}
if float64obj1 == float64obj2 {
return compareEqual, true
}
if float64obj1 < float64obj2 {
return compareLess, true
}
}
case reflect.String:
{
stringobj1 := obj1.(string)
stringobj2 := obj2.(string)
if stringobj1 > stringobj2 {
return compareGreater, true
}
if stringobj1 == stringobj2 {
return compareEqual, true
}
if stringobj1 < stringobj2 {
return compareLess, true
}
}
}
return compareEqual, false
}
// Greater asserts that the first element is greater than the second
//
// assert.Greater(t, 2, 1)
// assert.Greater(t, float64(2), float64(1))
// assert.Greater(t, "b", "a")
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
}
// GreaterOrEqual asserts that the first element is greater than or equal to the second
//
// assert.GreaterOrEqual(t, 2, 1)
// assert.GreaterOrEqual(t, 2, 2)
// assert.GreaterOrEqual(t, "b", "a")
// assert.GreaterOrEqual(t, "b", "b")
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
}
// Less asserts that the first element is less than the second
//
// assert.Less(t, 1, 2)
// assert.Less(t, float64(1), float64(2))
// assert.Less(t, "a", "b")
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
}
// LessOrEqual asserts that the first element is less than or equal to the second
//
// assert.LessOrEqual(t, 1, 2)
// assert.LessOrEqual(t, 2, 2)
// assert.LessOrEqual(t, "a", "b")
// assert.LessOrEqual(t, "b", "b")
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
}
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
e1Kind := reflect.ValueOf(e1).Kind()
e2Kind := reflect.ValueOf(e2).Kind()
if e1Kind != e2Kind {
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
compareResult, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if !containsValue(allowedComparesResults, compareResult) {
return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...)
}
return true
}
func containsValue(values []CompareType, value CompareType) bool {
for _, v := range values {
if v == value {
return true
}
}
return false
}

View File

@ -0,0 +1,644 @@
/*
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
* THIS FILE MUST NOT BE EDITED BY HAND
*/
package assert
import (
http "net/http"
url "net/url"
time "time"
)
// Conditionf uses a Comparison to assert a complex condition.
func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Condition(t, comp, append([]interface{}{msg}, args...)...)
}
// Containsf asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Contains(t, s, contains, append([]interface{}{msg}, args...)...)
}
// DirExistsf checks whether a directory exists in the given path. It also fails
// if the path is a file rather a directory or there is an error checking whether it exists.
func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return DirExists(t, path, append([]interface{}{msg}, args...)...)
}
// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should match.
//
// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
}
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// assert.Emptyf(t, obj, "error message %s", "formatted")
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Empty(t, object, append([]interface{}{msg}, args...)...)
}
// Equalf asserts that two objects are equal.
//
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
//
// Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses). Function equality
// cannot be determined and will always fail.
func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Equal(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
}
// EqualValuesf asserts that two objects are equal or convertable to the same types
// and equal.
//
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`).
//
// actualObj, err := SomeFunction()
// if assert.Errorf(t, err, "error message %s", "formatted") {
// assert.Equal(t, expectedErrorf, err)
// }
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Error(t, err, append([]interface{}{msg}, args...)...)
}
// Eventuallyf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick.
//
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
}
// Exactlyf asserts that two objects are equal in value and type.
//
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Failf reports a failure through
func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, failureMessage, append([]interface{}{msg}, args...)...)
}
// FailNowf fails test
func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...)
}
// Falsef asserts that the specified value is false.
//
// assert.Falsef(t, myBool, "error message %s", "formatted")
func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return False(t, value, append([]interface{}{msg}, args...)...)
}
// FileExistsf checks whether a file exists in the given path. It also fails if
// the path points to a directory or there is an error when trying to check the file.
func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return FileExists(t, path, append([]interface{}{msg}, args...)...)
}
// Greaterf asserts that the first element is greater than the second
//
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Greater(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// GreaterOrEqualf asserts that the first element is greater than or equal to the second
//
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// HTTPBodyContainsf asserts that a specified handler returns a
// body that contains a string.
//
// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
}
// HTTPBodyNotContainsf asserts that a specified handler returns a
// body that does not contain a string.
//
// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
}
// HTTPErrorf asserts that a specified handler returns an error status code.
//
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
}
// HTTPRedirectf asserts that a specified handler returns a redirect status code.
//
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
}
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
//
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...)
}
// HTTPSuccessf asserts that a specified handler returns a success status code.
//
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
}
// Implementsf asserts that an object is implemented by the specified interface.
//
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...)
}
// InDeltaf asserts that the two numerals are within delta of each other.
//
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// InDeltaSlicef is the same as InDelta, except it compares two slices.
func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// InEpsilonf asserts that expected and actual have a relative error less than epsilon
func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
}
// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
}
// IsTypef asserts that the specified objects are of the same type.
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...)
}
// JSONEqf asserts that two JSON strings are equivalent.
//
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Lenf asserts that the specified object has specific length.
// Lenf also fails if the object has a type that len() not accept.
//
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Len(t, object, length, append([]interface{}{msg}, args...)...)
}
// Lessf asserts that the first element is less than the second
//
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Less(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// LessOrEqualf asserts that the first element is less than or equal to the second
//
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick.
//
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
}
// Nilf asserts that the specified object is nil.
//
// assert.Nilf(t, err, "error message %s", "formatted")
func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Nil(t, object, append([]interface{}{msg}, args...)...)
}
// NoDirExistsf checks whether a directory does not exist in the given path.
// It fails if the path points to an existing _directory_ only.
func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NoDirExists(t, path, append([]interface{}{msg}, args...)...)
}
// NoErrorf asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
// assert.Equal(t, expectedObj, actualObj)
// }
func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NoError(t, err, append([]interface{}{msg}, args...)...)
}
// NoFileExistsf checks whether a file does not exist in a given path. It fails
// if the path points to an existing _file_ only.
func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NoFileExists(t, path, append([]interface{}{msg}, args...)...)
}
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
}
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
// assert.Equal(t, "two", obj[1])
// }
func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotEmpty(t, object, append([]interface{}{msg}, args...)...)
}
// NotEqualf asserts that the specified values are NOT equal.
//
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
//
// Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses).
func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
//
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// NotNilf asserts that the specified object is not nil.
//
// assert.NotNilf(t, err, "error message %s", "formatted")
func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotNil(t, object, append([]interface{}{msg}, args...)...)
}
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
//
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotPanics(t, f, append([]interface{}{msg}, args...)...)
}
// NotRegexpf asserts that a specified regexp does not match a string.
//
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...)
}
// NotSamef asserts that two pointers do not reference the same object.
//
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted")
//
// Both arguments must be pointer variables. Pointer variable sameness is
// determined based on the equality of both type and value.
func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// NotSubsetf asserts that the specified list(array, slice...) contains not all
// elements given in the specified subset(array, slice...).
//
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...)
}
// NotZerof asserts that i is not the zero value for its type.
func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotZero(t, i, append([]interface{}{msg}, args...)...)
}
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
//
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Panics(t, f, append([]interface{}{msg}, args...)...)
}
// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc
// panics, and that the recovered panic value is an error that satisfies the
// EqualError comparison.
//
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...)
}
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
// the recovered panic value equals the expected panic value.
//
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
}
// Regexpf asserts that a specified regexp matches a string.
//
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
}
// Samef asserts that two pointers reference the same object.
//
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
//
// Both arguments must be pointer variables. Pointer variable sameness is
// determined based on the equality of both type and value.
func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Same(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Subsetf asserts that the specified list(array, slice...) contains all
// elements given in the specified subset(array, slice...).
//
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Subset(t, list, subset, append([]interface{}{msg}, args...)...)
}
// Truef asserts that the specified value is true.
//
// assert.Truef(t, myBool, "error message %s", "formatted")
func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return True(t, value, append([]interface{}{msg}, args...)...)
}
// WithinDurationf asserts that the two times are within duration delta of each other.
//
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// YAMLEqf asserts that two YAML strings are equivalent.
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Zerof asserts that i is the zero value for its type.
func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Zero(t, i, append([]interface{}{msg}, args...)...)
}

View File

@ -0,0 +1,5 @@
{{.CommentFormat}}
func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool {
if h, ok := t.(tHelper); ok { h.Helper() }
return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}})
}

Some files were not shown because too many files have changed in this diff Show More