feat(selfupdate): defer self-replacement until response flushed
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// Package selfupdate handles the special case where the update target
|
||||
// is the running service's own container. We must finish writing the
|
||||
// HTTP response (and flush + close the connection) before exec'ing
|
||||
// `docker compose up -d` against ourselves, otherwise the response is
|
||||
// lost when the container is replaced.
|
||||
package selfupdate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/shcizo/package-updater/internal/discovery"
|
||||
)
|
||||
|
||||
// IsSelf reports whether job targets the running service.
|
||||
// Matches on Compose project name (which is also typically the
|
||||
// service name for single-service stacks).
|
||||
func IsSelf(job discovery.Job, selfProject string) bool {
|
||||
return job.Project == selfProject
|
||||
}
|
||||
|
||||
// innerExec is the executor abstraction we wrap.
|
||||
type innerExec interface {
|
||||
Execute(ctx context.Context, job discovery.Job) error
|
||||
}
|
||||
|
||||
// Wrapped wraps an Executor with self-update-aware deferred execution.
|
||||
type Wrapped struct {
|
||||
inner innerExec
|
||||
selfProject string
|
||||
delay time.Duration
|
||||
}
|
||||
|
||||
// Wrap returns a Wrapped that defers exec until after flush() for
|
||||
// self-updates. The delay is added after flush before exec, so the
|
||||
// kernel TCP buffer has time to drain.
|
||||
func Wrap(inner innerExec, selfProject string, delay time.Duration) *Wrapped {
|
||||
return &Wrapped{inner: inner, selfProject: selfProject, delay: delay}
|
||||
}
|
||||
|
||||
// ExecuteWithFlush runs job, invoking flush() before exec for self-updates
|
||||
// and waiting `delay` after flush. For non-self jobs, exec happens
|
||||
// normally and flush is not invoked at all (the HTTP layer decides
|
||||
// when to flush in that case).
|
||||
func (w *Wrapped) ExecuteWithFlush(ctx context.Context, job discovery.Job, flush func()) error {
|
||||
if !IsSelf(job, w.selfProject) {
|
||||
return w.inner.Execute(ctx, job)
|
||||
}
|
||||
flush()
|
||||
select {
|
||||
case <-time.After(w.delay):
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
return w.inner.Execute(ctx, job)
|
||||
}
|
||||
Reference in New Issue
Block a user