Skip to content

Commit

Permalink
Merge pull request #45 from aaronlehmann/configs
Browse files Browse the repository at this point in the history
Add support for configs
  • Loading branch information
cpuguy83 authored May 12, 2017
2 parents bb1a403 + db56200 commit 05267be
Show file tree
Hide file tree
Showing 64 changed files with 2,547 additions and 50 deletions.
4 changes: 4 additions & 0 deletions cli/command/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/checkpoint"
"github.com/docker/cli/cli/command/config"
"github.com/docker/cli/cli/command/container"
"github.com/docker/cli/cli/command/image"
"github.com/docker/cli/cli/command/network"
Expand All @@ -26,6 +27,9 @@ func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
// checkpoint
checkpoint.NewCheckpointCommand(dockerCli),

// config
config.NewConfigCommand(dockerCli),

// container
container.NewContainerCommand(dockerCli),
container.NewRunCommand(dockerCli),
Expand Down
44 changes: 44 additions & 0 deletions cli/command/config/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package config

import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"golang.org/x/net/context"
)

type fakeClient struct {
client.Client
configCreateFunc func(swarm.ConfigSpec) (types.ConfigCreateResponse, error)
configInspectFunc func(string) (swarm.Config, []byte, error)
configListFunc func(types.ConfigListOptions) ([]swarm.Config, error)
configRemoveFunc func(string) error
}

func (c *fakeClient) ConfigCreate(ctx context.Context, spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
if c.configCreateFunc != nil {
return c.configCreateFunc(spec)
}
return types.ConfigCreateResponse{}, nil
}

func (c *fakeClient) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) {
if c.configInspectFunc != nil {
return c.configInspectFunc(id)
}
return swarm.Config{}, nil, nil
}

func (c *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
if c.configListFunc != nil {
return c.configListFunc(options)
}
return []swarm.Config{}, nil
}

func (c *fakeClient) ConfigRemove(ctx context.Context, name string) error {
if c.configRemoveFunc != nil {
return c.configRemoveFunc(name)
}
return nil
}
27 changes: 27 additions & 0 deletions cli/command/config/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package config

import (
"github.com/spf13/cobra"

"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
)

// NewConfigCommand returns a cobra command for `config` subcommands
// nolint: interfacer
func NewConfigCommand(dockerCli *command.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "Manage Docker configs",
Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()),
Tags: map[string]string{"version": "1.30"},
}
cmd.AddCommand(
newConfigListCommand(dockerCli),
newConfigCreateCommand(dockerCli),
newConfigInspectCommand(dockerCli),
newConfigRemoveCommand(dockerCli),
)
return cmd
}
80 changes: 80 additions & 0 deletions cli/command/config/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package config

import (
"fmt"
"io"
"io/ioutil"

"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/system"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)

type createOptions struct {
name string
file string
labels opts.ListOpts
}

func newConfigCreateCommand(dockerCli command.Cli) *cobra.Command {
createOpts := createOptions{
labels: opts.NewListOpts(opts.ValidateEnv),
}

cmd := &cobra.Command{
Use: "create [OPTIONS] CONFIG file|-",
Short: "Create a configuration file from a file or STDIN as content",
Args: cli.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
createOpts.name = args[0]
createOpts.file = args[1]
return runConfigCreate(dockerCli, createOpts)
},
}
flags := cmd.Flags()
flags.VarP(&createOpts.labels, "label", "l", "Config labels")

return cmd
}

func runConfigCreate(dockerCli command.Cli, options createOptions) error {
client := dockerCli.Client()
ctx := context.Background()

var in io.Reader = dockerCli.In()
if options.file != "-" {
file, err := system.OpenSequential(options.file)
if err != nil {
return err
}
in = file
defer file.Close()
}

configData, err := ioutil.ReadAll(in)
if err != nil {
return errors.Errorf("Error reading content from %q: %v", options.file, err)
}

spec := swarm.ConfigSpec{
Annotations: swarm.Annotations{
Name: options.name,
Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()),
},
Data: configData,
}

r, err := client.ConfigCreate(ctx, spec)
if err != nil {
return err
}

