| ... | ... |
@@ -1,5 +1,43 @@ |
| 1 | 1 |
# Changelog |
| 2 | 2 |
|
| 3 |
+## 0.6.7 (2013-11-21) |
|
| 4 |
+ |
|
| 5 |
+#### Runtime |
|
| 6 |
+ |
|
| 7 |
+* Improved stability, fixes some race conditons |
|
| 8 |
+* Skip the volumes mounted when deleting the volumes of container. |
|
| 9 |
+* Fix layer size computation: handle hard links correctly |
|
| 10 |
+* Use the work Path for docker cp CONTAINER:PATH |
|
| 11 |
+* Fix tmp dir never cleanup |
|
| 12 |
+* Speedup docker ps |
|
| 13 |
+* More informative error message on name collisions |
|
| 14 |
+* Fix nameserver regex |
|
| 15 |
+* Always return long id's |
|
| 16 |
+* Fix container restart race condition |
|
| 17 |
+* Keep published ports on docker stop;docker start |
|
| 18 |
+* Fix container networking on Fedora |
|
| 19 |
+* Correctly express "any address" to iptables |
|
| 20 |
+* Fix network setup when reconnecting to ghost container |
|
| 21 |
+* Prevent deletion if image is used by a running container |
|
| 22 |
+* Lock around read operations in graph |
|
| 23 |
+ |
|
| 24 |
+#### RemoteAPI |
|
| 25 |
+ |
|
| 26 |
+* Return full ID on docker rmi |
|
| 27 |
+ |
|
| 28 |
+#### Client |
|
| 29 |
+ |
|
| 30 |
++ Add -tree option to images |
|
| 31 |
++ Offline image transfer |
|
| 32 |
+* Exit with status 2 on usage error and display usage on stderr |
|
| 33 |
+* Do not forward SIGCHLD to container |
|
| 34 |
+* Use string timestamp for docker events -since |
|
| 35 |
+ |
|
| 36 |
+#### Other |
|
| 37 |
+ |
|
| 38 |
+* Update to go 1.2rc5 |
|
| 39 |
++ Add /etc/default/docker support to upstart |
|
| 40 |
+ |
|
| 3 | 41 |
## 0.6.6 (2013-11-06) |
| 4 | 42 |
|
| 5 | 43 |
#### Runtime |
| ... | ... |
@@ -17,6 +55,7 @@ |
| 17 | 17 |
+ Prevent DNS server conflicts in CreateBridgeIface |
| 18 | 18 |
+ Validate bind mounts on the server side |
| 19 | 19 |
+ Use parent image config in docker build |
| 20 |
+* Fix regression in /etc/hosts |
|
| 20 | 21 |
|
| 21 | 22 |
#### Client |
| 22 | 23 |
|
| ... | ... |
@@ -1736,60 +1736,60 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, |
| 1736 | 1736 |
} |
| 1737 | 1737 |
|
| 1738 | 1738 |
func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
|
| 1739 |
+ var ( |
|
| 1740 |
+ // FIXME: use utils.ListOpts for attach and volumes? |
|
| 1741 |
+ flAttach = NewAttachOpts() |
|
| 1742 |
+ flVolumes = NewPathOpts() |
|
| 1743 |
+ |
|
| 1744 |
+ flPublish utils.ListOpts |
|
| 1745 |
+ flExpose utils.ListOpts |
|
| 1746 |
+ flEnv utils.ListOpts |
|
| 1747 |
+ flDns utils.ListOpts |
|
| 1748 |
+ flVolumesFrom utils.ListOpts |
|
| 1749 |
+ flLxcOpts utils.ListOpts |
|
| 1750 |
+ flLinks utils.ListOpts |
|
| 1751 |
+ |
|
| 1752 |
+ flAutoRemove = cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
|
|
| 1753 |
+ flDetach = cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id")
|
|
| 1754 |
+ flNetwork = cmd.Bool("n", true, "Enable networking for this container")
|
|
| 1755 |
+ flPrivileged = cmd.Bool("privileged", false, "Give extended privileges to this container")
|
|
| 1756 |
+ flPublishAll = cmd.Bool("P", false, "Publish all exposed ports to the host interfaces")
|
|
| 1757 |
+ flStdin = cmd.Bool("i", false, "Keep stdin open even if not attached")
|
|
| 1758 |
+ flTty = cmd.Bool("t", false, "Allocate a pseudo-tty")
|
|
| 1759 |
+ flContainerIDFile = cmd.String("cidfile", "", "Write the container ID to the file")
|
|
| 1760 |
+ flEntrypoint = cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
|
|
| 1761 |
+ flHostname = cmd.String("h", "", "Container host name")
|
|
| 1762 |
+ flMemoryString = cmd.String("m", "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
|
|
| 1763 |
+ flUser = cmd.String("u", "", "Username or UID")
|
|
| 1764 |
+ flWorkingDir = cmd.String("w", "", "Working directory inside the container")
|
|
| 1765 |
+ flCpuShares = cmd.Int64("c", 0, "CPU shares (relative weight)")
|
|
| 1766 |
+ |
|
| 1767 |
+ // For documentation purpose |
|
| 1768 |
+ _ = cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)")
|
|
| 1769 |
+ _ = cmd.String("name", "", "Assign a name to the container")
|
|
| 1770 |
+ ) |
|
| 1739 | 1771 |
|
| 1740 |
- flHostname := cmd.String("h", "", "Container host name")
|
|
| 1741 |
- flWorkingDir := cmd.String("w", "", "Working directory inside the container")
|
|
| 1742 |
- flUser := cmd.String("u", "", "Username or UID")
|
|
| 1743 |
- flDetach := cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id")
|
|
| 1744 |
- flAttach := NewAttachOpts() |
|
| 1745 | 1772 |
cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.") |
| 1746 |
- flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
|
|
| 1747 |
- flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
|
|
| 1748 |
- flMemoryString := cmd.String("m", "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
|
|
| 1749 |
- flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file")
|
|
| 1750 |
- flNetwork := cmd.Bool("n", true, "Enable networking for this container")
|
|
| 1751 |
- flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container")
|
|
| 1752 |
- flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
|
|
| 1753 |
- cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)")
|
|
| 1754 |
- cmd.String("name", "", "Assign a name to the container")
|
|
| 1755 |
- flPublishAll := cmd.Bool("P", false, "Publish all exposed ports to the host interfaces")
|
|
| 1756 |
- |
|
| 1757 |
- if capabilities != nil && *flMemoryString != "" && !capabilities.MemoryLimit {
|
|
| 1758 |
- //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") |
|
| 1759 |
- *flMemoryString = "" |
|
| 1760 |
- } |
|
| 1761 |
- |
|
| 1762 |
- flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)")
|
|
| 1763 |
- |
|
| 1764 |
- var flPublish utils.ListOpts |
|
| 1765 |
- cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)") |
|
| 1773 |
+ cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") |
|
| 1766 | 1774 |
|
| 1767 |
- var flExpose utils.ListOpts |
|
| 1775 |
+ cmd.Var(&flPublish, "p", fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", PortSpecTemplateFormat))
|
|
| 1768 | 1776 |
cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host") |
| 1769 |
- |
|
| 1770 |
- var flEnv utils.ListOpts |
|
| 1771 | 1777 |
cmd.Var(&flEnv, "e", "Set environment variables") |
| 1772 |
- |
|
| 1773 |
- var flDns utils.ListOpts |
|
| 1774 | 1778 |
cmd.Var(&flDns, "dns", "Set custom dns servers") |
| 1775 |
- |
|
| 1776 |
- flVolumes := NewPathOpts() |
|
| 1777 |
- cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") |
|
| 1778 |
- |
|
| 1779 |
- var flVolumesFrom utils.ListOpts |
|
| 1780 | 1779 |
cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)") |
| 1781 |
- |
|
| 1782 |
- flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
|
|
| 1783 |
- |
|
| 1784 |
- var flLxcOpts utils.ListOpts |
|
| 1785 | 1780 |
cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") |
| 1786 |
- |
|
| 1787 |
- var flLinks utils.ListOpts |
|
| 1788 | 1781 |
cmd.Var(&flLinks, "link", "Add link to another container (name:alias)") |
| 1789 | 1782 |
|
| 1790 | 1783 |
if err := cmd.Parse(args); err != nil {
|
| 1791 | 1784 |
return nil, nil, cmd, err |
| 1792 | 1785 |
} |
| 1786 |
+ |
|
| 1787 |
+ // Check if the kernel supports memory limit cgroup. |
|
| 1788 |
+ if capabilities != nil && *flMemoryString != "" && !capabilities.MemoryLimit {
|
|
| 1789 |
+ *flMemoryString = "" |
|
| 1790 |
+ } |
|
| 1791 |
+ |
|
| 1792 |
+ // Validate input params |
|
| 1793 | 1793 |
if *flDetach && len(flAttach) > 0 {
|
| 1794 | 1794 |
return nil, nil, cmd, ErrConflictAttachDetach |
| 1795 | 1795 |
} |
| ... | ... |
@@ -1811,8 +1811,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co |
| 1811 | 1811 |
} |
| 1812 | 1812 |
} |
| 1813 | 1813 |
|
| 1814 |
- envs := []string{}
|
|
| 1815 |
- |
|
| 1814 |
+ var envs []string |
|
| 1816 | 1815 |
for _, env := range flEnv {
|
| 1817 | 1816 |
arr := strings.Split(env, "=") |
| 1818 | 1817 |
if len(arr) > 1 {
|
| ... | ... |
@@ -1824,19 +1823,15 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co |
| 1824 | 1824 |
} |
| 1825 | 1825 |
|
| 1826 | 1826 |
var flMemory int64 |
| 1827 |
- |
|
| 1828 | 1827 |
if *flMemoryString != "" {
|
| 1829 | 1828 |
parsedMemory, err := utils.RAMInBytes(*flMemoryString) |
| 1830 |
- |
|
| 1831 | 1829 |
if err != nil {
|
| 1832 | 1830 |
return nil, nil, cmd, err |
| 1833 | 1831 |
} |
| 1834 |
- |
|
| 1835 | 1832 |
flMemory = parsedMemory |
| 1836 | 1833 |
} |
| 1837 | 1834 |
|
| 1838 | 1835 |
var binds []string |
| 1839 |
- |
|
| 1840 | 1836 |
// add any bind targets to the list of container volumes |
| 1841 | 1837 |
for bind := range flVolumes {
|
| 1842 | 1838 |
arr := strings.Split(bind, ":") |
| ... | ... |
@@ -1851,10 +1846,12 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co |
| 1851 | 1851 |
} |
| 1852 | 1852 |
} |
| 1853 | 1853 |
|
| 1854 |
- parsedArgs := cmd.Args() |
|
| 1855 |
- runCmd := []string{}
|
|
| 1856 |
- entrypoint := []string{}
|
|
| 1857 |
- image := "" |
|
| 1854 |
+ var ( |
|
| 1855 |
+ parsedArgs = cmd.Args() |
|
| 1856 |
+ runCmd []string |
|
| 1857 |
+ entrypoint []string |
|
| 1858 |
+ image string |
|
| 1859 |
+ ) |
|
| 1858 | 1860 |
if len(parsedArgs) >= 1 {
|
| 1859 | 1861 |
image = cmd.Arg(0) |
| 1860 | 1862 |
} |
| ... | ... |
@@ -1865,16 +1862,16 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co |
| 1865 | 1865 |
entrypoint = []string{*flEntrypoint}
|
| 1866 | 1866 |
} |
| 1867 | 1867 |
|
| 1868 |
- var lxcConf []KeyValuePair |
|
| 1869 | 1868 |
lxcConf, err := parseLxcConfOpts(flLxcOpts) |
| 1870 | 1869 |
if err != nil {
|
| 1871 | 1870 |
return nil, nil, cmd, err |
| 1872 | 1871 |
} |
| 1873 | 1872 |
|
| 1874 |
- hostname := *flHostname |
|
| 1875 |
- domainname := "" |
|
| 1876 |
- |
|
| 1877 |
- parts := strings.SplitN(hostname, ".", 2) |
|
| 1873 |
+ var ( |
|
| 1874 |
+ domainname string |
|
| 1875 |
+ hostname = *flHostname |
|
| 1876 |
+ parts = strings.SplitN(hostname, ".", 2) |
|
| 1877 |
+ ) |
|
| 1878 | 1878 |
if len(parts) > 1 {
|
| 1879 | 1879 |
hostname = parts[0] |
| 1880 | 1880 |
domainname = parts[1] |
| ... | ... |
@@ -1952,30 +1949,33 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 1952 | 1952 |
return nil |
| 1953 | 1953 |
} |
| 1954 | 1954 |
|
| 1955 |
- flRm := cmd.Lookup("rm")
|
|
| 1956 |
- autoRemove, _ := strconv.ParseBool(flRm.Value.String()) |
|
| 1955 |
+ // Retrieve relevant client-side config |
|
| 1956 |
+ var ( |
|
| 1957 |
+ flName = cmd.Lookup("name")
|
|
| 1958 |
+ flRm = cmd.Lookup("rm")
|
|
| 1959 |
+ flSigProxy = cmd.Lookup("sig-proxy")
|
|
| 1960 |
+ autoRemove, _ = strconv.ParseBool(flRm.Value.String()) |
|
| 1961 |
+ sigProxy, _ = strconv.ParseBool(flSigProxy.Value.String()) |
|
| 1962 |
+ ) |
|
| 1957 | 1963 |
|
| 1958 |
- flSigProxy := cmd.Lookup("sig-proxy")
|
|
| 1959 |
- sigProxy, _ := strconv.ParseBool(flSigProxy.Value.String()) |
|
| 1960 |
- flName := cmd.Lookup("name")
|
|
| 1964 |
+ // Disable sigProxy in case on TTY |
|
| 1961 | 1965 |
if config.Tty {
|
| 1962 | 1966 |
sigProxy = false |
| 1963 | 1967 |
} |
| 1964 | 1968 |
|
| 1965 |
- var containerIDFile *os.File |
|
| 1969 |
+ var containerIDFile io.WriteCloser |
|
| 1966 | 1970 |
if len(hostConfig.ContainerIDFile) > 0 {
|
| 1967 |
- if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil {
|
|
| 1971 |
+ if _, err := os.Stat(hostConfig.ContainerIDFile); err == nil {
|
|
| 1968 | 1972 |
return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile)
|
| 1969 | 1973 |
} |
| 1970 |
- containerIDFile, err = os.Create(hostConfig.ContainerIDFile) |
|
| 1971 |
- if err != nil {
|
|
| 1974 |
+ if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
|
|
| 1972 | 1975 |
return fmt.Errorf("failed to create the container ID file: %s", err)
|
| 1973 | 1976 |
} |
| 1974 | 1977 |
defer containerIDFile.Close() |
| 1975 | 1978 |
} |
| 1979 |
+ |
|
| 1976 | 1980 |
containerValues := url.Values{}
|
| 1977 |
- name := flName.Value.String() |
|
| 1978 |
- if name != "" {
|
|
| 1981 |
+ if name := flName.Value.String(); name != "" {
|
|
| 1979 | 1982 |
containerValues.Set("name", name)
|
| 1980 | 1983 |
} |
| 1981 | 1984 |
|
| ... | ... |
@@ -1996,8 +1996,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 1996 | 1996 |
v.Set("tag", tag)
|
| 1997 | 1997 |
|
| 1998 | 1998 |
// Resolve the Repository name from fqn to endpoint + name |
| 1999 |
- var endpoint string |
|
| 2000 |
- endpoint, _, err = registry.ResolveRepositoryName(repos) |
|
| 1999 |
+ endpoint, _, err := registry.ResolveRepositoryName(repos) |
|
| 2001 | 2000 |
if err != nil {
|
| 2002 | 2001 |
return err |
| 2003 | 2002 |
} |
| ... | ... |
@@ -2015,14 +2014,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 2015 | 2015 |
registryAuthHeader := []string{
|
| 2016 | 2016 |
base64.URLEncoding.EncodeToString(buf), |
| 2017 | 2017 |
} |
| 2018 |
- err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{
|
|
| 2019 |
- "X-Registry-Auth": registryAuthHeader, |
|
| 2020 |
- }) |
|
| 2021 |
- if err != nil {
|
|
| 2018 |
+ if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
|
|
| 2022 | 2019 |
return err |
| 2023 | 2020 |
} |
| 2024 |
- body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config)
|
|
| 2025 |
- if err != nil {
|
|
| 2021 |
+ if body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config); err != nil {
|
|
| 2026 | 2022 |
return err |
| 2027 | 2023 |
} |
| 2028 | 2024 |
} |
| ... | ... |
@@ -2030,17 +2025,17 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 2030 | 2030 |
return err |
| 2031 | 2031 |
} |
| 2032 | 2032 |
|
| 2033 |
- runResult := &APIRun{}
|
|
| 2034 |
- err = json.Unmarshal(body, runResult) |
|
| 2035 |
- if err != nil {
|
|
| 2033 |
+ var runResult APIRun |
|
| 2034 |
+ if err := json.Unmarshal(body, &runResult); err != nil {
|
|
| 2036 | 2035 |
return err |
| 2037 | 2036 |
} |
| 2038 | 2037 |
|
| 2039 | 2038 |
for _, warning := range runResult.Warnings {
|
| 2040 | 2039 |
fmt.Fprintf(cli.err, "WARNING: %s\n", warning) |
| 2041 | 2040 |
} |
| 2041 |
+ |
|
| 2042 | 2042 |
if len(hostConfig.ContainerIDFile) > 0 {
|
| 2043 |
- if _, err = containerIDFile.WriteString(runResult.ID); err != nil {
|
|
| 2043 |
+ if _, err = containerIDFile.Write([]byte(runResult.ID)); err != nil {
|
|
| 2044 | 2044 |
return fmt.Errorf("failed to write the container ID to the file: %s", err)
|
| 2045 | 2045 |
} |
| 2046 | 2046 |
} |
| ... | ... |
@@ -2051,27 +2046,29 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 2051 | 2051 |
} |
| 2052 | 2052 |
|
| 2053 | 2053 |
var ( |
| 2054 |
- wait chan struct{}
|
|
| 2055 |
- errCh chan error |
|
| 2054 |
+ waitDisplayId chan struct{}
|
|
| 2055 |
+ errCh chan error |
|
| 2056 | 2056 |
) |
| 2057 | 2057 |
|
| 2058 | 2058 |
if !config.AttachStdout && !config.AttachStderr {
|
| 2059 | 2059 |
// Make this asynchrone in order to let the client write to stdin before having to read the ID |
| 2060 |
- wait = make(chan struct{})
|
|
| 2060 |
+ waitDisplayId = make(chan struct{})
|
|
| 2061 | 2061 |
go func() {
|
| 2062 |
- defer close(wait) |
|
| 2062 |
+ defer close(waitDisplayId) |
|
| 2063 | 2063 |
fmt.Fprintf(cli.out, "%s\n", runResult.ID) |
| 2064 | 2064 |
}() |
| 2065 | 2065 |
} |
| 2066 | 2066 |
|
| 2067 |
+ // We need to make the chan because the select needs to have a closing |
|
| 2068 |
+ // chan, it can't be uninitialized |
|
| 2067 | 2069 |
hijacked := make(chan bool) |
| 2068 |
- |
|
| 2069 | 2070 |
if config.AttachStdin || config.AttachStdout || config.AttachStderr {
|
| 2070 |
- |
|
| 2071 |
- v := url.Values{}
|
|
| 2071 |
+ var ( |
|
| 2072 |
+ out, stderr io.Writer |
|
| 2073 |
+ in io.ReadCloser |
|
| 2074 |
+ v = url.Values{}
|
|
| 2075 |
+ ) |
|
| 2072 | 2076 |
v.Set("stream", "1")
|
| 2073 |
- var out, stderr io.Writer |
|
| 2074 |
- var in io.ReadCloser |
|
| 2075 | 2077 |
|
| 2076 | 2078 |
if config.AttachStdin {
|
| 2077 | 2079 |
v.Set("stdin", "1")
|
| ... | ... |
@@ -2125,31 +2122,37 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 2125 | 2125 |
} |
| 2126 | 2126 |
} |
| 2127 | 2127 |
|
| 2128 |
+ // Detached mode: wait for the id to be displayed and return. |
|
| 2128 | 2129 |
if !config.AttachStdout && !config.AttachStderr {
|
| 2129 | 2130 |
// Detached mode |
| 2130 |
- <-wait |
|
| 2131 |
- } else {
|
|
| 2132 |
- running, status, err := getExitCode(cli, runResult.ID) |
|
| 2133 |
- if err != nil {
|
|
| 2131 |
+ <-waitDisplayId |
|
| 2132 |
+ return nil |
|
| 2133 |
+ } |
|
| 2134 |
+ |
|
| 2135 |
+ var status int |
|
| 2136 |
+ |
|
| 2137 |
+ // Attached mode |
|
| 2138 |
+ if autoRemove {
|
|
| 2139 |
+ // Autoremove: wait for the container to finish, retrieve |
|
| 2140 |
+ // the exit code and remove the container |
|
| 2141 |
+ if _, _, err := cli.call("POST", "/containers/"+runResult.ID+"/wait", nil); err != nil {
|
|
| 2134 | 2142 |
return err |
| 2135 | 2143 |
} |
| 2136 |
- if autoRemove {
|
|
| 2137 |
- if running {
|
|
| 2138 |
- return fmt.Errorf("Impossible to auto-remove a detached container")
|
|
| 2139 |
- } |
|
| 2140 |
- // Wait for the process to |
|
| 2141 |
- if _, _, err := cli.call("POST", "/containers/"+runResult.ID+"/wait", nil); err != nil {
|
|
| 2142 |
- return err |
|
| 2143 |
- } |
|
| 2144 |
- if _, _, err := cli.call("DELETE", "/containers/"+runResult.ID, nil); err != nil {
|
|
| 2145 |
- return err |
|
| 2146 |
- } |
|
| 2144 |
+ if _, status, err = getExitCode(cli, runResult.ID); err != nil {
|
|
| 2145 |
+ return err |
|
| 2147 | 2146 |
} |
| 2148 |
- if status != 0 {
|
|
| 2149 |
- return &utils.StatusError{Status: status}
|
|
| 2147 |
+ if _, _, err := cli.call("DELETE", "/containers/"+runResult.ID, nil); err != nil {
|
|
| 2148 |
+ return err |
|
| 2149 |
+ } |
|
| 2150 |
+ } else {
|
|
| 2151 |
+ // No Autoremove: Simply retrieve the exit code |
|
| 2152 |
+ if _, status, err = getExitCode(cli, runResult.ID); err != nil {
|
|
| 2153 |
+ return err |
|
| 2150 | 2154 |
} |
| 2151 | 2155 |
} |
| 2152 |
- |
|
| 2156 |
+ if status != 0 {
|
|
| 2157 |
+ return &utils.StatusError{Status: status}
|
|
| 2158 |
+ } |
|
| 2153 | 2159 |
return nil |
| 2154 | 2160 |
} |
| 2155 | 2161 |
|
| ... | ... |
@@ -2334,7 +2337,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h |
| 2334 | 2334 |
} |
| 2335 | 2335 |
|
| 2336 | 2336 |
if matchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
| 2337 |
- return utils.DisplayJSONMessagesStream(resp.Body, out) |
|
| 2337 |
+ return utils.DisplayJSONMessagesStream(resp.Body, out, cli.isTerminal) |
|
| 2338 | 2338 |
} |
| 2339 | 2339 |
if _, err := io.Copy(out, resp.Body); err != nil {
|
| 2340 | 2340 |
return err |
| ... | ... |
@@ -661,6 +661,9 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) {
|
| 661 | 661 |
} |
| 662 | 662 |
|
| 663 | 663 |
func (manager *NetworkManager) Close() error {
|
| 664 |
+ if manager.disabled {
|
|
| 665 |
+ return nil |
|
| 666 |
+ } |
|
| 664 | 667 |
err1 := manager.tcpPortAllocator.Close() |
| 665 | 668 |
err2 := manager.udpPortAllocator.Close() |
| 666 | 669 |
err3 := manager.ipAllocator.Close() |
| ... | ... |
@@ -63,7 +63,10 @@ func jobInitApi(job *engine.Job) string {
|
| 63 | 63 |
}() |
| 64 | 64 |
job.Eng.Hack_SetGlobalVar("httpapi.server", srv)
|
| 65 | 65 |
job.Eng.Hack_SetGlobalVar("httpapi.runtime", srv.runtime)
|
| 66 |
- job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", srv.runtime.networkManager.bridgeNetwork.IP)
|
|
| 66 |
+ // https://github.com/dotcloud/docker/issues/2768 |
|
| 67 |
+ if srv.runtime.networkManager.bridgeNetwork != nil {
|
|
| 68 |
+ job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", srv.runtime.networkManager.bridgeNetwork.IP)
|
|
| 69 |
+ } |
|
| 67 | 70 |
if err := job.Eng.Register("create", srv.ContainerCreate); err != nil {
|
| 68 | 71 |
return err.Error() |
| 69 | 72 |
} |
| ... | ... |
@@ -235,14 +235,23 @@ func parseLxcOpt(opt string) (string, string, error) {
|
| 235 | 235 |
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil |
| 236 | 236 |
} |
| 237 | 237 |
|
| 238 |
+// FIXME: network related stuff (including parsing) should be grouped in network file |
|
| 239 |
+const ( |
|
| 240 |
+ PortSpecTemplate = "ip:hostPort:containerPort" |
|
| 241 |
+ PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort" |
|
| 242 |
+) |
|
| 243 |
+ |
|
| 238 | 244 |
// We will receive port specs in the format of ip:public:private/proto and these need to be |
| 239 | 245 |
// parsed in the internal types |
| 240 | 246 |
func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
|
| 241 |
- exposedPorts := make(map[Port]struct{}, len(ports))
|
|
| 242 |
- bindings := make(map[Port][]PortBinding) |
|
| 247 |
+ var ( |
|
| 248 |
+ exposedPorts = make(map[Port]struct{}, len(ports))
|
|
| 249 |
+ bindings = make(map[Port][]PortBinding) |
|
| 250 |
+ ) |
|
| 243 | 251 |
|
| 244 | 252 |
for _, rawPort := range ports {
|
| 245 | 253 |
proto := "tcp" |
| 254 |
+ |
|
| 246 | 255 |
if i := strings.LastIndex(rawPort, "/"); i != -1 {
|
| 247 | 256 |
proto = rawPort[i+1:] |
| 248 | 257 |
rawPort = rawPort[:i] |
| ... | ... |
@@ -253,13 +262,16 @@ func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
|
| 253 | 253 |
rawPort = fmt.Sprintf(":%s", rawPort)
|
| 254 | 254 |
} |
| 255 | 255 |
|
| 256 |
- parts, err := utils.PartParser("ip:hostPort:containerPort", rawPort)
|
|
| 256 |
+ parts, err := utils.PartParser(PortSpecTemplate, rawPort) |
|
| 257 | 257 |
if err != nil {
|
| 258 | 258 |
return nil, nil, err |
| 259 | 259 |
} |
| 260 |
- containerPort := parts["containerPort"] |
|
| 261 |
- rawIp := parts["ip"] |
|
| 262 |
- hostPort := parts["hostPort"] |
|
| 260 |
+ |
|
| 261 |
+ var ( |
|
| 262 |
+ containerPort = parts["containerPort"] |
|
| 263 |
+ rawIp = parts["ip"] |
|
| 264 |
+ hostPort = parts["hostPort"] |
|
| 265 |
+ ) |
|
| 263 | 266 |
|
| 264 | 267 |
if containerPort == "" {
|
| 265 | 268 |
return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
|
| ... | ... |
@@ -779,14 +779,19 @@ func NewHTTPRequestError(msg string, res *http.Response) error {
|
| 779 | 779 |
} |
| 780 | 780 |
} |
| 781 | 781 |
|
| 782 |
-func (jm *JSONMessage) Display(out io.Writer) error {
|
|
| 782 |
+func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
|
|
| 783 | 783 |
if jm.Error != nil {
|
| 784 | 784 |
if jm.Error.Code == 401 {
|
| 785 | 785 |
return fmt.Errorf("Authentication is required.")
|
| 786 | 786 |
} |
| 787 | 787 |
return jm.Error |
| 788 | 788 |
} |
| 789 |
- fmt.Fprintf(out, "%c[2K\r", 27) |
|
| 789 |
+ endl := "" |
|
| 790 |
+ if isTerminal {
|
|
| 791 |
+ // <ESC>[2K = erase entire current line |
|
| 792 |
+ fmt.Fprintf(out, "%c[2K\r", 27) |
|
| 793 |
+ endl = "\r" |
|
| 794 |
+ } |
|
| 790 | 795 |
if jm.Time != 0 {
|
| 791 | 796 |
fmt.Fprintf(out, "[%s] ", time.Unix(jm.Time, 0)) |
| 792 | 797 |
} |
| ... | ... |
@@ -797,14 +802,14 @@ func (jm *JSONMessage) Display(out io.Writer) error {
|
| 797 | 797 |
fmt.Fprintf(out, "(from %s) ", jm.From) |
| 798 | 798 |
} |
| 799 | 799 |
if jm.Progress != "" {
|
| 800 |
- fmt.Fprintf(out, "%s %s\r", jm.Status, jm.Progress) |
|
| 800 |
+ fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress, endl) |
|
| 801 | 801 |
} else {
|
| 802 |
- fmt.Fprintf(out, "%s\r\n", jm.Status) |
|
| 802 |
+ fmt.Fprintf(out, "%s%s\n", jm.Status, endl) |
|
| 803 | 803 |
} |
| 804 | 804 |
return nil |
| 805 | 805 |
} |
| 806 | 806 |
|
| 807 |
-func DisplayJSONMessagesStream(in io.Reader, out io.Writer) error {
|
|
| 807 |
+func DisplayJSONMessagesStream(in io.Reader, out io.Writer, isTerminal bool) error {
|
|
| 808 | 808 |
dec := json.NewDecoder(in) |
| 809 | 809 |
ids := make(map[string]int) |
| 810 | 810 |
diff := 0 |
| ... | ... |
@@ -825,11 +830,17 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer) error {
|
| 825 | 825 |
} else {
|
| 826 | 826 |
diff = len(ids) - line |
| 827 | 827 |
} |
| 828 |
- fmt.Fprintf(out, "%c[%dA", 27, diff) |
|
| 828 |
+ if isTerminal {
|
|
| 829 |
+ // <ESC>[{diff}A = move cursor up diff rows
|
|
| 830 |
+ fmt.Fprintf(out, "%c[%dA", 27, diff) |
|
| 831 |
+ } |
|
| 829 | 832 |
} |
| 830 |
- err := jm.Display(out) |
|
| 833 |
+ err := jm.Display(out, isTerminal) |
|
| 831 | 834 |
if jm.ID != "" {
|
| 832 |
- fmt.Fprintf(out, "%c[%dB", 27, diff) |
|
| 835 |
+ if isTerminal {
|
|
| 836 |
+ // <ESC>[{diff}B = move cursor down diff rows
|
|
| 837 |
+ fmt.Fprintf(out, "%c[%dB", 27, diff) |
|
| 838 |
+ } |
|
| 833 | 839 |
} |
| 834 | 840 |
if err != nil {
|
| 835 | 841 |
return err |
| ... | ... |
@@ -1226,12 +1237,14 @@ func IsClosedError(err error) bool {
|
| 1226 | 1226 |
|
| 1227 | 1227 |
func PartParser(template, data string) (map[string]string, error) {
|
| 1228 | 1228 |
// ip:public:private |
| 1229 |
- templateParts := strings.Split(template, ":") |
|
| 1230 |
- parts := strings.Split(data, ":") |
|
| 1229 |
+ var ( |
|
| 1230 |
+ templateParts = strings.Split(template, ":") |
|
| 1231 |
+ parts = strings.Split(data, ":") |
|
| 1232 |
+ out = make(map[string]string, len(templateParts)) |
|
| 1233 |
+ ) |
|
| 1231 | 1234 |
if len(parts) != len(templateParts) {
|
| 1232 | 1235 |
return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template)
|
| 1233 | 1236 |
} |
| 1234 |
- out := make(map[string]string, len(templateParts)) |
|
| 1235 | 1237 |
|
| 1236 | 1238 |
for i, t := range templateParts {
|
| 1237 | 1239 |
value := "" |