Merge component 'cli' from git@github.com:docker/cli master

This commit is contained in:
Andrew Hsu
2017-05-31 16:43:39 +00:00
13 changed files with 194 additions and 40 deletions

View File

@ -28,7 +28,9 @@ func ElectAuthServer(ctx context.Context, cli Cli) string {
// the default registry URL might be Windows specific.
serverAddress := registry.IndexServer
if info, err := cli.Client().Info(ctx); err != nil {
fmt.Fprintf(cli.Out(), "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
fmt.Fprintf(cli.Err(), "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
} else if info.IndexServerAddress == "" {
fmt.Fprintf(cli.Err(), "Warning: Empty registry endpoint from daemon. Using system default: %s\n", serverAddress)
} else {
serverAddress = info.IndexServerAddress
}

View File

@ -0,0 +1,79 @@
package command_test
import (
"bytes"
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
// Prevents a circular import with "github.com/docker/cli/cli/internal/test"
. "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
type fakeClient struct {
client.Client
infoFunc func() (types.Info, error)
}
func (cli *fakeClient) Info(_ context.Context) (types.Info, error) {
if cli.infoFunc != nil {
return cli.infoFunc()
}
return types.Info{}, nil
}
func TestElectAuthServer(t *testing.T) {
testCases := []struct {
expectedAuthServer string
expectedWarning string
infoFunc func() (types.Info, error)
}{
{
expectedAuthServer: "https://index.docker.io/v1/",
expectedWarning: "",
infoFunc: func() (types.Info, error) {
return types.Info{IndexServerAddress: "https://index.docker.io/v1/"}, nil
},
},
{
expectedAuthServer: "https://index.docker.io/v1/",
expectedWarning: "Empty registry endpoint from daemon",
infoFunc: func() (types.Info, error) {
return types.Info{IndexServerAddress: ""}, nil
},
},
{
expectedAuthServer: "https://foo.bar",
expectedWarning: "",
infoFunc: func() (types.Info, error) {
return types.Info{IndexServerAddress: "https://foo.bar"}, nil
},
},
{
expectedAuthServer: "https://index.docker.io/v1/",
expectedWarning: "failed to get default registry endpoint from daemon",
infoFunc: func() (types.Info, error) {
return types.Info{}, errors.Errorf("error getting info")
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cli := test.NewFakeCli(&fakeClient{infoFunc: tc.infoFunc}, buf)
errBuf := new(bytes.Buffer)
cli.SetErr(errBuf)
server := ElectAuthServer(context.Background(), cli)
assert.Equal(t, tc.expectedAuthServer, server)
actual := errBuf.String()
if tc.expectedWarning == "" {
assert.Empty(t, actual)
} else {
assert.Contains(t, actual, tc.expectedWarning)
}
}
}

View File

@ -17,17 +17,21 @@ type fakeClient struct {
services []string
networks []string
secrets []string
configs []string
removedServices []string
removedNetworks []string
removedSecrets []string
removedConfigs []string
serviceListFunc func(options types.ServiceListOptions) ([]swarm.Service, error)
networkListFunc func(options types.NetworkListOptions) ([]types.NetworkResource, error)
secretListFunc func(options types.SecretListOptions) ([]swarm.Secret, error)
configListFunc func(options types.ConfigListOptions) ([]swarm.Config, error)
serviceRemoveFunc func(serviceID string) error
networkRemoveFunc func(networkID string) error
secretRemoveFunc func(secretID string) error
configRemoveFunc func(configID string) error
}
func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
@ -75,6 +79,21 @@ func (cli *fakeClient) SecretList(ctx context.Context, options types.SecretListO
return secretsList, nil
}
func (cli *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
if cli.configListFunc != nil {
return cli.configListFunc(options)
}
namespace := namespaceFromFilters(options.Filters)
configsList := []swarm.Config{}
for _, name := range cli.configs {
if belongToNamespace(name, namespace) {
configsList = append(configsList, configFromName(name))
}
}
return configsList, nil
}
func (cli *fakeClient) ServiceRemove(ctx context.Context, serviceID string) error {
if cli.serviceRemoveFunc != nil {
return cli.serviceRemoveFunc(serviceID)
@ -102,6 +121,15 @@ func (cli *fakeClient) SecretRemove(ctx context.Context, secretID string) error
return nil
}
func (cli *fakeClient) ConfigRemove(ctx context.Context, configID string) error {
if cli.configRemoveFunc != nil {
return cli.configRemoveFunc(configID)
}
cli.removedConfigs = append(cli.removedConfigs, configID)
return nil
}
func serviceFromName(name string) swarm.Service {
return swarm.Service{
ID: "ID-" + name,
@ -127,6 +155,15 @@ func secretFromName(name string) swarm.Secret {
}
}
func configFromName(name string) swarm.Config {
return swarm.Config{
ID: "ID-" + name,
Spec: swarm.ConfigSpec{
Annotations: swarm.Annotations{Name: name},
},
}
}
func namespaceFromFilters(filters filters.Args) string {
label := filters.Get("label")[0]
return strings.TrimPrefix(label, convert.LabelNamespace+"=")

View File

@ -61,3 +61,13 @@ func getStackSecrets(
ctx,
types.SecretListOptions{Filters: getStackFilter(namespace)})
}
func getStackConfigs(
ctx context.Context,
apiclient client.APIClient,
namespace string,
) ([]swarm.Config, error) {
return apiclient.ConfigList(
ctx,
types.ConfigListOptions{Filters: getStackFilter(namespace)})
}

View File

@ -55,13 +55,19 @@ func runRemove(dockerCli command.Cli, opts removeOptions) error {
return err
}
if len(services)+len(networks)+len(secrets) == 0 {
configs, err := getStackConfigs(ctx, client, namespace)
if err != nil {
return err
}
if len(services)+len(networks)+len(secrets)+len(configs) == 0 {
fmt.Fprintf(dockerCli.Out(), "Nothing found in stack: %s\n", namespace)
continue
}
hasError := removeServices(ctx, dockerCli, services)
hasError = removeSecrets(ctx, dockerCli, secrets) || hasError
hasError = removeConfigs(ctx, dockerCli, configs) || hasError
hasError = removeNetworks(ctx, dockerCli, networks) || hasError
if hasError {
@ -119,3 +125,18 @@ func removeSecrets(
}
return err != nil
}
func removeConfigs(
ctx context.Context,
dockerCli command.Cli,
configs []swarm.Config,
) bool {
var err error
for _, config := range configs {
fmt.Fprintf(dockerCli.Err(), "Removing config %s\n", config.Spec.Name)
if err = dockerCli.Client().ConfigRemove(ctx, config.ID); err != nil {
fmt.Fprintf(dockerCli.Err(), "Failed to remove config %s: %s", config.ID, err)
}
}
return err != nil
}

View File

@ -32,10 +32,18 @@ func TestRemoveStack(t *testing.T) {
}
allSecretIDs := buildObjectIDs(allSecrets)
allConfigs := []string{
objectName("foo", "config1"),
objectName("foo", "config2"),
objectName("bar", "config1"),
}
allConfigIDs := buildObjectIDs(allConfigs)
cli := &fakeClient{
services: allServices,
networks: allNetworks,
secrets: allSecrets,
configs: allConfigs,
}
cmd := newRemoveCommand(test.NewFakeCli(cli, &bytes.Buffer{}))
cmd.SetArgs([]string{"foo", "bar"})
@ -44,6 +52,7 @@ func TestRemoveStack(t *testing.T) {
assert.Equal(t, allServiceIDs, cli.removedServices)
assert.Equal(t, allNetworkIDs, cli.removedNetworks)
assert.Equal(t, allSecretIDs, cli.removedSecrets)
assert.Equal(t, allConfigIDs, cli.removedConfigs)
}
func TestSkipEmptyStack(t *testing.T) {
@ -57,10 +66,14 @@ func TestSkipEmptyStack(t *testing.T) {
allSecrets := []string{objectName("bar", "secret1")}
allSecretIDs := buildObjectIDs(allSecrets)
allConfigs := []string{objectName("bar", "config1")}
allConfigIDs := buildObjectIDs(allConfigs)
cli := &fakeClient{
services: allServices,
networks: allNetworks,
secrets: allSecrets,
configs: allConfigs,
}
cmd := newRemoveCommand(test.NewFakeCli(cli, buf))
cmd.SetArgs([]string{"foo", "bar"})
@ -70,6 +83,7 @@ func TestSkipEmptyStack(t *testing.T) {
assert.Equal(t, allServiceIDs, cli.removedServices)
assert.Equal(t, allNetworkIDs, cli.removedNetworks)
assert.Equal(t, allSecretIDs, cli.removedSecrets)
assert.Equal(t, allConfigIDs, cli.removedConfigs)
}
func TestContinueAfterError(t *testing.T) {
@ -82,11 +96,15 @@ func TestContinueAfterError(t *testing.T) {
allSecrets := []string{objectName("foo", "secret1"), objectName("bar", "secret1")}
allSecretIDs := buildObjectIDs(allSecrets)
allConfigs := []string{objectName("foo", "config1"), objectName("bar", "config1")}
allConfigIDs := buildObjectIDs(allConfigs)
removedServices := []string{}
cli := &fakeClient{
services: allServices,
networks: allNetworks,
secrets: allSecrets,
configs: allConfigs,
serviceRemoveFunc: func(serviceID string) error {
removedServices = append(removedServices, serviceID)
@ -104,4 +122,5 @@ func TestContinueAfterError(t *testing.T) {
assert.Equal(t, allServiceIDs, removedServices)
assert.Equal(t, allNetworkIDs, cli.removedNetworks)
assert.Equal(t, allSecretIDs, cli.removedSecrets)
assert.Equal(t, allConfigIDs, cli.removedConfigs)
}

View File

@ -12,9 +12,10 @@ import (
)
type pruneOptions struct {
force bool
all bool
filter opts.FilterOpt
force bool
all bool
pruneVolumes bool
filter opts.FilterOpt
}
// NewPruneCommand creates a new cobra.Command for `docker prune`
@ -34,6 +35,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused images not just dangling ones")
flags.BoolVar(&options.pruneVolumes, "volumes", false, "Prune volumes")
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'label=<key>=<value>')")
// "filter" flag is available in 1.28 (docker 17.04) and up
flags.SetAnnotation("filter", "version", []string{"1.28"})
@ -67,12 +69,15 @@ func runPrune(dockerCli command.Cli, options pruneOptions) error {
}
var spaceReclaimed uint64
for _, pruneFn := range []func(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error){
pruneFuncs := []func(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error){
prune.RunContainerPrune,
prune.RunVolumePrune,
prune.RunNetworkPrune,
} {
}
if options.pruneVolumes {
pruneFuncs = append(pruneFuncs, prune.RunVolumePrune)
}
for _, pruneFn := range pruneFuncs {
spc, output, err := pruneFn(dockerCli, options.filter)
if err != nil {
return err

View File

@ -141,6 +141,7 @@ func convertService(
TTY: service.Tty,
OpenStdin: service.StdinOpen,
Secrets: secrets,
Configs: configs,
ReadOnly: service.ReadOnly,
Privileges: &privileges,
},

View File

@ -11,10 +11,6 @@ RUN apt-get update -qq && apt-get install -y -q \
parallel \
;
RUN go get github.com/mitchellh/gox && \
cp /go/bin/gox /usr/bin && \
rm -rf /go/src/* /go/pkg/* /go/bin/*
COPY dockerfiles/osx-cross.sh /tmp/
RUN /tmp/osx-cross.sh
ENV PATH /osxcross/target/bin:$PATH

View File

@ -7,10 +7,6 @@ RUN go get github.com/LK4D4/vndr && \
cp /go/bin/vndr /usr/bin && \
rm -rf /go/src/* /go/pkg/* /go/bin/*
RUN go get github.com/mitchellh/gox && \
cp /go/bin/gox /usr/bin && \
rm -rf /go/src/* /go/pkg/* /go/bin/*
RUN go get github.com/jteeuwen/go-bindata/go-bindata && \
cp /go/bin/go-bindata /usr/bin && \
rm -rf /go/src/* /go/pkg/* /go/bin/*

View File

@ -1,5 +1,4 @@
#!/usr/bin/env bash
set -eu
VERSION=${VERSION:-"unknown-version"}
@ -14,5 +13,7 @@ export LDFLAGS="\
${LDFLAGS:-} \
"
export TARGET="build/docker-$(go env GOHOSTOS)-$(go env GOHOSTARCH)"
GOOS="${GOOS:-$(go env GOHOSTOS)}"
GOARCH="${GOARCH:-$(go env GOHOSTARCH)}"
export TARGET="build/docker-$GOOS-$GOARCH"
export SOURCE="github.com/docker/cli/cmd/docker"

View File

@ -1,12 +1,18 @@
#!/usr/bin/env bash
#
# Build a binary for all supported platforms
#
set -eu -o pipefail
BUILDDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo "Building all binaries"
echo "Building binaries for all platforms"
SHELL=/bin/bash parallel ::: \
"$BUILDDIR/linux-cross" \
"$BUILDDIR/windows" \
"$BUILDDIR/osx" \
"GOOS=linux GOARCH=amd64 $BUILDDIR/binary" \
"GOOS=linux GOARCH=arm $BUILDDIR/binary" \
"GOOS=linux GOARCH=ppc64le $BUILDDIR/binary" \
"GOOS=linux GOARCH=s390x $BUILDDIR/binary" \
;

View File

@ -1,19 +0,0 @@
#!/usr/bin/env bash
#
# Build static linux binary for multiple architectures
#
set -eu -o pipefail
source ./scripts/build/.variables
CROSS_OSARCH="linux/amd64 linux/arm linux/ppc64le"
# Not yet supported by gox
# linux/s390x
echo "Building all linux binaries"
gox -output build/docker-{{.OS}}-{{.Arch}} \
-osarch "${CROSS_OSARCH}" \
--ldflags "${LDFLAGS}" \
"${SOURCE}"