diff --git a/go.mod b/go.mod index d23998728..fbcf4c569 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/google/uuid v1.6.0 github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.60.0 + github.com/prometheus/common v0.60.1 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 @@ -15,16 +15,16 @@ require ( golang.org/x/net v0.30.0 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.1 - k8s.io/api v0.31.1 - k8s.io/apimachinery v0.31.1 - k8s.io/client-go v0.31.1 - k8s.io/component-base v0.31.1 - k8s.io/component-helpers v0.31.1 + k8s.io/api v0.31.2 + k8s.io/apimachinery v0.31.2 + k8s.io/client-go v0.31.2 + k8s.io/component-base v0.31.2 + k8s.io/component-helpers v0.31.2 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 - sigs.k8s.io/controller-runtime v0.19.0 - sigs.k8s.io/e2e-framework v0.4.0 + sigs.k8s.io/controller-runtime v0.19.1 + sigs.k8s.io/e2e-framework v0.5.0 ) require ( @@ -60,7 +60,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/vladimirvivien/gexe v0.2.0 // indirect + github.com/vladimirvivien/gexe v0.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.26.0 // indirect diff --git a/go.sum b/go.sum index f5863ee8f..335400249 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -116,8 +116,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/vladimirvivien/gexe v0.2.0 h1:nbdAQ6vbZ+ZNsolCgSVb9Fno60kzSuvtzVh6Ytqi/xY= -github.com/vladimirvivien/gexe v0.2.0/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w= +github.com/vladimirvivien/gexe v0.3.0 h1:4xwiOwGrDob5OMR6E92B9olDXYDglXdHhzR1ggYtWJM= +github.com/vladimirvivien/gexe v0.3.0/go.mod h1:fp7cy60ON1xjhtEI/+bfSEIXX35qgmI+iRYlGOqbBFM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -190,28 +190,28 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= -k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= -k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= -k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= -k8s.io/component-helpers v0.31.1 h1:5hZUf3747atdgtR3gPntrG35rC2CkK7rYq2KUraz6Os= -k8s.io/component-helpers v0.31.1/go.mod h1:ye0Gi8KzFNTfpIuzvVDtxJQMP/0Owkukf1vGf22Hl6U= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= +k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= +k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= +k8s.io/component-helpers v0.31.2 h1:V2yjoNeyg8WfvwrJwzfYz+RUwjlbcAIaDaHEStBbaZM= +k8s.io/component-helpers v0.31.2/go.mod h1:cNz+1ck38R0qWrjcw/rhQgGP6+Gwgw8ngr2ziDNmSXM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= -sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= -sigs.k8s.io/e2e-framework v0.4.0 h1:4yYmFDNNoTnazqmZJXQ6dlQF1vrnDbutmxlyvBpC5rY= -sigs.k8s.io/e2e-framework v0.4.0/go.mod h1:JilFQPF1OL1728ABhMlf9huse7h+uBJDXl9YeTs49A8= +sigs.k8s.io/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk= +sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/e2e-framework v0.5.0 h1:YLhk8R7EHuTFQAe6Fxy5eBzn5Vb+yamR5u8MH1Rq3cE= +sigs.k8s.io/e2e-framework v0.5.0/go.mod h1:jJSH8u2RNmruekUZgHAtmRjb5Wj67GErli9UjLSY7Zc= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/vendor/github.com/vladimirvivien/gexe/TODO.md b/vendor/github.com/vladimirvivien/gexe/TODO.md index 0d9bb6c47..022f44d11 100644 --- a/vendor/github.com/vladimirvivien/gexe/TODO.md +++ b/vendor/github.com/vladimirvivien/gexe/TODO.md @@ -3,7 +3,7 @@ The following are high-level tasks that will be considered for upcoming release * [ ] Support and test on Windows platform (#27) * [ ] Ability to map and access program flags (#20) -* [ ] Provide a simple API to submit HTTP requests and retrieve HTTP documents (think of wget/curl) +* [x] Provide a simple API to submit HTTP requests and retrieve HTTP documents (think of wget/curl) * [x] Support for scatter/gather exec commands * [x] Support for concurrent exec of os commands * [x] Piping/chaining OS exec commands (#29) diff --git a/vendor/github.com/vladimirvivien/gexe/config.go b/vendor/github.com/vladimirvivien/gexe/config.go deleted file mode 100644 index 932f9b875..000000000 --- a/vendor/github.com/vladimirvivien/gexe/config.go +++ /dev/null @@ -1,41 +0,0 @@ -package gexe - -// Config stores configuration -type Config struct { - panicOnErr bool - verbose bool - escapeChar rune -} - -// SetPanicOnErr panics program on any error -func (c *Config) SetPanicOnErr(val bool) *Config { - c.panicOnErr = val - return c -} - -// IsPanicOnErr returns panic-on-error flag -func (c *Config) IsPanicOnErr() bool { - return c.panicOnErr -} - -// SetVerbose sets verbosity -func (c *Config) SetVerbose(val bool) *Config { - c.verbose = val - return c -} - -// IsVerbose returns verbosity flag -func (c *Config) IsVerbose() bool { - return c.verbose -} - -// SetEscapeChar sets the escape char for command-line parsing -func (c *Config) SetEscapeChar(r rune) *Config { - c.escapeChar = r - return c -} - -// GetEscapeChar returns the escape char set for command-line parsing -func (c *Config) GetEscapeChar() rune { - return c.escapeChar -} diff --git a/vendor/github.com/vladimirvivien/gexe/echo.go b/vendor/github.com/vladimirvivien/gexe/echo.go index 53156ca2b..70eac5eb0 100644 --- a/vendor/github.com/vladimirvivien/gexe/echo.go +++ b/vendor/github.com/vladimirvivien/gexe/echo.go @@ -1,6 +1,10 @@ package gexe import ( + "fmt" + "os" + "os/exec" + "github.com/vladimirvivien/gexe/prog" "github.com/vladimirvivien/gexe/vars" ) @@ -16,7 +20,6 @@ type Echo struct { err error vars *vars.Variables // session vars prog *prog.Info - Conf *Config // session config } // New creates a new Echo session @@ -24,7 +27,21 @@ func New() *Echo { e := &Echo{ vars: vars.New(), prog: prog.Prog(), - Conf: &Config{escapeChar: '\\'}, } return e } + +// AddExecPath adds an executable path to PATH +func (e *Echo) AddExecPath(execPath string) { + oldPath := os.Getenv("PATH") + os.Setenv("PATH", fmt.Sprintf("%s%c%s", oldPath, os.PathListSeparator, e.Eval(execPath))) +} + +// ProgAvail returns the full path of the program if found on exec PATH +func (e *Echo) ProgAvail(progName string) string { + path, err := exec.LookPath(e.Eval(progName)) + if err != nil { + return "" + } + return path +} diff --git a/vendor/github.com/vladimirvivien/gexe/exec/builder.go b/vendor/github.com/vladimirvivien/gexe/exec/builder.go index 93709dcde..c1b41f905 100644 --- a/vendor/github.com/vladimirvivien/gexe/exec/builder.go +++ b/vendor/github.com/vladimirvivien/gexe/exec/builder.go @@ -1,7 +1,10 @@ package exec import ( + "fmt" "sync" + + "github.com/vladimirvivien/gexe/vars" ) type CommandPolicy byte @@ -19,29 +22,75 @@ type CommandResult struct { errProcs []*Proc } +// Procs return all executed processes func (cr *CommandResult) Procs() []*Proc { cr.mu.RLock() defer cr.mu.RUnlock() return cr.procs } + +// ErrProcs returns errored processes func (cr *CommandResult) ErrProcs() []*Proc { cr.mu.RLock() defer cr.mu.RUnlock() return cr.errProcs } +// Errs returns all errors +func (cr *CommandResult) Errs() (errs []error) { + cr.mu.RLock() + defer cr.mu.RUnlock() + + for _, proc := range cr.errProcs { + errs = append(errs, fmt.Errorf("%s: %s", proc.Err(), proc.Result())) + } + return +} + +// ErrStrings returns errors as []string +func (cr *CommandResult) ErrStrings() (errStrings []string) { + errs := cr.Errs() + for _, err := range errs { + errStrings = append(errStrings, err.Error()) + } + return +} + +// PipedCommandResult stores results of piped commands type PipedCommandResult struct { procs []*Proc errProcs []*Proc lastProc *Proc } +// Procs return all executed processes in pipe func (cr *PipedCommandResult) Procs() []*Proc { return cr.procs } + +// ErrProcs returns errored piped processes func (cr *PipedCommandResult) ErrProcs() []*Proc { return cr.errProcs } + +// Errs returns all errors +func (cr *PipedCommandResult) Errs() (errs []error) { + for _, proc := range cr.errProcs { + errs = append(errs, fmt.Errorf("%s: %s", proc.Err(), proc.Result())) + } + return +} + +// ErrStrings returns errors as []string +func (cr *PipedCommandResult) ErrStrings() (errStrings []string) { + errs := cr.Errs() + for _, err := range errs { + errStrings = append(errStrings, err.Error()) + } + return +} + +// LastProc executes last executed process func (cr *PipedCommandResult) LastProc() *Proc { procLen := len(cr.procs) if procLen == 0 { @@ -50,21 +99,34 @@ func (cr *PipedCommandResult) LastProc() *Proc { return cr.procs[procLen-1] } +// CommandBuilder is a batch command builder that +// can execute commands using different execution policies (i.e. serial, piped, concurrent) type CommandBuilder struct { cmdPolicy CommandPolicy procs []*Proc + vars *vars.Variables } // Commands creates a *CommandBuilder used to collect // command strings to be executed. func Commands(cmds ...string) *CommandBuilder { cb := new(CommandBuilder) + cb.vars = &vars.Variables{} for _, cmd := range cmds { cb.procs = append(cb.procs, NewProc(cmd)) } return cb } +// CommandsWithVars creates a new CommandBuilder and sets session varialbes for it +func CommandsWithVars(variables *vars.Variables, cmds ...string) *CommandBuilder { + cb := &CommandBuilder{vars: variables} + for _, cmd := range cmds { + cb.procs = append(cb.procs, NewProc(variables.Eval(cmd))) + } + return cb +} + // WithPolicy sets one or more command policy mask values, i.e. (CmdOnErrContinue | CmdExecConcurrent) func (cb *CommandBuilder) WithPolicy(policyMask CommandPolicy) *CommandBuilder { cb.cmdPolicy = policyMask @@ -74,7 +136,7 @@ func (cb *CommandBuilder) WithPolicy(policyMask CommandPolicy) *CommandBuilder { // Add adds a new command string to the builder func (cb *CommandBuilder) Add(cmds ...string) *CommandBuilder { for _, cmd := range cmds { - cb.procs = append(cb.procs, NewProc(cmd)) + cb.procs = append(cb.procs, NewProc(cb.vars.Eval(cmd))) } return cb } diff --git a/vendor/github.com/vladimirvivien/gexe/exec/proc.go b/vendor/github.com/vladimirvivien/gexe/exec/proc.go index 00899a43f..a91db489d 100644 --- a/vendor/github.com/vladimirvivien/gexe/exec/proc.go +++ b/vendor/github.com/vladimirvivien/gexe/exec/proc.go @@ -8,6 +8,8 @@ import ( osexec "os/exec" "strings" "time" + + "github.com/vladimirvivien/gexe/vars" ) // Proc stores process info when running a process @@ -21,6 +23,7 @@ type Proc struct { inputPipe io.WriteCloser cmd *osexec.Cmd process *os.Process + vars *vars.Variables } // NewProc sets up command string to be started as an OS process, however @@ -34,7 +37,6 @@ func NewProc(cmdStr string) *Proc { command := osexec.Command(words[0], words[1:]...) pipeout, outerr := command.StdoutPipe() pipeerr, errerr := command.StderrPipe() - //output := io.MultiReader(pipeout, pipeerr) if outerr != nil || errerr != nil { return &Proc{err: fmt.Errorf("combinedOutput pipe: %s; %s", outerr, errerr)} @@ -51,9 +53,17 @@ func NewProc(cmdStr string) *Proc { errorPipe: pipeerr, inputPipe: pipein, result: new(bytes.Buffer), + vars: &vars.Variables{}, } } +// NewProcWithVars sets up new command string and session variables for a new proc +func NewProcWithVars(cmdStr string, variables *vars.Variables) *Proc { + p := NewProc(variables.Eval(cmdStr)) + p.vars = variables + return p +} + // StartProc starts an OS process (setup a combined output of stdout, stderr) and does not wait for // it to complete. You must follow this with either proc.Wait() to wait for result directly. Otherwise, // call proc.Out() or proc.Result() which automatically waits and gather result. @@ -67,6 +77,13 @@ func StartProc(cmdStr string) *Proc { return proc.Start() } +// StartProcWithVars sets session variables and calls StartProc +func StartProcWithVars(cmdStr string, variables *vars.Variables) *Proc { + proc := StartProc(variables.Eval(cmdStr)) + proc.vars = variables + return proc +} + // RunProc starts a new process and waits for its completion. Use Proc.Out() or Proc.Result() // to access the combined result from stdout and stderr. func RunProc(cmdStr string) *Proc { @@ -78,12 +95,30 @@ func RunProc(cmdStr string) *Proc { return proc } +// RunProcWithVars sets session variables and calls RunProc +func RunProcWithVars(cmdStr string, variables *vars.Variables) *Proc { + proc := RunProc(variables.Eval(cmdStr)) + proc.vars = variables + return proc +} + // Run creates and runs a process and waits for its result (combined stdin,stderr) returned as a string value. // This is equivalent to calling Proc.RunProc() followed by Proc.Result(). func Run(cmdStr string) (result string) { return RunProc(cmdStr).Result() } +// RunWithVars sets session variables and call Run +func RunWithVars(cmdStr string, variables *vars.Variables) string { + return RunProcWithVars(cmdStr, variables).Result() +} + +// SetVars sets session variables for Proc +func (p *Proc) SetVars(variables *vars.Variables) *Proc { + p.vars = variables + return p +} + // Start starts the associated command as an OS process and does not wait for its result. // Ensure proper access to the process' input/output (stdin,stdout,stderr) has been // setup prior to calling p.Start(). @@ -280,3 +315,8 @@ func (p *Proc) GetErrorPipe() io.Reader { func (p *Proc) hasStarted() bool { return (p.cmd.Process != nil && p.cmd.Process.Pid != 0) } + +// Parse parses the command string and returns its tokens +func Parse(cmd string) ([]string, error) { + return parse(cmd) +} diff --git a/vendor/github.com/vladimirvivien/gexe/filesys.go b/vendor/github.com/vladimirvivien/gexe/filesys.go index bc0d91c5a..ca1cde7f7 100644 --- a/vendor/github.com/vladimirvivien/gexe/filesys.go +++ b/vendor/github.com/vladimirvivien/gexe/filesys.go @@ -1,15 +1,53 @@ package gexe import ( + "os" + "github.com/vladimirvivien/gexe/fs" ) -// Read creates an fs.FileReader using the provided path -func (e *Echo) Read(path string) fs.FileReader { - return fs.Read(e.Eval(path)) +// PathExists returns true if path exists. +// All errors causes to return false. +func (e *Echo) PathExists(path string) bool { + return fs.PathWithVars(path, e.vars).Exists() +} + +// MkDir creates a directory at specified path with mode value. +// FSInfo contains information about the path or error if occured +func (e *Echo) MkDir(path string, mode os.FileMode) *fs.FSInfo { + p := fs.PathWithVars(path, e.vars) + return p.MkDir(mode) +} + +// RmPath removes specified path (dir or file). +// Error is returned FSInfo.Err() +func (e *Echo) RmPath(path string) *fs.FSInfo { + p := fs.PathWithVars(path, e.vars) + return p.Remove() +} + +// PathInfo +func (e *Echo) PathInfo(path string) *fs.FSInfo { + return fs.PathWithVars(path, e.vars).Info() +} + +// FileRead provides methods to read file content +// +// FileRead(path).Lines() +func (e *Echo) FileRead(path string) *fs.FileReader { + return fs.PathWithVars(path, e.vars).Read() +} + +// FileWrite provides methods to write content to provided path +// +// FileWrite(path).String("hello world") +func (e *Echo) FileWrite(path string) *fs.FileWriter { + return fs.PathWithVars(path, e.vars).Write() } -// Write creates an fs.FileWriter using the provided path -func (e *Echo) Write(path string) fs.FileWriter { - return fs.Write(e.Eval(path)) +// FileAppend provides methods to append content to provided path +// +// FileAppend(path).String("hello world") +func (e *Echo) FileAppend(path string) *fs.FileWriter { + return fs.PathWithVars(path, e.vars).Append() } diff --git a/vendor/github.com/vladimirvivien/gexe/fs/file_reader.go b/vendor/github.com/vladimirvivien/gexe/fs/file_reader.go index 061700498..8e5be7f4c 100644 --- a/vendor/github.com/vladimirvivien/gexe/fs/file_reader.go +++ b/vendor/github.com/vladimirvivien/gexe/fs/file_reader.go @@ -5,40 +5,54 @@ import ( "bytes" "io" "os" + + "github.com/vladimirvivien/gexe/vars" ) -type fileReader struct { - err error - path string - finfo os.FileInfo +type FileReader struct { + err error + path string + info os.FileInfo + mode os.FileMode + vars *vars.Variables } -// Read creates a FileReader using the provided path. +// Read creates a new FileReader using the provided path. // A non-nil FileReader.Err() is returned if file does not exist // or another error is generated. -func Read(path string) FileReader { - fr := &fileReader{path: path} - info, err := os.Stat(fr.path) +func Read(path string) *FileReader { + info, err := os.Stat(path) if err != nil { - fr.err = err - return fr + return &FileReader{err: err, path: path} } - fr.finfo = info + return &FileReader{path: path, info: info, mode: info.Mode()} +} + +// ReadWithVars creates a new FileReader and sets the reader's session variables +func ReadWithVars(path string, variables *vars.Variables) *FileReader { + reader := Read(variables.Eval(path)) + reader.vars = variables + return reader +} + +// SetVars sets the FileReader's session variables +func (fr *FileReader) SetVars(variables *vars.Variables) *FileReader { + fr.vars = variables return fr } // Err returns an operation error during file read. -func (fr *fileReader) Err() error { +func (fr *FileReader) Err() error { return fr.err } // Info surfaces the os.FileInfo for the associated file -func (fr *fileReader) Info() os.FileInfo { - return fr.finfo +func (fr *FileReader) Info() os.FileInfo { + return fr.info } // String returns the content of the file as a string value -func (fr *fileReader) String() string { +func (fr *FileReader) String() string { file, err := os.Open(fr.path) if err != nil { fr.err = err @@ -56,7 +70,7 @@ func (fr *fileReader) String() string { } // Lines returns the content of the file as slice of string -func (fr *fileReader) Lines() []string { +func (fr *FileReader) Lines() []string { file, err := os.Open(fr.path) if err != nil { fr.err = err @@ -78,7 +92,7 @@ func (fr *fileReader) Lines() []string { } // Bytes returns the content of the file as []byte -func (fr *fileReader) Bytes() []byte { +func (fr *FileReader) Bytes() []byte { file, err := os.Open(fr.path) if err != nil { fr.err = err @@ -98,7 +112,7 @@ func (fr *fileReader) Bytes() []byte { // Into reads the content of the file and writes // it into the specified Writer -func (fr *fileReader) Into(w io.Writer) FileReader { +func (fr *FileReader) Into(w io.Writer) *FileReader { file, err := os.Open(fr.path) if err != nil { fr.err = err diff --git a/vendor/github.com/vladimirvivien/gexe/fs/file_writer.go b/vendor/github.com/vladimirvivien/gexe/fs/file_writer.go index 5b4d3683c..3360669d7 100644 --- a/vendor/github.com/vladimirvivien/gexe/fs/file_writer.go +++ b/vendor/github.com/vladimirvivien/gexe/fs/file_writer.go @@ -3,21 +3,22 @@ package fs import ( "io" "os" + + "github.com/vladimirvivien/gexe/vars" ) -type fileWriter struct { +type FileWriter struct { path string err error finfo os.FileInfo mode os.FileMode flags int + vars *vars.Variables } -// Write creates a new file,or truncates an existing one, -// using the path provided and sets it up for write operations. -// Operation error is returned by FileWriter.Err(). -func Write(path string) FileWriter { - fw := &fileWriter{path: path, flags: os.O_CREATE | os.O_TRUNC | os.O_WRONLY, mode: 0644} +// Write creates a new FileWriter with flags os.O_CREATE | os.O_TRUNC | os.O_WRONLY and mode 0644. +func Write(path string) *FileWriter { + fw := &FileWriter{path: path, flags: os.O_CREATE | os.O_TRUNC | os.O_WRONLY, mode: 0644, vars: &vars.Variables{}} info, err := os.Stat(fw.path) if err == nil { fw.finfo = info @@ -25,11 +26,16 @@ func Write(path string) FileWriter { return fw } -// Append creates a new file, or append to an existing one, -// using the path provided and sets it up for write operation only. -// Any error generated is returned by FileWriter.Err(). -func Append(path string) FileWriter { - fw := &fileWriter{path: path, flags: os.O_CREATE | os.O_APPEND | os.O_WRONLY, mode: 0644} +// WriteWithVars creates a new FileWriter and sets sessions variables +func WriteWithVars(path string, variables *vars.Variables) *FileWriter { + fw := Write(variables.Eval(path)) + fw.vars = variables + return fw +} + +// Append creates a new FileWriter with flags os.O_CREATE | os.O_APPEND | os.O_WRONLY and mode 0644 +func Append(path string) *FileWriter { + fw := &FileWriter{path: path, flags: os.O_CREATE | os.O_APPEND | os.O_WRONLY, mode: 0644} info, err := os.Stat(fw.path) if err == nil { fw.finfo = info @@ -38,19 +44,39 @@ func Append(path string) FileWriter { return fw } -// Err returns an error during execution -func (fw *fileWriter) Err() error { +// AppendWithVars creates a new FileWriter and sets session variables +func AppendWitVars(path string, variables *vars.Variables) *FileWriter { + fw := Append(variables.Eval(path)) + fw.vars = variables + return fw +} + +// SetVars sets session variables for FileWriter +func (fw *FileWriter) SetVars(variables *vars.Variables) *FileWriter { + if variables != nil { + fw.vars = variables + } + return fw +} + +func (fw *FileWriter) WithMode(mode os.FileMode) *FileWriter { + fw.mode = mode + return fw +} + +// Err returns FileWriter error during execution +func (fw *FileWriter) Err() error { return fw.err } // Info returns the os.FileInfo for the associated file -func (fw *fileWriter) Info() os.FileInfo { +func (fw *FileWriter) Info() os.FileInfo { return fw.finfo } // String writes the provided str into the file. Any // error that occurs can be accessed with FileWriter.Err(). -func (fw *fileWriter) String(str string) FileWriter { +func (fw *FileWriter) String(str string) *FileWriter { file, err := os.OpenFile(fw.path, fw.flags, fw.mode) if err != nil { fw.err = err @@ -69,7 +95,7 @@ func (fw *fileWriter) String(str string) FileWriter { // Lines writes the slice of strings into the file. // Any error will be captured and returned via FileWriter.Err(). -func (fw *fileWriter) Lines(lines []string) FileWriter { +func (fw *FileWriter) Lines(lines []string) *FileWriter { file, err := os.OpenFile(fw.path, fw.flags, fw.mode) if err != nil { fw.err = err @@ -98,7 +124,7 @@ func (fw *fileWriter) Lines(lines []string) FileWriter { // Bytes writes the []bytre provided into the file. // Any error can be accessed using FileWriter.Err(). -func (fw *fileWriter) Bytes(data []byte) FileWriter { +func (fw *FileWriter) Bytes(data []byte) *FileWriter { file, err := os.OpenFile(fw.path, fw.flags, fw.mode) if err != nil { fw.err = err @@ -118,7 +144,7 @@ func (fw *fileWriter) Bytes(data []byte) FileWriter { // From streams bytes from the provided io.Reader r and // writes them to the file. Any error will be captured // and returned by fw.Err(). -func (fw *fileWriter) From(r io.Reader) FileWriter { +func (fw *FileWriter) From(r io.Reader) *FileWriter { file, err := os.OpenFile(fw.path, fw.flags, fw.mode) if err != nil { fw.err = err diff --git a/vendor/github.com/vladimirvivien/gexe/fs/fsinfo.go b/vendor/github.com/vladimirvivien/gexe/fs/fsinfo.go new file mode 100644 index 000000000..b51e4470b --- /dev/null +++ b/vendor/github.com/vladimirvivien/gexe/fs/fsinfo.go @@ -0,0 +1,51 @@ +package fs + +import ( + "io/fs" + "os" + "time" + + "github.com/vladimirvivien/gexe/vars" +) + +type FSInfo struct { + err error + path string + mode os.FileMode + info os.FileInfo + vars *vars.Variables +} + +// Err returns last opertion error on the directory +func (i *FSInfo) Err() error { + return i.err +} + +// Path is the original path for the directory +func (i *FSInfo) Path() string { + return i.path +} + +func (i *FSInfo) Name() string { + return i.info.Name() +} + +// Mode returns the fs.FileMode for the directory +func (i *FSInfo) Mode() fs.FileMode { + return i.mode +} + +// Size returns the directory size or -1 if not known or error +func (i *FSInfo) Size() int64 { + return i.info.Size() +} + +// IsDir returns true if path points to a directory +func (i *FSInfo) IsDir() bool { + return i.info.IsDir() +} + +// ModTime returns the last know modification time. +func (i *FSInfo) ModTime() time.Time { + return i.info.ModTime() +} diff --git a/vendor/github.com/vladimirvivien/gexe/fs/fspath.go b/vendor/github.com/vladimirvivien/gexe/fs/fspath.go new file mode 100644 index 000000000..4215fd4c7 --- /dev/null +++ b/vendor/github.com/vladimirvivien/gexe/fs/fspath.go @@ -0,0 +1,115 @@ +package fs + +import ( + "errors" + "io/fs" + "os" + + "github.com/vladimirvivien/gexe/vars" +) + +type FSPath struct { + err error + path string + vars *vars.Variables +} + +// Path points to a node path +func Path(path string) *FSPath { + return &FSPath{path: path} +} + +// PathWithVars points to a path and applies variables to the path value +func PathWithVars(path string, variables *vars.Variables) *FSPath { + p := Path(variables.Eval(path)) + p.vars = variables + return p +} + +// Info returns information about the specified path +func (p *FSPath) Info() *FSInfo { + info, err := os.Stat(p.path) + if err != nil { + return &FSInfo{err: err, path: p.path} + } + return &FSInfo{path: p.path, info: info, mode: info.Mode()} +} + +// Exists returns true only if os.Stat nil error. +// Any other scenarios will return false. +func (p *FSPath) Exists() bool { + if _, err := os.Stat(p.path); err != nil { + return false + } + return true +} + +// MkDir creates a directory with file mode at specified +func (p *FSPath) MkDir(mode fs.FileMode) *FSInfo { + if err := os.MkdirAll(p.path, mode); err != nil { + if !errors.Is(err, os.ErrExist) { + return &FSInfo{err: err, path: p.path} + } + } + info, err := os.Stat(p.path) + if err != nil { + return &FSInfo{err: err, path: p.path} + } + return &FSInfo{path: p.path, info: info, mode: info.Mode(), vars: p.vars} +} + +// Remove removes entry at path +func (p *FSPath) Remove() *FSInfo { + info, err := os.Stat(p.path) + if err != nil { + if os.IsNotExist(err) { + return &FSInfo{path: p.path} + } + return &FSInfo{err: err, path: p.path} + } + if err := os.RemoveAll(p.path); err != nil { + return &FSInfo{err: err, path: p.path, info: info} + } + return &FSInfo{path: p.path, info: info, mode: info.Mode()} +} + +// Read wraps call to create a new *FileReader instance +func (p *FSPath) Read() *FileReader { + if p.vars != nil { + return ReadWithVars(p.path, p.vars) + } + return Read(p.path) +} + +// Write wraps call to create a new *FileWriter instance +func (p *FSPath) Write() *FileWriter { + if p.vars != nil { + return WriteWithVars(p.path, p.vars) + } + return Write(p.path) +} + +// Append wraps call to create a new *FileWriter instance for file append operations +func (p *FSPath) Append() *FileWriter { + if p.vars != nil { + return AppendWitVars(p.path, p.vars) + } + return Append(p.path) +} + +// Dirs returns info about dirs in path +func (p *FSPath) Dirs() (infos []*FSInfo) { + entries, err := os.ReadDir(p.path) + if err != nil { + p.err = err + return nil + } + for _, entry := range entries { + info, err := entry.Info() + if err != nil { + infos = append(infos, &FSInfo{err: err}) + } + infos = append(infos, &FSInfo{info: info, mode: info.Mode()}) + } + return +} diff --git a/vendor/github.com/vladimirvivien/gexe/fs/types.go b/vendor/github.com/vladimirvivien/gexe/fs/types.go deleted file mode 100644 index 8ef18d104..000000000 --- a/vendor/github.com/vladimirvivien/gexe/fs/types.go +++ /dev/null @@ -1,33 +0,0 @@ -package fs - -import ( - "io" - "os" -) - -// FileReader aggregates read operations from -// diverse sources into a file -type FileReader interface { - Err() error - Info() os.FileInfo - String() string - Lines() []string - Bytes() []byte - Into(io.Writer) FileReader -} - -// FileWriter aggregates several file-writing operations from -// diverse sources into a provided file. -type FileWriter interface { - Err() error - Info() os.FileInfo - String(string) FileWriter - Lines([]string) FileWriter - Bytes([]byte) FileWriter - From(io.Reader) FileWriter -} - -// FileAppender is FileWriter with append behavior -type FileAppender interface { - FileWriter -} diff --git a/vendor/github.com/vladimirvivien/gexe/functions.go b/vendor/github.com/vladimirvivien/gexe/functions.go index 7f76889c0..22555a8b2 100644 --- a/vendor/github.com/vladimirvivien/gexe/functions.go +++ b/vendor/github.com/vladimirvivien/gexe/functions.go @@ -1,10 +1,13 @@ package gexe import ( + "os" + "github.com/vladimirvivien/gexe/exec" "github.com/vladimirvivien/gexe/fs" "github.com/vladimirvivien/gexe/http" "github.com/vladimirvivien/gexe/prog" + "github.com/vladimirvivien/gexe/str" "github.com/vladimirvivien/gexe/vars" ) @@ -123,16 +126,40 @@ func Pipe(cmdStrs ...string) *exec.PipedCommandResult { return DefaultEcho.Pipe(cmdStrs...) } -// Read creates an fs.FileReader that -// can be used to read content from files. -func Read(path string) fs.FileReader { - return DefaultEcho.Read(path) +// PathExists returns true if specified path exists. +// Any error will cause it to return false. +func PathExists(path string) bool { + return DefaultEcho.PathExists(path) +} + +// PathInfo returns information for specified path (i.e. size, etc) +func PathInfo(path string) *fs.FSInfo { + return DefaultEcho.PathInfo(path) +} + +// MkDirs creates one or more directories along the specified path +func MkDirs(path string, mode os.FileMode) *fs.FSInfo { + return DefaultEcho.MkDir(path, mode) } -// Write creates an fs.FileWriter that -// can be used to write content to files -func Write(path string) fs.FileWriter { - return DefaultEcho.Write(path) +// MkDir creates a directory with default mode 0744 +func MkDir(path string) *fs.FSInfo { + return DefaultEcho.MkDir(path, 0744) +} + +// RmPath removes files or directories along specified path +func RmPath(path string) *fs.FSInfo { + return DefaultEcho.RmPath(path) +} + +// FileRead provides methods to read file content from path +func FileRead(path string) *fs.FileReader { + return DefaultEcho.FileRead(path) +} + +// FileWrite provides methods to write file content to path +func FileWrite(path string) *fs.FileWriter { + return DefaultEcho.FileWrite(path) } // GetUrl creates a *http.ResourceReader to retrieve HTTP content @@ -149,3 +176,22 @@ func PostUrl(url string) *http.ResourceWriter { func Prog() *prog.Info { return DefaultEcho.Prog() } + +// ProgAvail returns the full path of the program if available. +func ProgAvail(program string) string { + return DefaultEcho.ProgAvail(program) +} + +// Workdir returns the current program's working directory +func Workdir() string { + return DefaultEcho.Workdir() +} + +// AddExecPath adds an executable path to PATH +func AddExecPath(execPath string) { + DefaultEcho.AddExecPath(execPath) +} + +func String(s string) *str.Str { + return DefaultEcho.String(s) +} diff --git a/vendor/github.com/vladimirvivien/gexe/http.go b/vendor/github.com/vladimirvivien/gexe/http.go index a22406ee3..95c657cba 100644 --- a/vendor/github.com/vladimirvivien/gexe/http.go +++ b/vendor/github.com/vladimirvivien/gexe/http.go @@ -1,13 +1,27 @@ package gexe -import "github.com/vladimirvivien/gexe/http" +import ( + "strings" + + "github.com/vladimirvivien/gexe/http" +) // Get creates a *http.ResourceReader to read resource content from HTTP server -func (e *Echo) Get(url string) *http.ResourceReader { - return http.Get(url) +func (e *Echo) Get(url string, paths ...string) *http.ResourceReader { + var exapandedUrl strings.Builder + exapandedUrl.WriteString(e.vars.Eval(url)) + for _, path := range paths { + exapandedUrl.WriteString(e.vars.Eval(path)) + } + return http.GetWithVars(exapandedUrl.String(), e.vars) } // Post creates a *http.ResourceWriter to write content to an HTTP server -func (e *Echo) Post(url string) *http.ResourceWriter { - return http.Post(url) +func (e *Echo) Post(url string, paths ...string) *http.ResourceWriter { + var exapandedUrl strings.Builder + exapandedUrl.WriteString(e.vars.Eval(url)) + for _, path := range paths { + exapandedUrl.WriteString(e.vars.Eval(path)) + } + return http.PostWithVars(exapandedUrl.String(), e.vars) } diff --git a/vendor/github.com/vladimirvivien/gexe/http/http_reader.go b/vendor/github.com/vladimirvivien/gexe/http/http_reader.go index 16653bdf1..60db91d03 100644 --- a/vendor/github.com/vladimirvivien/gexe/http/http_reader.go +++ b/vendor/github.com/vladimirvivien/gexe/http/http_reader.go @@ -3,6 +3,9 @@ package http import ( "io" "net/http" + "time" + + "github.com/vladimirvivien/gexe/vars" ) // ResourceReader provides types and methods to read content of resources from a server using HTTP @@ -10,12 +13,25 @@ type ResourceReader struct { client *http.Client err error url string - res *Response + vars *vars.Variables } // Get initiates a "GET" operation for the specified resource func Get(url string) *ResourceReader { - return &ResourceReader{url: url, client: &http.Client{}} + return &ResourceReader{url: url, client: &http.Client{}, vars: &vars.Variables{}} +} + +// Get initiates a "GET" operation and sets session variables +func GetWithVars(url string, variables *vars.Variables) *ResourceReader { + r := Get(variables.Eval(url)) + r.vars = variables + return r +} + +// SetVars sets session variables for ResourceReader +func (r *ResourceReader) SetVars(variables *vars.Variables) *ResourceReader { + r.vars = variables + return r } // Err returns the last known error @@ -23,62 +39,52 @@ func (r *ResourceReader) Err() error { return r.err } -// Response returns the server's response info -func (r *ResourceReader) Response() *Response { - return r.res +// WithTimeout sets the HTTP reader's timeout +func (r *ResourceReader) WithTimeout(to time.Duration) *ResourceReader { + r.client.Timeout = to + return r +} + +// Do invokes the client.Get to "GET" the content from server +// Use Response.Err() to access server response errors +func (r *ResourceReader) Do() *Response { + res, err := r.client.Get(r.url) + if err != nil { + return &Response{err: err} + } + return &Response{stat: res.Status, statCode: res.StatusCode, body: res.Body} } // Bytes returns the server response as a []byte -func (b *ResourceReader) Bytes() []byte { - if err := b.Do().Err(); err != nil { - b.err = err +// This is a shorthad for ResourceReader.Do().Bytes() +func (r *ResourceReader) Bytes() []byte { + resp := r.Do() + if resp.Err() != nil { + r.err = resp.Err() return nil } - return b.read() + return resp.Bytes() } -// String returns the server response as a string -func (b *ResourceReader) String() string { - if err := b.Do().Err(); err != nil { - b.err = err +// String returns the server response as a string. +// It is a shorthad for ResourceReader.Do().String() +func (r *ResourceReader) String() string { + resp := r.Do() + if resp.Err() != nil { + r.err = resp.Err() return "" } - return string(b.read()) + return resp.String() } -// Body returns an io.ReadCloser to stream the server response. +// Body returns the server response body (as io.ReadCloser). +// It is a shorthand for ResourceReader().Do().Body() // NOTE: ensure to close the stream when finished. func (r *ResourceReader) Body() io.ReadCloser { - if err := r.Do().Err(); err != nil { - r.err = err - return nil - } - return r.res.body -} - -// Do invokes the client.Get to "GET" the content from server -func (r *ResourceReader) Do() *ResourceReader { - res, err := r.client.Get(r.url) - if err != nil { - r.err = err - r.res = &Response{} - return r - } - r.res = &Response{stat: res.Status, statCode: res.StatusCode, body: res.Body} - return r -} - -// read reads the content of the response body and returns a []byte -func (r *ResourceReader) read() []byte { - if r.res.body == nil { - return nil - } - - data, err := io.ReadAll(r.res.body) - defer r.res.body.Close() - if err != nil { - r.err = err + resp := r.Do() + if resp.Err() != nil { + r.err = resp.Err() return nil } - return data + return resp.Body() } diff --git a/vendor/github.com/vladimirvivien/gexe/http/http_response.go b/vendor/github.com/vladimirvivien/gexe/http/http_response.go index adf2de977..6ed82450e 100644 --- a/vendor/github.com/vladimirvivien/gexe/http/http_response.go +++ b/vendor/github.com/vladimirvivien/gexe/http/http_response.go @@ -7,6 +7,7 @@ type Response struct { stat string statCode int body io.ReadCloser + err error } // Status returns the standard lib http.Response.Status value from the server @@ -24,3 +25,33 @@ func (res *Response) StatusCode() int { func (res *Response) Body() io.ReadCloser { return res.body } + +// Err returns the response known error +func (r *Response) Err() error { + return r.err +} + +// Bytes returns the server response as a []byte +func (r *Response) Bytes() []byte { + return r.read() +} + +// String returns the server response as a string +func (r *Response) String() string { + return string(r.read()) +} + +// read reads the content of the response body and returns as []byte +func (r *Response) read() []byte { + if r.body == nil { + return nil + } + + data, err := io.ReadAll(r.body) + defer r.body.Close() + if err != nil { + r.err = err + return nil + } + return data +} diff --git a/vendor/github.com/vladimirvivien/gexe/http/http_writer.go b/vendor/github.com/vladimirvivien/gexe/http/http_writer.go index c3ea4acc2..3aa91637c 100644 --- a/vendor/github.com/vladimirvivien/gexe/http/http_writer.go +++ b/vendor/github.com/vladimirvivien/gexe/http/http_writer.go @@ -6,6 +6,9 @@ import ( "net/http" "net/url" "strings" + "time" + + "github.com/vladimirvivien/gexe/vars" ) // ResourceWriter represents types and methods used to post resource data to an HTTP server @@ -16,11 +19,31 @@ type ResourceWriter struct { headers http.Header data io.Reader res *Response + vars *vars.Variables } // Post starts a "POST" HTTP operation to the provided resource. func Post(resource string) *ResourceWriter { - return &ResourceWriter{url: resource, client: &http.Client{}, headers: make(http.Header)} + return &ResourceWriter{url: resource, client: &http.Client{}, headers: make(http.Header), vars: &vars.Variables{}} +} + +// PostWithVars sets up a "POST" operation and sets its session variables +func PostWithVars(resource string, variables *vars.Variables) *ResourceWriter { + w := Post(variables.Eval(resource)) + w.vars = variables + return w +} + +// SetVars sets session variables for the ResourceWriter +func (w *ResourceWriter) SetVars(variables *vars.Variables) *ResourceWriter { + w.vars = variables + return w +} + +// WithTimeout sets the HTTP client's timeout +func (w *ResourceWriter) WithTimeout(to time.Duration) *ResourceWriter { + w.client.Timeout = to + return w } // Err returns the last known error for the post operation @@ -61,19 +84,19 @@ func (w *ResourceWriter) WithHeaders(h http.Header) *ResourceWriter { // AddHeader is a convenience method to add a single header func (w *ResourceWriter) AddHeader(key, value string) *ResourceWriter { - w.headers.Add(key, value) + w.headers.Add(w.vars.Eval(key), w.vars.Eval(value)) return w } // SetHeader is a convenience method to sets a specific header func (w *ResourceWriter) SetHeader(key, value string) *ResourceWriter { - w.headers.Set(key, value) + w.headers.Set(w.vars.Eval(key), w.vars.Eval(value)) return w } // String posts the string value as content to the server func (w *ResourceWriter) String(val string) *ResourceWriter { - w.data = strings.NewReader(val) + w.data = strings.NewReader(w.vars.Eval(val)) return w.Do() } diff --git a/vendor/github.com/vladimirvivien/gexe/net.go b/vendor/github.com/vladimirvivien/gexe/net.go new file mode 100644 index 000000000..641b72667 --- /dev/null +++ b/vendor/github.com/vladimirvivien/gexe/net.go @@ -0,0 +1,9 @@ +package gexe + +import ( + "github.com/vladimirvivien/gexe/net" +) + +func (e *Echo) AddressUsable(addr string) error { + return net.AddrUsable(e.Eval(addr)) +} diff --git a/vendor/github.com/vladimirvivien/gexe/net/listener.go b/vendor/github.com/vladimirvivien/gexe/net/listener.go new file mode 100644 index 000000000..71a17b59a --- /dev/null +++ b/vendor/github.com/vladimirvivien/gexe/net/listener.go @@ -0,0 +1,29 @@ +package net + +import ( + "errors" + "fmt" + "net" + "os" + "syscall" +) + +func AddrUsable(address string) error { + addr, err := net.ResolveTCPAddr("tcp", address) + if err != nil { + return fmt.Errorf("net: address parsing: %s", err) + } + + lsnr, err := net.Listen(addr.Network(), addr.String()) + + if err != nil { + sysErr, ok := err.(*os.SyscallError) + if ok && errors.Is(sysErr.Err, syscall.EADDRINUSE) { + return fmt.Errorf("net: addr in use: %s", sysErr.Err) + } + return fmt.Errorf("net: %s", err) + } + + defer lsnr.Close() + return nil +} diff --git a/vendor/github.com/vladimirvivien/gexe/procs.go b/vendor/github.com/vladimirvivien/gexe/procs.go index 251e579ff..ed230675b 100644 --- a/vendor/github.com/vladimirvivien/gexe/procs.go +++ b/vendor/github.com/vladimirvivien/gexe/procs.go @@ -10,25 +10,25 @@ import ( // without starting. Use Proc.Wait to wait for exection and then retrieve process result. // Information about the running process is stored in *exec.Proc. func (e *Echo) NewProc(cmdStr string) *exec.Proc { - return exec.NewProc(cmdStr) + return exec.NewProcWithVars(cmdStr, e.vars) } // StartProc executes the command in cmdStr and returns immediately // without waiting. Use Proc.Wait to wait for exection and then retrieve process result. // Information about the running process is stored in *Proc. func (e *Echo) StartProc(cmdStr string) *exec.Proc { - return exec.StartProc(e.Eval(cmdStr)) + return exec.StartProcWithVars(cmdStr, e.vars) } // RunProc executes command in cmdStr and waits for the result. // It returns a *Proc with information about the executed process. func (e *Echo) RunProc(cmdStr string) *exec.Proc { - return exec.RunProc(e.Eval(cmdStr)) + return exec.RunProcWithVars(cmdStr, e.vars) } // Run executes cmdStr, waits, and returns the result as a string. func (e *Echo) Run(cmdStr string) string { - return exec.Run(e.Eval(cmdStr)) + return exec.RunWithVars(cmdStr, e.vars) } // Runout executes command cmdStr and prints out the result @@ -38,52 +38,45 @@ func (e *Echo) Runout(cmdStr string) { // Commands returns a *exe.CommandBuilder to build a multi-command execution flow. func (e *Echo) Commands(cmdStrs ...string) *exec.CommandBuilder { - for i, cmd := range cmdStrs { - cmdStrs[i] = e.Eval(cmd) - } - return exec.Commands(cmdStrs...) + return exec.CommandsWithVars(e.vars, cmdStrs...) } // StartAll starts the sequential execution of each command, in cmdStrs, and does not // wait for their completion. func (e *Echo) StartAll(cmdStrs ...string) *exec.CommandResult { - for i, cmd := range cmdStrs { - cmdStrs[i] = e.Eval(cmd) - } - return exec.Commands(cmdStrs...).Start() + return exec.CommandsWithVars(e.vars, cmdStrs...).Start() } // RunAll executes each command sequentially, in cmdStrs, and wait for their completion. func (e *Echo) RunAll(cmdStrs ...string) *exec.CommandResult { - for i, cmd := range cmdStrs { - cmdStrs[i] = e.Eval(cmd) - } - return exec.Commands(cmdStrs...).Run() + return exec.CommandsWithVars(e.vars, cmdStrs...).Run() } // StartConcur starts the concurrent execution of each command, in cmdStrs, and does not // wait for their completion. func (e *Echo) StartConcur(cmdStrs ...string) *exec.CommandResult { - for i, cmd := range cmdStrs { - cmdStrs[i] = e.Eval(cmd) - } - return exec.Commands(cmdStrs...).Concurr() + return exec.CommandsWithVars(e.vars, cmdStrs...).Concurr() } // RunConcur executes each command concurrently, in cmdStrs, and waits // their completion. func (e *Echo) RunConcur(cmdStrs ...string) *exec.CommandResult { - for i, cmd := range cmdStrs { - cmdStrs[i] = e.Eval(cmd) - } - return exec.Commands(cmdStrs...).Concurr().Wait() + return exec.CommandsWithVars(e.vars, cmdStrs...).Concurr().Wait() } // Pipe executes each command, in cmdStrs, by piping the result // of the previous command as input to the next command until done. func (e *Echo) Pipe(cmdStrs ...string) *exec.PipedCommandResult { - for i, cmd := range cmdStrs { - cmdStrs[i] = e.Eval(cmd) + return exec.CommandsWithVars(e.vars, cmdStrs...).Pipe() +} + +// ParseCommand parses the string into individual command tokens +func (e *Echo) ParseCommand(cmdStr string) (cmdName string, args []string) { + result, err := exec.Parse(e.vars.Eval(cmdStr)) + if err != nil { + e.err = err } - return exec.Commands(cmdStrs...).Pipe() + cmdName = result[0] + args = result[1:] + return } diff --git a/vendor/github.com/vladimirvivien/gexe/prog.go b/vendor/github.com/vladimirvivien/gexe/prog.go index 2f342ce88..e353a74d6 100644 --- a/vendor/github.com/vladimirvivien/gexe/prog.go +++ b/vendor/github.com/vladimirvivien/gexe/prog.go @@ -4,8 +4,12 @@ import ( "github.com/vladimirvivien/gexe/prog" ) -// Prog creates a new prog.Info to get information -// about the running program +// Prog makes info available about currently executing program func (e *Echo) Prog() *prog.Info { return e.prog } + +// Workdir returns the current program's working directory +func (e *Echo) Workdir() string { + return e.Prog().Workdir() +} diff --git a/vendor/github.com/vladimirvivien/gexe/prog/prog.go b/vendor/github.com/vladimirvivien/gexe/prog/prog.go index 908e0c6db..c7697ca9b 100644 --- a/vendor/github.com/vladimirvivien/gexe/prog/prog.go +++ b/vendor/github.com/vladimirvivien/gexe/prog/prog.go @@ -62,6 +62,7 @@ func (p *Info) Name() string { } // Avail returns full path of binary name if available +// Deprecated: use echo.ProgAvail or gexe.ProgAvail func (p *Info) Avail(progName string) string { path, err := exec.LookPath(progName) if err != nil { diff --git a/vendor/github.com/vladimirvivien/gexe/str.go b/vendor/github.com/vladimirvivien/gexe/str.go new file mode 100644 index 000000000..251dfffc6 --- /dev/null +++ b/vendor/github.com/vladimirvivien/gexe/str.go @@ -0,0 +1,8 @@ +package gexe + +import "github.com/vladimirvivien/gexe/str" + +// String creates a new str.Str value with string manipulation methods +func (e *Echo) String(s string) *str.Str { + return str.StringWithVars(s, e.vars) +} diff --git a/vendor/github.com/vladimirvivien/gexe/str/functions.go b/vendor/github.com/vladimirvivien/gexe/str/functions.go new file mode 100644 index 000000000..75c779bcd --- /dev/null +++ b/vendor/github.com/vladimirvivien/gexe/str/functions.go @@ -0,0 +1,53 @@ +package str + +import ( + "fmt" +) + +// IsEmpty tests for str == "" +func IsEmpty(str string) bool { + return String(str).IsEmpty() +} + +// SplitLines splits each line from str into []string +func SplitLines(str string) []string { + return String(str).SplitLines() +} + +// SplitSpaces splits str by blank chars (space,\t,\n) +func SplitSpaces(str string) []string { + return String(str).SplitSpaces() +} + +// Bool returns the bool equivalent of str ("true" = true, etc) +// A parsing error will cause a program panic. +func Bool(str string) bool { + s := String(str) + result := s.Bool() + if s.Err() != nil { + panic(fmt.Sprintf("%s", s.Err())) + } + return result +} + +// Int returns the int representation of str. +// A parsing error will cause a program panic. +func Int(str string) int { + s := String(str) + result := s.Int() + if s.Err() != nil { + panic(fmt.Sprintf("%s", s.Err())) + } + return result +} + +// Float64 returns the float64 representation of str. +// A parsing error will cause a program panic. +func Float64(str string) float64 { + s := String(str) + result := s.Float64() + if s.Err() != nil { + panic(fmt.Sprintf("%s", s.Err())) + } + return result +} diff --git a/vendor/github.com/vladimirvivien/gexe/str/strings.go b/vendor/github.com/vladimirvivien/gexe/str/strings.go new file mode 100644 index 000000000..53e2157df --- /dev/null +++ b/vendor/github.com/vladimirvivien/gexe/str/strings.go @@ -0,0 +1,185 @@ +package str + +import ( + "bytes" + "io" + "regexp" + "strconv" + "strings" + + "github.com/vladimirvivien/gexe/vars" +) + +var ( + notSpaceRegex = regexp.MustCompile(`\S`) +) + +// Str represents a string value +type Str struct { + val string + err error + vars *vars.Variables +} + +// String is constructor function that returns *Str +func String(str string) *Str { + return &Str{val: str, vars: &vars.Variables{}} +} + +// StringWithVars sets session variables and calls func String +func StringWithVars(str string, variables *vars.Variables) *Str { + s := String(variables.Eval(str)) + s.vars = variables + return s +} + +// String returns the string value +func (s *Str) String() string { + return s.val +} + +// Err returns any captured error +func (s *Str) Err() error { + return s.err +} + +// IsEmpty returns true if len(s) == 0 +func (s *Str) IsEmpty() bool { + return s.val == "" +} + +// Eq returns true if both strings are equal +func (s *Str) Eq(val1 string) bool { + return strings.EqualFold(s.val, val1) +} + +// Split s.val using the sep as delimiter +func (s *Str) Split(sep string) []string { + return strings.Split(s.val, sep) +} + +// SplitLines splits s.val using \n as delimiter +func (s *Str) SplitLines() []string { + return strings.Split(s.val, "\n") +} + +// SplitSpaces properly splits s.val into []elements +// separated by one or more Unicode.IsSpace characters +// i.e. SplitSpaces("ab cd e\tf\ng") returns 5 elements +func (s *Str) SplitSpaces() []string { + return notSpaceRegex.Split(s.val, -1) +} + +// SplitRegex uses regular expression exp to split s.val +func (s *Str) SplitRegex(exp string) []string { + return regexp.MustCompile(exp).Split(s.val, -1) +} + +// Bytes returns []byte(s.val) +func (s *Str) Bytes() []byte { + return []byte(s.val) +} + +// Bool converts s.val from string to a bool representation +// Check s.Err() for parsing errors +func (s *Str) Bool() bool { + val, err := strconv.ParseBool(s.val) + if err != nil { + s.err = err + } + return val +} + +// Int converts s.val from string to a int representation +// Check s.Err() for parsing errors +func (s *Str) Int() int { + val, err := strconv.Atoi(s.val) + if err != nil { + s.err = err + } + return val +} + +// Float64 converts s.val from string to a float64 representation +// Check s.Error() for parsing errors +func (s *Str) Float64() float64 { + val, err := strconv.ParseFloat(s.val, 64) + if err != nil { + s.err = err + } + return val +} + +// Reader returns an io.Reader to access the content. +func (s *Str) Reader() io.Reader { + return bytes.NewReader([]byte(s.val)) +} + +// ToLower returns val as lower case +func (s *Str) ToLower() *Str { + s.val = strings.ToLower(s.val) + return s +} + +// ToUpper returns val as upper case +func (s *Str) ToUpper() *Str { + s.val = strings.ToUpper(s.val) + return s +} + +// ToTitle returns strings.ToTitle for s.val +func (s *Str) ToTitle() *Str { + s.val = strings.ToTitle(s.val) + return s +} + +// TrimSpaces removes spaces around a val +func (s *Str) TrimSpaces() *Str { + s.val = strings.TrimSpace(s.val) + return s +} + +// TrimLeft removes each character in cutset at the +// start of s.val +func (s *Str) TrimLeft(cutset string) *Str { + s.val = strings.TrimLeft(s.val, cutset) + return s +} + +// TrimRight removes each character in cutset removed at the +// start of s.val +func (s *Str) TrimRight(cutset string) *Str { + s.val = strings.TrimRight(s.val, cutset) + return s +} + +// Trim removes each character in cutset from around s.val +func (s *Str) Trim(cutset string) *Str { + s.val = strings.Trim(s.val, cutset) + return s +} + +// ReplaceAll replaces all occurrences of old with new in s.val +func (s *Str) ReplaceAll(old, new string) *Str { + s.val = strings.ReplaceAll(s.val, old, new) + return s +} + +// Concat concatenates val1 to s.val +func (s *Str) Concat(vals ...string) *Str { + evals := []string{s.val} + for _, val := range vals { + evals = append(evals, s.vars.Eval(val)) + } + s.val = strings.Join(evals, "") + return s +} + +// CopyTo copies s.val unto dest +// Check s.Error() for copy error. +func (s *Str) CopyTo(dest io.Writer) *Str { + if _, err := io.Copy(dest, bytes.NewBufferString(s.val)); err != nil { + s.err = err + } + return s +} diff --git a/vendor/github.com/vladimirvivien/gexe/variables.go b/vendor/github.com/vladimirvivien/gexe/variables.go index 54230ee0a..657d6d58e 100644 --- a/vendor/github.com/vladimirvivien/gexe/variables.go +++ b/vendor/github.com/vladimirvivien/gexe/variables.go @@ -52,6 +52,13 @@ func (e *Echo) SetVar(name, value string) *Echo { return e } +// UnsetVar removes a session variable. +func (e *Echo) UnsetVar(name string) *Echo { + vars := e.vars.UnsetVar(name) + e.err = vars.Err() + return e +} + // Val retrieves a session or environment variable func (e *Echo) Val(name string) string { return e.vars.Val(name) diff --git a/vendor/github.com/vladimirvivien/gexe/vars/variables.go b/vendor/github.com/vladimirvivien/gexe/vars/variables.go index ae6cdaf65..4d32de8c0 100644 --- a/vendor/github.com/vladimirvivien/gexe/vars/variables.go +++ b/vendor/github.com/vladimirvivien/gexe/vars/variables.go @@ -95,6 +95,14 @@ func (v *Variables) SetVar(name, value string) *Variables { return v } +// UnsetVar removes a previously set local variable. +func (v *Variables) UnsetVar(name string) *Variables { + v.Lock() + defer v.Unlock() + delete(v.vars, name) + return v +} + // Val searches for a Var with provided key, if not found // searches for environment var, for running process, with same key func (v *Variables) Val(name string) string { diff --git a/vendor/modules.txt b/vendor/modules.txt index e0b99cae7..abefa6fab 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -134,7 +134,7 @@ github.com/prometheus/client_golang/prometheus/testutil/promlint/validations # github.com/prometheus/client_model v0.6.1 ## explicit; go 1.19 github.com/prometheus/client_model/go -# github.com/prometheus/common v0.60.0 +# github.com/prometheus/common v0.60.1 ## explicit; go 1.21 github.com/prometheus/common/expfmt github.com/prometheus/common/model @@ -152,13 +152,15 @@ github.com/spf13/pflag # github.com/stretchr/testify v1.9.0 ## explicit; go 1.17 github.com/stretchr/testify/assert -# github.com/vladimirvivien/gexe v0.2.0 -## explicit; go 1.16 +# github.com/vladimirvivien/gexe v0.3.0 +## explicit; go 1.22 github.com/vladimirvivien/gexe github.com/vladimirvivien/gexe/exec github.com/vladimirvivien/gexe/fs github.com/vladimirvivien/gexe/http +github.com/vladimirvivien/gexe/net github.com/vladimirvivien/gexe/prog +github.com/vladimirvivien/gexe/str github.com/vladimirvivien/gexe/vars # github.com/x448/float16 v0.8.4 ## explicit; go 1.11 @@ -315,7 +317,7 @@ gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# k8s.io/api v0.31.1 +# k8s.io/api v0.31.2 ## explicit; go 1.22.0 k8s.io/api/admissionregistration/v1 k8s.io/api/admissionregistration/v1alpha1 @@ -373,7 +375,7 @@ k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 k8s.io/api/storagemigration/v1alpha1 -# k8s.io/apimachinery v0.31.1 +# k8s.io/apimachinery v0.31.2 ## explicit; go 1.22.0 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -435,7 +437,7 @@ k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/netutil k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/client-go v0.31.1 +# k8s.io/client-go v0.31.2 ## explicit; go 1.22.0 k8s.io/client-go/applyconfigurations k8s.io/client-go/applyconfigurations/admissionregistration/v1 @@ -641,7 +643,7 @@ k8s.io/client-go/util/homedir k8s.io/client-go/util/keyutil k8s.io/client-go/util/watchlist k8s.io/client-go/util/workqueue -# k8s.io/component-base v0.31.1 +# k8s.io/component-base v0.31.2 ## explicit; go 1.22.0 k8s.io/component-base/featuregate k8s.io/component-base/metrics @@ -649,7 +651,7 @@ k8s.io/component-base/metrics/legacyregistry k8s.io/component-base/metrics/prometheus/feature k8s.io/component-base/metrics/prometheusextension k8s.io/component-base/version -# k8s.io/component-helpers v0.31.1 +# k8s.io/component-helpers v0.31.2 ## explicit; go 1.22.0 k8s.io/component-helpers/apimachinery/lease # k8s.io/klog/v2 v2.130.1 @@ -690,13 +692,13 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/metrics sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/common/metrics sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/common/metrics/testing sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client -# sigs.k8s.io/controller-runtime v0.19.0 +# sigs.k8s.io/controller-runtime v0.19.1 ## explicit; go 1.22.0 sigs.k8s.io/controller-runtime/pkg/client sigs.k8s.io/controller-runtime/pkg/client/apiutil sigs.k8s.io/controller-runtime/pkg/controller/controllerutil sigs.k8s.io/controller-runtime/pkg/log -# sigs.k8s.io/e2e-framework v0.4.0 +# sigs.k8s.io/e2e-framework v0.5.0 ## explicit; go 1.22.3 sigs.k8s.io/e2e-framework/klient sigs.k8s.io/e2e-framework/klient/conf diff --git a/vendor/sigs.k8s.io/e2e-framework/klient/decoder/decoder.go b/vendor/sigs.k8s.io/e2e-framework/klient/decoder/decoder.go index 95e18af76..6f558d1d0 100644 --- a/vendor/sigs.k8s.io/e2e-framework/klient/decoder/decoder.go +++ b/vendor/sigs.k8s.io/e2e-framework/klient/decoder/decoder.go @@ -74,7 +74,7 @@ func DecodeEachFile(ctx context.Context, fsys fs.FS, pattern string, handlerFn H } defer f.Close() if err := DecodeEach(ctx, f, handlerFn, options...); err != nil { - return err + return fmt.Errorf("failed to decode file %q: %w", file, err) } if err := f.Close(); err != nil { return err @@ -109,7 +109,7 @@ func DeleteWithManifestDir(ctx context.Context, r *resources.Resources, dirPath, return err } -// Decode a stream of documents of any Kind using either the innate typing of the scheme. +// DecodeEach a stream of documents of any Kind using either the innate typing of the scheme. // Falls back to the unstructured.Unstructured type if a matching type cannot be found for the Kind. // // If handlerFn returns an error, decoding is halted. diff --git a/vendor/sigs.k8s.io/e2e-framework/klient/k8s/resources/resources.go b/vendor/sigs.k8s.io/e2e-framework/klient/k8s/resources/resources.go index 5ddd0a22a..f0d3e5667 100644 --- a/vendor/sigs.k8s.io/e2e-framework/klient/k8s/resources/resources.go +++ b/vendor/sigs.k8s.io/e2e-framework/klient/k8s/resources/resources.go @@ -269,7 +269,7 @@ func (r *Resources) GetScheme() *runtime.Scheme { return r.scheme } -// GetClient return the controller-runtime client instance +// GetControllerRuntimeClient return the controller-runtime client instance func (r *Resources) GetControllerRuntimeClient() cr.Client { return r.client } diff --git a/vendor/sigs.k8s.io/e2e-framework/klient/k8s/watcher/watch.go b/vendor/sigs.k8s.io/e2e-framework/klient/k8s/watcher/watch.go index cf3afc92b..a8e23a5be 100644 --- a/vendor/sigs.k8s.io/e2e-framework/klient/k8s/watcher/watch.go +++ b/vendor/sigs.k8s.io/e2e-framework/klient/k8s/watcher/watch.go @@ -109,19 +109,19 @@ func (e *EventHandlerFuncs) Stop() { e.watcher.Stop() } -// SetAddFunc used to set action on create event +// WithAddFunc used to set action on create event func (e *EventHandlerFuncs) WithAddFunc(addfn func(obj interface{})) *EventHandlerFuncs { e.addFunc = addfn return e } -// SetUpdateFunc sets action for any update events +// WithUpdateFunc sets action for any update events func (e *EventHandlerFuncs) WithUpdateFunc(updatefn func(updated interface{})) *EventHandlerFuncs { e.updateFunc = updatefn return e } -// SetDeleteFunc sets action for delete events +// WithDeleteFunc sets action for delete events func (e *EventHandlerFuncs) WithDeleteFunc(deletefn func(obj interface{})) *EventHandlerFuncs { e.deleteFunc = deletefn return e diff --git a/vendor/sigs.k8s.io/e2e-framework/pkg/env/action.go b/vendor/sigs.k8s.io/e2e-framework/pkg/env/action.go index cb47cb956..8e9fdfa5e 100644 --- a/vendor/sigs.k8s.io/e2e-framework/pkg/env/action.go +++ b/vendor/sigs.k8s.io/e2e-framework/pkg/env/action.go @@ -72,6 +72,7 @@ type action struct { // runWithT will run the action and inject *testing.T into the callback function. func (a *action) runWithT(ctx context.Context, cfg *envconf.Config, t *testing.T) (context.Context, error) { + t.Helper() switch a.role { case roleBeforeTest, roleAfterTest: if cfg.DryRunMode() { @@ -98,6 +99,7 @@ func (a *action) runWithT(ctx context.Context, cfg *envconf.Config, t *testing.T // runWithFeature will run the action and inject a FeatureInfo object into the callback function. func (a *action) runWithFeature(ctx context.Context, cfg *envconf.Config, t *testing.T, fi types.Feature) (context.Context, error) { + t.Helper() switch a.role { case roleBeforeFeature, roleAfterFeature: if cfg.DryRunMode() { diff --git a/vendor/sigs.k8s.io/e2e-framework/pkg/env/env.go b/vendor/sigs.k8s.io/e2e-framework/pkg/env/env.go index d952d3135..df5c4523c 100644 --- a/vendor/sigs.k8s.io/e2e-framework/pkg/env/env.go +++ b/vendor/sigs.k8s.io/e2e-framework/pkg/env/env.go @@ -29,6 +29,7 @@ import ( klog "k8s.io/klog/v2" + "sigs.k8s.io/e2e-framework/klient" "sigs.k8s.io/e2e-framework/pkg/envconf" "sigs.k8s.io/e2e-framework/pkg/featuregate" "sigs.k8s.io/e2e-framework/pkg/features" @@ -116,6 +117,21 @@ func newTestEnvWithParallel() *testEnv { } } +type ctxName string + +// newChildTestEnv returns a child testEnv based on the one passed as an argument. +// The child env inherits the context and actions from the parent and +// creates a deep copy of the config so that it can be mutated without +// affecting the parent's. +func newChildTestEnv(e *testEnv) *testEnv { + childCtx := context.WithValue(e.ctx, ctxName("parent"), fmt.Sprintf("%s", e.ctx)) + return &testEnv{ + ctx: childCtx, + cfg: e.deepCopyConfig(), + actions: append([]action{}, e.actions...), + } +} + // WithContext returns a new environment with the context set to ctx. // Argument ctx cannot be nil func (e *testEnv) WithContext(ctx context.Context) types.Environment { @@ -191,10 +207,11 @@ func (e *testEnv) panicOnMissingContext() { // processTestActions is used to run a series of test action that were configured as // BeforeEachTest or AfterEachTest func (e *testEnv) processTestActions(ctx context.Context, t *testing.T, actions []action) context.Context { + t.Helper() var err error out := ctx for _, action := range actions { - out, err = action.runWithT(ctx, e.cfg, t) + out, err = action.runWithT(out, e.cfg, t) if err != nil { t.Fatalf("%s failure: %s", action.role, err) } @@ -206,6 +223,7 @@ func (e *testEnv) processTestActions(ctx context.Context, t *testing.T, actions // workflow of orchestrating the feature execution be running the action configured by BeforeEachFeature / // AfterEachFeature. func (e *testEnv) processTestFeature(ctx context.Context, t *testing.T, featureName string, feature types.Feature) context.Context { + t.Helper() skipped, message := e.requireFeatureProcessing(feature) if skipped { t.Skipf(message) @@ -223,6 +241,7 @@ func (e *testEnv) processTestFeature(ctx context.Context, t *testing.T, featureN // processFeatureActions is used to run a series of feature action that were configured as // BeforeEachFeature or AfterEachFeature func (e *testEnv) processFeatureActions(ctx context.Context, t *testing.T, feature types.Feature, actions []action) context.Context { + t.Helper() var err error out := ctx for _, action := range actions { @@ -241,7 +260,9 @@ func (e *testEnv) processFeatureActions(ctx context.Context, t *testing.T, featu // In case if the parallel run of test features are enabled, this function will invoke the processTestFeature // as a go-routine to get them to run in parallel func (e *testEnv) processTests(ctx context.Context, t *testing.T, enableParallelRun bool, testFeatures ...types.Feature) context.Context { - if e.cfg.DryRunMode() { + t.Helper() + dedicatedTestEnv := newChildTestEnv(e) + if dedicatedTestEnv.cfg.DryRunMode() { klog.V(2).Info("e2e-framework is being run in dry-run mode. This will skip all the before/after step functions configured around your test assessments and features") } if ctx == nil { @@ -251,19 +272,20 @@ func (e *testEnv) processTests(ctx context.Context, t *testing.T, enableParallel t.Log("No test testFeatures provided, skipping test") return ctx } - beforeTestActions := e.getBeforeTestActions() - afterTestActions := e.getAfterTestActions() + beforeTestActions := dedicatedTestEnv.getBeforeTestActions() + afterTestActions := dedicatedTestEnv.getAfterTestActions() - runInParallel := e.cfg.ParallelTestEnabled() && enableParallelRun + runInParallel := dedicatedTestEnv.cfg.ParallelTestEnabled() && enableParallelRun if runInParallel { klog.V(4).Info("Running test features in parallel") } - ctx = e.processTestActions(ctx, t, beforeTestActions) + ctx = dedicatedTestEnv.processTestActions(ctx, t, beforeTestActions) var wg sync.WaitGroup for i, feature := range testFeatures { + featureTestEnv := newChildTestEnv(dedicatedTestEnv) featureCopy := feature featName := feature.Name() if featName == "" { @@ -273,13 +295,13 @@ func (e *testEnv) processTests(ctx context.Context, t *testing.T, enableParallel wg.Add(1) go func(ctx context.Context, w *sync.WaitGroup, featName string, f types.Feature) { defer w.Done() - _ = e.processTestFeature(ctx, t, featName, f) + _ = featureTestEnv.processTestFeature(ctx, t, featName, f) }(ctx, &wg, featName, featureCopy) } else { - ctx = e.processTestFeature(ctx, t, featName, featureCopy) + ctx = featureTestEnv.processTestFeature(ctx, t, featName, featureCopy) // In case if the feature under test has failed, skip reset of the features // that are part of the same test - if e.cfg.FailFast() && t.Failed() { + if featureTestEnv.cfg.FailFast() && t.Failed() { break } } @@ -287,7 +309,7 @@ func (e *testEnv) processTests(ctx context.Context, t *testing.T, enableParallel if runInParallel { wg.Wait() } - return e.processTestActions(ctx, t, afterTestActions) + return dedicatedTestEnv.processTestActions(ctx, t, afterTestActions) } // TestInParallel executes a series a feature tests from within a @@ -309,6 +331,7 @@ func (e *testEnv) processTests(ctx context.Context, t *testing.T, enableParallel // are executed in parallel to avoid duplication of action that might happen // in BeforeTest and AfterTest actions func (e *testEnv) TestInParallel(t *testing.T, testFeatures ...types.Feature) context.Context { + t.Helper() return e.processTests(e.ctx, t, true, testFeatures...) } @@ -325,6 +348,7 @@ func (e *testEnv) TestInParallel(t *testing.T, testFeatures ...types.Feature) co // BeforeTest and AfterTest operations are executed before and after // the feature is tested respectively. func (e *testEnv) Test(t *testing.T, testFeatures ...types.Feature) context.Context { + t.Helper() return e.processTests(e.ctx, t, false, testFeatures...) } @@ -339,6 +363,12 @@ func (e *testEnv) Finish(funcs ...Func) types.Environment { return e } +// EnvConf returns the test environment's environment configuration +func (e *testEnv) EnvConf() *envconf.Config { + cfg := *e.cfg + return &cfg +} + // Run is to launch the test suite from a TestMain function. // It will run m.Run() and exercise all test functions in the // package. This method will all Env.Setup operations prior to @@ -382,7 +412,7 @@ func (e *testEnv) Run(m *testing.M) (exitCode int) { // context passed down to each setup if ctx, err = setup.run(ctx, e.cfg); err != nil { klog.Errorf("%s failure: %s", setup.role, err) - break + return 1 } } e.ctx = ctx @@ -437,6 +467,7 @@ func (e *testEnv) getFinishActions() []action { } func (e *testEnv) executeSteps(ctx context.Context, t *testing.T, steps []types.Step) context.Context { + t.Helper() if e.cfg.DryRunMode() { return ctx } @@ -447,8 +478,11 @@ func (e *testEnv) executeSteps(ctx context.Context, t *testing.T, steps []types. } func (e *testEnv) execFeature(ctx context.Context, t *testing.T, featName string, f types.Feature) context.Context { + t.Helper() // feature-level subtest t.Run(featName, func(newT *testing.T) { + newT.Helper() + if fDescription, ok := f.(types.DescribableFeature); ok && fDescription.Description() != "" { t.Logf("Processing Feature: %s", fDescription.Description()) } @@ -473,6 +507,7 @@ func (e *testEnv) execFeature(ctx context.Context, t *testing.T, featName string // If it is, we won't proceed with the next assessment. var shouldFailNow bool newT.Run(assessName, func(internalT *testing.T) { + internalT.Helper() skipped, message := e.requireAssessmentProcessing(assess, i+1) if skipped { internalT.Skipf(message) @@ -583,7 +618,54 @@ func (e *testEnv) requireProcessing(kind, testName string, requiredRegexp, skipR return skip, message } -// deepCopyFeature just copies the values from the Feature but creates a deep +// deepCopyConfig just copies the values from the Config to create a deep +// copy to avoid mutation when we just want an informational copy. +func (e *testEnv) deepCopyConfig() *envconf.Config { + // Basic copy which takes care of all the basic types (str, bool...) + configCopy := *e.cfg + + // Manually setting fields that are struct types + if client := e.cfg.GetClient(); client != nil { + // Need to recreate the underlying client because client.Resource is not thread safe + // Panic on error because this should never happen since the client was built once already + clientCopy, err := klient.New(client.RESTConfig()) + if err != nil { + panic(err) + } + configCopy.WithClient(clientCopy) + } + if e.cfg.AssessmentRegex() != nil { + configCopy.WithAssessmentRegex(e.cfg.AssessmentRegex().String()) + } + if e.cfg.FeatureRegex() != nil { + configCopy.WithFeatureRegex(e.cfg.FeatureRegex().String()) + } + if e.cfg.SkipAssessmentRegex() != nil { + configCopy.WithSkipAssessmentRegex(e.cfg.SkipAssessmentRegex().String()) + } + if e.cfg.SkipFeatureRegex() != nil { + configCopy.WithSkipFeatureRegex(e.cfg.SkipFeatureRegex().String()) + } + + labels := make(map[string][]string, len(e.cfg.Labels())) + for k, vals := range e.cfg.Labels() { + copyVals := make([]string, len(vals)) + copyVals = append(copyVals, vals...) + labels[k] = copyVals + } + configCopy.WithLabels(labels) + + skipLabels := make(map[string][]string, len(e.cfg.SkipLabels())) + for k, vals := range e.cfg.SkipLabels() { + copyVals := make([]string, len(vals)) + copyVals = append(copyVals, vals...) + skipLabels[k] = copyVals + } + configCopy.WithSkipLabels(e.cfg.SkipLabels()) + return &configCopy +} + +// deepCopyFeature just copies the values from the Feature to create a deep // copy to avoid mutation when we just want an informational copy. func deepCopyFeature(f types.Feature) types.Feature { fcopy := features.New(f.Name()) diff --git a/vendor/sigs.k8s.io/e2e-framework/pkg/envconf/config.go b/vendor/sigs.k8s.io/e2e-framework/pkg/envconf/config.go index f2267ae2b..ec7283342 100644 --- a/vendor/sigs.k8s.io/e2e-framework/pkg/envconf/config.go +++ b/vendor/sigs.k8s.io/e2e-framework/pkg/envconf/config.go @@ -17,11 +17,10 @@ limitations under the License. package envconf import ( + "crypto/rand" "encoding/hex" "fmt" - "math/rand" "regexp" - "time" log "k8s.io/klog/v2" @@ -107,6 +106,11 @@ func (c *Config) WithClient(client klient.Client) *Config { return c } +// GetClient returns the client for the environment +func (c *Config) GetClient() klient.Client { + return c.client +} + // NewClient is a constructor function that returns a previously // created klient.Client or create a new one based on configuration // previously set. Will return an error if unable to do so. @@ -117,11 +121,10 @@ func (c *Config) NewClient() (klient.Client, error) { client, err := klient.NewWithKubeConfigFile(c.kubeconfig) if err != nil { - return nil, fmt.Errorf("envconfig: client failed: %w", err) + return nil, fmt.Errorf("client failed: %w", err) } - c.client = client - return c.client, nil + return client, nil } // Client is a constructor function that returns a previously @@ -136,10 +139,9 @@ func (c *Config) Client() klient.Client { client, err := klient.NewWithKubeConfigFile(c.kubeconfig) if err != nil { - panic(fmt.Errorf("envconfig: client failed: %w", err).Error()) + panic(fmt.Errorf("client failed: %w", err).Error()) } - c.client = client - return c.client + return client } // WithNamespace updates the environment namespace value @@ -282,7 +284,7 @@ func (c *Config) WithKubeContext(kubeContext string) *Config { return c } -// WithKubeContext is used to get the kubeconfig context +// KubeContext is used to get the kubeconfig context func (c *Config) KubeContext() string { return c.kubeContext } @@ -300,9 +302,13 @@ func RandomName(prefix string, n int) string { if len(prefix) >= n { return prefix } - r := rand.New(rand.NewSource(time.Now().UnixNano())) + p := make([]byte, n) - r.Read(p) + _, err := rand.Read(p) + if err != nil { + log.ErrorS(err, "failed to generate random name. falling back to prefix directly") + return prefix + } if prefix == "" { return hex.EncodeToString(p)[:n] } diff --git a/vendor/sigs.k8s.io/e2e-framework/pkg/envfuncs/ns_funcs.go b/vendor/sigs.k8s.io/e2e-framework/pkg/envfuncs/ns_funcs.go index b4ed39c33..0f4418178 100644 --- a/vendor/sigs.k8s.io/e2e-framework/pkg/envfuncs/ns_funcs.go +++ b/vendor/sigs.k8s.io/e2e-framework/pkg/envfuncs/ns_funcs.go @@ -38,7 +38,7 @@ func WithLabels(labels map[string]string) CreateNamespaceOpts { } } -// WithLabels provides an option to set custom annotations on the namespace. +// WithAnnotations provides an option to set custom annotations on the namespace. func WithAnnotations(annotations map[string]string) CreateNamespaceOpts { return func(client klient.Client, ns *corev1.Namespace) { client.Resources().Annotate(ns, annotations) diff --git a/vendor/sigs.k8s.io/e2e-framework/pkg/flags/flags.go b/vendor/sigs.k8s.io/e2e-framework/pkg/flags/flags.go index b6a9d62b5..ccabd3737 100644 --- a/vendor/sigs.k8s.io/e2e-framework/pkg/flags/flags.go +++ b/vendor/sigs.k8s.io/e2e-framework/pkg/flags/flags.go @@ -187,7 +187,7 @@ func Parse() (*EnvFlags, error) { return ParseArgs(os.Args[1:]) } -// Context returns an optional kubeconfig context to use +// KubeContext returns an optional kubeconfig context to use func (f *EnvFlags) KubeContext() string { return f.kubeContext } diff --git a/vendor/sigs.k8s.io/e2e-framework/pkg/types/types.go b/vendor/sigs.k8s.io/e2e-framework/pkg/types/types.go index f0f710c80..f921d86ac 100644 --- a/vendor/sigs.k8s.io/e2e-framework/pkg/types/types.go +++ b/vendor/sigs.k8s.io/e2e-framework/pkg/types/types.go @@ -85,6 +85,9 @@ type Environment interface { // Run Launches the test suite from within a TestMain Run(*testing.M) int + + // EnvConf returns the test environment's environment configuration + EnvConf() *envconf.Config } type Labels = flags.LabelsMap