Merge pull request #1415 from ulyssessouza/add-restart

Add restart command
This commit is contained in:
Guillaume Tardif 2021-03-19 15:04:57 +01:00 коммит произвёл GitHub
Родитель 3366131096 1926820daa
Коммит e0344ea7b4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
17 изменённых файлов: 281 добавлений и 0 удалений

Просмотреть файл

@ -64,6 +64,10 @@ func (cs *aciComposeService) Start(ctx context.Context, project *types.Project,
return errdefs.ErrNotImplemented
}
func (cs *aciComposeService) Restart(ctx context.Context, project *types.Project, options compose.RestartOptions) error {
return errdefs.ErrNotImplemented
}
func (cs *aciComposeService) Stop(ctx context.Context, project *types.Project, options compose.StopOptions) error {
return errdefs.ErrNotImplemented
}

Просмотреть файл

@ -48,6 +48,10 @@ func (c *composeService) Start(ctx context.Context, project *types.Project, opti
return errdefs.ErrNotImplemented
}
func (c *composeService) Restart(ctx context.Context, project *types.Project, options compose.RestartOptions) error {
return errdefs.ErrNotImplemented
}
func (c *composeService) Stop(ctx context.Context, project *types.Project, options compose.StopOptions) error {
return errdefs.ErrNotImplemented
}

Просмотреть файл

@ -38,6 +38,8 @@ type Service interface {
Create(ctx context.Context, project *types.Project, opts CreateOptions) error
// Start executes the equivalent to a `compose start`
Start(ctx context.Context, project *types.Project, options StartOptions) error
// Restart restarts containers
Restart(ctx context.Context, project *types.Project, options RestartOptions) error
// Stop executes the equivalent to a `compose stop`
Stop(ctx context.Context, project *types.Project, options StopOptions) error
// Up executes the equivalent to a `compose up`
@ -106,6 +108,12 @@ type StartOptions struct {
Services []string
}
// RestartOptions group options of the Restart API
type RestartOptions struct {
// Timeout override container restart timeout
Timeout *time.Duration
}
// StopOptions group options of the Stop API
type StopOptions struct {
// Timeout override container stop timeout

Просмотреть файл

@ -68,6 +68,16 @@ func StartedEvent(ID string) Event {
return NewEvent(ID, Done, "Started")
}
// RestartingEvent creates a new Restarting in progress Event
func RestartingEvent(ID string) Event {
return NewEvent(ID, Working, "Restarting")
}
// RestartedEvent creates a new Restarted in progress Event
func RestartedEvent(ID string) Event {
return NewEvent(ID, Done, "Restarted")
}
// RunningEvent creates a new Running in progress Event
func RunningEvent(ID string) Event {
return NewEvent(ID, Done, "Running")

Просмотреть файл

@ -127,6 +127,7 @@ func Command(contextType string) *cobra.Command {
upCommand(&opts, contextType),
downCommand(&opts, contextType),
startCommand(&opts),
restartCommand(&opts),
stopCommand(&opts),
psCommand(&opts),
listCommand(contextType),

Просмотреть файл

@ -0,0 +1,70 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"time"
"github.com/spf13/cobra"
"github.com/docker/compose-cli/api/client"
"github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/api/progress"
)
type restartOptions struct {
*projectOptions
timeout int
}
func restartCommand(p *projectOptions) *cobra.Command {
opts := restartOptions{
projectOptions: p,
}
restartCmd := &cobra.Command{
Use: "restart",
Short: "Restart containers",
RunE: func(cmd *cobra.Command, args []string) error {
return runRestart(cmd.Context(), opts, args)
},
}
flags := restartCmd.Flags()
flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")
return restartCmd
}
func runRestart(ctx context.Context, opts restartOptions, services []string) error {
c, err := client.New(ctx)
if err != nil {
return err
}
project, err := opts.toProject(services)
if err != nil {
return err
}
timeout := time.Duration(opts.timeout) * time.Second
_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
return "", c.ComposeService().Restart(ctx, project, compose.RestartOptions{
Timeout: &timeout,
})
})
return err
}

Просмотреть файл

Просмотреть файл

@ -62,6 +62,7 @@ cname:
- docker compose ps
- docker compose pull
- docker compose push
- docker compose restart
- docker compose rm
- docker compose run
- docker compose start
@ -83,6 +84,7 @@ clink:
- docker_compose_ps.yaml
- docker_compose_pull.yaml
- docker_compose_push.yaml
- docker_compose_restart.yaml
- docker_compose_rm.yaml
- docker_compose_run.yaml
- docker_compose_start.yaml

Просмотреть файл

@ -0,0 +1,23 @@
command: docker compose restart
short: Restart containers
long: Restart containers
usage: docker compose restart
pname: docker compose
plink: docker_compose.yaml
options:
- option: timeout
shorthand: t
value_type: int
default_value: "10"
description: Specify a shutdown timeout in seconds
deprecated: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
deprecated: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false

Просмотреть файл

@ -57,6 +57,10 @@ func (e ecsLocalSimulation) Start(ctx context.Context, project *types.Project, o
return e.compose.Start(ctx, project, options)
}
func (e ecsLocalSimulation) Restart(ctx context.Context, project *types.Project, options compose.RestartOptions) error {
return e.compose.Restart(ctx, project, options)
}
func (e ecsLocalSimulation) Stop(ctx context.Context, project *types.Project, options compose.StopOptions) error {
return e.compose.Stop(ctx, project, options)
}

Просмотреть файл

@ -51,6 +51,10 @@ func (b *ecsAPIService) Start(ctx context.Context, project *types.Project, optio
return errdefs.ErrNotImplemented
}
func (b *ecsAPIService) Restart(ctx context.Context, project *types.Project, options compose.RestartOptions) error {
return errdefs.ErrNotImplemented
}
func (b *ecsAPIService) Stop(ctx context.Context, project *types.Project, options compose.StopOptions) error {
return errdefs.ErrNotImplemented
}

Просмотреть файл

@ -188,6 +188,11 @@ func (s *composeService) Start(ctx context.Context, project *types.Project, opti
return errdefs.ErrNotImplemented
}
// Restart executes the equivalent to a `compose restart`
func (s *composeService) Restart(ctx context.Context, project *types.Project, options compose.RestartOptions) error {
return errdefs.ErrNotImplemented
}
// Stop executes the equivalent to a `compose stop`
func (s *composeService) Stop(ctx context.Context, project *types.Project, options compose.StopOptions) error {
return errdefs.ErrNotImplemented

Просмотреть файл

@ -390,3 +390,26 @@ func (s *composeService) startService(ctx context.Context, project *types.Projec
}
return eg.Wait()
}
func (s *composeService) restartService(ctx context.Context, serviceName string, timeout *time.Duration) error {
containerState, err := GetContextContainerState(ctx)
if err != nil {
return err
}
containers := containerState.GetContainers().filter(isService(serviceName))
w := progress.ContextWriter(ctx)
eg, ctx := errgroup.WithContext(ctx)
for _, c := range containers {
container := c
eg.Go(func() error {
eventName := getContainerProgressName(container)
w.Event(progress.RestartingEvent(eventName))
err := s.apiClient.ContainerRestart(ctx, container.ID, timeout)
if err == nil {
w.Event(progress.StartedEvent(eventName))
}
return err
})
}
return eg.Wait()
}

39
local/compose/restart.go Normal file
Просмотреть файл

@ -0,0 +1,39 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"github.com/docker/compose-cli/api/compose"
"github.com/compose-spec/compose-go/types"
)
func (s *composeService) Restart(ctx context.Context, project *types.Project, options compose.RestartOptions) error {
ctx, err := s.getUpdatedContainersStateContext(ctx, project.Name)
if err != nil {
return err
}
err = InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
return s.restartService(ctx, service.Name, options.Timeout)
})
if err != nil {
return err
}
return nil
}

