Refactor pre/post update commands

This commit is contained in:
darknode 2018-10-31 21:04:04 +02:00 committed by Alexandre Menif
parent a362cb34a8
commit 7f6d4db0b7
2 changed files with 112 additions and 62 deletions

View file

@ -50,14 +50,7 @@ func Update(client container.Client, filter container.Filter, cleanup bool, noRe
if container.Stale {
// Execute the pre-update command if it is defined.
preUpdateCommand := container.PreUpdateCommand()
if enableUpdateCmd && len(preUpdateCommand) > 0 {
log.Info("Executing pre-update command.")
if err := client.ExecuteCommand(container, preUpdateCommand); err != nil {
log.Error(err)
}
}
executePreUpdateCommand(enableUpdateCmd, client, container)
if err := client.StopContainer(container, timeout); err != nil {
log.Error(err)
@ -81,23 +74,11 @@ func Update(client container.Client, filter container.Filter, cleanup bool, noRe
if !noRestart {
// We need to get the command before starting
// the container, because `client.StartContainer`
// has some side effects that erase the labels
// from the container if they are defined in the
// the image.
postUpdateCommand := container.PostUpdateCommand()
if err := client.StartContainer(container); err != nil {
newContainerID, err := client.StartContainer(container)
if err != nil {
log.Error(err)
} else {
// Execute the post-update command if it is defined.
if enableUpdateCmd && len(postUpdateCommand) > 0 {
log.Info("Executing post-update command.")
if err := client.ExecuteCommand(container, postUpdateCommand); err != nil {
log.Error(err)
}
}
executePostUpdateCommand(enableUpdateCmd, client, newContainerID)
}
}
@ -110,6 +91,40 @@ func Update(client container.Client, filter container.Filter, cleanup bool, noRe
return nil
}
func executePreUpdateCommand(enabled bool, client container.Client, container container.Container) {
if !enabled {
return
}
command := container.PreUpdateCommand()
if len(command) > 0 {
log.Info("Executing pre-update command.")
if err := client.ExecuteCommand(container.ID(), command); err != nil {
log.Error(err)
}
}
}
func executePostUpdateCommand(enabled bool, client container.Client, newContainerID string) {
if !enabled {
return
}
newContainer, err := client.GetContainer(newContainerID)
if err != nil {
log.Error(err)
return
}
command := newContainer.PostUpdateCommand()
if len(command) > 0 {
log.Info("Executing post-update command.")
if err := client.ExecuteCommand(newContainerID, command); err != nil {
log.Error(err)
}
}
}
func checkDependencies(containers []container.Container) {
for i, parent := range containers {

View file

@ -1,8 +1,10 @@
package container
import (
"bytes"
"fmt"
"io/ioutil"
"strings"
"time"
"github.com/docker/docker/api/types"
@ -20,12 +22,13 @@ const (
// Docker API.
type Client interface {
ListContainers(Filter) ([]Container, error)
GetContainer(containerID string) (Container, error)
StopContainer(Container, time.Duration) error
StartContainer(Container) error
StartContainer(Container) (string, error)
RenameContainer(Container, string) error
IsContainerStale(Container) (bool, error)
RemoveImage(Container) error
ExecuteCommand(Container, string) error
ExecuteCommand(containerID string, command string) error
}
// NewClient returns a new Client instance which can be used to interact with
@ -64,26 +67,36 @@ func (client dockerClient) ListContainers(fn Filter) ([]Container, error) {
}
for _, runningContainer := range runningContainers {
containerInfo, err := client.api.ContainerInspect(bg, runningContainer.ID)
container, err := client.GetContainer(runningContainer.ID)
if err != nil {
return nil, err
}
imageInfo, _, err := client.api.ImageInspectWithRaw(bg, containerInfo.Image)
if err != nil {
return nil, err
}
c := Container{containerInfo: &containerInfo, imageInfo: &imageInfo}
if fn(c) {
cs = append(cs, c)
if fn(container) {
cs = append(cs, container)
}
}
return cs, nil
}
func (client dockerClient) GetContainer(containerID string) (Container, error) {
bg := context.Background()
containerInfo, err := client.api.ContainerInspect(bg, containerID)
if err != nil {
return Container{}, err
}
imageInfo, _, err := client.api.ImageInspectWithRaw(bg, containerInfo.Image)
if err != nil {
return Container{}, err
}
container := Container{containerInfo: &containerInfo, imageInfo: &imageInfo}
return container, nil
}
func (client dockerClient) StopContainer(c Container, timeout time.Duration) error {
bg := context.Background()
signal := c.StopSignal()
@ -118,7 +131,7 @@ func (client dockerClient) StopContainer(c Container, timeout time.Duration) err
return nil
}
func (client dockerClient) StartContainer(c Container) error {
func (client dockerClient) StartContainer(c Container) (string, error) {
bg := context.Background()
config := c.runtimeConfig()
hostConfig := c.hostConfig()
@ -140,7 +153,7 @@ func (client dockerClient) StartContainer(c Container) error {
log.Infof("Creating %s", name)
creation, err := client.api.ContainerCreate(bg, config, hostConfig, simpleNetworkConfig, name)
if err != nil {
return err
return "", err
}
if !(hostConfig.NetworkMode.IsHost()) {
@ -148,14 +161,14 @@ func (client dockerClient) StartContainer(c Container) error {
for k := range simpleNetworkConfig.EndpointsConfig {
err = client.api.NetworkDisconnect(bg, k, creation.ID, true)
if err != nil {
return err
return "", err
}
}
for k, v := range networkConfig.EndpointsConfig {
err = client.api.NetworkConnect(bg, k, creation.ID, v)
if err != nil {
return err
return "", err
}
}
@ -165,10 +178,10 @@ func (client dockerClient) StartContainer(c Container) error {
err = client.api.ContainerStart(bg, creation.ID, types.ContainerStartOptions{})
if err != nil {
return err
return "", err
}
return nil
return creation.ID, nil
}
@ -223,16 +236,9 @@ func (client dockerClient) IsContainerStale(c Container) (bool, error) {
return false, nil
}
func (client dockerClient) ExecuteCommand(c Container, command string) error {
func (client dockerClient) ExecuteCommand(containerID string, command string) error {
bg := context.Background()
// Get the id of the actual container
containerJSON, err := client.api.ContainerInspect(bg, c.Name())
if err != nil {
return err
}
containerID := containerJSON.ID
// Create the exec
execConfig := types.ExecConfig{
Tty: true,
@ -241,11 +247,22 @@ func (client dockerClient) ExecuteCommand(c Container, command string) error {
Detach: false,
Cmd: []string{"sh", "-c", command},
}
exec, err := client.api.ContainerExecCreate(bg, containerID, execConfig)
if err != nil {
return err
}
response, attachErr := client.api.ContainerExecAttach(bg, exec.ID, types.ExecConfig{
Tty: true,
AttachStderr: true,
AttachStdout: true,
Detach: false,
})
if attachErr != nil {
log.Errorf("Failed to extract command exec logs: %v", attachErr)
}
// Run the exec
execStartCheck := types.ExecStartCheck{Detach: false, Tty: true}
err = client.api.ContainerExecStart(bg, exec.ID, execStartCheck)
@ -253,14 +270,32 @@ func (client dockerClient) ExecuteCommand(c Container, command string) error {
return err
}
var execOutput string
if attachErr == nil {
defer response.Close()
var writer bytes.Buffer
written, err := writer.ReadFrom(response.Reader)
if err != nil {
log.Error(err)
} else if written > 0 {
execOutput = strings.TrimSpace(writer.String())
}
}
// Inspect the exec to get the exit code and print a message if the
// exit code is not success.
execInspect, err := client.api.ContainerExecInspect(bg, exec.ID)
if err != nil {
return err
}
if execInspect.ExitCode > 0 {
log.Errorf("Command exited with code %v.", execInspect.ExitCode)
log.Error(execOutput)
} else {
if len(execOutput) > 0 {
log.Infof("Command output:\n%v", execOutput)
}
}
return nil