Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion model/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,16 @@ type RuntimeDefinition struct {
}

type UpdatePolicyDefinition struct {
OnNoChange UpdatePolicyMode `yaml:"on_no_change,omitempty"`
OnNoChange UpdatePolicyMode `yaml:"on_no_change,omitempty"`
PreRestartHook *Hook `yaml:"pre_restart_hook,omitempty"` // exec when the previous container state is running
}

type Hook struct {
ExecCommand []string `yaml:"exec_command,omitempty"`
ExecTimeout *int `yaml:"exec_timeout,omitempty"` // 0 means no timeout, default to 10(s)
RetriesInterval *int `yaml:"retries_interval,omitempty"` // 0 means no waiting, default to 1(s)
MaxRetries int `yaml:"max_retries,omitempty"` // 0 means no retries, default to 0
IgnoreFailed bool `yaml:"ignore_failed,omitempty"` // ignore on hook failed
}

type UpdatePolicyMode string
Expand Down
58 changes: 55 additions & 3 deletions plugin/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,11 @@ func (w *executor) runAndWaitContainers(ctx context.Context, containers ...model
}

const (
startProbeCheckInterval = 5 * time.Second
defaultCheckTimeout = 3 * time.Second
defaultProbeTimeout = 2 * time.Minute
startProbeCheckInterval = 5 * time.Second
defaultCheckTimeout = 3 * time.Second
defaultProbeTimeout = 2 * time.Minute
defaultHookExecTimeout = 10 * time.Second
defaultHookRetriesInterval = time.Second
)

func (w *executor) waitContainersReady(ctx context.Context, containers ...model.ContainerDefinition) error {
Expand Down Expand Up @@ -509,6 +511,9 @@ func (w *executor) runContainers(ctx context.Context, containers ...model.Contai
}
fallthrough
case model.UpdatePolicyModeRestart:
if err := w.doContainerPreRestart(ctx, c); err != nil {
return fmt.Errorf("pre-restart container %s: %w", c.Name, err)
}
if _, err := w.runtime.GetContainer(ctx, c.Name); err == nil || !errdefs.IsNotFound(err) {
w.Infof("remove container %s from containerd", c.Name)
_ = w.runtime.RemoveContainer(ctx, c.Name)
Expand Down Expand Up @@ -626,6 +631,53 @@ func (w *executor) doUpdateOperationMetadata(ctx context.Context, pluginHash, ti
return errdefs.FromGRPC(lo.T2(c.Update(ctx, &req)).B)
}

func (w *executor) doContainerPreRestart(ctx context.Context, c model.ContainerDefinition) error {
if c.UpdatePolicy == nil || c.UpdatePolicy.PreRestartHook == nil {
return nil
}
status, err := w.runtime.GetContainerStatus(ctx, c.Name)
if errdefs.IsNotFound(err) || err == nil && status.Status.Status != containerd.Running {
return nil
}
return w.doContainerExecHook(ctx, c.Name, "pre-restart", c.UpdatePolicy.PreRestartHook)
}

func (w *executor) doContainerExecHook(ctx context.Context, name, hookName string, hook *model.Hook) error {
interval := defaultHookRetriesInterval
if hook.RetriesInterval != nil {
interval = time.Duration(*hook.RetriesInterval) * time.Second
}
timeout := defaultHookExecTimeout
if hook.ExecTimeout != nil {
timeout = time.Duration(*hook.ExecTimeout) * time.Second
}

var herr error

for retries := hook.MaxRetries; retries >= 0; retries-- {
w.Infof("exec %s hook in container %s", hookName, name)
func() {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
result, err := w.runtime.ExecCommand(ctx, name, hook.ExecCommand)
herr = client.HandleTaskResult(result, err)
}()
if herr == nil {
break
}
w.Warningf("exec %s hook in container %s: %s", hookName, name, herr)
if retries > 0 {
time.Sleep(interval)
}
}

if herr != nil && hook.IgnoreFailed {
w.Warningf("exec %s hook in container %s: failed after %d retries", hookName, name, hook.MaxRetries+1)
return nil
}
return herr
}

func (w *executor) setupLogging(ctx context.Context) error {
if w.logging == nil {
return nil
Expand Down
Loading