fmt.Fprintln(dockerCli.Out(), r.ID)
return nil
}
112 changes: 112 additions & 0 deletions cli/command/config/create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package config

import (
"bytes"
"io/ioutil"
"path/filepath"
"reflect"
"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/docker/docker/pkg/testutil/golden"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

const configDataFile = "config-create-with-name.golden"

func TestConfigCreateErrors(t *testing.T) {
testCases := []struct {
args []string
configCreateFunc func(swarm.ConfigSpec) (types.ConfigCreateResponse, error)
expectedError string
}{
{
args: []string{"too_few"},
expectedError: "requires exactly 2 argument(s)",
},
{args: []string{"too", "many", "arguments"},
expectedError: "requires exactly 2 argument(s)",
},
{
args: []string{"name", filepath.Join("testdata", configDataFile)},
configCreateFunc: func(configSpec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
return types.ConfigCreateResponse{}, errors.Errorf("error creating config")
},
expectedError: "error creating config",
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := newConfigCreateCommand(
test.NewFakeCli(&fakeClient{
configCreateFunc: tc.configCreateFunc,
}, buf),
)
cmd.SetArgs(tc.args)
cmd.SetOutput(ioutil.Discard)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
}
}

func TestConfigCreateWithName(t *testing.T) {
name := "foo"
buf := new(bytes.Buffer)
var actual []byte
cli := test.NewFakeCli(&fakeClient{
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
if spec.Name != name {
return types.ConfigCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
}

actual = spec.Data

return types.ConfigCreateResponse{
ID: "ID-" + spec.Name,
}, nil
},
}, buf)

cmd := newConfigCreateCommand(cli)
cmd.SetArgs([]string{name, filepath.Join("testdata", configDataFile)})
assert.NoError(t, cmd.Execute())
expected := golden.Get(t, actual, configDataFile)
assert.Equal(t, string(expected), string(actual))
assert.Equal(t, "ID-"+name, strings.TrimSpace(buf.String()))
}

func TestConfigCreateWithLabels(t *testing.T) {
expectedLabels := map[string]string{
"lbl1": "Label-foo",
"lbl2": "Label-bar",
}
name := "foo"

buf := new(bytes.Buffer)
cli := test.NewFakeCli(&fakeClient{
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
if spec.Name != name {
return types.ConfigCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
}

if !reflect.DeepEqual(spec.Labels, expectedLabels) {
return types.ConfigCreateResponse{}, errors.Errorf("expected labels %v, got %v", expectedLabels, spec.Labels)
}

return types.ConfigCreateResponse{
ID: "ID-" + spec.Name,
}, nil
},
}, buf)

cmd := newConfigCreateCommand(cli)
cmd.SetArgs([]string{name, filepath.Join("testdata", configDataFile)})
cmd.Flags().Set("label", "lbl1=Label-foo")
cmd.Flags().Set("label", "lbl2=Label-bar")
assert.NoError(t, cmd.Execute())
assert.Equal(t, "ID-"+name, strings.TrimSpace(buf.String()))
}
41 changes: 41 additions & 0 deletions cli/command/config/inspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package config

import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/inspect"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)

type inspectOptions struct {
names []string
format string
}

func newConfigInspectCommand(dockerCli command.Cli) *cobra.Command {
opts := inspectOptions{}
cmd := &cobra.Command{
Use: "inspect [OPTIONS] CONFIG [CONFIG...]",
Short: "Display detailed information on one or more configuration files",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.names = args
return runConfigInspect(dockerCli, opts)
},
}

cmd.Flags().StringVarP(&opts.format, "format", "f", "", "Format the output using the given Go template")
return cmd
}

func runConfigInspect(dockerCli command.Cli, opts inspectOptions) error {
client := dockerCli.Client()
ctx := context.Background()

getRef := func(id string) (interface{}, []byte, error) {
return client.ConfigInspectWithRaw(ctx, id)
}

return inspect.Inspect(dockerCli.Out(), opts.names, opts.format, getRef)
}
Loading

0 comments on commit 05267be

Please sign in to comment.