-
-
Notifications
You must be signed in to change notification settings - Fork 512
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: resource clean up for tests and examples (#2738)
Ensure that all resources are cleaned up for tests and examples even if they fail. This leverages new helpers in testcontainers: * TerminateContainer for examples * CleanupContainer and CleanupNetwork for tests These are required ensuring that containers that are created but fail in later actions are returned alongside the error so that clean up can be performed. Consistently clean up created networks using a new context to ensure that the removal gets run even if original context has timed out or been cancelled. Use fmt.Print instead of log.Fatal to ensure that defers are run in all examples again ensuring that clean up is processed. Call Stop from Terminate to ensure that child containers are shutdown correctly on clean up as the hard coded timeout using by ContainerRemove is too short to allow this to happen correctly. Clean up of test logic replacing manual checks and asserts with require to make them more concise and hence easier to understand. Quiet test output by either capturing or disabling output so it's easier to identify issues when tests are run in non verbose mode. Clarify source of errors with wrapping and update tests to handle. Ensure that port forwarding container is shutdown if an error occurs during setup so it isn't orphaned. Shutdown the port forwarding container on both stop and terminate to prevent it being orphaned when the Stop is used. Add missing error checks to tests. Remove unused nolint directives and enable the nolintlint to catch any regressions. Don't use container as a variable as its overused.
- Loading branch information
Showing
241 changed files
with
3,914 additions
and
4,199 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,4 +14,7 @@ TEST-*.xml | |
|
||
tcvenv | ||
|
||
**/go.work | ||
**/go.work | ||
|
||
# VS Code settings | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ linters: | |
- nonamedreturns | ||
- testifylint | ||
- errcheck | ||
- nolintlint | ||
|
||
linters-settings: | ||
errorlint: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package testcontainers | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"reflect" | ||
"time" | ||
) | ||
|
||
// terminateOptions is a type that holds the options for terminating a container. | ||
type terminateOptions struct { | ||
ctx context.Context | ||
timeout *time.Duration | ||
volumes []string | ||
} | ||
|
||
// TerminateOption is a type that represents an option for terminating a container. | ||
type TerminateOption func(*terminateOptions) | ||
|
||
// StopContext returns a TerminateOption that sets the context. | ||
// Default: context.Background(). | ||
func StopContext(ctx context.Context) TerminateOption { | ||
return func(c *terminateOptions) { | ||
c.ctx = ctx | ||
} | ||
} | ||
|
||
// StopTimeout returns a TerminateOption that sets the timeout. | ||
// Default: See [Container.Stop]. | ||
func StopTimeout(timeout time.Duration) TerminateOption { | ||
return func(c *terminateOptions) { | ||
c.timeout = &timeout | ||
} | ||
} | ||
|
||
// RemoveVolumes returns a TerminateOption that sets additional volumes to remove. | ||
// This is useful when the container creates named volumes that should be removed | ||
// which are not removed by default. | ||
// Default: nil. | ||
func RemoveVolumes(volumes ...string) TerminateOption { | ||
return func(c *terminateOptions) { | ||
c.volumes = volumes | ||
} | ||
} | ||
|
||
// TerminateContainer calls [Container.Terminate] on the container if it is not nil. | ||
// | ||
// This should be called as a defer directly after [GenericContainer](...) | ||
// or a modules Run(...) to ensure the container is terminated when the | ||
// function ends. | ||
func TerminateContainer(container Container, options ...TerminateOption) error { | ||
if isNil(container) { | ||
return nil | ||
} | ||
|
||
c := &terminateOptions{ | ||
ctx: context.Background(), | ||
} | ||
|
||
for _, opt := range options { | ||
opt(c) | ||
} | ||
|
||
// TODO: Add a timeout when terminate supports it. | ||
err := container.Terminate(c.ctx) | ||
if !isCleanupSafe(err) { | ||
return fmt.Errorf("terminate: %w", err) | ||
} | ||
|
||
// Remove additional volumes if any. | ||
if len(c.volumes) == 0 { | ||
return nil | ||
} | ||
|
||
client, err := NewDockerClientWithOpts(c.ctx) | ||
if err != nil { | ||
return fmt.Errorf("docker client: %w", err) | ||
} | ||
|
||
defer client.Close() | ||
|
||
// Best effort to remove all volumes. | ||
var errs []error | ||
for _, volume := range c.volumes { | ||
if errRemove := client.VolumeRemove(c.ctx, volume, true); errRemove != nil { | ||
errs = append(errs, fmt.Errorf("volume remove %q: %w", volume, errRemove)) | ||
} | ||
} | ||
|
||
return errors.Join(errs...) | ||
} | ||
|
||
// isNil returns true if val is nil or an nil instance false otherwise. | ||
func isNil(val any) bool { | ||
if val == nil { | ||
return true | ||
} | ||
|
||
valueOf := reflect.ValueOf(val) | ||
switch valueOf.Kind() { | ||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice: | ||
return valueOf.IsNil() | ||
default: | ||
return false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.