mirror of
https://code.forgejo.org/forgejo/runner.git
synced 2025-09-15 18:57:01 +00:00
add test case for premature termination before health check completes
This commit is contained in:
parent
12347b019d
commit
aa70cb7d7b
5 changed files with 38 additions and 3 deletions
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"code.forgejo.org/forgejo/runner/v9/act/common"
|
"code.forgejo.org/forgejo/runner/v9/act/common"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewContainerInput the input for the New function
|
// NewContainerInput the input for the New function
|
||||||
|
@ -93,3 +94,5 @@ const (
|
||||||
HealthHealthy
|
HealthHealthy
|
||||||
HealthUnHealthy
|
HealthUnHealthy
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrContainerNotFound = errors.New("container not found")
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver"
|
"github.com/Masterminds/semver"
|
||||||
|
cerrdefs "github.com/containerd/errdefs"
|
||||||
"github.com/docker/cli/cli/compose/loader"
|
"github.com/docker/cli/cli/compose/loader"
|
||||||
"github.com/docker/cli/cli/connhelper"
|
"github.com/docker/cli/cli/connhelper"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -194,6 +195,9 @@ func (cr *containerReference) Remove() common.Executor {
|
||||||
|
|
||||||
func (cr *containerReference) GetHealth(ctx context.Context) (Health, error) {
|
func (cr *containerReference) GetHealth(ctx context.Context) (Health, error) {
|
||||||
resp, err := cr.cli.ContainerInspect(ctx, cr.id)
|
resp, err := cr.cli.ContainerInspect(ctx, cr.id)
|
||||||
|
if cerrdefs.IsNotFound(err) {
|
||||||
|
return HealthUnHealthy, ErrContainerNotFound
|
||||||
|
}
|
||||||
logger := common.Logger(ctx)
|
logger := common.Logger(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HealthUnHealthy, err
|
return HealthUnHealthy, err
|
||||||
|
|
|
@ -768,10 +768,16 @@ func (rc *RunContext) waitForServiceContainer(c container.ExecutionsEnvironment)
|
||||||
return fmt.Errorf("service container %s: timed out while waiting for healthy or unhealthy status to be reported", c.GetName())
|
return fmt.Errorf("service container %s: timed out while waiting for healthy or unhealthy status to be reported", c.GetName())
|
||||||
} else if errors.Is(err, context.Canceled) {
|
} else if errors.Is(err, context.Canceled) {
|
||||||
return err
|
return err
|
||||||
|
} else if errors.Is(err, container.ErrContainerNotFound) || (err == nil && health == container.HealthUnHealthy) {
|
||||||
|
// Container absent (terminated during health check) and unhealthy are difficult to consistently report
|
||||||
|
// differently from each other as, in docker, a terminated container will briefly appear unhealthy and
|
||||||
|
// then start reporting container not found; so, report both the same. Without any detection of the
|
||||||
|
// ErrContainerNotFound case we would just treat it as a transient failure and timeout, which would be a
|
||||||
|
// slower error mode that this is working to avoid.
|
||||||
|
return fmt.Errorf("service container %s: failed health check or terminated before becoming healthy", c.GetName())
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
// assume transient error in the execution environment
|
||||||
logger.Warnf("service container %s: error while checking for health state, will retry: %v", c.GetName(), err)
|
logger.Warnf("service container %s: error while checking for health state, will retry: %v", c.GetName(), err)
|
||||||
} else if health == container.HealthUnHealthy {
|
|
||||||
return fmt.Errorf("service container %s failed health check", c.GetName())
|
|
||||||
} else if health == container.HealthHealthy {
|
} else if health == container.HealthHealthy {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -323,7 +323,8 @@ func TestRunner_RunEvent(t *testing.T) {
|
||||||
{workdir, "services", "push", "", platforms, secrets},
|
{workdir, "services", "push", "", platforms, secrets},
|
||||||
{workdir, "services-with-container", "push", "", platforms, secrets},
|
{workdir, "services-with-container", "push", "", platforms, secrets},
|
||||||
{workdir, "mysql-service-container-with-health-check", "push", "", platforms, secrets},
|
{workdir, "mysql-service-container-with-health-check", "push", "", platforms, secrets},
|
||||||
{workdir, "mysql-service-container-failed-health-check", "push", "service container NAME failed health check", platforms, secrets},
|
{workdir, "mysql-service-container-failed-health-check", "push", "service container NAME: failed health check or terminated before becoming healthy", platforms, secrets},
|
||||||
|
{workdir, "mysql-service-container-premature-terminate", "push", "service container NAME: failed health check or terminated before becoming healthy", platforms, secrets},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
|
|
21
act/runner/testdata/mysql-service-container-premature-terminate/push.yml
vendored
Normal file
21
act/runner/testdata/mysql-service-container-premature-terminate/push.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: service-container
|
||||||
|
on: push
|
||||||
|
jobs:
|
||||||
|
service-container-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: code.forgejo.org/oci/mysql:8.4
|
||||||
|
services:
|
||||||
|
maindb:
|
||||||
|
image: code.forgejo.org/oci/mysql:8.4
|
||||||
|
# This container should immediately exit due to missing env variable for poassword config. ... [ERROR]
|
||||||
|
# [Entrypoint]: Database is uninitialized and password option is not specified You need to specify one of the
|
||||||
|
# following as an environment variable:
|
||||||
|
# - MYSQL_ROOT_PASSWORD
|
||||||
|
# - MYSQL_ALLOW_EMPTY_PASSWORD
|
||||||
|
# - MYSQL_RANDOM_ROOT_PASSWORD
|
||||||
|
#
|
||||||
|
# This container should retain the same health check config as the mysql-service-container-with-health-check
|
||||||
|
# case.
|
||||||
|
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||||
|
steps:
|
||||||
|
- run: exit 100 # should never be hit since service will never be healthy
|
Loading…
Add table
Add a link
Reference in a new issue