Skip to content

Commit

Permalink
feat: allow tunnel timeout configuration (#896)
Browse files Browse the repository at this point in the history
* feat: allow tunnel timeout configuration

* refactor: remove stutter
  • Loading branch information
alexplischke authored Apr 2, 2024
1 parent fde9414 commit 181b20e
Show file tree
Hide file tree
Showing 16 changed files with 106 additions and 24 deletions.
9 changes: 9 additions & 0 deletions api/saucectl.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@
"owner": {
"description": "The owner (username) of the tunnel. Must be specified if the user that created the tunnel differs from the user that is running the tests.",
"type": "string"
},
"timeout": {
"description": "How long to wait for the specified tunnel to be ready. Supports duration values like '10s', '30m' etc.",
"type": "string",
"pattern": "^(?:\\d+h)?(?:\\d+m)?(?:\\d+s)?(?:\\d+ms)?$",
"examples": [
"1m",
"30s"
]
}
},
"required": [
Expand Down
9 changes: 9 additions & 0 deletions api/v1alpha/subschema/sauce.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@
"owner": {
"description": "The owner (username) of the tunnel. Must be specified if the user that created the tunnel differs from the user that is running the tests.",
"type": "string"
},
"timeout": {
"description": "How long to wait for the specified tunnel to be ready. Supports duration values like '10s', '30m' etc.",
"type": "string",
"pattern": "^(?:\\d+h)?(?:\\d+m)?(?:\\d+s)?(?:\\d+ms)?$",
"examples": [
"1m",
"30s"
]
}
},
"required": [
Expand Down
9 changes: 8 additions & 1 deletion internal/apitest/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,14 @@ func FilterSuites(p *Project, suiteName string) error {
// RunProject runs the tests defined in apitest.Project
func (r *Runner) RunProject() (int, error) {
exitCode := 1
if err := tunnel.ValidateTunnel(r.TunnelService, r.Project.Sauce.Tunnel.Name, r.Project.Sauce.Tunnel.Owner, tunnel.V2AlphaFilter, false); err != nil {
if err := tunnel.Validate(
r.TunnelService,
r.Project.Sauce.Tunnel.Name,
r.Project.Sauce.Tunnel.Owner,
tunnel.V2AlphaFilter,
false,
r.Project.Sauce.Tunnel.Timeout,
); err != nil {
return 1, err
}

Expand Down
18 changes: 10 additions & 8 deletions internal/cmd/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ var (
var gFlags = globalFlags{}

type globalFlags struct {
cfgFilePath string
cfgFilePath string
selectedSuite string
testEnvSilent bool
async bool
failFast bool
noAutoTagging bool

globalTimeout time.Duration
selectedSuite string
testEnvSilent bool
async bool
failFast bool
appStoreTimeout time.Duration
noAutoTagging bool
}

// Command creates the `run` command
Expand Down Expand Up @@ -101,14 +102,15 @@ func Command() *cobra.Command {
cmd.PersistentFlags().DurationVarP(&gFlags.globalTimeout, "timeout", "t", 0, "Global timeout that limits how long saucectl can run in total. Supports duration values like '10s', '30m' etc. (default: no timeout)")
cmd.PersistentFlags().BoolVar(&gFlags.async, "async", false, "Launches tests without waiting for test results")
cmd.PersistentFlags().BoolVar(&gFlags.failFast, "fail-fast", false, "Stops suites after the first failure")
cmd.PersistentFlags().DurationVar(&gFlags.appStoreTimeout, "uploadTimeout", 5*time.Minute, "Upload timeout that limits how long saucectl will wait for an upload to finish. Supports duration values like '10s' '30m' etc. (default: 5m)")
cmd.PersistentFlags().DurationVar(&gFlags.appStoreTimeout, "upload-timeout", 5*time.Minute, "Upload timeout that limits how long saucectl will wait for an upload to finish. Supports duration values like '10s' '30m' etc. (default: 5m)")
cmd.PersistentFlags().DurationVar(&gFlags.appStoreTimeout, "uploadTimeout", 5*time.Minute, "Upload timeout that limits how long saucectl will wait for an upload to finish. Supports duration values like '10s', '30m' etc.")
cmd.PersistentFlags().DurationVar(&gFlags.appStoreTimeout, "upload-timeout", 5*time.Minute, "Upload timeout that limits how long saucectl will wait for an upload to finish. Supports duration values like '10s', '30m' etc.")
sc.StringP("region", "r", "sauce::region", "", "The sauce labs region. Options: us-west-1, eu-central-1.")
sc.StringToStringP("env", "e", "envFlag", map[string]string{}, "Set environment variables, e.g. -e foo=bar. Not supported for RDC or Espresso on virtual devices!")
sc.Bool("show-console-log", "showConsoleLog", false, "Shows suites console.log locally. By default console.log is only shown on failures.")
sc.Int("ccy", "sauce::concurrency", 2, "Concurrency specifies how many suites are run at the same time.")
sc.String("tunnel-name", "sauce::tunnel::name", "", "Sets the sauce-connect tunnel name to be used for the run.")
sc.String("tunnel-owner", "sauce::tunnel::owner", "", "Sets the sauce-connect tunnel owner to be used for the run.")
sc.Duration("tunnel-timeout", "sauce::tunnel::timeout", 30*time.Second, "How long to wait for the specified tunnel to be ready. Supports duration values like '10s', '30m' etc.")
sc.String("runner-version", "runnerVersion", "", "Overrides the automatically determined runner version.")
sc.String("sauceignore", "sauce::sauceignore", ".sauceignore", "Specifies the path to the .sauceignore file.")
sc.String("root-dir", "rootDir", ".", "Specifies the project directory. Not applicable to mobile frameworks.")
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ type Tunnel struct {
// Deprecated. Use Owner instead.
Parent string `yaml:"parent,omitempty" json:"parent,omitempty"`
Owner string `yaml:"owner,omitempty" json:"owner,omitempty"`
// Timeout represents the time to wait for the tunnel to be ready.
Timeout time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
}

// TypeDef represents the type definition of the config.
Expand Down
8 changes: 8 additions & 0 deletions internal/flags/binder.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package flags

import (
"time"

"github.com/rs/zerolog/log"
"github.com/spf13/pflag"

Expand Down Expand Up @@ -43,6 +45,12 @@ func (s *SnakeCharmer) BoolP(flagName, shorthand, fieldName string, value bool,
s.addBind(flagName, fieldName)
}

// Duration defines a duration flag with specified flagName, default value, usage string and then binds it to fieldName.
func (s *SnakeCharmer) Duration(flagName string, fieldName string, value time.Duration, usage string) {
s.Fset.Duration(flagName, value, usage)
s.addBind(flagName, fieldName)
}

// Float64 defines a float64 flag with specified flagName, default value, usage string and then binds it to fieldName.
func (s *SnakeCharmer) Float64(flagName, fieldName string, value float64, usage string) {
s.Fset.Float64(flagName, value, usage)
Expand Down
4 changes: 2 additions & 2 deletions internal/saucecloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,8 +792,8 @@ func (r *CloudRunner) logSuiteConsole(res result) {
fmt.Println()
}

func (r *CloudRunner) validateTunnel(name, owner string, dryRun bool) error {
return tunnel.ValidateTunnel(r.TunnelService, name, owner, tunnel.NoneFilter, dryRun)
func (r *CloudRunner) validateTunnel(name, owner string, dryRun bool, timeout time.Duration) error {
return tunnel.Validate(r.TunnelService, name, owner, tunnel.NoneFilter, dryRun, timeout)
}

// stopSuiteExecution stops the current execution on Sauce Cloud
Expand Down
7 changes: 6 additions & 1 deletion internal/saucecloud/cucumber.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ func (r *CucumberRunner) RunProject() (int, error) {
}
}

if err := r.validateTunnel(r.Project.Sauce.Tunnel.Name, r.Project.Sauce.Tunnel.Owner, r.Project.DryRun); err != nil {
if err := r.validateTunnel(
r.Project.Sauce.Tunnel.Name,
r.Project.Sauce.Tunnel.Owner,
r.Project.DryRun,
r.Project.Sauce.Tunnel.Timeout,
); err != nil {
return 1, err
}

Expand Down
7 changes: 6 additions & 1 deletion internal/saucecloud/cypress.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ func (r *CypressRunner) RunProject() (int, error) {
}
}

if err := r.validateTunnel(r.Project.GetSauceCfg().Tunnel.Name, r.Project.GetSauceCfg().Tunnel.Owner, r.Project.IsDryRun()); err != nil {
if err := r.validateTunnel(
r.Project.GetSauceCfg().Tunnel.Name,
r.Project.GetSauceCfg().Tunnel.Owner,
r.Project.IsDryRun(),
r.Project.GetSauceCfg().Tunnel.Timeout,
); err != nil {
return 1, err
}

Expand Down
7 changes: 6 additions & 1 deletion internal/saucecloud/espresso.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ type EspressoRunner struct {
func (r *EspressoRunner) RunProject() (int, error) {
exitCode := 1

if err := r.validateTunnel(r.Project.Sauce.Tunnel.Name, r.Project.Sauce.Tunnel.Owner, r.Project.DryRun); err != nil {
if err := r.validateTunnel(
r.Project.Sauce.Tunnel.Name,
r.Project.Sauce.Tunnel.Owner,
r.Project.DryRun,
r.Project.Sauce.Tunnel.Timeout,
); err != nil {
return 1, err
}

Expand Down
9 changes: 8 additions & 1 deletion internal/saucecloud/imagerunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ type execResult struct {
}

func (r *ImgRunner) RunProject() (int, error) {
if err := tunnel.ValidateTunnel(r.TunnelService, r.Project.Sauce.Tunnel.Name, r.Project.Sauce.Tunnel.Owner, tunnel.NoneFilter, false); err != nil {
if err := tunnel.Validate(
r.TunnelService,
r.Project.Sauce.Tunnel.Name,
r.Project.Sauce.Tunnel.Owner,
tunnel.NoneFilter,
false,
r.Project.Sauce.Tunnel.Timeout,
); err != nil {
return 1, err
}

Expand Down
7 changes: 6 additions & 1 deletion internal/saucecloud/playwright.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ func (r *PlaywrightRunner) RunProject() (int, error) {
r.Project.Suites[i].Params.BrowserVersion = m.BrowserDefaults[PlaywrightBrowserMap[s.Params.BrowserName]]
}

if err := r.validateTunnel(r.Project.Sauce.Tunnel.Name, r.Project.Sauce.Tunnel.Owner, r.Project.DryRun); err != nil {
if err := r.validateTunnel(
r.Project.Sauce.Tunnel.Name,
r.Project.Sauce.Tunnel.Owner,
r.Project.DryRun,
r.Project.Sauce.Tunnel.Timeout,
); err != nil {
return 1, err
}

Expand Down
7 changes: 6 additions & 1 deletion internal/saucecloud/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ func (r *ReplayRunner) RunProject() (int, error) {
}
}

if err := r.validateTunnel(r.Project.Sauce.Tunnel.Name, r.Project.Sauce.Tunnel.Owner, r.Project.DryRun); err != nil {
if err := r.validateTunnel(
r.Project.Sauce.Tunnel.Name,
r.Project.Sauce.Tunnel.Owner,
r.Project.DryRun,
r.Project.Sauce.Tunnel.Timeout,
); err != nil {
return 1, err
}

Expand Down
7 changes: 6 additions & 1 deletion internal/saucecloud/testcafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ func (r *TestcafeRunner) RunProject() (int, error) {
}
}

if err := r.validateTunnel(r.Project.Sauce.Tunnel.Name, r.Project.Sauce.Tunnel.Owner, r.Project.DryRun); err != nil {
if err := r.validateTunnel(
r.Project.Sauce.Tunnel.Name,
r.Project.Sauce.Tunnel.Owner,
r.Project.DryRun,
r.Project.Sauce.Tunnel.Timeout,
); err != nil {
return 1, err
}

Expand Down
7 changes: 6 additions & 1 deletion internal/saucecloud/xcuitest.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ var (
func (r *XcuitestRunner) RunProject() (int, error) {
exitCode := 1

if err := r.validateTunnel(r.Project.Sauce.Tunnel.Name, r.Project.Sauce.Tunnel.Owner, r.Project.DryRun); err != nil {
if err := r.validateTunnel(
r.Project.Sauce.Tunnel.Name,
r.Project.Sauce.Tunnel.Owner,
r.Project.DryRun,
r.Project.Sauce.Tunnel.Timeout,
); err != nil {
return exitCode, err
}

Expand Down
13 changes: 8 additions & 5 deletions internal/tunnel/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tunnel

import (
"context"
"errors"
"time"

"github.com/rs/zerolog/log"
Expand All @@ -25,7 +26,7 @@ type Service interface {
IsTunnelRunning(ctx context.Context, id, parent string, filter Filter, wait time.Duration) error
}

func ValidateTunnel(service Service, name string, owner string, filter Filter, dryRun bool) error {
func Validate(service Service, name string, owner string, filter Filter, dryRun bool, timeout time.Duration) error {
if name == "" {
return nil
}
Expand All @@ -35,10 +36,12 @@ func ValidateTunnel(service Service, name string, owner string, filter Filter, d
return nil
}

// This wait value is deliberately not configurable.
wait := 30 * time.Second
log.Info().Str("timeout", wait.String()).Str("tunnel", name).Msg("Performing tunnel readiness check...")
if err := service.IsTunnelRunning(context.Background(), name, owner, filter, wait); err != nil {
if timeout <= 0 {
return errors.New("tunnel timeout must be greater than 0")
}

log.Info().Str("timeout", timeout.String()).Str("tunnel", name).Msg("Performing tunnel readiness check...")
if err := service.IsTunnelRunning(context.Background(), name, owner, filter, timeout); err != nil {
return err
}

Expand Down

0 comments on commit 181b20e

Please sign in to comment.