feat(containertest): add standardRun YAML check for unmodified container startup#147
Conversation
Co-authored-by: Crow-Control <7613738+Crow-Control@users.noreply.github.com>
Co-authored-by: Crow-Control <7613738+Crow-Control@users.noreply.github.com>
Co-authored-by: Crow-Control <7613738+Crow-Control@users.noreply.github.com>
standardRun YAML check for unmodified container startup
|
@copilot fix issues |
There was a problem hiding this comment.
Pull request overview
Adds a new standardRun YAML check to the pkg/containertest runner so configs can validate that an image starts successfully using its default entrypoint/args (no command overrides).
Changes:
- Extended
ContainerTestYAML+ runner validation/execution to supportstandardRun. - Implemented
CheckStandardRuncontainer lifecycle check intesthelpers.go. - Updated JSON schema and sample YAML config to document/validate the new option, plus expanded unit tests around the YAML runner.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| testdata/container_test_configs/schema.yaml | Adds an example standardRun: true usage to the sample config. |
| pkg/containertest/yaml_runner_test.go | Extends YAML runner tests to parse/call/propagate errors for standardRun. |
| pkg/containertest/yaml_runner.go | Adds standardRun field + runner logic to treat it as a valid check type. |
| pkg/containertest/testhelpers.go | Introduces CheckStandardRun implementation for starting/tearing down a default container run. |
| pkg/containertest/container-test.schema.json | Adds standardRun property and includes it in accepted check groups. |
| "$id": "https://trueforge-org.github.io/containerforge/container-test.schema.json", | ||
| "title": "Container test configuration", | ||
| "description": "Schema for apps/*/container-test.yaml consumed by testhelpers.RunChecksFromYAML. Define one or more check groups (http, tcp, commands, filePaths) for a container image.", | ||
| "description": "Schema for apps/*/container-test.yaml consumed by testhelpers.RunChecksFromYAML. Define one or more check groups (http, tcp, commands, filePaths, standardRun) for a container image.", |
There was a problem hiding this comment.
The schema description says configs are consumed by testhelpers.RunChecksFromYAML, but the runner entrypoint in this repo is containertest.RunChecksFromYAML (pkg/containertest/yaml_runner.go). Updating this string will prevent readers from searching for a non-existent API.
| "description": "Schema for apps/*/container-test.yaml consumed by testhelpers.RunChecksFromYAML. Define one or more check groups (http, tcp, commands, filePaths, standardRun) for a container image.", | |
| "description": "Schema for apps/*/container-test.yaml consumed by containertest.RunChecksFromYAML. Define one or more check groups (http, tcp, commands, filePaths, standardRun) for a container image.", |
| @@ -140,6 +147,11 @@ | |||
| "required": [ | |||
| "filePaths" | |||
| ] | |||
| }, | |||
| { | |||
| "required": [ | |||
| "standardRun" | |||
| ] | |||
| } | |||
There was a problem hiding this comment.
anyOf only requires the presence of standardRun, so a config like {standardRun: false} will pass schema validation but still fail at runtime with "no checks configured" (because the Go runner treats standardRun as enabled only when true). Consider making standardRun a const: true in the schema (or otherwise constraining it) so schema validation matches runtime behavior.
| err = fmt.Errorf("failed to terminate container: %w", termErr) | ||
| } | ||
| }() | ||
|
|
There was a problem hiding this comment.
CheckStandardRun currently treats a successful testcontainers.Run as a pass without verifying the container is still running (or exited cleanly). This can produce false positives for images that start and immediately crash/exit. Consider checking container.State(ctx) after startup and failing if it’s not running (or if it exited with a non-zero code) before logging success.
| state, stateErr := container.State(ctx) | |
| if stateErr != nil { | |
| return fmt.Errorf("failed to inspect container state for standard run check: %w", stateErr) | |
| } | |
| if !state.Running { | |
| if state.ExitCode != 0 { | |
| return fmt.Errorf("container exited with non-zero exit code %d during standard run check", state.ExitCode) | |
| } | |
| return fmt.Errorf("container is not running during standard run check (exited with code 0)") | |
| } |
| // CheckStandardRun verifies the container can be started without altering entrypoint or args. | ||
| func CheckStandardRun(ctx context.Context, image string, config *ContainerConfig) (err error) { | ||
| logInfo("🧪 Standard run check: image=%s", image) | ||
| opts := applyContainerConfig(config) | ||
|
|
||
| container, err := runContainer(ctx, image, opts...) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer func() { | ||
| if shouldDumpContainerLogs(err != nil) { | ||
| dumpContainerLogs(ctx, container, "standard run check") | ||
| } else { | ||
| logDebug("Skipping container logs for standard run check (mode=%q, failed=%t)", strings.TrimSpace(strings.ToLower(os.Getenv("TESTHELPERS_CONTAINER_LOGS"))), err != nil) | ||
| } | ||
| termErr := terminateContainer(ctx, container, "standard run check") | ||
| if err == nil && termErr != nil { | ||
| err = fmt.Errorf("failed to terminate container: %w", termErr) | ||
| } | ||
| }() | ||
|
|
||
| logInfo("Standard run check completed successfully for image=%s", image) | ||
| return nil | ||
| } |
There was a problem hiding this comment.
CheckStandardRun is new behavior but doesn’t have direct unit coverage in testhelpers_unit_test.go (unlike the other high-level checks). Adding tests using the existing runContainerBackend seam would help lock in expected behavior (e.g., starts container with only env customizer, propagates run errors, and wraps terminate failures).
This adds a
standardRunoption to container-test YAML so configs can validate a plain container startup path (default entrypoint/args, no overrides). The runner now treatsstandardRunas a first-class check type alongsidehttp,tcp,filePaths, andcommands.YAML runner support
standardRuntoContainerTestYAML.standardRun: true.RunChecksFromYAMLto execute a standard-run check when enabled.Standard run check implementation
CheckStandardRun(ctx, image, config)inpkg/containertest/testhelpers.go.Schema and config examples
pkg/containertest/container-test.schema.json:standardRunboolean property.standardRunin accepted check groups (anyOf).testdata/container_test_configs/schema.yamlto demonstrate usage.Unit coverage updates
standardRun,Example YAML:
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
https://api.github.com/repos/trueforge-org/cluster-template/releases/latest/tmp/go-build2354131493/b001/forgetool.test /tmp/go-build2354131493/b001/forgetool.test -test.run=TestMainHelperProcess -stringintconv l/linux_amd64/vet /tmp/go-build2354131493/b2654/vet.cfg -bool t ux-amd64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert t ux-amd64/pkg/too-buildtags -ato�� -bool t ux-amd64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert t ux-amd64/pkg/too-buildtags(http block)invalid.domain.that.does.not.exist.example/tmp/go-build2354131493/b2799/helper.test /tmp/go-build2354131493/b2799/helper.test -test.testlogfile=/tmp/go-build2354131493/b2799/testlog.txt -test.paniconexit0 -test.timeout=10m0s --64�� -o t 0.1-go1.25.5.lin-nilfunc elf_x86_64 --hash-style=gnu-test.run=TestHelperProcessMain t 0.1-go1.25.5.lin-tests -ato�� -bool t.cfg ux-amd64/pkg/tool/linux_amd64/compile -errorsas -ifaceassert t ux-amd64/pkg/too-buildtags(dns block)invalid.invalid.invalid/tmp/go-build2354131493/b2799/helper.test /tmp/go-build2354131493/b2799/helper.test -test.testlogfile=/tmp/go-build2354131493/b2799/testlog.txt -test.paniconexit0 -test.timeout=10m0s --64�� -o t 0.1-go1.25.5.lin-nilfunc elf_x86_64 --hash-style=gnu-test.run=TestHelperProcessMain t 0.1-go1.25.5.lin-tests -ato�� -bool t.cfg ux-amd64/pkg/tool/linux_amd64/compile -errorsas -ifaceassert t ux-amd64/pkg/too-buildtags(dns block)/tmp/go-build2354131493/b2799/helper.test /tmp/go-build2354131493/b2799/helper.test -test.run=TestHelperProcessExitPaths --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -ato�� -bool t ux-amd64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert t ux-amd64/pkg/too-Wl,-z,now(dns block)oci.trueforge.org/tmp/go-build2354131493/b001/forgetool.test /tmp/go-build2354131493/b001/forgetool.test -test.run=TestMainHelperProcess -stringintconv l/linux_amd64/vet /tmp/go-build2354131493/b2654/vet.cfg -bool t ux-amd64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert t ux-amd64/pkg/too-buildtags -ato�� -bool t ux-amd64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert t ux-amd64/pkg/too-buildtags(dns block)/tmp/go-build2354131493/b2799/helper.test /tmp/go-build2354131493/b2799/helper.test -test.run=TestHelperProcessCheckReqDomains pviM5nxT1ez3f21FXFdb/pviM5nxT1ez3f21FXFdb -goversion go1.25.5 -c=4 -nolocalimports -importcfg /tmp/go-build2354131493/b2795/importcfg -pack /home/REDACTED/work/forgetool/forgetool/pkg/gencmd/apply.go /home/REDACTED/work/forgetool/forgetool/pkg/gencmd/bootstrap.go -ato�� -bool t ux-amd64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert t ux-amd64/pkg/too-test.timeout=10m0s(dns block)pool.ntp.org/tmp/go-build2354131493/b001/forgetool.test /tmp/go-build2354131493/b001/forgetool.test -test.run=TestMainHelperProcess -stringintconv l/linux_amd64/vet /tmp/go-build2354131493/b2654/vet.cfg -bool t ux-amd64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert t ux-amd64/pkg/too-buildtags -ato�� -bool t ux-amd64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert t ux-amd64/pkg/too-buildtags(dns block)/tmp/go-build2354131493/b2799/helper.test /tmp/go-build2354131493/b2799/helper.test -test.run=TestHelperProcessExitPaths /tmp/go-link-1533295041/go.o /tmp/go-link-1533295041/000000.o /tmp/go-link-1533295041/000001.o /tmp/go-link-1533295041/000002.o /tmp/go-link-1533295041/000003.o /tmp/go-link-1533295041/000004.o /tmp/go-link-1533295041/000005.o /tmp/go-link-1533295041/000006.o /tmp/go-link-1533295041/000007.o /tmp/go-link-1533295041/000008.o -ato�� -bool t ux-amd64/pkg/tool/linux_amd64/compile -errorsas -ifaceassert t ux-amd64/pkg/too-plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper(dns block)If you need me to access, download, or install something from one of these locations, you can either:
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.