mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
feat(pre-deploy): correct progress events for pre-deploy hooks, make other container events more aligned with plan
This commit is contained in:
@@ -14,6 +14,7 @@ require (
|
|||||||
github.com/caddyserver/caddy/v2 v2.8.4
|
github.com/caddyserver/caddy/v2 v2.8.4
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0
|
github.com/cenkalti/backoff/v4 v4.3.0
|
||||||
github.com/charmbracelet/colorprofile v0.4.2
|
github.com/charmbracelet/colorprofile v0.4.2
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6
|
||||||
github.com/compose-spec/compose-go/v2 v2.9.0
|
github.com/compose-spec/compose-go/v2 v2.9.0
|
||||||
github.com/containerd/errdefs v1.0.0
|
github.com/containerd/errdefs v1.0.0
|
||||||
github.com/containerd/platforms v1.0.0-rc.1
|
github.com/containerd/platforms v1.0.0-rc.1
|
||||||
@@ -111,7 +112,6 @@ require (
|
|||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
|
||||||
github.com/charmbracelet/x/exp/ordered v0.1.0 // indirect
|
github.com/charmbracelet/x/exp/ordered v0.1.0 // indirect
|
||||||
github.com/charmbracelet/x/exp/strings v0.0.0-20240919170804-a4978c8e603a // indirect
|
github.com/charmbracelet/x/exp/strings v0.0.0-20240919170804-a4978c8e603a // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
|
|||||||
@@ -1,12 +1,49 @@
|
|||||||
package progress
|
package progress
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PreDeployHookEventID returns a progress event identifier for pre-deploy hook operations.
|
type eventIDKey struct{}
|
||||||
|
|
||||||
|
// WithEventID returns a context that overrides the default event ID used by client methods.
|
||||||
|
func WithEventID(ctx context.Context, eventID string) context.Context {
|
||||||
|
return context.WithValue(ctx, eventIDKey{}, eventID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerEventID returns a progress event ID for operations on existing containers using the canonical
|
||||||
|
// service_name/short_id format. Allows to override it using WithEventID.
|
||||||
|
func ContainerEventID(ctx context.Context, serviceName, containerID, machineName string) string {
|
||||||
|
if id, ok := ctx.Value(eventIDKey{}).(string); ok && id != "" {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return tui.Faint.Render("Container ") +
|
||||||
|
serviceName + tui.Faint.Render("/") + stringid.TruncateID(containerID) +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainerEventID returns a progress event ID for new container creation where the Docker container ID
|
||||||
|
// is not yet known. Allows to override it using WithEventID.
|
||||||
|
func NewContainerEventID(ctx context.Context, containerName, machineName string) string {
|
||||||
|
if id, ok := ctx.Value(eventIDKey{}).(string); ok && id != "" {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return tui.Faint.Render("Container ") + containerName +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreDeployHookEventID returns a progress event ID for pre-deploy hook operations.
|
||||||
func PreDeployHookEventID(serviceName, machineName string) string {
|
func PreDeployHookEventID(serviceName, machineName string) string {
|
||||||
return fmt.Sprintf("Pre-deploy hook for %s on %s", tui.NameStyle.Render(serviceName), tui.Bold.Render(machineName))
|
return tui.Faint.Render("Pre-deploy hook ") + serviceName +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// OldPreDeployHookEventID returns a progress event ID for old pre-deploy hook container cleanup.
|
||||||
|
func OldPreDeployHookEventID(serviceName, containerID, machineName string) string {
|
||||||
|
return tui.Faint.Render("Old pre-deploy hook ") +
|
||||||
|
serviceName + tui.Faint.Render("/") + stringid.TruncateID(containerID) +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,3 +51,13 @@ func IsStdinTerminal() bool {
|
|||||||
func IsStdoutTerminal() bool {
|
func IsStdoutTerminal() bool {
|
||||||
return term.IsTerminal(int(os.Stdout.Fd()))
|
return term.IsTerminal(int(os.Stdout.Fd()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TerminalWidth returns the width of the terminal.
|
||||||
|
// Returns 0 if stdout is not a terminal or the width cannot be determined.
|
||||||
|
func TerminalWidth() int {
|
||||||
|
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
||||||
|
if err != nil || width <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -20,10 +20,10 @@ type Client interface {
|
|||||||
type ContainerClient interface {
|
type ContainerClient interface {
|
||||||
CreateContainer(
|
CreateContainer(
|
||||||
ctx context.Context, serviceID string, spec ServiceSpec, machineID string,
|
ctx context.Context, serviceID string, spec ServiceSpec, machineID string,
|
||||||
) (container.CreateResponse, error)
|
) (CreateContainerResponse, error)
|
||||||
CreatePreDeployHookContainer(
|
CreatePreDeployHookContainer(
|
||||||
ctx context.Context, serviceID string, spec ServiceSpec, machineID string,
|
ctx context.Context, serviceID string, spec ServiceSpec, machineID string,
|
||||||
) (container.CreateResponse, error)
|
) (CreateContainerResponse, error)
|
||||||
ExecContainer(ctx context.Context, serviceNameOrID, containerNameOrID string, config ExecOptions) (int, error)
|
ExecContainer(ctx context.Context, serviceNameOrID, containerNameOrID string, config ExecOptions) (int, error)
|
||||||
InspectContainer(ctx context.Context, serviceNameOrID, containerNameOrID string) (MachineServiceContainer, error)
|
InspectContainer(ctx context.Context, serviceNameOrID, containerNameOrID string) (MachineServiceContainer, error)
|
||||||
StartContainer(ctx context.Context, serviceNameOrID, containerNameOrID string) error
|
StartContainer(ctx context.Context, serviceNameOrID, containerNameOrID string) error
|
||||||
|
|||||||
@@ -164,6 +164,13 @@ func (c *Container) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateContainerResponse wraps a container creation response with the container name assigned during creation.
|
||||||
|
type CreateContainerResponse struct {
|
||||||
|
container.CreateResponse
|
||||||
|
// Name is the container name assigned during creation.
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
type ServiceContainer struct {
|
type ServiceContainer struct {
|
||||||
Container
|
Container
|
||||||
ServiceSpec ServiceSpec
|
ServiceSpec ServiceSpec
|
||||||
|
|||||||
+16
-13
@@ -2,13 +2,12 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
@@ -28,7 +27,7 @@ import (
|
|||||||
// CreateContainer creates a new container for the given service on the specified machine.
|
// CreateContainer creates a new container for the given service on the specified machine.
|
||||||
func (cli *Client) CreateContainer(
|
func (cli *Client) CreateContainer(
|
||||||
ctx context.Context, serviceID string, spec api.ServiceSpec, machineID string,
|
ctx context.Context, serviceID string, spec api.ServiceSpec, machineID string,
|
||||||
) (container.CreateResponse, error) {
|
) (api.CreateContainerResponse, error) {
|
||||||
return cli.createServiceContainerWithPull(ctx, serviceID, spec, machineID, pb.CreateServiceContainerRequest_SERVICE)
|
return cli.createServiceContainerWithPull(ctx, serviceID, spec, machineID, pb.CreateServiceContainerRequest_SERVICE)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +35,7 @@ func (cli *Client) CreateContainer(
|
|||||||
// on the specified machine.
|
// on the specified machine.
|
||||||
func (cli *Client) CreatePreDeployHookContainer(
|
func (cli *Client) CreatePreDeployHookContainer(
|
||||||
ctx context.Context, serviceID string, spec api.ServiceSpec, machineID string,
|
ctx context.Context, serviceID string, spec api.ServiceSpec, machineID string,
|
||||||
) (container.CreateResponse, error) {
|
) (api.CreateContainerResponse, error) {
|
||||||
return cli.createServiceContainerWithPull(
|
return cli.createServiceContainerWithPull(
|
||||||
ctx, serviceID, spec, machineID, pb.CreateServiceContainerRequest_PRE_DEPLOY)
|
ctx, serviceID, spec, machineID, pb.CreateServiceContainerRequest_PRE_DEPLOY)
|
||||||
}
|
}
|
||||||
@@ -49,8 +48,8 @@ func (cli *Client) createServiceContainerWithPull(
|
|||||||
spec api.ServiceSpec,
|
spec api.ServiceSpec,
|
||||||
machineID string,
|
machineID string,
|
||||||
containerType pb.CreateServiceContainerRequest_ContainerType,
|
containerType pb.CreateServiceContainerRequest_ContainerType,
|
||||||
) (container.CreateResponse, error) {
|
) (api.CreateContainerResponse, error) {
|
||||||
var resp container.CreateResponse
|
var resp api.CreateContainerResponse
|
||||||
|
|
||||||
spec = spec.SetDefaults()
|
spec = spec.SetDefaults()
|
||||||
if err := spec.Validate(); err != nil {
|
if err := spec.Validate(); err != nil {
|
||||||
@@ -69,16 +68,16 @@ func (cli *Client) createServiceContainerWithPull(
|
|||||||
}
|
}
|
||||||
|
|
||||||
containerName := fmt.Sprintf("%s-%s", spec.Name, suffix)
|
containerName := fmt.Sprintf("%s-%s", spec.Name, suffix)
|
||||||
eventID := fmt.Sprintf("Container %s on %s", containerName, machine.Machine.Name)
|
|
||||||
if containerType == pb.CreateServiceContainerRequest_PRE_DEPLOY {
|
if containerType == pb.CreateServiceContainerRequest_PRE_DEPLOY {
|
||||||
containerName = fmt.Sprintf("%s-%s-%s", spec.Name, api.LabelHookPreDeploy, suffix)
|
containerName = fmt.Sprintf("%s-%s-%s", spec.Name, api.LabelHookPreDeploy, suffix)
|
||||||
eventID = cliprogress.PreDeployHookEventID(spec.Name, machine.Machine.Name)
|
|
||||||
}
|
}
|
||||||
|
resp.Name = containerName
|
||||||
|
|
||||||
// Proxy Docker gRPC requests to the selected machine.
|
// Proxy Docker gRPC requests to the selected machine.
|
||||||
ctx = proxyToMachine(ctx, machine.Machine)
|
ctx = proxyToMachine(ctx, machine.Machine)
|
||||||
|
|
||||||
pw := progress.ContextWriter(ctx)
|
pw := progress.ContextWriter(ctx)
|
||||||
|
eventID := cliprogress.NewContainerEventID(ctx, containerName, machine.Machine.Name)
|
||||||
pw.Event(progress.CreatingEvent(eventID))
|
pw.Event(progress.CreatingEvent(eventID))
|
||||||
|
|
||||||
if spec.Container.PullPolicy == api.PullPolicyAlways {
|
if spec.Container.PullPolicy == api.PullPolicyAlways {
|
||||||
@@ -122,7 +121,7 @@ func (cli *Client) createServiceContainerWithPull(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = json.Unmarshal(grpcResp.Response, &resp); err != nil {
|
if err = json.Unmarshal(grpcResp.Response, &resp.CreateResponse); err != nil {
|
||||||
return resp, fmt.Errorf("unmarshal gRPC response: %w", err)
|
return resp, fmt.Errorf("unmarshal gRPC response: %w", err)
|
||||||
}
|
}
|
||||||
pw.Event(progress.CreatedEvent(eventID))
|
pw.Event(progress.CreatedEvent(eventID))
|
||||||
@@ -293,7 +292,7 @@ func (cli *Client) StartContainer(ctx context.Context, serviceNameOrID, containe
|
|||||||
ctx = proxyToMachine(ctx, machine.Machine)
|
ctx = proxyToMachine(ctx, machine.Machine)
|
||||||
|
|
||||||
pw := progress.ContextWriter(ctx)
|
pw := progress.ContextWriter(ctx)
|
||||||
eventID := fmt.Sprintf("Container %s on %s", ctr.Container.Name, machine.Machine.Name)
|
eventID := cliprogress.ContainerEventID(ctx, ctr.Container.ServiceSpec.Name, ctr.Container.ID, machine.Machine.Name)
|
||||||
|
|
||||||
pw.Event(progress.StartingEvent(eventID))
|
pw.Event(progress.StartingEvent(eventID))
|
||||||
if err = cli.Docker.StartContainer(ctx, ctr.Container.ID, container.StartOptions{}); err != nil {
|
if err = cli.Docker.StartContainer(ctx, ctr.Container.ID, container.StartOptions{}); err != nil {
|
||||||
@@ -305,6 +304,8 @@ func (cli *Client) StartContainer(ctx context.Context, serviceNameOrID, containe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StopContainer stops the specified container within the service.
|
// StopContainer stops the specified container within the service.
|
||||||
|
//
|
||||||
|
//nolint:dupl // Structurally similar to RemoveContainer but performs a different operation.
|
||||||
func (cli *Client) StopContainer(
|
func (cli *Client) StopContainer(
|
||||||
ctx context.Context, serviceNameOrID, containerNameOrID string, opts container.StopOptions,
|
ctx context.Context, serviceNameOrID, containerNameOrID string, opts container.StopOptions,
|
||||||
) error {
|
) error {
|
||||||
@@ -320,7 +321,7 @@ func (cli *Client) StopContainer(
|
|||||||
ctx = proxyToMachine(ctx, machine.Machine)
|
ctx = proxyToMachine(ctx, machine.Machine)
|
||||||
|
|
||||||
pw := progress.ContextWriter(ctx)
|
pw := progress.ContextWriter(ctx)
|
||||||
eventID := fmt.Sprintf("Container %s on %s", ctr.Container.Name, machine.Machine.Name)
|
eventID := cliprogress.ContainerEventID(ctx, ctr.Container.ServiceSpec.Name, ctr.Container.ID, machine.Machine.Name)
|
||||||
|
|
||||||
pw.Event(progress.StoppingEvent(eventID))
|
pw.Event(progress.StoppingEvent(eventID))
|
||||||
if err = cli.Docker.StopContainer(ctx, ctr.Container.ID, opts); err != nil {
|
if err = cli.Docker.StopContainer(ctx, ctr.Container.ID, opts); err != nil {
|
||||||
@@ -332,6 +333,8 @@ func (cli *Client) StopContainer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveContainer removes the specified container within the service.
|
// RemoveContainer removes the specified container within the service.
|
||||||
|
//
|
||||||
|
//nolint:dupl // Structurally similar to StopContainer but performs a different operation.
|
||||||
func (cli *Client) RemoveContainer(
|
func (cli *Client) RemoveContainer(
|
||||||
ctx context.Context, serviceNameOrID, containerNameOrID string, opts container.RemoveOptions,
|
ctx context.Context, serviceNameOrID, containerNameOrID string, opts container.RemoveOptions,
|
||||||
) error {
|
) error {
|
||||||
@@ -347,7 +350,7 @@ func (cli *Client) RemoveContainer(
|
|||||||
ctx = proxyToMachine(ctx, machine.Machine)
|
ctx = proxyToMachine(ctx, machine.Machine)
|
||||||
|
|
||||||
pw := progress.ContextWriter(ctx)
|
pw := progress.ContextWriter(ctx)
|
||||||
eventID := fmt.Sprintf("Container %s on %s", ctr.Container.Name, machine.Machine.Name)
|
eventID := cliprogress.ContainerEventID(ctx, ctr.Container.ServiceSpec.Name, ctr.Container.ID, machine.Machine.Name)
|
||||||
|
|
||||||
pw.Event(progress.RemovingEvent(eventID))
|
pw.Event(progress.RemovingEvent(eventID))
|
||||||
if err = cli.Docker.RemoveServiceContainer(ctx, ctr.Container.ID, opts); err != nil {
|
if err = cli.Docker.RemoveServiceContainer(ctx, ctr.Container.ID, opts); err != nil {
|
||||||
@@ -427,7 +430,7 @@ func (cli *Client) WaitContainerHealthy(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pw := progress.ContextWriter(ctx)
|
pw := progress.ContextWriter(ctx)
|
||||||
eventID := fmt.Sprintf("Container %s on %s", mc.Container.Name, machine.Machine.Name)
|
eventID := cliprogress.ContainerEventID(ctx, mc.Container.ServiceSpec.Name, mc.Container.ID, machine.Machine.Name)
|
||||||
|
|
||||||
var monitor time.Duration
|
var monitor time.Duration
|
||||||
if opts.MonitorPeriod == nil {
|
if opts.MonitorPeriod == nil {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
cliprogress "github.com/psviderski/uncloud/internal/cli/progress"
|
||||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
)
|
)
|
||||||
@@ -28,6 +29,10 @@ func (o *RunContainerOperation) Execute(ctx context.Context, cli Client) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create container: %w", err)
|
return fmt.Errorf("create container: %w", err)
|
||||||
}
|
}
|
||||||
|
// Override event ID so StartContainer and WaitContainerHealthy update the same progress line as creation.
|
||||||
|
// TODO: This is a hack to work around the limitations of the compose progress library.
|
||||||
|
// We likely need to fork or create our own to decouple event IDs from presentation layer.
|
||||||
|
ctx = cliprogress.WithEventID(ctx, cliprogress.NewContainerEventID(ctx, resp.Name, o.MachineName))
|
||||||
if err = cli.StartContainer(ctx, o.ServiceID, resp.ID); err != nil {
|
if err = cli.StartContainer(ctx, o.ServiceID, resp.ID); err != nil {
|
||||||
return fmt.Errorf("start container: %w", err)
|
return fmt.Errorf("start container: %w", err)
|
||||||
}
|
}
|
||||||
@@ -171,13 +176,15 @@ func (o *ReplaceContainerOperation) Execute(ctx context.Context, cli Client) err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create new container: %w", err)
|
return fmt.Errorf("create new container: %w", err)
|
||||||
}
|
}
|
||||||
if err = cli.StartContainer(ctx, o.ServiceID, resp.ID); err != nil {
|
// Override event ID so StartContainer and WaitContainerHealthy update the same progress line as creation.
|
||||||
|
newCtx := cliprogress.WithEventID(ctx, cliprogress.NewContainerEventID(ctx, resp.Name, o.MachineName))
|
||||||
|
if err = cli.StartContainer(newCtx, o.ServiceID, resp.ID); err != nil {
|
||||||
return fmt.Errorf("start new container: %w", err)
|
return fmt.Errorf("start new container: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !o.SkipHealthMonitor {
|
if !o.SkipHealthMonitor {
|
||||||
opts := api.WaitContainerHealthyOptions{MonitorPeriod: o.Spec.UpdateConfig.MonitorPeriod}
|
opts := api.WaitContainerHealthyOptions{MonitorPeriod: o.Spec.UpdateConfig.MonitorPeriod}
|
||||||
if err = cli.WaitContainerHealthy(ctx, o.ServiceID, resp.ID, opts); err != nil {
|
if err = cli.WaitContainerHealthy(newCtx, o.ServiceID, resp.ID, opts); err != nil {
|
||||||
// New container failed to become healthy. Stop it and roll back to the previous container.
|
// New container failed to become healthy. Stop it and roll back to the previous container.
|
||||||
// Don't remove the new stopped container to allow users to inspect logs and state.
|
// Don't remove the new stopped container to allow users to inspect logs and state.
|
||||||
// TODO: collect logs from the new container and include in the error message to speed up debugging.
|
// TODO: collect logs from the new container and include in the error message to speed up debugging.
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"charm.land/lipgloss/v2"
|
||||||
|
"github.com/charmbracelet/x/ansi"
|
||||||
"github.com/containerd/errdefs"
|
"github.com/containerd/errdefs"
|
||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
@@ -65,13 +67,18 @@ type RunPreDeployOperation struct {
|
|||||||
func (o *RunPreDeployOperation) Execute(ctx context.Context, cli Client) error {
|
func (o *RunPreDeployOperation) Execute(ctx context.Context, cli Client) error {
|
||||||
// Remove old pre-deploy containers.
|
// Remove old pre-deploy containers.
|
||||||
for _, id := range o.OldContainerIDs {
|
for _, id := range o.OldContainerIDs {
|
||||||
_ = cli.StopContainer(ctx, o.ServiceID, id, container.StopOptions{})
|
oldCtx := cliprogress.WithEventID(ctx, cliprogress.OldPreDeployHookEventID(o.Spec.Name, id, o.MachineName))
|
||||||
err := cli.RemoveContainer(ctx, o.ServiceID, id, container.RemoveOptions{RemoveVolumes: true})
|
_ = cli.StopContainer(oldCtx, o.ServiceID, id, container.StopOptions{})
|
||||||
|
err := cli.RemoveContainer(oldCtx, o.ServiceID, id, container.RemoveOptions{RemoveVolumes: true})
|
||||||
if err != nil && !errdefs.IsNotFound(err) {
|
if err != nil && !errdefs.IsNotFound(err) {
|
||||||
return fmt.Errorf("remove old pre-deploy hook container '%s': %w", id, err)
|
return fmt.Errorf("remove old pre-deploy hook container '%s': %w", id, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the event ID override so all downstream client methods (create, start, stop) use the same
|
||||||
|
// pre-deploy hook progress event instead of the generic container one.
|
||||||
|
ctx = cliprogress.WithEventID(ctx, cliprogress.PreDeployHookEventID(o.Spec.Name, o.MachineName))
|
||||||
|
|
||||||
resp, err := cli.CreatePreDeployHookContainer(ctx, o.ServiceID, o.Spec, o.MachineID)
|
resp, err := cli.CreatePreDeployHookContainer(ctx, o.ServiceID, o.Spec, o.MachineID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create pre-deploy hook container: %w", err)
|
return fmt.Errorf("create pre-deploy hook container: %w", err)
|
||||||
@@ -107,7 +114,6 @@ func (o *RunPreDeployOperation) waitForExit(
|
|||||||
select {
|
select {
|
||||||
case <-timeoutCtx.Done():
|
case <-timeoutCtx.Done():
|
||||||
// Stop the container on timeout or context cancellation.
|
// Stop the container on timeout or context cancellation.
|
||||||
pw.Event(progress.StoppingEvent(eventID))
|
|
||||||
_ = cli.StopContainer(ctx, o.ServiceID, containerID, container.StopOptions{})
|
_ = cli.StopContainer(ctx, o.ServiceID, containerID, container.StopOptions{})
|
||||||
|
|
||||||
ctrID := containerID
|
ctrID := containerID
|
||||||
@@ -159,12 +165,25 @@ func (o *RunPreDeployOperation) waitForExit(
|
|||||||
func (o *RunPreDeployOperation) Format() string {
|
func (o *RunPreDeployOperation) Format() string {
|
||||||
cmd := strings.Join(o.Spec.PreDeploy.Command, " ")
|
cmd := strings.Join(o.Spec.PreDeploy.Command, " ")
|
||||||
|
|
||||||
return tui.BoldGreen.Render("▶") + " " +
|
prefix := tui.BoldGreen.Render("▶") + " " +
|
||||||
tui.Faint.Render("run pre-deploy hook") + " " +
|
tui.Faint.Render("run pre-deploy hook") + " " +
|
||||||
// TODO: truncate a long cmd to fit the width of the terminal.
|
o.Spec.Name + " ["
|
||||||
o.Spec.Name + " (" + cmd + ") " +
|
suffix := "] " +
|
||||||
tui.Faint.Render("on") + " " +
|
tui.Faint.Render("on") + " " +
|
||||||
o.MachineName
|
o.MachineName
|
||||||
|
|
||||||
|
// Truncate the command to fit within the terminal width.
|
||||||
|
termWidth := tui.TerminalWidth()
|
||||||
|
if termWidth > 0 {
|
||||||
|
buffer := 10
|
||||||
|
maxCmdWidth := termWidth - lipgloss.Width(prefix) - lipgloss.Width(suffix) - buffer
|
||||||
|
if maxCmdWidth <= 0 {
|
||||||
|
maxCmdWidth = 10
|
||||||
|
}
|
||||||
|
cmd = ansi.Truncate(cmd, maxCmdWidth, "…")
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix + cmd + suffix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *RunPreDeployOperation) String() string {
|
func (o *RunPreDeployOperation) String() string {
|
||||||
|
|||||||
Reference in New Issue
Block a user