fix(stack): ensure downstream deployer respect timeout context [BE-12960] (#3021)

This commit is contained in:
Oscar Zhou
2026-06-24 15:39:39 +12:00
committed by GitHub
parent 49bfbacfd7
commit ff106887f4
2 changed files with 27 additions and 12 deletions
+7
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"os"
"strings"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
@@ -28,6 +29,12 @@ func (c *Client) ApplyDynamic(ctx context.Context, manifests []string) (string,
return "", fmt.Errorf("failed to create REST config: %w", err)
}
// Propagate context deadline to the HTTP client so discovery calls (which
// don't accept a context) still honour the caller's timeout.
if deadline, ok := ctx.Deadline(); ok {
restConfig.Timeout = time.Until(deadline)
}
// Create dynamic client
dynamicClient, err := dynamic.NewForConfig(restConfig)
if err != nil {
+20 -12
View File
@@ -75,11 +75,15 @@ func NewSwarmDeployer() *SwarmDeployer { return &SwarmDeployer{} }
// Deploy creates or updates a Docker Swarm stack from the given compose files.
func (d *SwarmDeployer) Deploy(ctx context.Context, filePaths []string, options DeployOptions) error {
// WithCli replaces the context with Background internally, so we capture the
// caller's context here to preserve cancellation.
callerCtx := ctx
return libstack.WithCli(
ctx,
libstack.DockerCliOptions{Host: options.Host, Registries: options.Registries},
func(ctx context.Context, dockerCLI *command.DockerCli) error {
return deployStack(ctx, dockerCLI, filePaths, options)
func(_ context.Context, dockerCLI *command.DockerCli) error {
return deployStack(callerCtx, dockerCLI, filePaths, options)
})
}
@@ -91,28 +95,32 @@ func (d *SwarmDeployer) Validate(_ context.Context, filePaths []string, options
// Remove deletes all resources belonging to a Swarm stack and waits for tasks to terminate.
func (d *SwarmDeployer) Remove(ctx context.Context, projectName string, options RemoveOptions) error {
// WithCli replaces the context with Background internally, so we capture the
// caller's context here to preserve cancellation.
callerCtx := ctx
return libstack.WithCli(
ctx,
libstack.DockerCliOptions{Host: options.Host, Registries: options.Registries},
func(ctx context.Context, dockerCLI *command.DockerCli) error {
func(_ context.Context, dockerCLI *command.DockerCli) error {
apiClient := dockerCLI.Client()
services, err := getStackServices(ctx, apiClient, projectName)
services, err := getStackServices(callerCtx, apiClient, projectName)
if err != nil {
return err
}
secrets, err := getStackSecrets(ctx, apiClient, projectName)
secrets, err := getStackSecrets(callerCtx, apiClient, projectName)
if err != nil {
return err
}
configs, err := getStackConfigs(ctx, apiClient, projectName)
configs, err := getStackConfigs(callerCtx, apiClient, projectName)
if err != nil {
return err
}
networks, err := getStackNetworks(ctx, apiClient, projectName)
networks, err := getStackNetworks(callerCtx, apiClient, projectName)
if err != nil {
return err
}
@@ -124,19 +132,19 @@ func (d *SwarmDeployer) Remove(ctx context.Context, projectName string, options
var errs []error
if err := removeServices(ctx, apiClient, services); err != nil {
if err := removeServices(callerCtx, apiClient, services); err != nil {
errs = append(errs, err)
}
if err := removeSecrets(ctx, apiClient, secrets); err != nil {
if err := removeSecrets(callerCtx, apiClient, secrets); err != nil {
errs = append(errs, err)
}
if err := removeConfigs(ctx, apiClient, configs); err != nil {
if err := removeConfigs(callerCtx, apiClient, configs); err != nil {
errs = append(errs, err)
}
if err := removeNetworks(ctx, apiClient, networks); err != nil {
if err := removeNetworks(callerCtx, apiClient, networks); err != nil {
errs = append(errs, err)
}
@@ -146,7 +154,7 @@ func (d *SwarmDeployer) Remove(ctx context.Context, projectName string, options
// Wait for all tasks to reach a terminal state before returning, mirroring
// the behaviour of `docker stack rm --detach=false`.
return waitOnTasks(ctx, apiClient, projectName)
return waitOnTasks(callerCtx, apiClient, projectName)
})
}