mirror of https://github.com/docker/cli.git
Merge pull request #205 from redpanda/rollback
Add 'docker service rollback' subcommand
This commit is contained in:
commit
3c7ede6a68
|
@ -5,17 +5,18 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
// Import builders to get the builder function as package function
|
||||||
|
. "github.com/docker/cli/cli/internal/test/builders"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeClient struct {
|
type fakeClient struct {
|
||||||
client.Client
|
client.Client
|
||||||
serviceListFunc func(context.Context, types.ServiceListOptions) ([]swarm.Service, error)
|
serviceInspectWithRawFunc func(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error)
|
||||||
|
serviceUpdateFunc func(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
|
||||||
|
serviceListFunc func(context.Context, types.ServiceListOptions) ([]swarm.Service, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
func (f *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
||||||
if f.serviceListFunc != nil {
|
|
||||||
return f.serviceListFunc(ctx, options)
|
|
||||||
}
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +24,30 @@ func (f *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
func (f *fakeClient) ServiceInspectWithRaw(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||||
|
if f.serviceInspectWithRawFunc != nil {
|
||||||
|
return f.serviceInspectWithRawFunc(ctx, serviceID, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return *Service(ServiceID(serviceID)), []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
if f.serviceListFunc != nil {
|
||||||
|
return f.serviceListFunc(ctx, options)
|
||||||
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fakeClient) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||||
|
if f.serviceUpdateFunc != nil {
|
||||||
|
return f.serviceUpdateFunc(ctx, serviceID, version, service, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.ServiceUpdateResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func newService(id string, name string) swarm.Service {
|
func newService(id string, name string) swarm.Service {
|
||||||
return swarm.Service{
|
return swarm.Service{
|
||||||
ID: id,
|
ID: id,
|
||||||
|
|
|
@ -26,6 +26,7 @@ func NewServiceCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
newScaleCommand(dockerCli),
|
newScaleCommand(dockerCli),
|
||||||
newUpdateCommand(dockerCli),
|
newUpdateCommand(dockerCli),
|
||||||
newLogsCommand(dockerCli),
|
newLogsCommand(dockerCli),
|
||||||
|
newRollbackCommand(dockerCli),
|
||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
opts := newServiceOptions()
|
opts := newServiceOptions()
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
@ -62,7 +62,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *serviceOptions) error {
|
func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, opts *serviceOptions) error {
|
||||||
apiClient := dockerCli.Client()
|
apiClient := dockerCli.Client()
|
||||||
createOpts := types.ServiceCreateOptions{}
|
createOpts := types.ServiceCreateOptions{}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
|
|
||||||
// waitOnService waits for the service to converge. It outputs a progress bar,
|
// waitOnService waits for the service to converge. It outputs a progress bar,
|
||||||
// if appropriate based on the CLI flags.
|
// if appropriate based on the CLI flags.
|
||||||
func waitOnService(ctx context.Context, dockerCli *command.DockerCli, serviceID string, quiet bool) error {
|
func waitOnService(ctx context.Context, dockerCli command.Cli, serviceID string, quiet bool) error {
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
pipeReader, pipeWriter := io.Pipe()
|
pipeReader, pipeWriter := io.Pipe()
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ type inspectOptions struct {
|
||||||
pretty bool
|
pretty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
var opts inspectOptions
|
var opts inspectOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
@ -43,7 +43,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "rm SERVICE [SERVICE...]",
|
Use: "rm SERVICE [SERVICE...]",
|
||||||
|
@ -27,7 +27,7 @@ func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRemove(dockerCli *command.DockerCli, sids []string) error {
|
func runRemove(dockerCli command.Cli, sids []string) error {
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli"
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newRollbackCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
|
options := newServiceOptions()
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "rollback [OPTIONS] SERVICE",
|
||||||
|
Short: "Revert changes to a service's configuration",
|
||||||
|
Args: cli.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runRollback(dockerCli, cmd.Flags(), options, args[0])
|
||||||
|
},
|
||||||
|
Tags: map[string]string{"version": "1.31"},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.BoolVarP(&options.quiet, flagQuiet, "q", false, "Suppress progress output")
|
||||||
|
addDetachFlag(flags, &options.detach)
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRollback(dockerCli command.Cli, flags *pflag.FlagSet, options *serviceOptions, serviceID string) error {
|
||||||
|
apiClient := dockerCli.Client()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
spec := &service.Spec
|
||||||
|
updateOpts := types.ServiceUpdateOptions{
|
||||||
|
Rollback: "previous",
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, *spec, updateOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, warning := range response.Warnings {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), warning)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
|
||||||
|
|
||||||
|
if options.detach {
|
||||||
|
warnDetachDefault(dockerCli.Err(), apiClient.ClientVersion(), flags, "rolled back")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return waitOnService(ctx, dockerCli, serviceID, options.quiet)
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/internal/test"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/pkg/testutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRollback(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
serviceUpdateFunc func(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
|
||||||
|
expectedDockerCliErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "rollback-service",
|
||||||
|
args: []string{"service-id"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rollback-service-with-warnings",
|
||||||
|
args: []string{"service-id"},
|
||||||
|
serviceUpdateFunc: func(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||||
|
response := types.ServiceUpdateResponse{}
|
||||||
|
|
||||||
|
response.Warnings = []string{
|
||||||
|
"- warning 1",
|
||||||
|
"- warning 2",
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
},
|
||||||
|
expectedDockerCliErr: "- warning 1\n- warning 2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
cli := test.NewFakeCli(&fakeClient{
|
||||||
|
serviceUpdateFunc: tc.serviceUpdateFunc,
|
||||||
|
})
|
||||||
|
cmd := newRollbackCommand(cli)
|
||||||
|
cmd.SetArgs(tc.args)
|
||||||
|
cmd.Flags().Set("quiet", "true")
|
||||||
|
cmd.SetOutput(ioutil.Discard)
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
assert.Equal(t, strings.TrimSpace(cli.ErrBuffer().String()), tc.expectedDockerCliErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRollbackWithErrors(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
serviceInspectWithRawFunc func(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error)
|
||||||
|
serviceUpdateFunc func(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "not-enough-args",
|
||||||
|
expectedError: "requires exactly 1 argument",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "too-many-args",
|
||||||
|
args: []string{"service-id-1", "service-id-2"},
|
||||||
|
expectedError: "requires exactly 1 argument",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "service-does-not-exists",
|
||||||
|
args: []string{"service-id"},
|
||||||
|
serviceInspectWithRawFunc: func(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||||
|
return swarm.Service{}, []byte{}, fmt.Errorf("no such services: %s", serviceID)
|
||||||
|
},
|
||||||
|
expectedError: "no such services: service-id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "service-update-failed",
|
||||||
|
args: []string{"service-id"},
|
||||||
|
serviceUpdateFunc: func(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
||||||
|
return types.ServiceUpdateResponse{}, fmt.Errorf("no such services: %s", serviceID)
|
||||||
|
},
|
||||||
|
expectedError: "no such services: service-id",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
cmd := newRollbackCommand(
|
||||||
|
test.NewFakeCli(&fakeClient{
|
||||||
|
serviceInspectWithRawFunc: tc.serviceInspectWithRawFunc,
|
||||||
|
serviceUpdateFunc: tc.serviceUpdateFunc,
|
||||||
|
}))
|
||||||
|
cmd.SetArgs(tc.args)
|
||||||
|
cmd.Flags().Set("quiet", "true")
|
||||||
|
cmd.SetOutput(ioutil.Discard)
|
||||||
|
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ type scaleOptions struct {
|
||||||
detach bool
|
detach bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newScaleCommand(dockerCli *command.DockerCli) *cobra.Command {
|
func newScaleCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
options := &scaleOptions{}
|
options := &scaleOptions{}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
@ -54,7 +54,7 @@ func scaleArgs(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runScale(dockerCli *command.DockerCli, flags *pflag.FlagSet, options *scaleOptions, args []string) error {
|
func runScale(dockerCli command.Cli, flags *pflag.FlagSet, options *scaleOptions, args []string) error {
|
||||||
var errs []string
|
var errs []string
|
||||||
var serviceIDs []string
|
var serviceIDs []string
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -96,7 +96,7 @@ func runScale(dockerCli *command.DockerCli, flags *pflag.FlagSet, options *scale
|
||||||
return errors.Errorf(strings.Join(errs, "\n"))
|
return errors.Errorf(strings.Join(errs, "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServiceScale(ctx context.Context, dockerCli *command.DockerCli, serviceID string, scale uint64) error {
|
func runServiceScale(ctx context.Context, dockerCli command.Cli, serviceID string, scale uint64) error {
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
|
|
||||||
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
options := newServiceOptions()
|
options := newServiceOptions()
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
@ -103,7 +103,7 @@ func newListOptsVar() *opts.ListOpts {
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
// nolint: gocyclo
|
||||||
func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, options *serviceOptions, serviceID string) error {
|
func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, options *serviceOptions, serviceID string) error {
|
||||||
apiClient := dockerCli.Client()
|
apiClient := dockerCli.Client()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
|
@ -875,9 +875,10 @@ x3ti0erg11rjpg64m75kej2mz-hosttempl
|
||||||
* [service inspect](service_inspect.md)
|
* [service inspect](service_inspect.md)
|
||||||
* [service logs](service_logs.md)
|
* [service logs](service_logs.md)
|
||||||
* [service ls](service_ls.md)
|
* [service ls](service_ls.md)
|
||||||
* [service rm](service_rm.md)
|
|
||||||
* [service scale](service_scale.md)
|
|
||||||
* [service ps](service_ps.md)
|
* [service ps](service_ps.md)
|
||||||
|
* [service rm](service_rm.md)
|
||||||
|
* [service rollback](service_rollback.md)
|
||||||
|
* [service scale](service_scale.md)
|
||||||
* [service update](service_update.md)
|
* [service update](service_update.md)
|
||||||
|
|
||||||
<style>table tr > td:first-child { white-space: nowrap;}</style>
|
<style>table tr > td:first-child { white-space: nowrap;}</style>
|
||||||
|
|
|
@ -164,7 +164,8 @@ $ docker service inspect --format='{{.Spec.Mode.Replicated.Replicas}}' redis
|
||||||
* [service create](service_create.md)
|
* [service create](service_create.md)
|
||||||
* [service logs](service_logs.md)
|
* [service logs](service_logs.md)
|
||||||
* [service ls](service_ls.md)
|
* [service ls](service_ls.md)
|
||||||
* [service rm](service_rm.md)
|
|
||||||
* [service scale](service_scale.md)
|
|
||||||
* [service ps](service_ps.md)
|
* [service ps](service_ps.md)
|
||||||
|
* [service rm](service_rm.md)
|
||||||
|
* [service rollback](service_rollback.md)
|
||||||
|
* [service scale](service_scale.md)
|
||||||
* [service update](service_update.md)
|
* [service update](service_update.md)
|
||||||
|
|
|
@ -79,7 +79,8 @@ fraction of a second no more than nine digits long. You can combine the
|
||||||
* [service create](service_create.md)
|
* [service create](service_create.md)
|
||||||
* [service inspect](service_inspect.md)
|
* [service inspect](service_inspect.md)
|
||||||
* [service ls](service_ls.md)
|
* [service ls](service_ls.md)
|
||||||
* [service rm](service_rm.md)
|
|
||||||
* [service scale](service_scale.md)
|
|
||||||
* [service ps](service_ps.md)
|
* [service ps](service_ps.md)
|
||||||
|
* [service rm](service_rm.md)
|
||||||
|
* [service rollback](service_rollback.md)
|
||||||
|
* [service scale](service_scale.md)
|
||||||
* [service update](service_update.md)
|
* [service update](service_update.md)
|
||||||
|
|
|
@ -158,7 +158,8 @@ fm6uf97exkul: global 5/5
|
||||||
* [service create](service_create.md)
|
* [service create](service_create.md)
|
||||||
* [service inspect](service_inspect.md)
|
* [service inspect](service_inspect.md)
|
||||||
* [service logs](service_logs.md)
|
* [service logs](service_logs.md)
|
||||||
* [service rm](service_rm.md)
|
|
||||||
* [service scale](service_scale.md)
|
|
||||||
* [service ps](service_ps.md)
|
* [service ps](service_ps.md)
|
||||||
|
* [service rm](service_rm.md)
|
||||||
|
* [service rollback](service_rollback.md)
|
||||||
|
* [service scale](service_scale.md)
|
||||||
* [service update](service_update.md)
|
* [service update](service_update.md)
|
||||||
|
|
|
@ -190,5 +190,6 @@ top.3: busybox
|
||||||
* [service logs](service_logs.md)
|
* [service logs](service_logs.md)
|
||||||
* [service ls](service_ls.md)
|
* [service ls](service_ls.md)
|
||||||
* [service rm](service_rm.md)
|
* [service rm](service_rm.md)
|
||||||
|
* [service rollback](service_rollback.md)
|
||||||
* [service scale](service_scale.md)
|
* [service scale](service_scale.md)
|
||||||
* [service update](service_update.md)
|
* [service update](service_update.md)
|
||||||
|
|
|
@ -55,6 +55,7 @@ ID NAME MODE REPLICAS IMAGE
|
||||||
* [service inspect](service_inspect.md)
|
* [service inspect](service_inspect.md)
|
||||||
* [service logs](service_logs.md)
|
* [service logs](service_logs.md)
|
||||||
* [service ls](service_ls.md)
|
* [service ls](service_ls.md)
|
||||||
* [service scale](service_scale.md)
|
|
||||||
* [service ps](service_ps.md)
|
* [service ps](service_ps.md)
|
||||||
|
* [service rollback](service_rollback.md)
|
||||||
|
* [service scale](service_scale.md)
|
||||||
* [service update](service_update.md)
|
* [service update](service_update.md)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
---
|
||||||
|
title: "service rollback"
|
||||||
|
description: "The service rollback command description and usage"
|
||||||
|
keywords: "service, rollback"
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- This file is maintained within the docker/cli Github
|
||||||
|
repository at https://github.com/docker/cli/. Make all
|
||||||
|
pull requests against that repo. If you see this file in
|
||||||
|
another repository, consider it read-only there, as it will
|
||||||
|
periodically be overwritten by the definitive file. Pull
|
||||||
|
requests which include edits to this file in other repositories
|
||||||
|
will be rejected.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# service rollback
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Usage: docker service rollback SERVICE
|
||||||
|
|
||||||
|
Revert changes to a service's configuration
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-d, --detach Exit immediately instead of waiting for the service to converge (default true)
|
||||||
|
--help Print usage
|
||||||
|
-q, --quiet Suppress progress output
|
||||||
|
```
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Roll back a specified service to its previous version from the swarm. This command must be run
|
||||||
|
targeting a manager node.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Roll back to the previous version of a service
|
||||||
|
|
||||||
|
Use the `docker service rollback` command to roll back to the previous version
|
||||||
|
of a service. After executing this command, the service is reverted to the
|
||||||
|
configuration that was in place before the most recent `docker service update`
|
||||||
|
command.
|
||||||
|
|
||||||
|
The following example creates a service with a single replica, updates the
|
||||||
|
service to use three replicas, and then rolls back the service to the
|
||||||
|
previous version, having one replica.
|
||||||
|
|
||||||
|
Create a service with a single replica:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker service create --name my-service -p 8080:80 nginx:alpine
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm that the service is running with a single replica:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker service ls
|
||||||
|
|
||||||
|
ID NAME MODE REPLICAS IMAGE PORTS
|
||||||
|
xbw728mf6q0d my-service replicated 1/1 nginx:alpine *:8080->80/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
Update the service to use three replicas:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker service update --replicas=3 my-service
|
||||||
|
|
||||||
|
$ docker service ls
|
||||||
|
|
||||||
|
ID NAME MODE REPLICAS IMAGE PORTS
|
||||||
|
xbw728mf6q0d my-service replicated 3/3 nginx:alpine *:8080->80/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
Now roll back the service to its previous version, and confirm it is
|
||||||
|
running a single replica again:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker service rollback my-service
|
||||||
|
|
||||||
|
$ docker service ls
|
||||||
|
|
||||||
|
ID NAME MODE REPLICAS IMAGE PORTS
|
||||||
|
xbw728mf6q0d my-service replicated 1/1 nginx:alpine *:8080->80/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related commands
|
||||||
|
|
||||||
|
* [service create](service_create.md)
|
||||||
|
* [service inspect](service_inspect.md)
|
||||||
|
* [service logs](service_logs.md)
|
||||||
|
* [service ls](service_ls.md)
|
||||||
|
* [service ps](service_ps.md)
|
||||||
|
* [service rm](service_rm.md)
|
||||||
|
* [service scale](service_scale.md)
|
||||||
|
* [service update](service_update.md)
|
|
@ -101,5 +101,6 @@ ID NAME MODE REPLICAS IMAGE
|
||||||
* [service logs](service_logs.md)
|
* [service logs](service_logs.md)
|
||||||
* [service ls](service_ls.md)
|
* [service ls](service_ls.md)
|
||||||
* [service rm](service_rm.md)
|
* [service rm](service_rm.md)
|
||||||
|
* [service rollback](service_rollback.md)
|
||||||
* [service ps](service_ps.md)
|
* [service ps](service_ps.md)
|
||||||
* [service update](service_update.md)
|
* [service update](service_update.md)
|
||||||
|
|
|
@ -174,9 +174,9 @@ $ docker service update --mount-rm /somewhere myservice
|
||||||
myservice
|
myservice
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rolling back to the previous version of a service
|
### Roll back to the previous version of a service
|
||||||
|
|
||||||
Use the `--rollback` option to roll back to the previous version of the service.
|
Use the `--rollback` option to roll back to the previous version of the service.
|
||||||
|
|
||||||
This will revert the service to the configuration that was in place before the most recent `docker service update` command.
|
This will revert the service to the configuration that was in place before the most recent `docker service update` command.
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ ID NAME MODE REPLICAS IMAGE
|
||||||
80bvrzp6vxf3 web replicated 0/5 nginx:alpine
|
80bvrzp6vxf3 web replicated 0/5 nginx:alpine
|
||||||
|
|
||||||
```
|
```
|
||||||
Roll back the `web` service...
|
Roll back the `web` service...
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ docker service update --rollback web
|
$ docker service update --rollback web
|
||||||
|
@ -266,4 +266,5 @@ See [`service create`](./service_create.md#templating) for the reference.
|
||||||
* [service ls](service_ls.md)
|
* [service ls](service_ls.md)
|
||||||
* [service ps](service_ps.md)
|
* [service ps](service_ps.md)
|
||||||
* [service rm](service_rm.md)
|
* [service rm](service_rm.md)
|
||||||
|
* [service rollback](service_rollback.md)
|
||||||
* [service scale](service_scale.md)
|
* [service scale](service_scale.md)
|
||||||
|
|
Loading…
Reference in New Issue