Просмотреть файл

@ -20,6 +20,7 @@ import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/pkg/errors"
)
@ -100,3 +101,17 @@ func GetContextContainerState(ctx context.Context) (ContainersState, error) {
}
return cState, nil
}
func (s composeService) getUpdatedContainersStateContext(ctx context.Context, projectName string) (context.Context, error) {
observedState, err := s.apiClient.ContainerList(ctx, types.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(projectName),
),
All: true,
})
if err != nil {
return nil, err
}
containerState := NewContainersState(observedState)
return context.WithValue(ctx, ContainersKey{}, containerState), nil
}

Просмотреть файл

@ -0,0 +1,4 @@
services:
restart:
image: busybox
command: ash -c "if [[ -f /tmp/restart.lock ]] ; then sleep infinity; else touch /tmp/restart.lock; fi"

Просмотреть файл

@ -0,0 +1,65 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package e2e
import (
"fmt"
"strings"
"testing"
"time"
testify "github.com/stretchr/testify/assert"
"gotest.tools/v3/assert"
. "github.com/docker/compose-cli/utils/e2e"
)
func TestRestart(t *testing.T) {
c := NewParallelE2eCLI(t, binDir)
const projectName = "e2e-restart"
getServiceRegx := func(service string, status string) string {
// match output with random spaces like:
// e2e-start-stop_db_1 db running
return fmt.Sprintf("%s_%s_1\\s+%s\\s+%s", projectName, service, service, status)
}
t.Run("Up a project", func(t *testing.T) {
// This is just to ensure the containers do NOT exist
c.RunDockerOrExitError("compose", "--project-name", projectName, "down")
res := c.RunDockerOrExitError("compose", "-f", "./fixtures/restart-test/compose.yml", "--project-name", projectName, "up", "-d")
assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-restart_restart_1 Started"), res.Combined())
// Give the time for it to exit
time.Sleep(time.Second)
res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps", "-a")
testify.Regexp(t, getServiceRegx("restart", "exited"), res.Stdout())
_ = c.RunDockerOrExitError("compose", "-f", "./fixtures/restart-test/compose.yml", "--project-name", projectName, "restart")
// Give the same time but it must NOT exit
time.Sleep(time.Second)
res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps")
testify.Regexp(t, getServiceRegx("restart", "running"), res.Stdout())
// Clean up
c.RunDockerOrExitError("compose", "--project-name", projectName, "down")
})
}