Merge component 'cli' from git@github.com:docker/cli master
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"],
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
+4
@@ -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
|
||||
|
||||
+4
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user