mirror of https://github.com/docker/cli.git
Merge pull request #155 from vdemeester/fix-and-update-vendor
Update vendoring of docker/docker
This commit is contained in:
commit
583ed2e3be
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -65,7 +64,7 @@ func runConfigCreate(dockerCli command.Cli, options createOptions) error {
|
||||||
spec := swarm.ConfigSpec{
|
spec := swarm.ConfigSpec{
|
||||||
Annotations: swarm.Annotations{
|
Annotations: swarm.Annotations{
|
||||||
Name: options.name,
|
Name: options.name,
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()),
|
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
|
||||||
},
|
},
|
||||||
Data: configData,
|
Data: configData,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
networktypes "github.com/docker/docker/api/types/network"
|
networktypes "github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/api/types/strslice"
|
"github.com/docker/docker/api/types/strslice"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -409,13 +408,13 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all the environment variables for the container
|
// collect all the environment variables for the container
|
||||||
envVariables, err := runconfigopts.ReadKVStrings(copts.envFile.GetAll(), copts.env.GetAll())
|
envVariables, err := opts.ReadKVStrings(copts.envFile.GetAll(), copts.env.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all the labels for the container
|
// collect all the labels for the container
|
||||||
labels, err := runconfigopts.ReadKVStrings(copts.labelsFile.GetAll(), copts.labels.GetAll())
|
labels, err := opts.ReadKVStrings(copts.labelsFile.GetAll(), copts.labels.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -440,7 +439,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
|
||||||
return nil, errors.Errorf("--userns: invalid USER mode")
|
return nil, errors.Errorf("--userns: invalid USER mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
restartPolicy, err := runconfigopts.ParseRestartPolicy(copts.restartPolicy)
|
restartPolicy, err := opts.ParseRestartPolicy(copts.restartPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -553,7 +552,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
|
||||||
MacAddress: copts.macAddress,
|
MacAddress: copts.macAddress,
|
||||||
Entrypoint: entrypoint,
|
Entrypoint: entrypoint,
|
||||||
WorkingDir: copts.workingDir,
|
WorkingDir: copts.workingDir,
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(labels),
|
Labels: opts.ConvertKVStringsToMap(labels),
|
||||||
Healthcheck: healthConfig,
|
Healthcheck: healthConfig,
|
||||||
}
|
}
|
||||||
if flags.Changed("stop-signal") {
|
if flags.Changed("stop-signal") {
|
||||||
|
@ -666,7 +665,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) {
|
func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) {
|
||||||
loggingOptsMap := runconfigopts.ConvertKVStringsToMap(loggingOpts)
|
loggingOptsMap := opts.ConvertKVStringsToMap(loggingOpts)
|
||||||
if loggingDriver == "none" && len(loggingOpts) > 0 {
|
if loggingDriver == "none" && len(loggingOpts) > 0 {
|
||||||
return map[string]string{}, errors.Errorf("invalid logging opts for driver %s", loggingDriver)
|
return map[string]string{}, errors.Errorf("invalid logging opts for driver %s", loggingDriver)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -17,7 +18,6 @@ import (
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
"github.com/docker/libnetwork/resolvconf/dns"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -77,13 +77,25 @@ func warnOnOomKillDisable(hostConfig container.HostConfig, stderr io.Writer) {
|
||||||
// they are trying to set a DNS to a localhost address
|
// they are trying to set a DNS to a localhost address
|
||||||
func warnOnLocalhostDNS(hostConfig container.HostConfig, stderr io.Writer) {
|
func warnOnLocalhostDNS(hostConfig container.HostConfig, stderr io.Writer) {
|
||||||
for _, dnsIP := range hostConfig.DNS {
|
for _, dnsIP := range hostConfig.DNS {
|
||||||
if dns.IsLocalhost(dnsIP) {
|
if isLocalhost(dnsIP) {
|
||||||
fmt.Fprintf(stderr, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
fmt.Fprintf(stderr, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
|
||||||
|
const ipLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
|
||||||
|
|
||||||
|
var localhostIPRegexp = regexp.MustCompile(ipLocalhost)
|
||||||
|
|
||||||
|
// IsLocalhost returns true if ip matches the localhost IP regular expression.
|
||||||
|
// Used for determining if nameserver settings are being passed which are
|
||||||
|
// localhost addresses
|
||||||
|
func isLocalhost(ip string) bool {
|
||||||
|
return localhostIPRegexp.MatchString(ip)
|
||||||
|
}
|
||||||
|
|
||||||
func runRun(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *runOptions, copts *containerOptions) error {
|
func runRun(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *runOptions, copts *containerOptions) error {
|
||||||
containerConfig, err := parse(flags, copts)
|
containerConfig, err := parse(flags, copts)
|
||||||
// just in case the parse does not exit
|
// just in case the parse does not exit
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -82,7 +81,7 @@ func runUpdate(dockerCli *command.DockerCli, options *updateOptions) error {
|
||||||
|
|
||||||
var restartPolicy containertypes.RestartPolicy
|
var restartPolicy containertypes.RestartPolicy
|
||||||
if options.restartPolicy != "" {
|
if options.restartPolicy != "" {
|
||||||
restartPolicy, err = runconfigopts.ParseRestartPolicy(options.restartPolicy)
|
restartPolicy, err = opts.ParseRestartPolicy(options.restartPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"github.com/docker/docker/pkg/progress"
|
"github.com/docker/docker/pkg/progress"
|
||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
"github.com/docker/docker/pkg/urlutil"
|
"github.com/docker/docker/pkg/urlutil"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -291,9 +290,9 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
|
||||||
Dockerfile: relDockerfile,
|
Dockerfile: relDockerfile,
|
||||||
ShmSize: options.shmSize.Value(),
|
ShmSize: options.shmSize.Value(),
|
||||||
Ulimits: options.ulimits.GetList(),
|
Ulimits: options.ulimits.GetList(),
|
||||||
BuildArgs: runconfigopts.ConvertKVStringsToMapWithNil(options.buildArgs.GetAll()),
|
BuildArgs: opts.ConvertKVStringsToMapWithNil(options.buildArgs.GetAll()),
|
||||||
AuthConfigs: authConfigs,
|
AuthConfigs: authConfigs,
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()),
|
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
|
||||||
CacheFrom: options.cacheFrom,
|
CacheFrom: options.cacheFrom,
|
||||||
SecurityOpt: options.securityOpt,
|
SecurityOpt: options.securityOpt,
|
||||||
NetworkMode: options.networkMode,
|
NetworkMode: options.networkMode,
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
package build
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"archive/tar"
|
|
||||||
"bytes"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/builder/remotecontext/git"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/fileutils"
|
"github.com/docker/docker/pkg/fileutils"
|
||||||
"github.com/docker/docker/pkg/gitutils"
|
|
||||||
"github.com/docker/docker/pkg/httputils"
|
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/progress"
|
"github.com/docker/docker/pkg/progress"
|
||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
|
@ -143,7 +142,7 @@ func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error)
|
||||||
if _, err := exec.LookPath("git"); err != nil {
|
if _, err := exec.LookPath("git"); err != nil {
|
||||||
return "", "", errors.Wrapf(err, "unable to find 'git'")
|
return "", "", errors.Wrapf(err, "unable to find 'git'")
|
||||||
}
|
}
|
||||||
absContextDir, err := gitutils.Clone(gitURL)
|
absContextDir, err := git.Clone(gitURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", errors.Wrapf(err, "unable to 'git clone' to temporary context directory")
|
return "", "", errors.Wrapf(err, "unable to 'git clone' to temporary context directory")
|
||||||
}
|
}
|
||||||
|
@ -161,7 +160,7 @@ func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error)
|
||||||
// Returns the tar archive used for the context and a path of the
|
// Returns the tar archive used for the context and a path of the
|
||||||
// dockerfile inside the tar.
|
// dockerfile inside the tar.
|
||||||
func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.ReadCloser, string, error) {
|
func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.ReadCloser, string, error) {
|
||||||
response, err := httputils.Download(remoteURL)
|
response, err := getWithStatusError(remoteURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.Errorf("unable to download remote context %s: %v", remoteURL, err)
|
return nil, "", errors.Errorf("unable to download remote context %s: %v", remoteURL, err)
|
||||||
}
|
}
|
||||||
|
@ -173,6 +172,24 @@ func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.Read
|
||||||
return GetContextFromReader(ioutils.NewReadCloserWrapper(progReader, func() error { return response.Body.Close() }), dockerfileName)
|
return GetContextFromReader(ioutils.NewReadCloserWrapper(progReader, func() error { return response.Body.Close() }), dockerfileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getWithStatusError does an http.Get() and returns an error if the
|
||||||
|
// status code is 4xx or 5xx.
|
||||||
|
func getWithStatusError(url string) (resp *http.Response, err error) {
|
||||||
|
if resp, err = http.Get(url); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode < 400 {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
msg := fmt.Sprintf("failed to GET %s with status %s", url, resp.Status)
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, msg+": error reading body")
|
||||||
|
}
|
||||||
|
return nil, errors.Errorf(msg+": %s", bytes.TrimSpace(body))
|
||||||
|
}
|
||||||
|
|
||||||
// GetContextFromLocalDir uses the given local directory as context for a
|
// GetContextFromLocalDir uses the given local directory as context for a
|
||||||
// `docker build`. Returns the absolute path to the local context directory,
|
// `docker build`. Returns the absolute path to the local context directory,
|
||||||
// the relative path of the dockerfile in that context directory, and a non-nil
|
// the relative path of the dockerfile in that context directory, and a non-nil
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -107,7 +106,7 @@ func runCreate(dockerCli *command.DockerCli, options createOptions) error {
|
||||||
Ingress: options.ingress,
|
Ingress: options.ingress,
|
||||||
Scope: options.scope,
|
Scope: options.scope,
|
||||||
ConfigOnly: options.configOnly,
|
ConfigOnly: options.configOnly,
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()),
|
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if from := options.configFrom; from != "" {
|
if from := options.configFrom; from != "" {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -95,7 +94,7 @@ func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) error {
|
||||||
}
|
}
|
||||||
if flags.Changed(flagLabelAdd) {
|
if flags.Changed(flagLabelAdd) {
|
||||||
labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
|
labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
|
||||||
for k, v := range runconfigopts.ConvertKVStringsToMap(labels) {
|
for k, v := range opts.ConvertKVStringsToMap(labels) {
|
||||||
spec.Annotations.Labels[k] = v
|
spec.Annotations.Labels[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -65,7 +64,7 @@ func runSecretCreate(dockerCli command.Cli, options createOptions) error {
|
||||||
spec := swarm.SecretSpec{
|
spec := swarm.SecretSpec{
|
||||||
Annotations: swarm.Annotations{
|
Annotations: swarm.Annotations{
|
||||||
Name: options.name,
|
Name: options.name,
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()),
|
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
|
||||||
},
|
},
|
||||||
Data: secretData,
|
Data: secretData,
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/docker/swarmkit/api"
|
"github.com/docker/swarmkit/api"
|
||||||
"github.com/docker/swarmkit/api/defaults"
|
"github.com/docker/swarmkit/api/defaults"
|
||||||
shlex "github.com/flynn-archive/go-shlex"
|
shlex "github.com/flynn-archive/go-shlex"
|
||||||
|
@ -389,7 +388,7 @@ func (ldo *logDriverOptions) toLogDriver() *swarm.Driver {
|
||||||
// set the log driver only if specified.
|
// set the log driver only if specified.
|
||||||
return &swarm.Driver{
|
return &swarm.Driver{
|
||||||
Name: ldo.name,
|
Name: ldo.name,
|
||||||
Options: runconfigopts.ConvertKVStringsToMap(ldo.opts.GetAll()),
|
Options: opts.ConvertKVStringsToMap(ldo.opts.GetAll()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,36 +518,36 @@ func newServiceOptions() *serviceOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *serviceOptions) ToServiceMode() (swarm.ServiceMode, error) {
|
func (options *serviceOptions) ToServiceMode() (swarm.ServiceMode, error) {
|
||||||
serviceMode := swarm.ServiceMode{}
|
serviceMode := swarm.ServiceMode{}
|
||||||
switch opts.mode {
|
switch options.mode {
|
||||||
case "global":
|
case "global":
|
||||||
if opts.replicas.Value() != nil {
|
if options.replicas.Value() != nil {
|
||||||
return serviceMode, errors.Errorf("replicas can only be used with replicated mode")
|
return serviceMode, errors.Errorf("replicas can only be used with replicated mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceMode.Global = &swarm.GlobalService{}
|
serviceMode.Global = &swarm.GlobalService{}
|
||||||
case "replicated":
|
case "replicated":
|
||||||
serviceMode.Replicated = &swarm.ReplicatedService{
|
serviceMode.Replicated = &swarm.ReplicatedService{
|
||||||
Replicas: opts.replicas.Value(),
|
Replicas: options.replicas.Value(),
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return serviceMode, errors.Errorf("Unknown mode: %s, only replicated and global supported", opts.mode)
|
return serviceMode, errors.Errorf("Unknown mode: %s, only replicated and global supported", options.mode)
|
||||||
}
|
}
|
||||||
return serviceMode, nil
|
return serviceMode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *serviceOptions) ToStopGracePeriod(flags *pflag.FlagSet) *time.Duration {
|
func (options *serviceOptions) ToStopGracePeriod(flags *pflag.FlagSet) *time.Duration {
|
||||||
if flags.Changed(flagStopGracePeriod) {
|
if flags.Changed(flagStopGracePeriod) {
|
||||||
return opts.stopGrace.Value()
|
return options.stopGrace.Value()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.NetworkAPIClient, flags *pflag.FlagSet) (swarm.ServiceSpec, error) {
|
func (options *serviceOptions) ToService(ctx context.Context, apiClient client.NetworkAPIClient, flags *pflag.FlagSet) (swarm.ServiceSpec, error) {
|
||||||
var service swarm.ServiceSpec
|
var service swarm.ServiceSpec
|
||||||
|
|
||||||
envVariables, err := runconfigopts.ReadKVStrings(opts.envFile.GetAll(), opts.env.GetAll())
|
envVariables, err := opts.ReadKVStrings(options.envFile.GetAll(), options.env.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return service, err
|
return service, err
|
||||||
}
|
}
|
||||||
|
@ -567,68 +566,68 @@ func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.Netw
|
||||||
currentEnv = append(currentEnv, env)
|
currentEnv = append(currentEnv, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
healthConfig, err := opts.healthcheck.toHealthConfig()
|
healthConfig, err := options.healthcheck.toHealthConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return service, err
|
return service, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceMode, err := opts.ToServiceMode()
|
serviceMode, err := options.ToServiceMode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return service, err
|
return service, err
|
||||||
}
|
}
|
||||||
|
|
||||||
networks, err := convertNetworks(ctx, apiClient, opts.networks)
|
networks, err := convertNetworks(ctx, apiClient, options.networks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return service, err
|
return service, err
|
||||||
}
|
}
|
||||||
|
|
||||||
service = swarm.ServiceSpec{
|
service = swarm.ServiceSpec{
|
||||||
Annotations: swarm.Annotations{
|
Annotations: swarm.Annotations{
|
||||||
Name: opts.name,
|
Name: options.name,
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(opts.labels.GetAll()),
|
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
|
||||||
},
|
},
|
||||||
TaskTemplate: swarm.TaskSpec{
|
TaskTemplate: swarm.TaskSpec{
|
||||||
ContainerSpec: swarm.ContainerSpec{
|
ContainerSpec: swarm.ContainerSpec{
|
||||||
Image: opts.image,
|
Image: options.image,
|
||||||
Args: opts.args,
|
Args: options.args,
|
||||||
Command: opts.entrypoint.Value(),
|
Command: options.entrypoint.Value(),
|
||||||
Env: currentEnv,
|
Env: currentEnv,
|
||||||
Hostname: opts.hostname,
|
Hostname: options.hostname,
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(opts.containerLabels.GetAll()),
|
Labels: opts.ConvertKVStringsToMap(options.containerLabels.GetAll()),
|
||||||
Dir: opts.workdir,
|
Dir: options.workdir,
|
||||||
User: opts.user,
|
User: options.user,
|
||||||
Groups: opts.groups.GetAll(),
|
Groups: options.groups.GetAll(),
|
||||||
StopSignal: opts.stopSignal,
|
StopSignal: options.stopSignal,
|
||||||
TTY: opts.tty,
|
TTY: options.tty,
|
||||||
ReadOnly: opts.readOnly,
|
ReadOnly: options.readOnly,
|
||||||
Mounts: opts.mounts.Value(),
|
Mounts: options.mounts.Value(),
|
||||||
DNSConfig: &swarm.DNSConfig{
|
DNSConfig: &swarm.DNSConfig{
|
||||||
Nameservers: opts.dns.GetAll(),
|
Nameservers: options.dns.GetAll(),
|
||||||
Search: opts.dnsSearch.GetAll(),
|
Search: options.dnsSearch.GetAll(),
|
||||||
Options: opts.dnsOption.GetAll(),
|
Options: options.dnsOption.GetAll(),
|
||||||
},
|
},
|
||||||
Hosts: convertExtraHostsToSwarmHosts(opts.hosts.GetAll()),
|
Hosts: convertExtraHostsToSwarmHosts(options.hosts.GetAll()),
|
||||||
StopGracePeriod: opts.ToStopGracePeriod(flags),
|
StopGracePeriod: options.ToStopGracePeriod(flags),
|
||||||
Healthcheck: healthConfig,
|
Healthcheck: healthConfig,
|
||||||
},
|
},
|
||||||
Networks: networks,
|
Networks: networks,
|
||||||
Resources: opts.resources.ToResourceRequirements(),
|
Resources: options.resources.ToResourceRequirements(),
|
||||||
RestartPolicy: opts.restartPolicy.ToRestartPolicy(flags),
|
RestartPolicy: options.restartPolicy.ToRestartPolicy(flags),
|
||||||
Placement: &swarm.Placement{
|
Placement: &swarm.Placement{
|
||||||
Constraints: opts.constraints.GetAll(),
|
Constraints: options.constraints.GetAll(),
|
||||||
Preferences: opts.placementPrefs.prefs,
|
Preferences: options.placementPrefs.prefs,
|
||||||
},
|
},
|
||||||
LogDriver: opts.logDriver.toLogDriver(),
|
LogDriver: options.logDriver.toLogDriver(),
|
||||||
},
|
},
|
||||||
Mode: serviceMode,
|
Mode: serviceMode,
|
||||||
UpdateConfig: opts.update.updateConfig(flags),
|
UpdateConfig: options.update.updateConfig(flags),
|
||||||
RollbackConfig: opts.update.rollbackConfig(flags),
|
RollbackConfig: options.update.rollbackConfig(flags),
|
||||||
EndpointSpec: opts.endpoint.ToEndpointSpec(),
|
EndpointSpec: options.endpoint.ToEndpointSpec(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.credentialSpec.Value() != nil {
|
if options.credentialSpec.Value() != nil {
|
||||||
service.TaskTemplate.ContainerSpec.Privileges = &swarm.Privileges{
|
service.TaskTemplate.ContainerSpec.Privileges = &swarm.Privileges{
|
||||||
CredentialSpec: opts.credentialSpec.Value(),
|
CredentialSpec: options.credentialSpec.Value(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/docker/swarmkit/api/defaults"
|
"github.com/docker/swarmkit/api/defaults"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -519,7 +518,7 @@ func updateContainerLabels(flags *pflag.FlagSet, field *map[string]string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
values := flags.Lookup(flagContainerLabelAdd).Value.(*opts.ListOpts).GetAll()
|
values := flags.Lookup(flagContainerLabelAdd).Value.(*opts.ListOpts).GetAll()
|
||||||
for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
|
for key, value := range opts.ConvertKVStringsToMap(values) {
|
||||||
(*field)[key] = value
|
(*field)[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -539,7 +538,7 @@ func updateLabels(flags *pflag.FlagSet, field *map[string]string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
values := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
|
values := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
|
||||||
for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
|
for key, value := range opts.ConvertKVStringsToMap(values) {
|
||||||
(*field)[key] = value
|
(*field)[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -933,7 +932,7 @@ func updateLogDriver(flags *pflag.FlagSet, taskTemplate *swarm.TaskSpec) error {
|
||||||
|
|
||||||
taskTemplate.LogDriver = &swarm.Driver{
|
taskTemplate.LogDriver = &swarm.Driver{
|
||||||
Name: name,
|
Name: name,
|
||||||
Options: runconfigopts.ConvertKVStringsToMap(flags.Lookup(flagLogOpt).Value.(*opts.ListOpts).GetAll()),
|
Options: opts.ConvertKVStringsToMap(flags.Lookup(flagLogOpt).Value.(*opts.ListOpts).GetAll()),
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
volumetypes "github.com/docker/docker/api/types/volume"
|
volumetypes "github.com/docker/docker/api/types/volume"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -57,7 +56,7 @@ func runCreate(dockerCli command.Cli, options createOptions) error {
|
||||||
Driver: options.driver,
|
Driver: options.driver,
|
||||||
DriverOpts: options.driverOpts.GetAll(),
|
DriverOpts: options.driverOpts.GetAll(),
|
||||||
Name: options.name,
|
Name: options.name,
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()),
|
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
|
||||||
}
|
}
|
||||||
|
|
||||||
vol, err := client.VolumeCreate(context.Background(), volReq)
|
vol, err := client.VolumeCreate(context.Background(), volReq)
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -404,7 +403,7 @@ func convertHealthcheck(healthcheck *composetypes.HealthCheckConfig) (*container
|
||||||
func convertRestartPolicy(restart string, source *composetypes.RestartPolicy) (*swarm.RestartPolicy, error) {
|
func convertRestartPolicy(restart string, source *composetypes.RestartPolicy) (*swarm.RestartPolicy, error) {
|
||||||
// TODO: log if restart is being ignored
|
// TODO: log if restart is being ignored
|
||||||
if source == nil {
|
if source == nil {
|
||||||
policy, err := runconfigopts.ParseRestartPolicy(restart)
|
policy, err := opts.ParseRestartPolicy(restart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"github.com/docker/cli/cli/compose/template"
|
"github.com/docker/cli/cli/compose/template"
|
||||||
"github.com/docker/cli/cli/compose/types"
|
"github.com/docker/cli/cli/compose/types"
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
shellwords "github.com/mattn/go-shellwords"
|
shellwords "github.com/mattn/go-shellwords"
|
||||||
|
@ -351,14 +350,14 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string, l
|
||||||
|
|
||||||
for _, file := range serviceConfig.EnvFile {
|
for _, file := range serviceConfig.EnvFile {
|
||||||
filePath := absPath(workingDir, file)
|
filePath := absPath(workingDir, file)
|
||||||
fileVars, err := runconfigopts.ParseEnvFile(filePath)
|
fileVars, err := opts.ParseEnvFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
envVars = append(envVars, fileVars...)
|
envVars = append(envVars, fileVars...)
|
||||||
}
|
}
|
||||||
updateEnvironment(environment,
|
updateEnvironment(environment,
|
||||||
runconfigopts.ConvertKVStringsToMapWithNil(envVars), lookupEnv)
|
opts.ConvertKVStringsToMapWithNil(envVars), lookupEnv)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEnvironment(environment, serviceConfig.Environment, lookupEnv)
|
updateEnvironment(environment, serviceConfig.Environment, lookupEnv)
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
package opts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tmpFileWithContent(content string, t *testing.T) string {
|
||||||
|
tmpFile, err := ioutil.TempFile("", "envfile-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tmpFile.Close()
|
||||||
|
|
||||||
|
tmpFile.WriteString(content)
|
||||||
|
return tmpFile.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ParseEnvFile for a file with a few well formatted lines
|
||||||
|
func TestParseEnvFileGoodFile(t *testing.T) {
|
||||||
|
content := `foo=bar
|
||||||
|
baz=quux
|
||||||
|
# comment
|
||||||
|
|
||||||
|
_foobar=foobaz
|
||||||
|
with.dots=working
|
||||||
|
and_underscore=working too
|
||||||
|
`
|
||||||
|
// Adding a newline + a line with pure whitespace.
|
||||||
|
// This is being done like this instead of the block above
|
||||||
|
// because it's common for editors to trim trailing whitespace
|
||||||
|
// from lines, which becomes annoying since that's the
|
||||||
|
// exact thing we need to test.
|
||||||
|
content += "\n \t "
|
||||||
|
tmpFile := tmpFileWithContent(content, t)
|
||||||
|
defer os.Remove(tmpFile)
|
||||||
|
|
||||||
|
lines, err := ParseEnvFile(tmpFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedLines := []string{
|
||||||
|
"foo=bar",
|
||||||
|
"baz=quux",
|
||||||
|
"_foobar=foobaz",
|
||||||
|
"with.dots=working",
|
||||||
|
"and_underscore=working too",
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(lines, expectedLines) {
|
||||||
|
t.Fatal("lines not equal to expectedLines")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ParseEnvFile for an empty file
|
||||||
|
func TestParseEnvFileEmptyFile(t *testing.T) {
|
||||||
|
tmpFile := tmpFileWithContent("", t)
|
||||||
|
defer os.Remove(tmpFile)
|
||||||
|
|
||||||
|
lines, err := ParseEnvFile(tmpFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(lines) != 0 {
|
||||||
|
t.Fatal("lines not empty; expected empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ParseEnvFile for a non existent file
|
||||||
|
func TestParseEnvFileNonExistentFile(t *testing.T) {
|
||||||
|
_, err := ParseEnvFile("foo_bar_baz")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("ParseEnvFile succeeded; expected failure")
|
||||||
|
}
|
||||||
|
if _, ok := err.(*os.PathError); !ok {
|
||||||
|
t.Fatalf("Expected a PathError, got [%v]", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ParseEnvFile for a badly formatted file
|
||||||
|
func TestParseEnvFileBadlyFormattedFile(t *testing.T) {
|
||||||
|
content := `foo=bar
|
||||||
|
f =quux
|
||||||
|
`
|
||||||
|
|
||||||
|
tmpFile := tmpFileWithContent(content, t)
|
||||||
|
defer os.Remove(tmpFile)
|
||||||
|
|
||||||
|
_, err := ParseEnvFile(tmpFile)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected an ErrBadEnvVariable, got nothing")
|
||||||
|
}
|
||||||
|
if _, ok := err.(ErrBadEnvVariable); !ok {
|
||||||
|
t.Fatalf("Expected an ErrBadEnvVariable, got [%v]", err)
|
||||||
|
}
|
||||||
|
expectedMessage := "poorly formatted environment: variable 'f ' has white spaces"
|
||||||
|
if err.Error() != expectedMessage {
|
||||||
|
t.Fatalf("Expected [%v], got [%v]", expectedMessage, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ParseEnvFile for a file with a line exceeding bufio.MaxScanTokenSize
|
||||||
|
func TestParseEnvFileLineTooLongFile(t *testing.T) {
|
||||||
|
content := strings.Repeat("a", bufio.MaxScanTokenSize+42)
|
||||||
|
content = fmt.Sprint("foo=", content)
|
||||||
|
|
||||||
|
tmpFile := tmpFileWithContent(content, t)
|
||||||
|
defer os.Remove(tmpFile)
|
||||||
|
|
||||||
|
_, err := ParseEnvFile(tmpFile)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("ParseEnvFile succeeded; expected failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseEnvFile with a random file, pass through
|
||||||
|
func TestParseEnvFileRandomFile(t *testing.T) {
|
||||||
|
content := `first line
|
||||||
|
another invalid line`
|
||||||
|
tmpFile := tmpFileWithContent(content, t)
|
||||||
|
defer os.Remove(tmpFile)
|
||||||
|
|
||||||
|
_, err := ParseEnvFile(tmpFile)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected an ErrBadEnvVariable, got nothing")
|
||||||
|
}
|
||||||
|
if _, ok := err.(ErrBadEnvVariable); !ok {
|
||||||
|
t.Fatalf("Expected an ErrBadEnvVariable, got [%v]", err)
|
||||||
|
}
|
||||||
|
expectedMessage := "poorly formatted environment: variable 'first line' has white spaces"
|
||||||
|
if err.Error() != expectedMessage {
|
||||||
|
t.Fatalf("Expected [%v], got [%v]", expectedMessage, err.Error())
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,13 +7,12 @@ github.com/coreos/etcd 824277cb3a577a0e8c829ca9ec557b973fe06d20
|
||||||
github.com/cpuguy83/go-md2man a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa
|
github.com/cpuguy83/go-md2man a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa
|
||||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
||||||
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
|
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
|
||||||
github.com/docker/docker 45c6f4262a865a03adaac291a9ce33c0f2190d77
|
github.com/docker/docker c8141a1fb1ff33b2bfab85a40e5da9a282f36cdc
|
||||||
github.com/docker/docker-credential-helpers v0.5.0
|
github.com/docker/docker-credential-helpers v0.5.1
|
||||||
github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06
|
github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06 #?
|
||||||
github.com/docker/go-connections e15c02316c12de00874640cd76311849de2aeed5
|
github.com/docker/go-connections e15c02316c12de00874640cd76311849de2aeed5
|
||||||
github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
|
github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
|
||||||
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
|
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
|
||||||
github.com/docker/libnetwork b13e0604016a4944025aaff521d9c125850b0d04
|
|
||||||
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||||
github.com/docker/notary v0.4.2
|
github.com/docker/notary v0.4.2
|
||||||
github.com/docker/swarmkit 1a3e510517be82d18ac04380b5f71eddf06c2fc0
|
github.com/docker/swarmkit 1a3e510517be82d18ac04380b5f71eddf06c2fc0
|
||||||
|
@ -29,7 +28,7 @@ github.com/mitchellh/mapstructure f3009df150dadf309fdee4a54ed65c124afad715
|
||||||
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
||||||
github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
|
github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
|
||||||
github.com/opencontainers/runc 9c2d8d184e5da67c95d601382adf14862e4f2228 https://github.com/docker/runc.git
|
github.com/opencontainers/runc 9c2d8d184e5da67c95d601382adf14862e4f2228 https://github.com/docker/runc.git
|
||||||
github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d
|
github.com/opencontainers/selinux v1.0.0-rc1
|
||||||
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
||||||
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
||||||
github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
|
github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
|
||||||
|
|
|
@ -9,12 +9,27 @@ import (
|
||||||
"github.com/docker/docker-credential-helpers/credentials"
|
"github.com/docker/docker-credential-helpers/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// isValidCredsMessage checks if 'msg' contains invalid credentials error message.
|
||||||
|
// It returns whether the logs are free of invalid credentials errors and the error if it isn't.
|
||||||
|
// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername.
|
||||||
|
func isValidCredsMessage(msg string) error {
|
||||||
|
if credentials.IsCredentialsMissingServerURLMessage(msg) {
|
||||||
|
return credentials.NewErrCredentialsMissingServerURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
if credentials.IsCredentialsMissingUsernameMessage(msg) {
|
||||||
|
return credentials.NewErrCredentialsMissingUsername()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Store uses an external program to save credentials.
|
// Store uses an external program to save credentials.
|
||||||
func Store(program ProgramFunc, credentials *credentials.Credentials) error {
|
func Store(program ProgramFunc, creds *credentials.Credentials) error {
|
||||||
cmd := program("store")
|
cmd := program("store")
|
||||||
|
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
if err := json.NewEncoder(buffer).Encode(credentials); err != nil {
|
if err := json.NewEncoder(buffer).Encode(creds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd.Input(buffer)
|
cmd.Input(buffer)
|
||||||
|
@ -22,6 +37,11 @@ func Store(program ProgramFunc, credentials *credentials.Credentials) error {
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t := strings.TrimSpace(string(out))
|
t := strings.TrimSpace(string(out))
|
||||||
|
|
||||||
|
if isValidErr := isValidCredsMessage(t); isValidErr != nil {
|
||||||
|
err = isValidErr
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Errorf("error storing credentials - err: %v, out: `%s`", err, t)
|
return fmt.Errorf("error storing credentials - err: %v, out: `%s`", err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +61,10 @@ func Get(program ProgramFunc, serverURL string) (*credentials.Credentials, error
|
||||||
return nil, credentials.NewErrCredentialsNotFound()
|
return nil, credentials.NewErrCredentialsNotFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isValidErr := isValidCredsMessage(t); isValidErr != nil {
|
||||||
|
err = isValidErr
|
||||||
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("error getting credentials - err: %v, out: `%s`", err, t)
|
return nil, fmt.Errorf("error getting credentials - err: %v, out: `%s`", err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +86,11 @@ func Erase(program ProgramFunc, serverURL string) error {
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t := strings.TrimSpace(string(out))
|
t := strings.TrimSpace(string(out))
|
||||||
|
|
||||||
|
if isValidErr := isValidCredsMessage(t); isValidErr != nil {
|
||||||
|
err = isValidErr
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Errorf("error erasing credentials - err: %v, out: `%s`", err, t)
|
return fmt.Errorf("error erasing credentials - err: %v, out: `%s`", err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +104,11 @@ func List(program ProgramFunc) (map[string]string, error) {
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t := strings.TrimSpace(string(out))
|
t := strings.TrimSpace(string(out))
|
||||||
|
|
||||||
|
if isValidErr := isValidCredsMessage(t); isValidErr != nil {
|
||||||
|
err = isValidErr
|
||||||
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("error listing credentials - err: %v, out: `%s`", err, t)
|
return nil, fmt.Errorf("error listing credentials - err: %v, out: `%s`", err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,10 +18,16 @@ type ProgramFunc func(args ...string) Program
|
||||||
// NewShellProgramFunc creates programs that are executed in a Shell.
|
// NewShellProgramFunc creates programs that are executed in a Shell.
|
||||||
func NewShellProgramFunc(name string) ProgramFunc {
|
func NewShellProgramFunc(name string) ProgramFunc {
|
||||||
return func(args ...string) Program {
|
return func(args ...string) Program {
|
||||||
return &Shell{cmd: exec.Command(name, args...)}
|
return &Shell{cmd: newCmdRedirectErr(name, args)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newCmdRedirectErr(name string, args []string) *exec.Cmd {
|
||||||
|
newCmd := exec.Command(name, args...)
|
||||||
|
newCmd.Stderr = os.Stderr
|
||||||
|
return newCmd
|
||||||
|
}
|
||||||
|
|
||||||
// Shell invokes shell commands to talk with a remote credentials helper.
|
// Shell invokes shell commands to talk with a remote credentials helper.
|
||||||
type Shell struct {
|
type Shell struct {
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
|
|
|
@ -17,6 +17,22 @@ type Credentials struct {
|
||||||
Secret string
|
Secret string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isValid checks the integrity of Credentials object such that no credentials lack
|
||||||
|
// a server URL or a username.
|
||||||
|
// It returns whether the credentials are valid and the error if it isn't.
|
||||||
|
// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername
|
||||||
|
func (c *Credentials) isValid() (bool, error) {
|
||||||
|
if len(c.ServerURL) == 0 {
|
||||||
|
return false, NewErrCredentialsMissingServerURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Username) == 0 {
|
||||||
|
return false, NewErrCredentialsMissingUsername()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Docker credentials should be labeled as such in credentials stores that allow labelling.
|
// Docker credentials should be labeled as such in credentials stores that allow labelling.
|
||||||
// That label allows to filter out non-Docker credentials too at lookup/search in macOS keychain,
|
// That label allows to filter out non-Docker credentials too at lookup/search in macOS keychain,
|
||||||
// Windows credentials manager and Linux libsecret. Default value is "Docker Credentials"
|
// Windows credentials manager and Linux libsecret. Default value is "Docker Credentials"
|
||||||
|
@ -81,6 +97,10 @@ func Store(helper Helper, reader io.Reader) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ok, err := creds.isValid(); !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return helper.Add(&creds)
|
return helper.Add(&creds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +120,9 @@ func Get(helper Helper, reader io.Reader, writer io.Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
serverURL := strings.TrimSpace(buffer.String())
|
serverURL := strings.TrimSpace(buffer.String())
|
||||||
|
if len(serverURL) == 0 {
|
||||||
|
return NewErrCredentialsMissingServerURL()
|
||||||
|
}
|
||||||
|
|
||||||
username, secret, err := helper.Get(serverURL)
|
username, secret, err := helper.Get(serverURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -107,6 +130,7 @@ func Get(helper Helper, reader io.Reader, writer io.Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := Credentials{
|
resp := Credentials{
|
||||||
|
ServerURL: serverURL,
|
||||||
Username: username,
|
Username: username,
|
||||||
Secret: secret,
|
Secret: secret,
|
||||||
}
|
}
|
||||||
|
@ -135,6 +159,9 @@ func Erase(helper Helper, reader io.Reader) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
serverURL := strings.TrimSpace(buffer.String())
|
serverURL := strings.TrimSpace(buffer.String())
|
||||||
|
if len(serverURL) == 0 {
|
||||||
|
return NewErrCredentialsMissingServerURL()
|
||||||
|
}
|
||||||
|
|
||||||
return helper.Delete(serverURL)
|
return helper.Delete(serverURL)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
package credentials
|
package credentials
|
||||||
|
|
||||||
// ErrCredentialsNotFound standarizes the not found error, so every helper returns
|
const (
|
||||||
// the same message and docker can handle it properly.
|
// ErrCredentialsNotFound standardizes the not found error, so every helper returns
|
||||||
const errCredentialsNotFoundMessage = "credentials not found in native keychain"
|
// the same message and docker can handle it properly.
|
||||||
|
errCredentialsNotFoundMessage = "credentials not found in native keychain"
|
||||||
|
|
||||||
|
// ErrCredentialsMissingServerURL and ErrCredentialsMissingUsername standardize
|
||||||
|
// invalid credentials or credentials management operations
|
||||||
|
errCredentialsMissingServerURLMessage = "no credentials server URL"
|
||||||
|
errCredentialsMissingUsernameMessage = "no credentials username"
|
||||||
|
)
|
||||||
|
|
||||||
// errCredentialsNotFound represents an error
|
// errCredentialsNotFound represents an error
|
||||||
// raised when credentials are not in the store.
|
// raised when credentials are not in the store.
|
||||||
|
@ -35,3 +42,64 @@ func IsErrCredentialsNotFound(err error) bool {
|
||||||
func IsErrCredentialsNotFoundMessage(err string) bool {
|
func IsErrCredentialsNotFoundMessage(err string) bool {
|
||||||
return err == errCredentialsNotFoundMessage
|
return err == errCredentialsNotFoundMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// errCredentialsMissingServerURL represents an error raised
|
||||||
|
// when the credentials object has no server URL or when no
|
||||||
|
// server URL is provided to a credentials operation requiring
|
||||||
|
// one.
|
||||||
|
type errCredentialsMissingServerURL struct{}
|
||||||
|
|
||||||
|
func (errCredentialsMissingServerURL) Error() string {
|
||||||
|
return errCredentialsMissingServerURLMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// errCredentialsMissingUsername represents an error raised
|
||||||
|
// when the credentials object has no username or when no
|
||||||
|
// username is provided to a credentials operation requiring
|
||||||
|
// one.
|
||||||
|
type errCredentialsMissingUsername struct{}
|
||||||
|
|
||||||
|
func (errCredentialsMissingUsername) Error() string {
|
||||||
|
return errCredentialsMissingUsernameMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NewErrCredentialsMissingServerURL creates a new error for
|
||||||
|
// errCredentialsMissingServerURL.
|
||||||
|
func NewErrCredentialsMissingServerURL() error {
|
||||||
|
return errCredentialsMissingServerURL{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrCredentialsMissingUsername creates a new error for
|
||||||
|
// errCredentialsMissingUsername.
|
||||||
|
func NewErrCredentialsMissingUsername() error {
|
||||||
|
return errCredentialsMissingUsername{}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// IsCredentialsMissingServerURL returns true if the error
|
||||||
|
// was an errCredentialsMissingServerURL.
|
||||||
|
func IsCredentialsMissingServerURL(err error) bool {
|
||||||
|
_, ok := err.(errCredentialsMissingServerURL)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCredentialsMissingServerURLMessage checks for an
|
||||||
|
// errCredentialsMissingServerURL in the error message.
|
||||||
|
func IsCredentialsMissingServerURLMessage(err string) bool {
|
||||||
|
return err == errCredentialsMissingServerURLMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCredentialsMissingUsername returns true if the error
|
||||||
|
// was an errCredentialsMissingUsername.
|
||||||
|
func IsCredentialsMissingUsername(err error) bool {
|
||||||
|
_, ok := err.(errCredentialsMissingUsername)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCredentialsMissingUsernameMessage checks for an
|
||||||
|
// errCredentialsMissingUsername in the error message.
|
||||||
|
func IsCredentialsMissingUsernameMessage(err string) bool {
|
||||||
|
return err == errCredentialsMissingUsernameMessage
|
||||||
|
}
|
||||||
|
|
|
@ -67,9 +67,6 @@ type TaskSpec struct {
|
||||||
ForceUpdate uint64
|
ForceUpdate uint64
|
||||||
|
|
||||||
Runtime RuntimeType `json:",omitempty"`
|
Runtime RuntimeType `json:",omitempty"`
|
||||||
// TODO (ehazlett): this should be removed and instead
|
|
||||||
// use struct tags (proto) for the runtimes
|
|
||||||
RuntimeData []byte `json:",omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resources represents resources (CPU/Memory).
|
// Resources represents resources (CPU/Memory).
|
||||||
|
|
|
@ -277,7 +277,7 @@ type Health struct {
|
||||||
// ContainerState stores container's running state
|
// ContainerState stores container's running state
|
||||||
// it's part of ContainerJSONBase and will return by "inspect" command
|
// it's part of ContainerJSONBase and will return by "inspect" command
|
||||||
type ContainerState struct {
|
type ContainerState struct {
|
||||||
Status string
|
Status string // String representation of the container state. Can be one of "created", "running", "paused", "restarting", "removing", "exited", or "dead"
|
||||||
Running bool
|
Running bool
|
||||||
Paused bool
|
Paused bool
|
||||||
Restarting bool
|
Restarting bool
|
||||||
|
|
|
@ -7,6 +7,9 @@ package types
|
||||||
// swagger:model Volume
|
// swagger:model Volume
|
||||||
type Volume struct {
|
type Volume struct {
|
||||||
|
|
||||||
|
// Time volume was created.
|
||||||
|
CreatedAt string `json:"CreatedAt,omitempty"`
|
||||||
|
|
||||||
// Name of the volume driver used by the volume.
|
// Name of the volume driver used by the volume.
|
||||||
// Required: true
|
// Required: true
|
||||||
Driver string `json:"Driver"`
|
Driver string `json:"Driver"`
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package gitutils
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -247,6 +247,12 @@ func (cli *Client) UpdateClientVersion(v string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DaemonHost returns the host associated with this instance of the Client.
|
||||||
|
// This operation doesn't acquire a mutex.
|
||||||
|
func (cli *Client) DaemonHost() string {
|
||||||
|
return cli.host
|
||||||
|
}
|
||||||
|
|
||||||
// ParseHost verifies that the given host strings is valid.
|
// ParseHost verifies that the given host strings is valid.
|
||||||
func ParseHost(host string) (string, string, string, error) {
|
func ParseHost(host string) (string, string, string, error) {
|
||||||
protoAddrParts := strings.SplitN(host, "://", 2)
|
protoAddrParts := strings.SplitN(host, "://", 2)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerWait waits until the specified continer is in a certain state
|
// ContainerWait waits until the specified container is in a certain state
|
||||||
// indicated by the given condition, either "not-running" (default),
|
// indicated by the given condition, either "not-running" (default),
|
||||||
// "next-exit", or "removed".
|
// "next-exit", or "removed".
|
||||||
//
|
//
|
||||||
|
@ -31,7 +31,7 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit
|
||||||
}
|
}
|
||||||
|
|
||||||
resultC := make(chan container.ContainerWaitOKBody)
|
resultC := make(chan container.ContainerWaitOKBody)
|
||||||
errC := make(chan error)
|
errC := make(chan error, 1)
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("condition", string(condition))
|
query.Set("condition", string(condition))
|
||||||
|
|
|
@ -31,6 +31,7 @@ type CommonAPIClient interface {
|
||||||
SystemAPIClient
|
SystemAPIClient
|
||||||
VolumeAPIClient
|
VolumeAPIClient
|
||||||
ClientVersion() string
|
ClientVersion() string
|
||||||
|
DaemonHost() string
|
||||||
ServerVersion(ctx context.Context) (types.Version, error)
|
ServerVersion(ctx context.Context) (types.Version, error)
|
||||||
UpdateClientVersion(v string)
|
UpdateClientVersion(v string)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,18 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
|
||||||
headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
|
headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure that the image is tagged
|
||||||
|
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||||
|
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||||
|
}
|
||||||
|
|
||||||
// Contact the registry to retrieve digest and platform information
|
// Contact the registry to retrieve digest and platform information
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
distributionInspect, err := cli.DistributionInspect(ctx, service.TaskTemplate.ContainerSpec.Image, options.EncodedRegistryAuth)
|
distributionInspect, err := cli.DistributionInspect(ctx, service.TaskTemplate.ContainerSpec.Image, options.EncodedRegistryAuth)
|
||||||
distErr = err
|
distErr = err
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// now pin by digest if the image doesn't already contain a digest
|
// now pin by digest if the image doesn't already contain a digest
|
||||||
img := imageWithDigestString(service.TaskTemplate.ContainerSpec.Image, distributionInspect.Descriptor.Digest)
|
if img := imageWithDigestString(service.TaskTemplate.ContainerSpec.Image, distributionInspect.Descriptor.Digest); img != "" {
|
||||||
if img != "" {
|
|
||||||
service.TaskTemplate.ContainerSpec.Image = img
|
service.TaskTemplate.ContainerSpec.Image = img
|
||||||
}
|
}
|
||||||
// add platforms that are compatible with the service
|
// add platforms that are compatible with the service
|
||||||
|
@ -55,25 +59,33 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
|
||||||
}
|
}
|
||||||
|
|
||||||
// imageWithDigestString takes an image string and a digest, and updates
|
// imageWithDigestString takes an image string and a digest, and updates
|
||||||
// the image string if it didn't originally contain a digest. It assumes
|
// the image string if it didn't originally contain a digest. It returns
|
||||||
// that the image string is not an image ID
|
// an empty string if there are no updates.
|
||||||
func imageWithDigestString(image string, dgst digest.Digest) string {
|
func imageWithDigestString(image string, dgst digest.Digest) string {
|
||||||
isCanonical := false
|
namedRef, err := reference.ParseNormalizedNamed(image)
|
||||||
ref, err := reference.ParseAnyReference(image)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_, isCanonical = ref.(reference.Canonical)
|
if _, isCanonical := namedRef.(reference.Canonical); !isCanonical {
|
||||||
|
// ensure that image gets a default tag if none is provided
|
||||||
if !isCanonical {
|
|
||||||
namedRef, _ := ref.(reference.Named)
|
|
||||||
img, err := reference.WithDigest(namedRef, dgst)
|
img, err := reference.WithDigest(namedRef, dgst)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return img.String()
|
return reference.FamiliarString(img)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// imageWithTagString takes an image string, and returns a tagged image
|
||||||
|
// string, adding a 'latest' tag if one was not provided. It returns an
|
||||||
|
// emptry string if a canonical reference was provided
|
||||||
|
func imageWithTagString(image string) string {
|
||||||
|
namedRef, err := reference.ParseNormalizedNamed(image)
|
||||||
|
if err == nil {
|
||||||
|
return reference.FamiliarString(reference.TagNameOnly(namedRef))
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// updateServicePlatforms updates the Platforms in swarm.Placement to list
|
// updateServicePlatforms updates the Platforms in swarm.Placement to list
|
||||||
// all compatible platforms for the service, as found in distributionInspect
|
// all compatible platforms for the service, as found in distributionInspect
|
||||||
// and returns a pointer to the new or updated swarm.Placement struct
|
// and returns a pointer to the new or updated swarm.Placement struct
|
||||||
|
|
|
@ -35,6 +35,11 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
||||||
|
|
||||||
query.Set("version", strconv.FormatUint(version.Index, 10))
|
query.Set("version", strconv.FormatUint(version.Index, 10))
|
||||||
|
|
||||||
|
// ensure that the image is tagged
|
||||||
|
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||||
|
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||||
|
}
|
||||||
|
|
||||||
// Contact the registry to retrieve digest and platform information
|
// Contact the registry to retrieve digest and platform information
|
||||||
// This happens only when the image has changed
|
// This happens only when the image has changed
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
|
@ -42,8 +47,7 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
||||||
distErr = err
|
distErr = err
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// now pin by digest if the image doesn't already contain a digest
|
// now pin by digest if the image doesn't already contain a digest
|
||||||
img := imageWithDigestString(service.TaskTemplate.ContainerSpec.Image, distributionInspect.Descriptor.Digest)
|
if img := imageWithDigestString(service.TaskTemplate.ContainerSpec.Image, distributionInspect.Descriptor.Digest); img != "" {
|
||||||
if img != "" {
|
|
||||||
service.TaskTemplate.ContainerSpec.Image = img
|
service.TaskTemplate.ContainerSpec.Image = img
|
||||||
}
|
}
|
||||||
// add platforms that are compatible with the service
|
// add platforms that are compatible with the service
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
package opts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/csv"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ConfigOpt is a Value type for parsing configs
|
|
||||||
type ConfigOpt struct {
|
|
||||||
values []*swarmtypes.ConfigReference
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a new config value
|
|
||||||
func (o *ConfigOpt) Set(value string) error {
|
|
||||||
csvReader := csv.NewReader(strings.NewReader(value))
|
|
||||||
fields, err := csvReader.Read()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
options := &swarmtypes.ConfigReference{
|
|
||||||
File: &swarmtypes.ConfigReferenceFileTarget{
|
|
||||||
UID: "0",
|
|
||||||
GID: "0",
|
|
||||||
Mode: 0444,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// support a simple syntax of --config foo
|
|
||||||
if len(fields) == 1 {
|
|
||||||
options.File.Name = fields[0]
|
|
||||||
options.ConfigName = fields[0]
|
|
||||||
o.values = append(o.values, options)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, field := range fields {
|
|
||||||
parts := strings.SplitN(field, "=", 2)
|
|
||||||
key := strings.ToLower(parts[0])
|
|
||||||
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
|
|
||||||
}
|
|
||||||
|
|
||||||
value := parts[1]
|
|
||||||
switch key {
|
|
||||||
case "source", "src":
|
|
||||||
options.ConfigName = value
|
|
||||||
case "target":
|
|
||||||
options.File.Name = value
|
|
||||||
case "uid":
|
|
||||||
options.File.UID = value
|
|
||||||
case "gid":
|
|
||||||
options.File.GID = value
|
|
||||||
case "mode":
|
|
||||||
m, err := strconv.ParseUint(value, 0, 32)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid mode specified: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
options.File.Mode = os.FileMode(m)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("invalid field in config request: %s", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.ConfigName == "" {
|
|
||||||
return fmt.Errorf("source is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
o.values = append(o.values, options)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the type of this option
|
|
||||||
func (o *ConfigOpt) Type() string {
|
|
||||||
return "config"
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string repr of this option
|
|
||||||
func (o *ConfigOpt) String() string {
|
|
||||||
configs := []string{}
|
|
||||||
for _, config := range o.values {
|
|
||||||
repr := fmt.Sprintf("%s -> %s", config.ConfigName, config.File.Name)
|
|
||||||
configs = append(configs, repr)
|
|
||||||
}
|
|
||||||
return strings.Join(configs, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the config requests
|
|
||||||
func (o *ConfigOpt) Value() []*swarmtypes.ConfigReference {
|
|
||||||
return o.values
|
|
||||||
}
|
|
|
@ -2,13 +2,11 @@ package opts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"net"
|
"net"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -236,15 +234,6 @@ func ValidateIPAddress(val string) (string, error) {
|
||||||
return "", fmt.Errorf("%s is not an ip address", val)
|
return "", fmt.Errorf("%s is not an ip address", val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateMACAddress validates a MAC address.
|
|
||||||
func ValidateMACAddress(val string) (string, error) {
|
|
||||||
_, err := net.ParseMAC(strings.TrimSpace(val))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateDNSSearch validates domain for resolvconf search configuration.
|
// ValidateDNSSearch validates domain for resolvconf search configuration.
|
||||||
// A zero length domain is represented by a dot (.).
|
// A zero length domain is represented by a dot (.).
|
||||||
func ValidateDNSSearch(val string) (string, error) {
|
func ValidateDNSSearch(val string) (string, error) {
|
||||||
|
@ -274,114 +263,6 @@ func ValidateLabel(val string) (string, error) {
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateSysctl validates a sysctl and returns it.
|
|
||||||
func ValidateSysctl(val string) (string, error) {
|
|
||||||
validSysctlMap := map[string]bool{
|
|
||||||
"kernel.msgmax": true,
|
|
||||||
"kernel.msgmnb": true,
|
|
||||||
"kernel.msgmni": true,
|
|
||||||
"kernel.sem": true,
|
|
||||||
"kernel.shmall": true,
|
|
||||||
"kernel.shmmax": true,
|
|
||||||
"kernel.shmmni": true,
|
|
||||||
"kernel.shm_rmid_forced": true,
|
|
||||||
}
|
|
||||||
validSysctlPrefixes := []string{
|
|
||||||
"net.",
|
|
||||||
"fs.mqueue.",
|
|
||||||
}
|
|
||||||
arr := strings.Split(val, "=")
|
|
||||||
if len(arr) < 2 {
|
|
||||||
return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
|
|
||||||
}
|
|
||||||
if validSysctlMap[arr[0]] {
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, vp := range validSysctlPrefixes {
|
|
||||||
if strings.HasPrefix(arr[0], vp) {
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterOpt is a flag type for validating filters
|
|
||||||
type FilterOpt struct {
|
|
||||||
filter filters.Args
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFilterOpt returns a new FilterOpt
|
|
||||||
func NewFilterOpt() FilterOpt {
|
|
||||||
return FilterOpt{filter: filters.NewArgs()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *FilterOpt) String() string {
|
|
||||||
repr, err := filters.ToParam(o.filter)
|
|
||||||
if err != nil {
|
|
||||||
return "invalid filters"
|
|
||||||
}
|
|
||||||
return repr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the value of the opt by parsing the command line value
|
|
||||||
func (o *FilterOpt) Set(value string) error {
|
|
||||||
var err error
|
|
||||||
o.filter, err = filters.ParseFlag(value, o.filter)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the option type
|
|
||||||
func (o *FilterOpt) Type() string {
|
|
||||||
return "filter"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the value of this option
|
|
||||||
func (o *FilterOpt) Value() filters.Args {
|
|
||||||
return o.filter
|
|
||||||
}
|
|
||||||
|
|
||||||
// NanoCPUs is a type for fixed point fractional number.
|
|
||||||
type NanoCPUs int64
|
|
||||||
|
|
||||||
// String returns the string format of the number
|
|
||||||
func (c *NanoCPUs) String() string {
|
|
||||||
if *c == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return big.NewRat(c.Value(), 1e9).FloatString(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the value of the NanoCPU by passing a string
|
|
||||||
func (c *NanoCPUs) Set(value string) error {
|
|
||||||
cpus, err := ParseCPUs(value)
|
|
||||||
*c = NanoCPUs(cpus)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the type
|
|
||||||
func (c *NanoCPUs) Type() string {
|
|
||||||
return "decimal"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the value in int64
|
|
||||||
func (c *NanoCPUs) Value() int64 {
|
|
||||||
return int64(*c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseCPUs takes a string ratio and returns an integer value of nano cpus
|
|
||||||
func ParseCPUs(value string) (int64, error) {
|
|
||||||
cpu, ok := new(big.Rat).SetString(value)
|
|
||||||
if !ok {
|
|
||||||
return 0, fmt.Errorf("failed to parse %v as a rational number", value)
|
|
||||||
}
|
|
||||||
nano := cpu.Mul(cpu, big.NewRat(1e9, 1))
|
|
||||||
if !nano.IsInt() {
|
|
||||||
return 0, fmt.Errorf("value is too precise")
|
|
||||||
}
|
|
||||||
return nano.Num().Int64(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseLink parses and validates the specified string as a link format (name:alias)
|
// ParseLink parses and validates the specified string as a link format (name:alias)
|
||||||
func ParseLink(val string) (string, string, error) {
|
func ParseLink(val string) (string, string, error) {
|
||||||
if val == "" {
|
if val == "" {
|
||||||
|
@ -404,12 +285,6 @@ func ParseLink(val string) (string, string, error) {
|
||||||
return arr[0], arr[1], nil
|
return arr[0], arr[1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateLink validates that the specified string has a valid link format (containerName:alias).
|
|
||||||
func ValidateLink(val string) (string, error) {
|
|
||||||
_, _, err := ParseLink(val)
|
|
||||||
return val, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
|
// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
|
||||||
type MemBytes int64
|
type MemBytes int64
|
||||||
|
|
||||||
|
@ -450,39 +325,3 @@ func (m *MemBytes) UnmarshalJSON(s []byte) error {
|
||||||
*m = MemBytes(val)
|
*m = MemBytes(val)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemSwapBytes is a type for human readable memory bytes (like 128M, 2g, etc).
|
|
||||||
// It differs from MemBytes in that -1 is valid and the default.
|
|
||||||
type MemSwapBytes int64
|
|
||||||
|
|
||||||
// Set sets the value of the MemSwapBytes by passing a string
|
|
||||||
func (m *MemSwapBytes) Set(value string) error {
|
|
||||||
if value == "-1" {
|
|
||||||
*m = MemSwapBytes(-1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
val, err := units.RAMInBytes(value)
|
|
||||||
*m = MemSwapBytes(val)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the type
|
|
||||||
func (m *MemSwapBytes) Type() string {
|
|
||||||
return "bytes"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the value in int64
|
|
||||||
func (m *MemSwapBytes) Value() int64 {
|
|
||||||
return int64(*m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemSwapBytes) String() string {
|
|
||||||
b := MemBytes(*m)
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON is the customized unmarshaler for MemSwapBytes
|
|
||||||
func (m *MemSwapBytes) UnmarshalJSON(s []byte) error {
|
|
||||||
b := MemBytes(*m)
|
|
||||||
return b.UnmarshalJSON(s)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
package opts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/blkiodev"
|
|
||||||
"github.com/docker/go-units"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ValidatorThrottleFctType defines a validator function that returns a validated struct and/or an error.
|
|
||||||
type ValidatorThrottleFctType func(val string) (*blkiodev.ThrottleDevice, error)
|
|
||||||
|
|
||||||
// ValidateThrottleBpsDevice validates that the specified string has a valid device-rate format.
|
|
||||||
func ValidateThrottleBpsDevice(val string) (*blkiodev.ThrottleDevice, error) {
|
|
||||||
split := strings.SplitN(val, ":", 2)
|
|
||||||
if len(split) != 2 {
|
|
||||||
return nil, fmt.Errorf("bad format: %s", val)
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(split[0], "/dev/") {
|
|
||||||
return nil, fmt.Errorf("bad format for device path: %s", val)
|
|
||||||
}
|
|
||||||
rate, err := units.RAMInBytes(split[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
|
|
||||||
}
|
|
||||||
if rate < 0 {
|
|
||||||
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &blkiodev.ThrottleDevice{
|
|
||||||
Path: split[0],
|
|
||||||
Rate: uint64(rate),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateThrottleIOpsDevice validates that the specified string has a valid device-rate format.
|
|
||||||
func ValidateThrottleIOpsDevice(val string) (*blkiodev.ThrottleDevice, error) {
|
|
||||||
split := strings.SplitN(val, ":", 2)
|
|
||||||
if len(split) != 2 {
|
|
||||||
return nil, fmt.Errorf("bad format: %s", val)
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(split[0], "/dev/") {
|
|
||||||
return nil, fmt.Errorf("bad format for device path: %s", val)
|
|
||||||
}
|
|
||||||
rate, err := strconv.ParseUint(split[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
|
|
||||||
}
|
|
||||||
if rate < 0 {
|
|
||||||
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &blkiodev.ThrottleDevice{
|
|
||||||
Path: split[0],
|
|
||||||
Rate: uint64(rate),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ThrottledeviceOpt defines a map of ThrottleDevices
|
|
||||||
type ThrottledeviceOpt struct {
|
|
||||||
values []*blkiodev.ThrottleDevice
|
|
||||||
validator ValidatorThrottleFctType
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewThrottledeviceOpt creates a new ThrottledeviceOpt
|
|
||||||
func NewThrottledeviceOpt(validator ValidatorThrottleFctType) ThrottledeviceOpt {
|
|
||||||
values := []*blkiodev.ThrottleDevice{}
|
|
||||||
return ThrottledeviceOpt{
|
|
||||||
values: values,
|
|
||||||
validator: validator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set validates a ThrottleDevice and sets its name as a key in ThrottledeviceOpt
|
|
||||||
func (opt *ThrottledeviceOpt) Set(val string) error {
|
|
||||||
var value *blkiodev.ThrottleDevice
|
|
||||||
if opt.validator != nil {
|
|
||||||
v, err := opt.validator(val)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
value = v
|
|
||||||
}
|
|
||||||
(opt.values) = append((opt.values), value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns ThrottledeviceOpt values as a string.
|
|
||||||
func (opt *ThrottledeviceOpt) String() string {
|
|
||||||
var out []string
|
|
||||||
for _, v := range opt.values {
|
|
||||||
out = append(out, v.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v", out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetList returns a slice of pointers to ThrottleDevices.
|
|
||||||
func (opt *ThrottledeviceOpt) GetList() []*blkiodev.ThrottleDevice {
|
|
||||||
var throttledevice []*blkiodev.ThrottleDevice
|
|
||||||
throttledevice = append(throttledevice, opt.values...)
|
|
||||||
|
|
||||||
return throttledevice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the option type
|
|
||||||
func (opt *ThrottledeviceOpt) Type() string {
|
|
||||||
return "list"
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package httputils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
headerRegexp = regexp.MustCompile(`^(?:(.+)/(.+?))\((.+)\).*$`)
|
|
||||||
errInvalidHeader = errors.New("Bad header, should be in format `docker/version (platform)`")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Download requests a given URL and returns an io.Reader.
|
|
||||||
func Download(url string) (resp *http.Response, err error) {
|
|
||||||
if resp, err = http.Get(url); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode >= 400 {
|
|
||||||
return nil, fmt.Errorf("Got HTTP status code >= 400: %s", resp.Status)
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHTTPRequestError returns a JSON response error.
|
|
||||||
func NewHTTPRequestError(msg string, res *http.Response) error {
|
|
||||||
return &jsonmessage.JSONError{
|
|
||||||
Message: msg,
|
|
||||||
Code: res.StatusCode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerHeader contains the server information.
|
|
||||||
type ServerHeader struct {
|
|
||||||
App string // docker
|
|
||||||
Ver string // 1.8.0-dev
|
|
||||||
OS string // windows or linux
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseServerHeader extracts pieces from an HTTP server header
|
|
||||||
// which is in the format "docker/version (os)" e.g. docker/1.8.0-dev (windows).
|
|
||||||
func ParseServerHeader(hdr string) (*ServerHeader, error) {
|
|
||||||
matches := headerRegexp.FindStringSubmatch(hdr)
|
|
||||||
if len(matches) != 4 {
|
|
||||||
return nil, errInvalidHeader
|
|
||||||
}
|
|
||||||
return &ServerHeader{
|
|
||||||
App: strings.TrimSpace(matches[1]),
|
|
||||||
Ver: strings.TrimSpace(matches[2]),
|
|
||||||
OS: strings.TrimSpace(matches[3]),
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package httputils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mime"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MimeTypes stores the MIME content type.
|
|
||||||
var MimeTypes = struct {
|
|
||||||
TextPlain string
|
|
||||||
OctetStream string
|
|
||||||
}{"text/plain", "application/octet-stream"}
|
|
||||||
|
|
||||||
// DetectContentType returns a best guess representation of the MIME
|
|
||||||
// content type for the bytes at c. The value detected by
|
|
||||||
// http.DetectContentType is guaranteed not be nil, defaulting to
|
|
||||||
// application/octet-stream when a better guess cannot be made. The
|
|
||||||
// result of this detection is then run through mime.ParseMediaType()
|
|
||||||
// which separates the actual MIME string from any parameters.
|
|
||||||
func DetectContentType(c []byte) (string, map[string]string, error) {
|
|
||||||
|
|
||||||
ct := http.DetectContentType(c)
|
|
||||||
contentType, args, err := mime.ParseMediaType(ct)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return contentType, args, nil
|
|
||||||
}
|
|
|
@ -1,224 +0,0 @@
|
||||||
package ioutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type pos struct {
|
|
||||||
idx int
|
|
||||||
offset int64
|
|
||||||
}
|
|
||||||
|
|
||||||
type multiReadSeeker struct {
|
|
||||||
readers []io.ReadSeeker
|
|
||||||
pos *pos
|
|
||||||
posIdx map[io.ReadSeeker]int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
var tmpOffset int64
|
|
||||||
switch whence {
|
|
||||||
case os.SEEK_SET:
|
|
||||||
for i, rdr := range r.readers {
|
|
||||||
// get size of the current reader
|
|
||||||
s, err := rdr.Seek(0, os.SEEK_END)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if offset > tmpOffset+s {
|
|
||||||
if i == len(r.readers)-1 {
|
|
||||||
rdrOffset := s + (offset - tmpOffset)
|
|
||||||
if _, err := rdr.Seek(rdrOffset, os.SEEK_SET); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
r.pos = &pos{i, rdrOffset}
|
|
||||||
return offset, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpOffset += s
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
rdrOffset := offset - tmpOffset
|
|
||||||
idx := i
|
|
||||||
|
|
||||||
rdr.Seek(rdrOffset, os.SEEK_SET)
|
|
||||||
// make sure all following readers are at 0
|
|
||||||
for _, rdr := range r.readers[i+1:] {
|
|
||||||
rdr.Seek(0, os.SEEK_SET)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rdrOffset == s && i != len(r.readers)-1 {
|
|
||||||
idx++
|
|
||||||
rdrOffset = 0
|
|
||||||
}
|
|
||||||
r.pos = &pos{idx, rdrOffset}
|
|
||||||
return offset, nil
|
|
||||||
}
|
|
||||||
case os.SEEK_END:
|
|
||||||
for _, rdr := range r.readers {
|
|
||||||
s, err := rdr.Seek(0, os.SEEK_END)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
tmpOffset += s
|
|
||||||
}
|
|
||||||
r.Seek(tmpOffset+offset, os.SEEK_SET)
|
|
||||||
return tmpOffset + offset, nil
|
|
||||||
case os.SEEK_CUR:
|
|
||||||
if r.pos == nil {
|
|
||||||
return r.Seek(offset, os.SEEK_SET)
|
|
||||||
}
|
|
||||||
// Just return the current offset
|
|
||||||
if offset == 0 {
|
|
||||||
return r.getCurOffset()
|
|
||||||
}
|
|
||||||
|
|
||||||
curOffset, err := r.getCurOffset()
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
rdr, rdrOffset, err := r.getReaderForOffset(curOffset + offset)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.pos = &pos{r.posIdx[rdr], rdrOffset}
|
|
||||||
return curOffset + offset, nil
|
|
||||||
default:
|
|
||||||
return -1, fmt.Errorf("Invalid whence: %d", whence)
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1, fmt.Errorf("Error seeking for whence: %d, offset: %d", whence, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *multiReadSeeker) getReaderForOffset(offset int64) (io.ReadSeeker, int64, error) {
|
|
||||||
|
|
||||||
var offsetTo int64
|
|
||||||
|
|
||||||
for _, rdr := range r.readers {
|
|
||||||
size, err := getReadSeekerSize(rdr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, -1, err
|
|
||||||
}
|
|
||||||
if offsetTo+size > offset {
|
|
||||||
return rdr, offset - offsetTo, nil
|
|
||||||
}
|
|
||||||
if rdr == r.readers[len(r.readers)-1] {
|
|
||||||
return rdr, offsetTo + offset, nil
|
|
||||||
}
|
|
||||||
offsetTo += size
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *multiReadSeeker) getCurOffset() (int64, error) {
|
|
||||||
var totalSize int64
|
|
||||||
for _, rdr := range r.readers[:r.pos.idx+1] {
|
|
||||||
if r.posIdx[rdr] == r.pos.idx {
|
|
||||||
totalSize += r.pos.offset
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
size, err := getReadSeekerSize(rdr)
|
|
||||||
if err != nil {
|
|
||||||
return -1, fmt.Errorf("error getting seeker size: %v", err)
|
|
||||||
}
|
|
||||||
totalSize += size
|
|
||||||
}
|
|
||||||
return totalSize, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *multiReadSeeker) getOffsetToReader(rdr io.ReadSeeker) (int64, error) {
|
|
||||||
var offset int64
|
|
||||||
for _, r := range r.readers {
|
|
||||||
if r == rdr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
size, err := getReadSeekerSize(rdr)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
offset += size
|
|
||||||
}
|
|
||||||
return offset, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *multiReadSeeker) Read(b []byte) (int, error) {
|
|
||||||
if r.pos == nil {
|
|
||||||
// make sure all readers are at 0
|
|
||||||
r.Seek(0, os.SEEK_SET)
|
|
||||||
}
|
|
||||||
|
|
||||||
bLen := int64(len(b))
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
var rdr io.ReadSeeker
|
|
||||||
|
|
||||||
for _, rdr = range r.readers[r.pos.idx:] {
|
|
||||||
readBytes, err := io.CopyN(buf, rdr, bLen)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
bLen -= readBytes
|
|
||||||
|
|
||||||
if bLen == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdrPos, err := rdr.Seek(0, os.SEEK_CUR)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
r.pos = &pos{r.posIdx[rdr], rdrPos}
|
|
||||||
return buf.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getReadSeekerSize(rdr io.ReadSeeker) (int64, error) {
|
|
||||||
// save the current position
|
|
||||||
pos, err := rdr.Seek(0, os.SEEK_CUR)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the size
|
|
||||||
size, err := rdr.Seek(0, os.SEEK_END)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset the position
|
|
||||||
if _, err := rdr.Seek(pos, os.SEEK_SET); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
return size, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultiReadSeeker returns a ReadSeeker that's the logical concatenation of the provided
|
|
||||||
// input readseekers. After calling this method the initial position is set to the
|
|
||||||
// beginning of the first ReadSeeker. At the end of a ReadSeeker, Read always advances
|
|
||||||
// to the beginning of the next ReadSeeker and returns EOF at the end of the last ReadSeeker.
|
|
||||||
// Seek can be used over the sum of lengths of all readseekers.
|
|
||||||
//
|
|
||||||
// When a MultiReadSeeker is used, no Read and Seek operations should be made on
|
|
||||||
// its ReadSeeker components. Also, users should make no assumption on the state
|
|
||||||
// of individual readseekers while the MultiReadSeeker is used.
|
|
||||||
func MultiReadSeeker(readers ...io.ReadSeeker) io.ReadSeeker {
|
|
||||||
if len(readers) == 1 {
|
|
||||||
return readers[0]
|
|
||||||
}
|
|
||||||
idx := make(map[io.ReadSeeker]int)
|
|
||||||
for i, rdr := range readers {
|
|
||||||
idx[rdr] = i
|
|
||||||
}
|
|
||||||
return &multiReadSeeker{
|
|
||||||
readers: readers,
|
|
||||||
posIdx: idx,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
// Termios is the Unix API for terminal I/O.
|
// Termios is the Unix API for terminal I/O.
|
||||||
// It is passthrough for syscall.Termios in order to make it portable with
|
// It is passthrough for unix.Termios in order to make it portable with
|
||||||
// other platforms where it is not available or handled differently.
|
// other platforms where it is not available or handled differently.
|
||||||
type Termios unix.Termios
|
type Termios unix.Termios
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
|
||||||
newState := oldState.termios
|
newState := oldState.termios
|
||||||
|
|
||||||
newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON | syscall.IXANY)
|
newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON | unix.IXANY)
|
||||||
newState.Oflag &^= syscall.OPOST
|
newState.Oflag &^= unix.OPOST
|
||||||
newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
|
newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
|
||||||
newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
|
newState.Cflag &^= (unix.CSIZE | unix.PARENB)
|
||||||
newState.Cflag |= syscall.CS8
|
newState.Cflag |= unix.CS8
|
||||||
|
|
||||||
/*
|
/*
|
||||||
VMIN is the minimum number of characters that needs to be read in non-canonical mode for it to be returned
|
VMIN is the minimum number of characters that needs to be read in non-canonical mode for it to be returned
|
||||||
|
|
|
@ -27,7 +27,7 @@ func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
|
||||||
newState := oldState.termios
|
newState := oldState.termios
|
||||||
newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
|
newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
|
||||||
newState.Oflag &^= unix.OPOST
|
newState.Oflag |= unix.OPOST
|
||||||
newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
|
newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
|
||||||
newState.Cflag &^= (unix.CSIZE | unix.PARENB)
|
newState.Cflag &^= (unix.CSIZE | unix.PARENB)
|
||||||
newState.Cflag |= unix.CS8
|
newState.Cflag |= unix.CS8
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package httputils
|
package resumable
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type resumableRequestReader struct {
|
type requestReader struct {
|
||||||
client *http.Client
|
client *http.Client
|
||||||
request *http.Request
|
request *http.Request
|
||||||
lastRange int64
|
lastRange int64
|
||||||
|
@ -20,22 +20,22 @@ type resumableRequestReader struct {
|
||||||
waitDuration time.Duration
|
waitDuration time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResumableRequestReader makes it possible to resume reading a request's body transparently
|
// NewRequestReader makes it possible to resume reading a request's body transparently
|
||||||
// maxfail is the number of times we retry to make requests again (not resumes)
|
// maxfail is the number of times we retry to make requests again (not resumes)
|
||||||
// totalsize is the total length of the body; auto detect if not provided
|
// totalsize is the total length of the body; auto detect if not provided
|
||||||
func ResumableRequestReader(c *http.Client, r *http.Request, maxfail uint32, totalsize int64) io.ReadCloser {
|
func NewRequestReader(c *http.Client, r *http.Request, maxfail uint32, totalsize int64) io.ReadCloser {
|
||||||
return &resumableRequestReader{client: c, request: r, maxFailures: maxfail, totalSize: totalsize, waitDuration: 5 * time.Second}
|
return &requestReader{client: c, request: r, maxFailures: maxfail, totalSize: totalsize, waitDuration: 5 * time.Second}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResumableRequestReaderWithInitialResponse makes it possible to resume
|
// NewRequestReaderWithInitialResponse makes it possible to resume
|
||||||
// reading the body of an already initiated request.
|
// reading the body of an already initiated request.
|
||||||
func ResumableRequestReaderWithInitialResponse(c *http.Client, r *http.Request, maxfail uint32, totalsize int64, initialResponse *http.Response) io.ReadCloser {
|
func NewRequestReaderWithInitialResponse(c *http.Client, r *http.Request, maxfail uint32, totalsize int64, initialResponse *http.Response) io.ReadCloser {
|
||||||
return &resumableRequestReader{client: c, request: r, maxFailures: maxfail, totalSize: totalsize, currentResponse: initialResponse, waitDuration: 5 * time.Second}
|
return &requestReader{client: c, request: r, maxFailures: maxfail, totalSize: totalsize, currentResponse: initialResponse, waitDuration: 5 * time.Second}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resumableRequestReader) Read(p []byte) (n int, err error) {
|
func (r *requestReader) Read(p []byte) (n int, err error) {
|
||||||
if r.client == nil || r.request == nil {
|
if r.client == nil || r.request == nil {
|
||||||
return 0, fmt.Errorf("client and request can't be nil\n")
|
return 0, fmt.Errorf("client and request can't be nil")
|
||||||
}
|
}
|
||||||
isFreshRequest := false
|
isFreshRequest := false
|
||||||
if r.lastRange != 0 && r.currentResponse == nil {
|
if r.lastRange != 0 && r.currentResponse == nil {
|
||||||
|
@ -81,14 +81,14 @@ func (r *resumableRequestReader) Read(p []byte) (n int, err error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resumableRequestReader) Close() error {
|
func (r *requestReader) Close() error {
|
||||||
r.cleanUpResponse()
|
r.cleanUpResponse()
|
||||||
r.client = nil
|
r.client = nil
|
||||||
r.request = nil
|
r.request = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resumableRequestReader) cleanUpResponse() {
|
func (r *requestReader) cleanUpResponse() {
|
||||||
if r.currentResponse != nil {
|
if r.currentResponse != nil {
|
||||||
r.currentResponse.Body.Close()
|
r.currentResponse.Body.Close()
|
||||||
r.currentResponse = nil
|
r.currentResponse = nil
|
|
@ -23,10 +23,11 @@ import (
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
registrytypes "github.com/docker/docker/api/types/registry"
|
registrytypes "github.com/docker/docker/api/types/registry"
|
||||||
"github.com/docker/docker/pkg/httputils"
|
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/pkg/tarsum"
|
"github.com/docker/docker/pkg/tarsum"
|
||||||
|
"github.com/docker/docker/registry/resumable"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -226,7 +227,7 @@ func (r *Session) GetRemoteHistory(imgID, registry string) ([]string, error) {
|
||||||
if res.StatusCode == 401 {
|
if res.StatusCode == 401 {
|
||||||
return nil, errcode.ErrorCodeUnauthorized.WithArgs()
|
return nil, errcode.ErrorCodeUnauthorized.WithArgs()
|
||||||
}
|
}
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res)
|
return nil, newJSONError(fmt.Sprintf("Server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
var history []string
|
var history []string
|
||||||
|
@ -246,7 +247,7 @@ func (r *Session) LookupRemoteImage(imgID, registry string) error {
|
||||||
}
|
}
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
return httputils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d", res.StatusCode), res)
|
return newJSONError(fmt.Sprintf("HTTP code %d", res.StatusCode), res)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -259,7 +260,7 @@ func (r *Session) GetRemoteImageJSON(imgID, registry string) ([]byte, int64, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
return nil, -1, httputils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d", res.StatusCode), res)
|
return nil, -1, newJSONError(fmt.Sprintf("HTTP code %d", res.StatusCode), res)
|
||||||
}
|
}
|
||||||
// if the size header is not present, then set it to '-1'
|
// if the size header is not present, then set it to '-1'
|
||||||
imageSize := int64(-1)
|
imageSize := int64(-1)
|
||||||
|
@ -313,7 +314,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
|
||||||
|
|
||||||
if res.Header.Get("Accept-Ranges") == "bytes" && imgSize > 0 {
|
if res.Header.Get("Accept-Ranges") == "bytes" && imgSize > 0 {
|
||||||
logrus.Debug("server supports resume")
|
logrus.Debug("server supports resume")
|
||||||
return httputils.ResumableRequestReaderWithInitialResponse(r.client, req, 5, imgSize, res), nil
|
return resumable.NewRequestReaderWithInitialResponse(r.client, req, 5, imgSize, res), nil
|
||||||
}
|
}
|
||||||
logrus.Debug("server doesn't support resume")
|
logrus.Debug("server doesn't support resume")
|
||||||
return res.Body, nil
|
return res.Body, nil
|
||||||
|
@ -444,13 +445,13 @@ func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, erro
|
||||||
// TODO: Right now we're ignoring checksums in the response body.
|
// TODO: Right now we're ignoring checksums in the response body.
|
||||||
// In the future, we need to use them to check image validity.
|
// In the future, we need to use them to check image validity.
|
||||||
if res.StatusCode == 404 {
|
if res.StatusCode == 404 {
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("HTTP code: %d", res.StatusCode), res)
|
return nil, newJSONError(fmt.Sprintf("HTTP code: %d", res.StatusCode), res)
|
||||||
} else if res.StatusCode != 200 {
|
} else if res.StatusCode != 200 {
|
||||||
errBody, err := ioutil.ReadAll(res.Body)
|
errBody, err := ioutil.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error reading response body: %s", err)
|
logrus.Debugf("Error reading response body: %s", err)
|
||||||
}
|
}
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, reference.Path(name), errBody), res)
|
return nil, newJSONError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, reference.Path(name), errBody), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
var endpoints []string
|
var endpoints []string
|
||||||
|
@ -537,12 +538,12 @@ func (r *Session) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regist
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode == 401 && strings.HasPrefix(registry, "http://") {
|
if res.StatusCode == 401 && strings.HasPrefix(registry, "http://") {
|
||||||
return httputils.NewHTTPRequestError("HTTP code 401, Docker will not send auth headers over HTTP.", res)
|
return newJSONError("HTTP code 401, Docker will not send auth headers over HTTP.", res)
|
||||||
}
|
}
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
errBody, err := ioutil.ReadAll(res.Body)
|
errBody, err := ioutil.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httputils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res)
|
return newJSONError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res)
|
||||||
}
|
}
|
||||||
var jsonBody map[string]string
|
var jsonBody map[string]string
|
||||||
if err := json.Unmarshal(errBody, &jsonBody); err != nil {
|
if err := json.Unmarshal(errBody, &jsonBody); err != nil {
|
||||||
|
@ -550,7 +551,7 @@ func (r *Session) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regist
|
||||||
} else if jsonBody["error"] == "Image already exists" {
|
} else if jsonBody["error"] == "Image already exists" {
|
||||||
return ErrAlreadyExists
|
return ErrAlreadyExists
|
||||||
}
|
}
|
||||||
return httputils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata: %q", res.StatusCode, errBody), res)
|
return newJSONError(fmt.Sprintf("HTTP code %d while uploading metadata: %q", res.StatusCode, errBody), res)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -591,9 +592,9 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
errBody, err := ioutil.ReadAll(res.Body)
|
errBody, err := ioutil.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", httputils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res)
|
return "", "", newJSONError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res)
|
||||||
}
|
}
|
||||||
return "", "", httputils.NewHTTPRequestError(fmt.Sprintf("Received HTTP code %d while uploading layer: %q", res.StatusCode, errBody), res)
|
return "", "", newJSONError(fmt.Sprintf("Received HTTP code %d while uploading layer: %q", res.StatusCode, errBody), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
checksumPayload = "sha256:" + hex.EncodeToString(h.Sum(nil))
|
checksumPayload = "sha256:" + hex.EncodeToString(h.Sum(nil))
|
||||||
|
@ -619,7 +620,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr
|
||||||
}
|
}
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
if res.StatusCode != 200 && res.StatusCode != 201 {
|
if res.StatusCode != 200 && res.StatusCode != 201 {
|
||||||
return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, reference.Path(remote)), res)
|
return newJSONError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, reference.Path(remote)), res)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -683,7 +684,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error reading response body: %s", err)
|
logrus.Debugf("Error reading response body: %s", err)
|
||||||
}
|
}
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
|
return nil, newJSONError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
|
||||||
}
|
}
|
||||||
tokens = res.Header["X-Docker-Token"]
|
tokens = res.Header["X-Docker-Token"]
|
||||||
logrus.Debugf("Auth token: %v", tokens)
|
logrus.Debugf("Auth token: %v", tokens)
|
||||||
|
@ -701,7 +702,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error reading response body: %s", err)
|
logrus.Debugf("Error reading response body: %s", err)
|
||||||
}
|
}
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
|
return nil, newJSONError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +751,7 @@ func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.Sea
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Unexpected status code %d", res.StatusCode), res)
|
return nil, newJSONError(fmt.Sprintf("Unexpected status code %d", res.StatusCode), res)
|
||||||
}
|
}
|
||||||
result := new(registrytypes.SearchResults)
|
result := new(registrytypes.SearchResults)
|
||||||
return result, json.NewDecoder(res.Body).Decode(result)
|
return result, json.NewDecoder(res.Body).Decode(result)
|
||||||
|
@ -781,3 +782,10 @@ func isTimeout(err error) bool {
|
||||||
t, ok := e.(timeout)
|
t, ok := e.(timeout)
|
||||||
return ok && t.Timeout()
|
return ok && t.Timeout()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newJSONError(msg string, res *http.Response) error {
|
||||||
|
return &jsonmessage.JSONError{
|
||||||
|
Message: msg,
|
||||||
|
Code: res.StatusCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func validateNetContainerMode(c *container.Config, hc *container.HostConfig) err
|
||||||
parts := strings.Split(string(hc.NetworkMode), ":")
|
parts := strings.Split(string(hc.NetworkMode), ":")
|
||||||
if parts[0] == "container" {
|
if parts[0] == "container" {
|
||||||
if len(parts) < 2 || parts[1] == "" {
|
if len(parts) < 2 || parts[1] == "" {
|
||||||
return fmt.Errorf("--net: invalid net mode: invalid container format container:<name|id>")
|
return fmt.Errorf("Invalid network mode: invalid container format container:<name|id>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ func validateIsolation(hc *container.HostConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !hc.Isolation.IsValid() {
|
if !hc.Isolation.IsValid() {
|
||||||
return fmt.Errorf("invalid --isolation: %q - %s only supports 'default'", hc.Isolation, runtime.GOOS)
|
return fmt.Errorf("Invalid isolation: %q - %s only supports 'default'", hc.Isolation, runtime.GOOS)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -68,11 +68,11 @@ func validateQoS(hc *container.HostConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if hc.IOMaximumBandwidth != 0 {
|
if hc.IOMaximumBandwidth != 0 {
|
||||||
return fmt.Errorf("invalid QoS settings: %s does not support --io-maxbandwidth", runtime.GOOS)
|
return fmt.Errorf("Invalid QoS settings: %s does not support configuration of maximum bandwidth", runtime.GOOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hc.IOMaximumIOps != 0 {
|
if hc.IOMaximumIOps != 0 {
|
||||||
return fmt.Errorf("invalid QoS settings: %s does not support --io-maxiops", runtime.GOOS)
|
return fmt.Errorf("Invalid QoS settings: %s does not support configuration of maximum IOPs", runtime.GOOS)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -86,15 +86,15 @@ func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if hc.Resources.CPURealtimePeriod > 0 && !si.CPURealtimePeriod {
|
if hc.Resources.CPURealtimePeriod > 0 && !si.CPURealtimePeriod {
|
||||||
return fmt.Errorf("invalid --cpu-rt-period: Your kernel does not support cgroup rt period")
|
return fmt.Errorf("Your kernel does not support cgroup cpu real-time period")
|
||||||
}
|
}
|
||||||
|
|
||||||
if hc.Resources.CPURealtimeRuntime > 0 && !si.CPURealtimeRuntime {
|
if hc.Resources.CPURealtimeRuntime > 0 && !si.CPURealtimeRuntime {
|
||||||
return fmt.Errorf("invalid --cpu-rt-runtime: Your kernel does not support cgroup rt runtime")
|
return fmt.Errorf("Your kernel does not support cgroup cpu real-time runtime")
|
||||||
}
|
}
|
||||||
|
|
||||||
if hc.Resources.CPURealtimePeriod != 0 && hc.Resources.CPURealtimeRuntime != 0 && hc.Resources.CPURealtimeRuntime > hc.Resources.CPURealtimePeriod {
|
if hc.Resources.CPURealtimePeriod != 0 && hc.Resources.CPURealtimeRuntime != 0 && hc.Resources.CPURealtimeRuntime > hc.Resources.CPURealtimePeriod {
|
||||||
return fmt.Errorf("invalid --cpu-rt-runtime: rt runtime cannot be higher than rt period")
|
return fmt.Errorf("cpu real-time runtime cannot be higher than cpu real-time period")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ func validateNetMode(c *container.Config, hc *container.HostConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if hc.NetworkMode.IsContainer() && hc.Isolation.IsHyperV() {
|
if hc.NetworkMode.IsContainer() && hc.Isolation.IsHyperV() {
|
||||||
return fmt.Errorf("net mode --net=container:<NameOrId> unsupported for hyperv isolation")
|
return fmt.Errorf("Using the network stack of another container is not supported while using Hyper-V Containers")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -46,7 +46,7 @@ func validateIsolation(hc *container.HostConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !hc.Isolation.IsValid() {
|
if !hc.Isolation.IsValid() {
|
||||||
return fmt.Errorf("invalid --isolation: %q. Windows supports 'default', 'process', or 'hyperv'", hc.Isolation)
|
return fmt.Errorf("Invalid isolation: %q. Windows supports 'default', 'process', or 'hyperv'", hc.Isolation)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -63,10 +63,10 @@ func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if hc.Resources.CPURealtimePeriod != 0 {
|
if hc.Resources.CPURealtimePeriod != 0 {
|
||||||
return fmt.Errorf("invalid --cpu-rt-period: Windows does not support this feature")
|
return fmt.Errorf("Windows does not support CPU real-time period")
|
||||||
}
|
}
|
||||||
if hc.Resources.CPURealtimeRuntime != 0 {
|
if hc.Resources.CPURealtimeRuntime != 0 {
|
||||||
return fmt.Errorf("invalid --cpu-rt-runtime: Windows does not support this feature")
|
return fmt.Errorf("Windows does not support CPU real-time runtime")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ func validatePrivileged(hc *container.HostConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if hc.Privileged {
|
if hc.Privileged {
|
||||||
return fmt.Errorf("invalid --privileged: Windows does not support this feature")
|
return fmt.Errorf("Windows does not support privileged mode")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func validateReadonlyRootfs(hc *container.HostConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if hc.ReadonlyRootfs {
|
if hc.ReadonlyRootfs {
|
||||||
return fmt.Errorf("invalid --read-only: Windows does not support this feature")
|
return fmt.Errorf("Windows does not support root filesystem in read-only mode")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# the following lines are in sorted order, FYI
|
# the following lines are in sorted order, FYI
|
||||||
github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62
|
github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62
|
||||||
github.com/Microsoft/hcsshim v0.5.17
|
github.com/Microsoft/hcsshim v0.5.17
|
||||||
github.com/Microsoft/go-winio v0.4.0
|
github.com/Microsoft/go-winio v0.4.2
|
||||||
github.com/Sirupsen/logrus v0.11.0
|
github.com/Sirupsen/logrus v0.11.0
|
||||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
||||||
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||||
|
@ -26,7 +26,7 @@ github.com/imdario/mergo 0.2.1
|
||||||
golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
|
golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
|
||||||
|
|
||||||
#get libnetwork packages
|
#get libnetwork packages
|
||||||
github.com/docker/libnetwork b2bc1a68486ccf8ada503162d9f0df7d31bdd8fb
|
github.com/docker/libnetwork 83e1e49475b88a9f1f8ba89a690a7d5de42e24b9
|
||||||
github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
|
github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
|
||||||
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
||||||
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||||
|
@ -62,8 +62,8 @@ github.com/miekg/pkcs11 df8ae6ca730422dba20c768ff38ef7d79077a59f
|
||||||
|
|
||||||
# When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly
|
# When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly
|
||||||
github.com/opencontainers/runc 992a5be178a62e026f4069f443c6164912adbf09
|
github.com/opencontainers/runc 992a5be178a62e026f4069f443c6164912adbf09
|
||||||
github.com/opencontainers/runtime-spec v1.0.0-rc5 # specs
|
|
||||||
github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
|
github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
|
||||||
|
github.com/opencontainers/runtime-spec d42f1eb741e6361e858d83fc75aa6893b66292c4 # specs
|
||||||
|
|
||||||
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
|
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
|
||||||
|
|
||||||
|
@ -99,15 +99,12 @@ cloud.google.com/go 9d965e63e8cceb1b5d7977a202f0fcb8866d6525
|
||||||
github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7
|
github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7
|
||||||
google.golang.org/genproto b3e7c2fb04031add52c4817f53f43757ccbf9c18
|
google.golang.org/genproto b3e7c2fb04031add52c4817f53f43757ccbf9c18
|
||||||
|
|
||||||
# native credentials
|
|
||||||
github.com/docker/docker-credential-helpers v0.5.0
|
|
||||||
|
|
||||||
# containerd
|
# containerd
|
||||||
github.com/containerd/containerd 3addd840653146c90a254301d6c3a663c7fd6429
|
github.com/containerd/containerd 3addd840653146c90a254301d6c3a663c7fd6429
|
||||||
github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
|
github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
|
||||||
|
|
||||||
# cluster
|
# cluster
|
||||||
github.com/docker/swarmkit 1a3e510517be82d18ac04380b5f71eddf06c2fc0
|
github.com/docker/swarmkit eb07af52aa2216100cff1ad0b13df48daa8914bf
|
||||||
github.com/gogo/protobuf v0.4
|
github.com/gogo/protobuf v0.4
|
||||||
github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
|
github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
|
||||||
github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
|
github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
mounttypes "github.com/docker/docker/api/types/mount"
|
mounttypes "github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
@ -64,6 +65,8 @@ type Volume interface {
|
||||||
Mount(id string) (string, error)
|
Mount(id string) (string, error)
|
||||||
// Unmount unmounts the volume when it is no longer in use.
|
// Unmount unmounts the volume when it is no longer in use.
|
||||||
Unmount(id string) error
|
Unmount(id string) error
|
||||||
|
// CreatedAt returns Volume Creation time
|
||||||
|
CreatedAt() (time.Time, error)
|
||||||
// Status returns low-level status information about a volume
|
// Status returns low-level status information about a volume
|
||||||
Status() map[string]interface{}
|
Status() map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,202 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
# libnetwork - networking for containers
|
|
||||||
|
|
||||||
[![Circle CI](https://circleci.com/gh/docker/libnetwork/tree/master.svg?style=svg)](https://circleci.com/gh/docker/libnetwork/tree/master) [![Coverage Status](https://coveralls.io/repos/docker/libnetwork/badge.svg)](https://coveralls.io/r/docker/libnetwork) [![GoDoc](https://godoc.org/github.com/docker/libnetwork?status.svg)](https://godoc.org/github.com/docker/libnetwork)
|
|
||||||
|
|
||||||
Libnetwork provides a native Go implementation for connecting containers
|
|
||||||
|
|
||||||
The goal of libnetwork is to deliver a robust Container Network Model that provides a consistent programming interface and the required network abstractions for applications.
|
|
||||||
|
|
||||||
#### Design
|
|
||||||
Please refer to the [design](docs/design.md) for more information.
|
|
||||||
|
|
||||||
#### Using libnetwork
|
|
||||||
|
|
||||||
There are many networking solutions available to suit a broad range of use-cases. libnetwork uses a driver / plugin model to support all of these solutions while abstracting the complexity of the driver implementations by exposing a simple and consistent Network Model to users.
|
|
||||||
|
|
||||||
|
|
||||||
```go
|
|
||||||
func main() {
|
|
||||||
if reexec.Init() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select and configure the network driver
|
|
||||||
networkType := "bridge"
|
|
||||||
|
|
||||||
// Create a new controller instance
|
|
||||||
driverOptions := options.Generic{}
|
|
||||||
genericOption := make(map[string]interface{})
|
|
||||||
genericOption[netlabel.GenericData] = driverOptions
|
|
||||||
controller, err := libnetwork.New(config.OptionDriverConfig(networkType, genericOption))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("libnetwork.New: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a network for containers to join.
|
|
||||||
// NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can use.
|
|
||||||
network, err := controller.NewNetwork(networkType, "network1", "")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("controller.NewNetwork: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each new container: allocate IP and interfaces. The returned network
|
|
||||||
// settings will be used for container infos (inspect and such), as well as
|
|
||||||
// iptables rules for port publishing. This info is contained or accessible
|
|
||||||
// from the returned endpoint.
|
|
||||||
ep, err := network.CreateEndpoint("Endpoint1")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("network.CreateEndpoint: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the sandbox for the container.
|
|
||||||
// NewSandbox accepts Variadic optional arguments which libnetwork can use.
|
|
||||||
sbx, err := controller.NewSandbox("container1",
|
|
||||||
libnetwork.OptionHostname("test"),
|
|
||||||
libnetwork.OptionDomainname("docker.io"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("controller.NewSandbox: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A sandbox can join the endpoint via the join api.
|
|
||||||
err = ep.Join(sbx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("ep.Join: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// libnetwork client can check the endpoint's operational data via the Info() API
|
|
||||||
epInfo, err := ep.DriverInfo()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("ep.DriverInfo: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
macAddress, ok := epInfo[netlabel.MacAddress]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("failed to get mac address from endpoint info")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Joined endpoint %s (%s) to sandbox %s (%s)\n", ep.Name(), macAddress, sbx.ContainerID(), sbx.Key())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Future
|
|
||||||
Please refer to [roadmap](ROADMAP.md) for more information.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Want to hack on libnetwork? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply.
|
|
||||||
|
|
||||||
## Copyright and license
|
|
||||||
Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons.
|
|
|
@ -1 +0,0 @@
|
||||||
Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf
|
|
|
@ -1,26 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
|
|
||||||
const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
|
|
||||||
|
|
||||||
// IPv4Localhost is a regex pattern for IPv4 localhost address range.
|
|
||||||
const IPv4Localhost = `(127\.([0-9]{1,3}\.){2}[0-9]{1,3})`
|
|
||||||
|
|
||||||
var localhostIPRegexp = regexp.MustCompile(IPLocalhost)
|
|
||||||
var localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost)
|
|
||||||
|
|
||||||
// IsLocalhost returns true if ip matches the localhost IP regular expression.
|
|
||||||
// Used for determining if nameserver settings are being passed which are
|
|
||||||
// localhost addresses
|
|
||||||
func IsLocalhost(ip string) bool {
|
|
||||||
return localhostIPRegexp.MatchString(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression.
|
|
||||||
func IsIPv4Localhost(ip string) bool {
|
|
||||||
return localhostIPv4Regexp.MatchString(ip)
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
github.com/Azure/go-ansiterm 04b7f292a41fcb5da32dda536c0807fc13e8351c
|
|
||||||
github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
|
|
||||||
github.com/Microsoft/go-winio ce2922f643c8fd76b46cadc7f404a06282678b34
|
|
||||||
github.com/Microsoft/hcsshim e439b7d2b63f036d3a50c93a9e0b154a0d50e788
|
|
||||||
github.com/Sirupsen/logrus 4b6ea7319e214d98c938f12692336f7ca9348d6b
|
|
||||||
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
|
||||||
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
|
||||||
github.com/boltdb/bolt c6ba97b89e0454fec9aa92e1d33a4e2c5fc1f631
|
|
||||||
github.com/codegangsta/cli a65b733b303f0055f8d324d805f393cd3e7a7904
|
|
||||||
github.com/coreos/etcd 925d1d74cec8c3b169c52fd4b2dc234a35934fce
|
|
||||||
github.com/coreos/go-systemd b4a58d95188dd092ae20072bac14cece0e67c388
|
|
||||||
github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
|
|
||||||
|
|
||||||
github.com/docker/docker 9c96768eae4b3a65147b47a55c850c103ab8972d
|
|
||||||
github.com/docker/go-connections 34b5052da6b11e27f5f2e357b38b571ddddd3928
|
|
||||||
github.com/docker/go-events 2e7d352816128aa84f4d29b2a21d400133701a0d
|
|
||||||
github.com/docker/go-units 8e2d4523730c73120e10d4652f36ad6010998f4e
|
|
||||||
github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
|
|
||||||
|
|
||||||
github.com/godbus/dbus 5f6efc7ef2759c81b7ba876593971bfce311eab3
|
|
||||||
github.com/gogo/protobuf 8d70fb3182befc465c4a1eac8ad4d38ff49778e2
|
|
||||||
github.com/golang/protobuf/proto f7137ae6b19afbfd61a94b746fda3b3fe0491874
|
|
||||||
github.com/gorilla/context 215affda49addc4c8ef7e2534915df2c8c35c6cd
|
|
||||||
github.com/gorilla/mux 8096f47503459bcc74d1f4c487b7e6e42e5746b5
|
|
||||||
github.com/hashicorp/consul/api 954aec66231b79c161a4122b023fbcad13047f79
|
|
||||||
github.com/hashicorp/go-msgpack/codec 71c2886f5a673a35f909803f38ece5810165097b
|
|
||||||
github.com/hashicorp/go-multierror 2167c8ec40776024589f483a6b836489e47e1049
|
|
||||||
github.com/hashicorp/memberlist 88ac4de0d1a0ca6def284b571342db3b777a4c37
|
|
||||||
github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
|
|
||||||
github.com/mattn/go-shellwords 525bedee691b5a8df547cb5cf9f86b7fb1883e24
|
|
||||||
github.com/miekg/dns d27455715200c7d3e321a1e5cadb27c9ee0b0f02
|
|
||||||
github.com/opencontainers/runc/libcontainer ba1568de399395774ad84c2ace65937814c542ed
|
|
||||||
github.com/samuel/go-zookeeper/zk d0e0d8e11f318e000a8cc434616d69e329edc374
|
|
||||||
github.com/seccomp/libseccomp-golang 1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1
|
|
||||||
github.com/stretchr/testify dab07ac62d4905d3e48d17dc549c684ac3b7c15a
|
|
||||||
github.com/syndtr/gocapability/capability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
|
|
||||||
github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
|
|
||||||
github.com/vishvananda/netlink 1e86b2bee5b6a7d377e4c02bb7f98209d6a7297c
|
|
||||||
github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
|
|
||||||
golang.org/x/net c427ad74c6d7a814201695e9ffde0c5d400a7674
|
|
||||||
golang.org/x/sys 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
|
|
||||||
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
|
Loading…
Reference in New Issue