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

This commit is contained in:
GordonTheTurtle
2018-02-21 23:32:37 +00:00
24 changed files with 238 additions and 148 deletions
+1
View File
@@ -604,6 +604,7 @@ Wang Long <long.wanglong@huawei.com>
Wang Ping <present.wp@icloud.com>
Wang Xing <hzwangxing@corp.netease.com>
Wang Yuexiao <wang.yuexiao@zte.com.cn>
Wataru Ishida <ishida.wataru@lab.ntt.co.jp>
Wayne Song <wsong@docker.com>
Wen Cheng Ma <wenchma@cn.ibm.com>
Wenzhi Liang <wenzhi.liang@gmail.com>
+2 -2
View File
@@ -300,12 +300,12 @@ func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, er
Timeout: 30 * time.Second,
}).DialContext,
}
proto, addr, _, err := client.ParseHost(host)
hostURL, err := client.ParseHostURL(host)
if err != nil {
return nil, err
}
sockets.ConfigureTransport(tr, proto, addr)
sockets.ConfigureTransport(tr, hostURL.Scheme, hostURL.Host)
return &http.Client{
Transport: tr,
@@ -22,11 +22,11 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
}
// Parse the compose file
cfg, version, err := loader.LoadComposefile(dockerCli, opts)
cfg, err := loader.LoadComposefile(dockerCli, opts)
if err != nil {
return err
}
stack, err := LoadStack(opts.Namespace, version, *cfg)
stack, err := LoadStack(opts.Namespace, *cfg)
if err != nil {
return err
}
@@ -7,18 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type versionedConfig struct {
*composetypes.Config `yaml:",inline"`
Version string
}
// LoadStack loads a stack from a Compose config, with a given name.
func LoadStack(name, version string, cfg composetypes.Config) (*apiv1beta1.Stack, error) {
cfg.Filename = ""
res, err := yaml.Marshal(versionedConfig{
Version: version,
Config: &cfg,
})
func LoadStack(name string, cfg composetypes.Config) (*apiv1beta1.Stack, error) {
res, err := yaml.Marshal(cfg)
if err != nil {
return nil, err
}
@@ -10,7 +10,8 @@ import (
)
func TestLoadStack(t *testing.T) {
s, err := LoadStack("foo", "3.1", composetypes.Config{
s, err := LoadStack("foo", composetypes.Config{
Version: "3.1",
Filename: "banana",
Services: []composetypes.ServiceConfig{
{
@@ -29,15 +30,16 @@ func TestLoadStack(t *testing.T) {
Name: "foo",
},
Spec: apiv1beta1.StackSpec{
ComposeFile: string(`configs: {}
networks: {}
secrets: {}
ComposeFile: string(`version: "3.1"
services:
bar:
image: bar
foo:
image: foo
networks: {}
volumes: {}
secrets: {}
configs: {}
`),
},
}, s)
@@ -18,21 +18,21 @@ import (
)
// LoadComposefile parse the composefile specified in the cli and returns its Config and version.
func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Config, string, error) {
func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.Config, error) {
configDetails, err := getConfigDetails(opts.Composefiles, dockerCli.In())
if err != nil {
return nil, "", err
return nil, err
}
dicts := getDictsFrom(configDetails.ConfigFiles)
config, err := loader.Load(configDetails)
if err != nil {
if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
return nil, "", errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
propertyWarnings(fpe.Properties))
}
return nil, "", err
return nil, err
}
unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
@@ -46,7 +46,7 @@ func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.
fmt.Fprintf(dockerCli.Err(), "Ignoring deprecated options:\n\n%s\n\n",
propertyWarnings(deprecatedProperties))
}
return config, configDetails.Version, nil
return config, nil
}
func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]interface{} {
@@ -18,7 +18,7 @@ import (
)
func deployCompose(ctx context.Context, dockerCli command.Cli, opts options.Deploy) error {
config, _, err := loader.LoadComposefile(dockerCli, opts)
config, err := loader.LoadComposefile(dockerCli, opts)
if err != nil {
return err
}
@@ -8,6 +8,7 @@ import (
func fullExampleConfig(workingDir, homeDir string) *types.Config {
return &types.Config{
Version: "3.6",
Services: services(workingDir, homeDir),
Networks: networks(),
Volumes: volumes(),
+12 -3
View File
@@ -98,7 +98,9 @@ func validateForbidden(configDict map[string]interface{}) error {
func loadSections(config map[string]interface{}, configDetails types.ConfigDetails) (*types.Config, error) {
var err error
cfg := types.Config{}
cfg := types.Config{
Version: schema.Version(config),
}
var loaders = []struct {
key string
@@ -359,7 +361,9 @@ func LoadService(name string, serviceDict map[string]interface{}, workingDir str
return nil, err
}
resolveVolumePaths(serviceConfig.Volumes, workingDir, lookupEnv)
if err := resolveVolumePaths(serviceConfig.Volumes, workingDir, lookupEnv); err != nil {
return nil, err
}
return serviceConfig, nil
}
@@ -398,12 +402,16 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string, l
return nil
}
func resolveVolumePaths(volumes []types.ServiceVolumeConfig, workingDir string, lookupEnv template.Mapping) {
func resolveVolumePaths(volumes []types.ServiceVolumeConfig, workingDir string, lookupEnv template.Mapping) error {
for i, volume := range volumes {
if volume.Type != "bind" {
continue
}
if volume.Source == "" {
return errors.New(`invalid mount config for type "bind": field Source must not be empty`)
}
filePath := expandUser(volume.Source, lookupEnv)
// Check for a Unix absolute path first, to handle a Windows client
// with a Unix daemon. This handles a Windows client connecting to a
@@ -416,6 +424,7 @@ func resolveVolumePaths(volumes []types.ServiceVolumeConfig, workingDir string,
volume.Source = filePath
volumes[i] = volume
}
return nil
}
// TODO: make this more robust
@@ -119,6 +119,7 @@ func strPtr(val string) *string {
}
var sampleConfig = types.Config{
Version: "3.0",
Services: []types.ServiceConfig{
{
Name: "foo",
@@ -174,6 +175,7 @@ func TestParseYAML(t *testing.T) {
func TestLoad(t *testing.T) {
actual, err := Load(buildConfigDetails(sampleDict, nil))
require.NoError(t, err)
assert.Equal(t, sampleConfig.Version, actual.Version)
assert.Equal(t, serviceSort(sampleConfig.Services), serviceSort(actual.Services))
assert.Equal(t, sampleConfig.Networks, actual.Networks)
assert.Equal(t, sampleConfig.Volumes, actual.Volumes)
@@ -573,6 +575,7 @@ networks:
require.NoError(t, err)
expected := &types.Config{
Filename: "filename.yml",
Version: "3.4",
Services: []types.ServiceConfig{
{
Name: "web",
@@ -892,6 +895,46 @@ services:
assert.Contains(t, err.Error(), "services.tmpfs.volumes.0 Additional property tmpfs is not allowed")
}
func TestLoadBindMountSourceMustNotBeEmpty(t *testing.T) {
_, err := loadYAML(`
version: "3.5"
services:
tmpfs:
image: nginx:latest
volumes:
- type: bind
target: /app
`)
require.EqualError(t, err, `invalid mount config for type "bind": field Source must not be empty`)
}
func TestLoadBindMountWithSource(t *testing.T) {
config, err := loadYAML(`
version: "3.5"
services:
bind:
image: nginx:latest
volumes:
- type: bind
target: /app
source: "."
`)
require.NoError(t, err)
workingDir, err := os.Getwd()
require.NoError(t, err)
expected := types.ServiceVolumeConfig{
Type: "bind",
Source: workingDir,
Target: "/app",
}
require.Len(t, config.Services, 1)
assert.Len(t, config.Services[0].Volumes, 1)
assert.Equal(t, expected, config.Services[0].Volumes[0])
}
func TestLoadTmpfsVolumeSizeCanBeZero(t *testing.T) {
config, err := loadYAML(`
version: "3.6"
@@ -207,6 +207,7 @@ func TestLoadLogging(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &types.Config{
Filename: "base.yml",
Version: "3.4",
Services: []types.ServiceConfig{
{
Name: "foo",
@@ -325,6 +326,7 @@ func TestLoadMultipleServicePorts(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &types.Config{
Filename: "base.yml",
Version: "3.4",
Services: []types.ServiceConfig{
{
Name: "foo",
@@ -450,6 +452,7 @@ func TestLoadMultipleSecretsConfig(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &types.Config{
Filename: "base.yml",
Version: "3.4",
Services: []types.ServiceConfig{
{
Name: "foo",
@@ -575,6 +578,7 @@ func TestLoadMultipleConfigobjsConfig(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &types.Config{
Filename: "base.yml",
Version: "3.4",
Services: []types.ServiceConfig{
{
Name: "foo",
@@ -690,6 +694,7 @@ func TestLoadMultipleUlimits(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &types.Config{
Filename: "base.yml",
Version: "3.4",
Services: []types.ServiceConfig{
{
Name: "foo",
@@ -808,6 +813,7 @@ func TestLoadMultipleNetworks(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &types.Config{
Filename: "base.yml",
Version: "3.4",
Services: []types.ServiceConfig{
{
Name: "foo",
@@ -895,6 +901,7 @@ func TestLoadMultipleConfigs(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &types.Config{
Filename: "base.yml",
Version: "3.4",
Services: []types.ServiceConfig{
{
Name: "bar",
+21 -20
View File
@@ -9,26 +9,7 @@ import (
func TestMarshallConfig(t *testing.T) {
cfg := fullExampleConfig("/foo", "/bar")
expected := `configs: {}
networks:
external-network:
name: external-network
external: true
other-external-network:
name: my-cool-network
external: true
other-network:
driver: overlay
driver_opts:
baz: "1"
foo: bar
ipam:
driver: overlay
config:
- subnet: 172.16.238.0/24
- subnet: 2001:3984:3989::/64
some-network: {}
secrets: {}
expected := `version: "3.6"
services:
foo:
build:
@@ -292,6 +273,24 @@ services:
tmpfs:
size: 10000
working_dir: /code
networks:
external-network:
name: external-network
external: true
other-external-network:
name: my-cool-network
external: true
other-network:
driver: overlay
driver_opts:
baz: "1"
foo: bar
ipam:
driver: overlay
config:
- subnet: 172.16.238.0/24
- subnet: 2001:3984:3989::/64
some-network: {}
volumes:
another-volume:
name: user_specified_name
@@ -314,6 +313,8 @@ volumes:
baz: "1"
foo: bar
some-volume: {}
secrets: {}
configs: {}
`
actual, err := yaml.Marshal(cfg)
+35 -35
View File
@@ -427,41 +427,41 @@ eXTO87V/0u2+HN315DMgcTfoOwl7F1X4+r5JHZ6zN9+GTlz96VeHm/L/y+b/AQAA///+Z1URVUEAAA==
size: 17007,
modtime: 1518458244,
compressed: `
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
DoTuq9lAU9Q4O1xV/59X/w8AAP//zRo7vm9CAAA=
`,
},
@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "config_schema_v3.5.json",
"id": "config_schema_v3.6.json",
"type": "object",
"required": ["version"],
+12 -16
View File
@@ -70,34 +70,30 @@ func (cd ConfigDetails) LookupEnv(key string) (string, bool) {
// Config is a full compose file configuration
type Config struct {
Filename string
Services []ServiceConfig
Filename string `yaml:"-"`
Version string
Services Services
Networks map[string]NetworkConfig
Volumes map[string]VolumeConfig
Secrets map[string]SecretConfig
Configs map[string]ConfigObjConfig
}
// MarshalYAML makes Config implement yaml.Marshaller
func (c *Config) MarshalYAML() (interface{}, error) {
m := map[string]interface{}{}
// Services is a list of ServiceConfig
type Services []ServiceConfig
// MarshalYAML makes Services implement yaml.Marshaller
func (s Services) MarshalYAML() (interface{}, error) {
services := map[string]ServiceConfig{}
for _, service := range c.Services {
s := service
s.Name = ""
services[service.Name] = s
for _, service := range s {
services[service.Name] = service
}
m["services"] = services
m["networks"] = c.Networks
m["volumes"] = c.Volumes
m["secrets"] = c.Secrets
m["configs"] = c.Configs
return m, nil
return services, nil
}
// ServiceConfig is the configuration of one service
type ServiceConfig struct {
Name string `yaml:",omitempty"`
Name string `yaml:"-"`
Build BuildConfig `yaml:",omitempty"`
CapAdd []string `mapstructure:"cap_add" yaml:"cap_add,omitempty"`
@@ -736,7 +736,7 @@ etjpu59cykrptrgw0z0hk5snf
After you create an overlay network in swarm mode, all manager nodes have
access to the network.
When you create a service and pass the --network flag to attach the service to
When you create a service and pass the `--network` flag to attach the service to
the overlay network:
```bash
@@ -754,6 +754,9 @@ The swarm extends my-network to each node running the service.
Containers on the same network can access each other using
[service discovery](https://docs.docker.com/engine/swarm/networking/#use-swarm-mode-service-discovery).
Long form syntax of `--network` allows to specify list of aliases and driver options:
`--network name=my-network,alias=web1,driver-opt=field1=value1`
### Publish service ports externally to the swarm (-p, --publish)
You can publish service ports to make them available externally to the swarm
@@ -181,7 +181,7 @@ myservice
Use the `--publish-add` or `--publish-rm` flags to add or remove a published
port for a service. You can use the short or long syntax discussed in the
[docker service create](service_create/#attach-a-service-to-an-existing-network-network)
[docker service create](service_create/#publish-service-ports-externally-to-the-swarm)
reference.
The following example adds a published service port to an existing service.
@@ -192,6 +192,22 @@ $ docker service update \
myservice
```
### Add or remove network
Use the `--network-add` or `--network-rm` flags to add or remove a network for
a service. You can use the short or long syntax discussed in the
[docker service create](service_create/#attach-a-service-to-an-existing-network-network)
reference.
The following example adds a new alias name to an existing service already connected to network my-network:
```bash
$ docker service update \
--network-rm my-network \
--network-add name=my-network,alias=web1 \
myservice
```
### Roll back to the previous version of a service
Use the `--rollback` option to roll back to the previous version of the service.
+1 -1
View File
@@ -5,7 +5,7 @@ github.com/coreos/etcd v3.2.1
github.com/cpuguy83/go-md2man v1.0.8
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
github.com/docker/docker 079ed017b61eb819b8184b90013ce89465d3aaba
github.com/docker/docker 0ede01237c9ab871f1b8db0364427407f3e46541
github.com/docker/docker-credential-helpers 3c90bd29a46b943b2a9842987b58fb91a7c1819b
# the docker/go package contains a customized version of canonical/json
# and is used by Notary. The package is periodically rebased on current Go versions.
+1 -1
View File
@@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
// Common constants for daemon and client.
const (
// DefaultVersion of Current REST API
DefaultVersion string = "1.36"
DefaultVersion string = "1.37"
// NoBaseImageSpecifier is the symbol used by the FROM
// command to specify that no base image is to be used.
@@ -13,6 +13,10 @@ type Config struct {
type ConfigSpec struct {
Annotations
Data []byte `json:",omitempty"`
// Templating controls whether and how to evaluate the config payload as
// a template. If it is not set, no templating is used.
Templating *Driver `json:",omitempty"`
}
// ConfigReferenceFileTarget is a file target in a config reference
@@ -14,6 +14,10 @@ type SecretSpec struct {
Annotations
Data []byte `json:",omitempty"`
Driver *Driver `json:",omitempty"` // name of the secrets driver used to fetch the secret's value from an external secret store
// Templating controls whether and how to evaluate the secret payload as
// a template. If it is not set, no templating is used.
Templating *Driver `json:",omitempty"`
}
// SecretReferenceFileTarget is a file target in a secret reference
+54 -40
View File
@@ -42,8 +42,8 @@ For example, to list running containers (the equivalent of "docker ps"):
package client // import "github.com/docker/docker/client"
import (
"errors"
"fmt"
"net"
"net/http"
"net/url"
"os"
@@ -56,6 +56,7 @@ import (
"github.com/docker/docker/api/types/versions"
"github.com/docker/go-connections/sockets"
"github.com/docker/go-connections/tlsconfig"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
@@ -103,18 +104,21 @@ func CheckRedirect(req *http.Request, via []*http.Request) error {
}
// NewEnvClient initializes a new API client based on environment variables.
// Use DOCKER_HOST to set the url to the docker server.
// Use DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
// Use DOCKER_CERT_PATH to load the TLS certificates from.
// Use DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
// deprecated: use NewClientWithOpts(FromEnv)
// See FromEnv for a list of support environment variables.
//
// Deprecated: use NewClientWithOpts(FromEnv)
func NewEnvClient() (*Client, error) {
return NewClientWithOpts(FromEnv)
}
// FromEnv enhance the default client with values from environment variables
// FromEnv configures the client with values from environment variables.
//
// Supported environment variables:
// DOCKER_HOST to set the url to the docker server.
// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
// DOCKER_CERT_PATH to load the TLS certificates from.
// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
func FromEnv(c *Client) error {
var httpClient *http.Client
if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
options := tlsconfig.Options{
CAFile: filepath.Join(dockerCertPath, "ca.pem"),
@@ -127,30 +131,58 @@ func FromEnv(c *Client) error {
return err
}
httpClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsc,
},
c.client = &http.Client{
Transport: &http.Transport{TLSClientConfig: tlsc},
CheckRedirect: CheckRedirect,
}
WithHTTPClient(httpClient)(c)
}
host := os.Getenv("DOCKER_HOST")
if host != "" {
// WithHost will create an API client if it doesn't exist
if host := os.Getenv("DOCKER_HOST"); host != "" {
if err := WithHost(host)(c); err != nil {
return err
}
}
version := os.Getenv("DOCKER_API_VERSION")
if version != "" {
if version := os.Getenv("DOCKER_API_VERSION"); version != "" {
c.version = version
c.manualOverride = true
}
return nil
}
// WithTLSClientConfig applies a tls config to the client transport.
func WithTLSClientConfig(cacertPath, certPath, keyPath string) func(*Client) error {
return func(c *Client) error {
opts := tlsconfig.Options{
CAFile: cacertPath,
CertFile: certPath,
KeyFile: keyPath,
ExclusiveRootPools: true,
}
config, err := tlsconfig.Client(opts)
if err != nil {
return errors.Wrap(err, "failed to create tls config")
}
if transport, ok := c.client.Transport.(*http.Transport); ok {
transport.TLSClientConfig = config
return nil
}
return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport)
}
}
// WithDialer applies the dialer.DialContext to the client transport. This can be
// used to set the Timeout and KeepAlive settings of the client.
func WithDialer(dialer *net.Dialer) func(*Client) error {
return func(c *Client) error {
if transport, ok := c.client.Transport.(*http.Transport); ok {
transport.DialContext = dialer.DialContext
return nil
}
return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport)
}
}
// WithVersion overrides the client version with the specified one
func WithVersion(version string) func(*Client) error {
return func(c *Client) error {
@@ -159,8 +191,7 @@ func WithVersion(version string) func(*Client) error {
}
}
// WithHost overrides the client host with the specified one, creating a new
// http client if one doesn't exist
// WithHost overrides the client host with the specified one.
func WithHost(host string) func(*Client) error {
return func(c *Client) error {
hostURL, err := ParseHostURL(host)
@@ -171,17 +202,10 @@ func WithHost(host string) func(*Client) error {
c.proto = hostURL.Scheme
c.addr = hostURL.Host
c.basePath = hostURL.Path
if c.client == nil {
client, err := defaultHTTPClient(host)
if err != nil {
return err
}
return WithHTTPClient(client)(c)
}
if transport, ok := c.client.Transport.(*http.Transport); ok {
return sockets.ConfigureTransport(transport, c.proto, c.addr)
}
return fmt.Errorf("cannot apply host to http transport")
return errors.Errorf("cannot apply host to transport: %T", c.client.Transport)
}
}
@@ -266,7 +290,7 @@ func defaultHTTPClient(host string) (*http.Client, error) {
// It won't send any version information if the version number is empty. It is
// highly recommended that you set a version or your client may break if the
// server is upgraded.
// deprecated: use NewClientWithOpts
// Deprecated: use NewClientWithOpts
func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) {
return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders))
}
@@ -332,17 +356,6 @@ func (cli *Client) DaemonHost() string {
return cli.host
}
// ParseHost parses a url string, validates the strings is a host url, and returns
// the parsed host as: protocol, address, and base path
// Deprecated: use ParseHostURL
func ParseHost(host string) (string, string, string, error) {
hostURL, err := ParseHostURL(host)
if err != nil {
return "", "", "", err
}
return hostURL.Scheme, hostURL.Host, hostURL.Path, nil
}
// ParseHostURL parses a url string, validates the string is a host url, and
// returns the parsed URL
func ParseHostURL(host string) (*url.URL, error) {
@@ -378,6 +391,7 @@ func (cli *Client) CustomHTTPHeaders() map[string]string {
}
// SetCustomHTTPHeaders that will be set on every HTTP request made by the client.
// Deprecated: use WithHTTPHeaders when creating the client.
func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) {
cli.customHTTPHeaders = headers
}
+1
View File
@@ -37,6 +37,7 @@ type CommonAPIClient interface {
NegotiateAPIVersion(ctx context.Context)
NegotiateAPIVersionPing(types.Ping)
DialSession(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error)
Close() error
}
// ContainerAPIClient defines API client methods for the containers
+1 -4
View File
@@ -123,10 +123,7 @@ func (cli *Client) sendRequest(ctx context.Context, method, path string, query u
if err != nil {
return resp, err
}
if err := cli.checkResponseErr(resp); err != nil {
return resp, err
}
return resp, nil
return resp, cli.checkResponseErr(resp)
}
func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResponse, error) {