diff --git a/pkg/env/env.go b/pkg/env/env.go index 61325fd3..db43c87b 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -336,6 +336,29 @@ func (e *testEnv) Run(m *testing.M) int { setups := e.getSetupActions() // fail fast on setup, upon err exit var err error + + defer func() { + // Recover and see if the panic handler is disabled. If it is disabled, panic and stop the workflow. + // Otherwise, log and continue with running the Finish steps of the Test suite + rErr := recover() + if rErr != nil { + if e.cfg.DisableGracefulTeardown() { + panic(rErr) + } + klog.Error("Recovering from panic and running finish actions", rErr) + } + + finishes := e.getFinishActions() + // attempt to gracefully clean up. + // Upon error, log and continue. + for _, fin := range finishes { + // context passed down to each finish step + if e.ctx, err = fin.run(e.ctx, e.cfg); err != nil { + klog.V(2).ErrorS(err, "Finish action handlers") + } + } + }() + for _, setup := range setups { // context passed down to each setup if e.ctx, err = setup.run(e.ctx, e.cfg); err != nil { @@ -343,19 +366,8 @@ func (e *testEnv) Run(m *testing.M) int { } } - exitCode := m.Run() // exec test suite - - finishes := e.getFinishActions() - // attempt to gracefully clean up. - // Upon error, log and continue. - for _, fin := range finishes { - // context passed down to each finish step - if e.ctx, err = fin.run(e.ctx, e.cfg); err != nil { - klog.V(2).ErrorS(err, "Finish action handlers") - } - } - - return exitCode + // Execute the test suite + return m.Run() } func (e *testEnv) getActionsByRole(r actionRole) []action { diff --git a/pkg/envconf/config.go b/pkg/envconf/config.go index 6a9ab3d0..3248d77f 100644 --- a/pkg/envconf/config.go +++ b/pkg/envconf/config.go @@ -31,18 +31,19 @@ import ( // Config represents and environment configuration type Config struct { - client klient.Client - kubeconfig string - namespace string - assessmentRegex *regexp.Regexp - featureRegex *regexp.Regexp - labels map[string]string - skipFeatureRegex *regexp.Regexp - skipLabels map[string]string - skipAssessmentRegex *regexp.Regexp - parallelTests bool - dryRun bool - failFast bool + client klient.Client + kubeconfig string + namespace string + assessmentRegex *regexp.Regexp + featureRegex *regexp.Regexp + labels map[string]string + skipFeatureRegex *regexp.Regexp + skipLabels map[string]string + skipAssessmentRegex *regexp.Regexp + parallelTests bool + dryRun bool + failFast bool + disableGracefulTeardown bool } // New creates and initializes an empty environment configuration @@ -83,6 +84,7 @@ func NewFromFlags() (*Config, error) { e.parallelTests = envFlags.Parallel() e.dryRun = envFlags.DryRun() e.failFast = envFlags.FailFast() + e.disableGracefulTeardown = envFlags.DisableGracefulTeardown() return e, nil } @@ -259,6 +261,19 @@ func (c *Config) FailFast() bool { return c.failFast } +// WithDisableGracefulTeardown can be used to programmatically disabled the panic +// recovery enablement on test startup. This will prevent test Finish steps +// from being executed on panic +func (c *Config) WithDisableGracefulTeardown() *Config { + c.disableGracefulTeardown = true + return c +} + +// DisableGracefulTeardown is used to check the panic recovery handler should be enabled +func (c *Config) DisableGracefulTeardown() bool { + return c.disableGracefulTeardown +} + func randNS() string { return RandomName("testns-", 32) } diff --git a/pkg/envconf/config_test.go b/pkg/envconf/config_test.go index 1bd4db68..5da18c64 100644 --- a/pkg/envconf/config_test.go +++ b/pkg/envconf/config_test.go @@ -73,3 +73,15 @@ func TestConfig_New_WithFailFastAndIgnoreFinalize(t *testing.T) { t.Error("expected fail-fast mode to be enabled when -fail-fast argument is passed") } } + +func TestConfig_New_WithIgnorePanicRecovery(t *testing.T) { + flag.CommandLine = &flag.FlagSet{} + os.Args = []string{"test-binary", "-disable-graceful-teardown"} + cfg, err := NewFromFlags() + if err != nil { + t.Error("failed to parse args", err) + } + if !cfg.DisableGracefulTeardown() { + t.Error("expected ignore-panic-recovery mode to be enabled when -disable-graceful-teardown argument is passed") + } +} diff --git a/pkg/flags/flags.go b/pkg/flags/flags.go index 44054acf..199d9b98 100644 --- a/pkg/flags/flags.go +++ b/pkg/flags/flags.go @@ -26,17 +26,18 @@ import ( ) const ( - flagNamespaceName = "namespace" - flagKubecofigName = "kubeconfig" - flagFeatureName = "feature" - flagAssessName = "assess" - flagLabelsName = "labels" - flagSkipLabelName = "skip-labels" - flagSkipFeatureName = "skip-features" - flagSkipAssessmentName = "skip-assessment" - flagParallelTestsName = "parallel" - flagDryRunName = "dry-run" - flagFailFast = "fail-fast" + flagNamespaceName = "namespace" + flagKubecofigName = "kubeconfig" + flagFeatureName = "feature" + flagAssessName = "assess" + flagLabelsName = "labels" + flagSkipLabelName = "skip-labels" + flagSkipFeatureName = "skip-features" + flagSkipAssessmentName = "skip-assessment" + flagParallelTestsName = "parallel" + flagDryRunName = "dry-run" + flagFailFast = "fail-fast" + flagDisableGracefulTeardown = "disable-graceful-teardown" ) // Supported flag definitions @@ -81,26 +82,30 @@ var ( Name: flagDryRunName, Usage: "Run Test suite in dry-run mode. This will list the tests to be executed without actually running them", } - failFastFlag = flag.Flag{ Name: flagFailFast, Usage: "Fail immediately and stop running untested code", } + disableGracefulTeardownFlag = flag.Flag{ + Name: flagDisableGracefulTeardown, + Usage: "Ignore panic recovery while running tests. This will prevent test finish steps from getting executed on panic", + } ) // EnvFlags surfaces all resolved flag values for the testing framework type EnvFlags struct { - feature string - assess string - labels LabelsMap - kubeconfig string - namespace string - skiplabels LabelsMap - skipFeatures string - skipAssessments string - parallelTests bool - dryRun bool - failFast bool + feature string + assess string + labels LabelsMap + kubeconfig string + namespace string + skiplabels LabelsMap + skipFeatures string + skipAssessments string + parallelTests bool + dryRun bool + failFast bool + disableGracefulTeardown bool } // Feature returns value for `-feature` flag @@ -164,6 +169,12 @@ func (f *EnvFlags) FailFast() bool { return f.failFast } +// DisableGracefulTeardown is used to indicate that the panic handlers should not be registered while +// starting the test execution. This will prevent the test Finish steps from getting executed +func (f *EnvFlags) DisableGracefulTeardown() bool { + return f.disableGracefulTeardown +} + // Parse parses defined CLI args os.Args[1:] func Parse() (*EnvFlags, error) { return ParseArgs(os.Args[1:]) @@ -173,15 +184,16 @@ func Parse() (*EnvFlags, error) { // and returns a set of environment flag values. func ParseArgs(args []string) (*EnvFlags, error) { var ( - feature string - assess string - namespace string - kubeconfig string - skipFeature string - skipAssessment string - parallelTests bool - dryRun bool - failFast bool + feature string + assess string + namespace string + kubeconfig string + skipFeature string + skipAssessment string + parallelTests bool + dryRun bool + failFast bool + disableGracefulTeardown bool ) labels := make(LabelsMap) @@ -231,6 +243,10 @@ func ParseArgs(args []string) (*EnvFlags, error) { flag.BoolVar(&failFast, failFastFlag.Name, false, failFastFlag.Usage) } + if flag.Lookup(disableGracefulTeardownFlag.Name) == nil { + flag.BoolVar(&disableGracefulTeardown, disableGracefulTeardownFlag.Name, false, disableGracefulTeardownFlag.Usage) + } + // Enable klog/v2 flag integration klog.InitFlags(nil) @@ -249,17 +265,18 @@ func ParseArgs(args []string) (*EnvFlags, error) { } return &EnvFlags{ - feature: feature, - assess: assess, - labels: labels, - namespace: namespace, - kubeconfig: kubeconfig, - skiplabels: skipLabels, - skipFeatures: skipFeature, - skipAssessments: skipAssessment, - parallelTests: parallelTests, - dryRun: dryRun, - failFast: failFast, + feature: feature, + assess: assess, + labels: labels, + namespace: namespace, + kubeconfig: kubeconfig, + skiplabels: skipLabels, + skipFeatures: skipFeature, + skipAssessments: skipAssessment, + parallelTests: parallelTests, + dryRun: dryRun, + failFast: failFast, + disableGracefulTeardown: disableGracefulTeardown, }, nil } diff --git a/pkg/flags/flags_test.go b/pkg/flags/flags_test.go index 8911fe8d..18725010 100644 --- a/pkg/flags/flags_test.go +++ b/pkg/flags/flags_test.go @@ -29,7 +29,7 @@ func TestParseFlags(t *testing.T) { }{ { name: "with all", - args: []string{"-assess", "volume test", "--feature", "beta", "--labels", "k0=v0, k1=v1, k2=v2", "--skip-labels", "k0=v0, k1=v1", "-skip-features", "networking", "-skip-assessment", "volume test", "-parallel", "--dry-run"}, + args: []string{"-assess", "volume test", "--feature", "beta", "--labels", "k0=v0, k1=v1, k2=v2", "--skip-labels", "k0=v0, k1=v1", "-skip-features", "networking", "-skip-assessment", "volume test", "-parallel", "--dry-run", "--disable-graceful-teardown"}, flags: &EnvFlags{assess: "volume test", feature: "beta", labels: LabelsMap{"k0": "v0", "k1": "v1", "k2": "v2"}, skiplabels: LabelsMap{"k0": "v0", "k1": "v1"}, skipFeatures: "networking", skipAssessments: "volume test"}, }, } @@ -76,6 +76,10 @@ func TestParseFlags(t *testing.T) { if !testFlags.DryRun() { t.Errorf("unmatched flag parsed. Expected dryRun to be true.") } + + if !testFlags.DisableGracefulTeardown() { + t.Errorf("unmatched flag parsed. Expected disableGracefulTeardown to be true") + } }) } }