Merge pull request #1621 from djdongjin/compose-pause
Add compose pause and unpause commands
This commit is contained in:
commit
102bd6c4cc
16
README.md
16
README.md
|
|
@ -338,6 +338,8 @@ It does not necessarily mean that the corresponding features are missing in cont
|
|||
- [:whale: nerdctl compose ps](#whale-nerdctl-compose-ps)
|
||||
- [:whale: nerdctl compose pull](#whale-nerdctl-compose-pull)
|
||||
- [:whale: nerdctl compose push](#whale-nerdctl-compose-push)
|
||||
- [:whale: nerdctl compose pause](#whale-nerdctl-compose-pause)
|
||||
- [:whale: nerdctl compose unpause](#whale-nerdctl-compose-unpause)
|
||||
- [:whale: nerdctl compose config](#whale-nerdctl-compose-config)
|
||||
- [:whale: nerdctl compose kill](#whale-nerdctl-compose-kill)
|
||||
- [:whale: nerdctl compose restart](#whale-nerdctl-compose-restart)
|
||||
|
|
@ -1515,6 +1517,18 @@ Usage: `nerdctl compose push [OPTIONS] [SERVICE...]`
|
|||
|
||||
Unimplemented `docker-compose pull` (V1) flags: `--ignore-push-failures`
|
||||
|
||||
### :whale: nerdctl compose pause
|
||||
|
||||
Pause all processes within containers of service(s). They can be unpaused with `nerdctl compose unpause`
|
||||
|
||||
Usage: `nerdctl compose pause [SERVICE...]`
|
||||
|
||||
### :whale: nerdctl compose unpause
|
||||
|
||||
Unpause all processes within containers of service(s)
|
||||
|
||||
Usage: `nerdctl compose unpause [SERVICE...]`
|
||||
|
||||
### :whale: nerdctl compose config
|
||||
Validate and view the Compose file
|
||||
|
||||
|
|
@ -1647,7 +1661,7 @@ Registry:
|
|||
- `docker search`
|
||||
|
||||
Compose:
|
||||
- `docker-compose create|events|pause|port|scale|start|top|unpause`
|
||||
- `docker-compose create|events|port|scale|start|top`
|
||||
|
||||
Others:
|
||||
- `docker system df`
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ func newComposeCommand() *cobra.Command {
|
|||
newComposeRunCommand(),
|
||||
newComposeVersionCommand(),
|
||||
newComposeStopCommand(),
|
||||
newComposePauseCommand(),
|
||||
newComposeUnpauseCommand(),
|
||||
)
|
||||
|
||||
return composeCommand
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
Copyright The containerd 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/nerdctl/pkg/labels"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func newComposePauseCommand() *cobra.Command {
|
||||
var composePauseCommand = &cobra.Command{
|
||||
Use: "pause [SERVICE...]",
|
||||
Short: "Pause all processes within containers of service(s). They can be unpaused with nerdctl compose unpause",
|
||||
RunE: composePauseAction,
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
DisableFlagsInUseLine: true,
|
||||
}
|
||||
return composePauseCommand
|
||||
}
|
||||
|
||||
func composePauseAction(cmd *cobra.Command, args []string) error {
|
||||
client, ctx, cancel, err := newClient(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
c, err := getComposer(cmd, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serviceNames, err := c.ServiceNames(args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containers, err := c.Containers(ctx, serviceNames...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdout := cmd.OutOrStdout()
|
||||
var mu sync.Mutex
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for _, c := range containers {
|
||||
c := c
|
||||
eg.Go(func() error {
|
||||
if err := pauseContainer(ctx, client, c.ID()); err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := c.Info(ctx, containerd.WithoutRefreshedMetadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
_, err = fmt.Fprintf(stdout, "%s\n", info.Labels[labels.Name])
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
func newComposeUnpauseCommand() *cobra.Command {
|
||||
var composeUnpauseCommand = &cobra.Command{
|
||||
Use: "unpause [SERVICE...]",
|
||||
Short: "Unpause all processes within containers of service(s).",
|
||||
RunE: composeUnpauseAction,
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
DisableFlagsInUseLine: true,
|
||||
}
|
||||
return composeUnpauseCommand
|
||||
}
|
||||
|
||||
func composeUnpauseAction(cmd *cobra.Command, args []string) error {
|
||||
client, ctx, cancel, err := newClient(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
c, err := getComposer(cmd, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serviceNames, err := c.ServiceNames(args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containers, err := c.Containers(ctx, serviceNames...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdout := cmd.OutOrStdout()
|
||||
var mu sync.Mutex
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for _, c := range containers {
|
||||
c := c
|
||||
eg.Go(func() error {
|
||||
if err := unpauseContainer(ctx, client, c.ID()); err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := c.Info(ctx, containerd.WithoutRefreshedMetadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
_, err = fmt.Fprintf(stdout, "%s\n", info.Labels[labels.Name])
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
return eg.Wait()
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
Copyright The containerd 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/nerdctl/pkg/testutil"
|
||||
)
|
||||
|
||||
func TestComposePauseAndUnpause(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
switch base.Info().CgroupDriver {
|
||||
case "none", "":
|
||||
t.Skip("requires cgroup (for pausing)")
|
||||
}
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
svc1:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, testutil.CommonImage, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
t.Logf("projectName=%q", projectName)
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").AssertOK()
|
||||
|
||||
pausedAssertHandler := func(svc string) func(stdout string) error {
|
||||
return func(stdout string) error {
|
||||
// Docker Compose v1: "Paused", v2: "paused"
|
||||
if !strings.Contains(stdout, "Paused") && !strings.Contains(stdout, "paused") {
|
||||
return fmt.Errorf("service \"%s\" must have paused", svc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
upAssertHandler := func(svc string) func(stdout string) error {
|
||||
return func(stdout string) error {
|
||||
// Docker Compose v1: "Up", v2: "running"
|
||||
if !strings.Contains(stdout, "Up") && !strings.Contains(stdout, "running") {
|
||||
return fmt.Errorf("service \"%s\" must have been still running", svc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// pause a service should (only) pause its own container
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "pause", "svc0").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "svc0").AssertOutWithFunc(pausedAssertHandler("svc0"))
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "svc1").AssertOutWithFunc(upAssertHandler("svc1"))
|
||||
|
||||
// unpause should be able to recover the paused service container
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "unpause", "svc0").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "svc0").AssertOutWithFunc(upAssertHandler("svc0"))
|
||||
}
|
||||
Loading…
Reference in New Issue