Compare commits

..

1 Commits

Author SHA1 Message Date
Jessica Frazelle 746e830230 Bump version to v1.6.0
Docker-DCO-1.1-Signed-off-by: Jessie Frazelle <jess@docker.com> (github: jfrazelle)
2015-03-23 15:29:13 -07:00
75 changed files with 675 additions and 1193 deletions
+1 -3
View File
@@ -4,7 +4,6 @@
#### Builder
+ Building images from an image ID
+ build containers with resource constraints, ie `docker build --cpu-shares=100 --memory=1024m...`
+ `commit --change` to apply specified Dockerfile instructions while committing the image
+ `import --change` to apply specified Dockerfile instructions while importing the image
@@ -14,10 +13,9 @@
#### Runtime
+ Container and image Labels
+ `--cgroup-parent` for specifying a parent cgroup to place container cgroup within
+ Logging drivers, `json-file`, `syslog`, or `none`
+ Logging drivers, `json-file` or `syslog`
+ Pulling images by ID
+ `--ulimit` to set the ulimit on a container
+ `--default-ulimit` option on the daemon which applies to all created containers (and overwritten by `--ulimit` on run)
## 1.5.0 (2015-02-10)
+1 -1
View File
@@ -1 +1 @@
1.6.0-rc4
1.6.0-rc1
+96 -55
View File
@@ -441,7 +441,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
authconfig.ServerAddress = serverAddress
cli.configFile.Configs[serverAddress] = authconfig
stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], nil)
stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
if statusCode == 401 {
delete(cli.configFile.Configs, serverAddress)
registry.SaveConfig(cli.configFile)
@@ -527,7 +527,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
}
fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
body, _, err := readBody(cli.call("GET", "/version", nil, nil))
body, _, err := readBody(cli.call("GET", "/version", nil, false))
if err != nil {
return err
}
@@ -559,7 +559,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
cmd.Require(flag.Exact, 0)
utils.ParseFlags(cmd, args, false)
body, _, err := readBody(cli.call("GET", "/info", nil, nil))
body, _, err := readBody(cli.call("GET", "/info", nil, false))
if err != nil {
return err
}
@@ -696,7 +696,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, nil))
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
@@ -719,7 +719,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, nil))
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
@@ -748,7 +748,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
if sig == "" {
log.Errorf("Unsupported signal: %v. Discarding.", s)
}
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
log.Debugf("Error sending signal: %s", err)
}
}
@@ -774,7 +774,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
return fmt.Errorf("You cannot start and attach multiple containers at once.")
}
stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
if err != nil {
return err
}
@@ -834,7 +834,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, nil))
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
if err != nil {
if !*attach && !*openStdin {
// attach and openStdin is false means it could be starting multiple containers
@@ -882,7 +882,7 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
var encounteredError error
for _, name := range cmd.Args() {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
} else {
@@ -899,7 +899,7 @@ func (cli *DockerCli) CmdPause(args ...string) error {
var encounteredError error
for _, name := range cmd.Args() {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
} else {
@@ -922,7 +922,7 @@ func (cli *DockerCli) CmdRename(args ...string) error {
old_name := cmd.Arg(0)
new_name := cmd.Arg(1)
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, false)); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
return fmt.Errorf("Error: failed to rename container named %s", old_name)
}
@@ -951,7 +951,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
status := 0
for _, name := range cmd.Args() {
obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, nil))
obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
if err != nil {
if strings.Contains(err.Error(), "Too many") {
fmt.Fprintf(cli.err, "Error: %v", err)
@@ -959,7 +959,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
continue
}
obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
if err != nil {
if strings.Contains(err.Error(), "No such") {
fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
@@ -1022,7 +1022,7 @@ func (cli *DockerCli) CmdTop(args ...string) error {
val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
}
stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, nil)
stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
if err != nil {
return err
}
@@ -1048,7 +1048,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
cmd.Require(flag.Min, 1)
utils.ParseFlags(cmd, args, true)
stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
if err != nil {
return err
}
@@ -1113,7 +1113,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
var encounteredError error
for _, name := range cmd.Args() {
body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, nil))
body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
@@ -1144,7 +1144,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
utils.ParseFlags(cmd, args, true)
body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, nil))
body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
if err != nil {
return err
}
@@ -1211,7 +1211,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, nil))
_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
@@ -1232,7 +1232,7 @@ func (cli *DockerCli) CmdKill(args ...string) error {
var encounteredError error
for _, name := range cmd.Args() {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
} else {
@@ -1317,8 +1317,32 @@ func (cli *DockerCli) CmdPush(args ...string) error {
v := url.Values{}
v.Set("tag", tag)
_, _, err = cli.clientRequestAttemptLogin("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, repoInfo.Index, "push")
return err
push := func(authConfig registry.AuthConfig) error {
buf, err := json.Marshal(authConfig)
if err != nil {
return err
}
registryAuthHeader := []string{
base64.URLEncoding.EncodeToString(buf),
}
return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
"X-Registry-Auth": registryAuthHeader,
})
}
if err := push(authConfig); err != nil {
if strings.Contains(err.Error(), "Status 401") {
fmt.Fprintln(cli.out, "\nPlease login prior to push:")
if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
return err
}
authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
return push(authConfig)
}
return err
}
return nil
}
func (cli *DockerCli) CmdPull(args ...string) error {
@@ -1351,8 +1375,36 @@ func (cli *DockerCli) CmdPull(args ...string) error {
cli.LoadConfigFile()
_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
return err
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
pull := func(authConfig registry.AuthConfig) error {
buf, err := json.Marshal(authConfig)
if err != nil {
return err
}
registryAuthHeader := []string{
base64.URLEncoding.EncodeToString(buf),
}
return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
"X-Registry-Auth": registryAuthHeader,
})
}
if err := pull(authConfig); err != nil {
if strings.Contains(err.Error(), "Status 401") {
fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
return err
}
authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
return pull(authConfig)
}
return err
}
return nil
}
func (cli *DockerCli) CmdImages(args ...string) error {
@@ -1396,7 +1448,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
v.Set("filters", filterJson)
}
body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
if err != nil {
return err
}
@@ -1474,7 +1526,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
v.Set("all", "1")
}
body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
if err != nil {
return err
@@ -1672,7 +1724,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
v.Set("filters", filterJson)
}
body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, nil))
body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
if err != nil {
return err
}
@@ -1813,7 +1865,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
return err
}
}
stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, nil)
stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
if err != nil {
return err
}
@@ -1924,7 +1976,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
utils.ParseFlags(cmd, args, true)
body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, nil))
body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
if err != nil {
return err
@@ -1962,7 +2014,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
name := cmd.Arg(0)
stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
if err != nil {
return err
}
@@ -2003,7 +2055,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
utils.ParseFlags(cmd, args, true)
name := cmd.Arg(0)
stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
if err != nil {
return err
}
@@ -2074,27 +2126,16 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
utils.ParseFlags(cmd, args, true)
name := cmd.Arg(0)
v := url.Values{}
v.Set("term", name)
v.Set("term", cmd.Arg(0))
body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
// Resolve the Repository name from fqn to hostname + name
taglessRemote, _ := parsers.ParseRepositoryTag(name)
repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
if err != nil {
return err
}
cli.LoadConfigFile()
body, statusCode, errReq := cli.clientRequestAttemptLogin("GET", "/images/search?"+v.Encode(), nil, nil, repoInfo.Index, "search")
rawBody, _, err := readBody(body, statusCode, errReq)
if err != nil {
return err
}
outs := engine.NewTable("star_count", 0)
if _, err := outs.ReadListFrom(rawBody); err != nil {
if _, err := outs.ReadListFrom(body); err != nil {
return err
}
w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
@@ -2149,7 +2190,7 @@ func (cli *DockerCli) CmdTag(args ...string) error {
v.Set("force", "1")
}
if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
return err
}
return nil
@@ -2251,7 +2292,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
}
//create the container
stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil)
stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false)
//if image not found try to pull it
if statusCode == 404 {
repo, tag := parsers.ParseRepositoryTag(config.Image)
@@ -2265,7 +2306,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
return nil, err
}
// Retry
if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil); err != nil {
if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil {
return nil, err
}
} else if err != nil {
@@ -2455,7 +2496,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
//start the container
if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, nil)); err != nil {
if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, false)); err != nil {
return err
}
@@ -2485,13 +2526,13 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if *flAutoRemove {
// Autoremove: wait for the container to finish, retrieve
// the exit code and remove the container
if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, false)); err != nil {
return err
}
if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
return err
}
if _, _, err := readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, nil)); err != nil {
if _, _, err := readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, false)); err != nil {
return err
}
} else {
@@ -2531,7 +2572,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
copyData.Set("Resource", info[1])
copyData.Set("HostPath", cmd.Arg(1))
stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, nil)
stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
if stream != nil {
defer stream.Close()
}
@@ -2626,7 +2667,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
return &utils.StatusError{StatusCode: 1}
}
stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false)
if err != nil {
return err
}
@@ -2648,7 +2689,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
return err
}
} else {
if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil {
return err
}
// For now don't print this - wait for when we support exec wait()
@@ -2740,7 +2781,7 @@ type containerStats struct {
}
func (s *containerStats) Collect(cli *DockerCli) {
stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, nil)
stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false)
if err != nil {
s.err = err
return
+109 -147
View File
@@ -12,10 +12,8 @@ import (
"net/url"
"os"
gosignal "os/signal"
"runtime"
"strconv"
"strings"
"time"
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/api"
@@ -56,144 +54,124 @@ func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) {
return params, nil
}
func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers map[string][]string) (io.ReadCloser, string, int, error) {
expectedPayload := (method == "POST" || method == "PUT")
if expectedPayload && in == nil {
in = bytes.NewReader([]byte{})
}
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
if err != nil {
return nil, "", -1, err
}
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
req.URL.Host = cli.addr
req.URL.Scheme = cli.scheme
if headers != nil {
for k, v := range headers {
req.Header[k] = v
}
}
if expectedPayload && req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", "text/plain")
}
resp, err := cli.HTTPClient().Do(req)
statusCode := -1
if resp != nil {
statusCode = resp.StatusCode
}
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
return nil, "", statusCode, ErrConnectionRefused
}
if cli.tlsConfig == nil {
return nil, "", statusCode, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
}
return nil, "", statusCode, fmt.Errorf("An error occurred trying to connect: %v", err)
}
if statusCode < 200 || statusCode >= 400 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, "", statusCode, err
}
if len(body) == 0 {
return nil, "", statusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(statusCode), req.URL)
}
return nil, "", statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
}
return resp.Body, resp.Header.Get("Content-Type"), statusCode, nil
}
func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
cmdAttempt := func(authConfig registry.AuthConfig) (io.ReadCloser, int, error) {
buf, err := json.Marshal(authConfig)
if err != nil {
return nil, -1, err
}
registryAuthHeader := []string{
base64.URLEncoding.EncodeToString(buf),
}
// begin the request
body, contentType, statusCode, err := cli.clientRequest(method, path, in, map[string][]string{
"X-Registry-Auth": registryAuthHeader,
})
if err == nil && out != nil {
// If we are streaming output, complete the stream since
// errors may not appear until later.
err = cli.streamBody(body, contentType, true, out, nil)
}
if err != nil {
// Since errors in a stream appear after status 200 has been written,
// we may need to change the status code.
if strings.Contains(err.Error(), "Authentication is required") ||
strings.Contains(err.Error(), "Status 401") ||
strings.Contains(err.Error(), "status code 401") {
statusCode = http.StatusUnauthorized
}
}
return body, statusCode, err
}
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(index)
body, statusCode, err := cmdAttempt(authConfig)
if statusCode == http.StatusUnauthorized {
fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
return nil, -1, err
}
authConfig = cli.configFile.ResolveAuthConfig(index)
return cmdAttempt(authConfig)
}
return body, statusCode, err
}
func (cli *DockerCli) call(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
params, err := cli.encodeData(data)
if err != nil {
return nil, -1, err
}
if data != nil {
if headers == nil {
headers = make(map[string][]string)
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
if err != nil {
return nil, -1, err
}
if passAuthInfo {
cli.LoadConfigFile()
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.Configs[registry.IndexServerAddress()]
getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) {
buf, err := json.Marshal(authConfig)
if err != nil {
return nil, err
}
registryAuthHeader := []string{
base64.URLEncoding.EncodeToString(buf),
}
return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil
}
headers["Content-Type"] = []string{"application/json"}
if headers, err := getHeaders(authConfig); err == nil && headers != nil {
for k, v := range headers {
req.Header[k] = v
}
}
}
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
req.URL.Host = cli.addr
req.URL.Scheme = cli.scheme
if data != nil {
req.Header.Set("Content-Type", "application/json")
} else if method == "POST" {
req.Header.Set("Content-Type", "text/plain")
}
resp, err := cli.HTTPClient().Do(req)
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
return nil, -1, ErrConnectionRefused
}
if cli.tlsConfig == nil {
return nil, -1, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
}
return nil, -1, fmt.Errorf("An error occurred trying to connect: %v", err)
}
body, _, statusCode, err := cli.clientRequest(method, path, params, headers)
return body, statusCode, err
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, -1, err
}
if len(body) == 0 {
return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL)
}
return nil, resp.StatusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
}
return resp.Body, resp.StatusCode, nil
}
func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
return cli.streamHelper(method, path, true, in, out, nil, headers)
}
func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
body, contentType, _, err := cli.clientRequest(method, path, in, headers)
if (method == "POST" || method == "PUT") && in == nil {
in = bytes.NewReader([]byte{})
}
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
if err != nil {
return err
}
return cli.streamBody(body, contentType, setRawTerminal, stdout, stderr)
}
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
req.URL.Host = cli.addr
req.URL.Scheme = cli.scheme
if method == "POST" {
req.Header.Set("Content-Type", "text/plain")
}
func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, setRawTerminal bool, stdout, stderr io.Writer) error {
defer body.Close()
if headers != nil {
for k, v := range headers {
req.Header[k] = v
}
}
resp, err := cli.HTTPClient().Do(req)
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
}
return err
}
defer resp.Body.Close()
if api.MatchesContentType(contentType, "application/json") {
return utils.DisplayJSONMessagesStream(body, stdout, cli.outFd, cli.isTerminalOut)
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if len(body) == 0 {
return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
}
return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
}
if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut)
}
if stdout != nil || stderr != nil {
// When TTY is ON, use regular copy
var err error
if setRawTerminal {
_, err = io.Copy(stdout, body)
_, err = io.Copy(stdout, resp.Body)
} else {
_, err = stdcopy.StdCopy(stdout, stderr, body)
_, err = stdcopy.StdCopy(stdout, stderr, resp.Body)
}
log.Debugf("[stream] End of stdout")
return err
@@ -217,13 +195,13 @@ func (cli *DockerCli) resizeTty(id string, isExec bool) {
path = "/exec/" + id + "/resize?"
}
if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, nil)); err != nil {
if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, false)); err != nil {
log.Debugf("Error resize: %s", err)
}
}
func waitForExit(cli *DockerCli, containerID string) (int, error) {
stream, _, err := cli.call("POST", "/containers/"+containerID+"/wait", nil, nil)
func waitForExit(cli *DockerCli, containerId string) (int, error) {
stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false)
if err != nil {
return -1, err
}
@@ -237,8 +215,8 @@ func waitForExit(cli *DockerCli, containerID string) (int, error) {
// getExitCode perform an inspect on the container. It returns
// the running state and the exit code.
func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
stream, _, err := cli.call("GET", "/containers/"+containerID+"/json", nil, nil)
func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
stream, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil, false)
if err != nil {
// If we can't connect, then the daemon probably died.
if err != ErrConnectionRefused {
@@ -258,8 +236,8 @@ func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
// getExecExitCode perform an inspect on the exec command. It returns
// the running state and the exit code.
func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
stream, _, err := cli.call("GET", "/exec/"+execID+"/json", nil, nil)
func getExecExitCode(cli *DockerCli, execId string) (bool, int, error) {
stream, _, err := cli.call("GET", "/exec/"+execId+"/json", nil, false)
if err != nil {
// If we can't connect, then the daemon probably died.
if err != ErrConnectionRefused {
@@ -279,29 +257,13 @@ func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
cli.resizeTty(id, isExec)
if runtime.GOOS == "windows" {
go func() {
prevW, prevH := cli.getTtySize()
for {
time.Sleep(time.Millisecond * 250)
w, h := cli.getTtySize()
if prevW != w || prevH != h {
cli.resizeTty(id, isExec)
}
prevW = w
prevH = h
}
}()
} else {
sigchan := make(chan os.Signal, 1)
gosignal.Notify(sigchan, signal.SIGWINCH)
go func() {
for _ = range sigchan {
cli.resizeTty(id, isExec)
}
}()
}
sigchan := make(chan os.Signal, 1)
gosignal.Notify(sigchan, signal.SIGWINCH)
go func() {
for _ = range sigchan {
cli.resizeTty(id, isExec)
}
}()
return nil
}
+4 -3
View File
@@ -27,7 +27,7 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/daemon/networkdriver/bridge"
"github.com/docker/docker/daemon/networkdriver/portallocator"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/listenbuffer"
"github.com/docker/docker/pkg/parsers"
@@ -38,7 +38,7 @@ import (
)
var (
activationLock chan struct{} = make(chan struct{})
activationLock chan struct{}
)
type HttpServer struct {
@@ -1527,7 +1527,7 @@ func allocateDaemonPort(addr string) error {
}
for _, hostIP := range hostIPs {
if _, err := bridge.RequestPort(hostIP, "tcp", intPort); err != nil {
if _, err := portallocator.RequestPort(hostIP, "tcp", intPort); err != nil {
return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
}
}
@@ -1578,6 +1578,7 @@ func ServeApi(job *engine.Job) engine.Status {
protoAddrs = job.Args
chErrors = make(chan error, len(protoAddrs))
)
activationLock = make(chan struct{})
for _, protoAddr := range protoAddrs {
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
+1 -3
View File
@@ -95,9 +95,7 @@ func AcceptConnections(job *engine.Job) engine.Status {
go systemd.SdNotify("READY=1")
// close the lock so the listeners start accepting connections
select {
case <-activationLock:
default:
if activationLock != nil {
close(activationLock)
}
+8 -44
View File
@@ -58,18 +58,6 @@ __docker_containers_unpauseable() {
__docker_containers_all '.State.Paused'
}
__docker_container_names() {
local containers=( $(__docker_q ps -aq --no-trunc) )
local names=( $(__docker_q inspect --format '{{.Name}}' "${containers[@]}") )
names=( "${names[@]#/}" ) # trim off the leading "/" from the container names
COMPREPLY=( $(compgen -W "${names[*]}" -- "$cur") )
}
__docker_container_ids() {
local containers=( $(__docker_q ps -aq) )
COMPREPLY=( $(compgen -W "${containers[*]}" -- "$cur") )
}
__docker_image_repos() {
local repos="$(__docker_q images | awk 'NR>1 && $1 != "<none>" { print $1 }')"
COMPREPLY=( $(compgen -W "$repos" -- "$cur") )
@@ -337,7 +325,7 @@ _docker_cp() {
(( counter++ ))
if [ $cword -eq $counter ]; then
_filedir -d
_filedir
return
fi
;;
@@ -449,10 +437,7 @@ _docker_history() {
_docker_images() {
case "$prev" in
--filter|-f)
COMPREPLY=( $( compgen -W "dangling=true label=" -- "$cur" ) )
if [ "$COMPREPLY" = "label=" ]; then
compopt -o nospace
fi
COMPREPLY=( $( compgen -W "dangling=true" -- "$cur" ) )
return
;;
esac
@@ -462,20 +447,17 @@ _docker_images() {
COMPREPLY=( $( compgen -W "true false" -- "${cur#=}" ) )
return
;;
*label=*)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--all -a --filter -f --help --no-trunc --quiet -q" -- "$cur" ) )
;;
=)
return
;;
*)
__docker_image_repos
local counter=$(__docker_pos_first_nonflag)
if [ $cword -eq $counter ]; then
__docker_image_repos
fi
;;
esac
}
@@ -634,7 +616,7 @@ _docker_ps() {
__docker_containers_all
;;
--filter|-f)
COMPREPLY=( $( compgen -S = -W "exited id label name status" -- "$cur" ) )
COMPREPLY=( $( compgen -S = -W "exited status" -- "$cur" ) )
compopt -o nospace
return
;;
@@ -644,16 +626,6 @@ _docker_ps() {
esac
case "${words[$cword-2]}$prev=" in
*id=*)
cur="${cur#=}"
__docker_container_ids
return
;;
*name=*)
cur="${cur#=}"
__docker_container_names
return
;;
*status=*)
COMPREPLY=( $( compgen -W "exited paused restarting running" -- "${cur#=}" ) )
return
@@ -762,7 +734,6 @@ _docker_run() {
--attach -a
--cap-add
--cap-drop
--cgroup-parent
--cidfile
--cpuset
--cpu-shares -c
@@ -775,10 +746,7 @@ _docker_run() {
--expose
--hostname -h
--ipc
--label -l
--label-file
--link
--log-driver
--lxc-conf
--mac-address
--memory -m
@@ -830,7 +798,7 @@ _docker_run() {
__docker_capabilities
return
;;
--cidfile|--env-file|--label-file)
--cidfile|--env-file)
_filedir
return
;;
@@ -882,10 +850,6 @@ _docker_run() {
esac
return
;;
--log-driver)
COMPREPLY=( $( compgen -W "json-file syslog none" -- "$cur") )
return
;;
--net)
case "$cur" in
container:*)
-4
View File
@@ -360,10 +360,6 @@ func (container *Container) Start() (err error) {
return nil
}
if container.removalInProgress || container.Dead {
return fmt.Errorf("Container is marked for removal and cannot be started.")
}
// if we encounter an error during start we need to ensure that any other
// setup has been cleaned up properly
defer func() {
+8 -2
View File
@@ -25,6 +25,7 @@ import (
"github.com/docker/docker/daemon/graphdriver"
_ "github.com/docker/docker/daemon/graphdriver/vfs"
_ "github.com/docker/docker/daemon/networkdriver/bridge"
"github.com/docker/docker/daemon/networkdriver/portallocator"
"github.com/docker/docker/engine"
"github.com/docker/docker/graph"
"github.com/docker/docker/image"
@@ -826,6 +827,12 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error)
}
config.DisableNetwork = config.BridgeIface == disableNetworkBridge
// register portallocator release on shutdown
eng.OnShutdown(func() {
if err := portallocator.ReleaseAll(); err != nil {
log.Errorf("portallocator.ReleaseAll(): %s", err)
}
})
// Claim the pidfile first, to avoid any and all unexpected race conditions.
// Some of the init doesn't need a pidfile lock - but let's not try to be smart.
if config.Pidfile != "" {
@@ -1005,8 +1012,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error)
}
sysInfo := sysinfo.New(false)
const runDir = "/var/run/docker"
ed, err := execdrivers.NewDriver(config.ExecDriver, runDir, config.Root, sysInitPath, sysInfo)
ed, err := execdrivers.NewDriver(config.ExecDriver, config.Root, sysInitPath, sysInfo)
if err != nil {
return nil, err
}
+10 -48
View File
@@ -61,15 +61,8 @@ func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status {
return job.Errorf("Conflict, You cannot remove a running container. Stop the container before attempting removal or use -f")
}
}
if forceRemove {
if err := daemon.ForceRm(container); err != nil {
log.Errorf("Cannot destroy container %s: %v", name, err)
}
} else {
if err := daemon.Rm(container); err != nil {
return job.Errorf("Cannot destroy container %s: %v", name, err)
}
if err := daemon.Rm(container); err != nil {
return job.Errorf("Cannot destroy container %s: %s", name, err)
}
container.LogEvent("destroy")
if removeVolume {
@@ -88,16 +81,8 @@ func (daemon *Daemon) DeleteVolumes(volumeIDs map[string]struct{}) {
}
}
func (daemon *Daemon) Rm(container *Container) (err error) {
return daemon.commonRm(container, false)
}
func (daemon *Daemon) ForceRm(container *Container) (err error) {
return daemon.commonRm(container, true)
}
// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
func (daemon *Daemon) commonRm(container *Container, forceRemove bool) (err error) {
func (daemon *Daemon) Rm(container *Container) error {
if container == nil {
return fmt.Errorf("The given container is <nil>")
}
@@ -107,40 +92,19 @@ func (daemon *Daemon) commonRm(container *Container, forceRemove bool) (err erro
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
}
// Container state RemovalInProgress should be used to avoid races.
if err = container.SetRemovalInProgress(); err != nil {
return fmt.Errorf("Failed to set container state to RemovalInProgress: %s", err)
}
defer container.ResetRemovalInProgress()
if err = container.Stop(3); err != nil {
if err := container.Stop(3); err != nil {
return err
}
// Mark container dead. We don't want anybody to be restarting it.
container.SetDead()
// Save container state to disk. So that if error happens before
// container meta file got removed from disk, then a restart of
// docker should not make a dead container alive.
container.ToDisk()
// If force removal is required, delete container from various
// indexes even if removal failed.
defer func() {
if err != nil && forceRemove {
daemon.idIndex.Delete(container.ID)
daemon.containers.Delete(container.ID)
}
}()
// Deregister the container before removing its directory, to avoid race conditions
daemon.idIndex.Delete(container.ID)
daemon.containers.Delete(container.ID)
container.derefVolumes()
if _, err := daemon.containerGraph.Purge(container.ID); err != nil {
log.Debugf("Unable to remove container from link graph: %s", err)
}
if err = daemon.driver.Remove(container.ID); err != nil {
if err := daemon.driver.Remove(container.ID); err != nil {
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err)
}
@@ -149,17 +113,15 @@ func (daemon *Daemon) commonRm(container *Container, forceRemove bool) (err erro
return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
}
if err = os.RemoveAll(container.root); err != nil {
if err := os.RemoveAll(container.root); err != nil {
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
}
if err = daemon.execDriver.Clean(container.ID); err != nil {
if err := daemon.execDriver.Clean(container.ID); err != nil {
return fmt.Errorf("Unable to remove execdriver data for %s: %s", container.ID, err)
}
selinuxFreeLxcContexts(container.ProcessLabel)
daemon.idIndex.Delete(container.ID)
daemon.containers.Delete(container.ID)
return nil
}
+2 -2
View File
@@ -10,13 +10,13 @@ import (
"github.com/docker/docker/pkg/sysinfo"
)
func NewDriver(name, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
switch name {
case "lxc":
// we want to give the lxc driver the full docker root because it needs
// to access and write config and template files in /var/lib/docker/containers/*
// to be backwards compatible
return lxc.NewDriver(root, libPath, initPath, sysInfo.AppArmor)
return lxc.NewDriver(root, initPath, sysInfo.AppArmor)
case "native":
return native.NewDriver(path.Join(root, "execdriver", "native"), initPath)
}
+3 -19
View File
@@ -20,7 +20,6 @@ import (
"github.com/docker/docker/daemon/execdriver"
sysinfo "github.com/docker/docker/pkg/system"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/pkg/version"
"github.com/docker/docker/utils"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/cgroups"
@@ -36,7 +35,6 @@ var ErrExec = errors.New("Unsupported: Exec is not supported by the lxc driver")
type driver struct {
root string // root path for the driver to use
libPath string
initPath string
apparmor bool
sharedRoot bool
@@ -50,10 +48,7 @@ type activeContainer struct {
cmd *exec.Cmd
}
func NewDriver(root, libPath, initPath string, apparmor bool) (*driver, error) {
if err := os.MkdirAll(root, 0700); err != nil {
return nil, err
}
func NewDriver(root, initPath string, apparmor bool) (*driver, error) {
// setup unconfined symlink
if err := linkLxcStart(root); err != nil {
return nil, err
@@ -65,7 +60,6 @@ func NewDriver(root, libPath, initPath string, apparmor bool) (*driver, error) {
return &driver{
apparmor: apparmor,
root: root,
libPath: libPath,
initPath: initPath,
sharedRoot: rootIsShared(),
activeContainers: make(map[string]*activeContainer),
@@ -121,13 +115,6 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
"-n", c.ID,
"-f", configPath,
}
// From lxc>=1.1 the default behavior is to daemonize containers after start
lxcVersion := version.Version(d.version())
if lxcVersion.GreaterThanOrEqualTo(version.Version("1.1")) {
params = append(params, "-F")
}
if c.Network.ContainerID != "" {
params = append(params,
"--share-net", c.Network.ContainerID,
@@ -671,7 +658,7 @@ func rootIsShared() bool {
}
func (d *driver) containerDir(containerId string) string {
return path.Join(d.libPath, "containers", containerId)
return path.Join(d.root, "containers", containerId)
}
func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
@@ -701,7 +688,7 @@ func (d *driver) generateEnvConfig(c *execdriver.Command) error {
if err != nil {
return err
}
p := path.Join(d.libPath, "containers", c.ID, "config.env")
p := path.Join(d.root, "containers", c.ID, "config.env")
c.Mounts = append(c.Mounts, execdriver.Mount{
Source: p,
Destination: "/.dockerenv",
@@ -793,8 +780,5 @@ func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
}
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
if _, ok := d.activeContainers[id]; !ok {
return nil, fmt.Errorf("%s is not a key in active containers", id)
}
return execdriver.Stats(d.containerDir(id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory)
}
@@ -39,7 +39,7 @@ func TestLXCConfig(t *testing.T) {
cpu = cpuMin + rand.Intn(cpuMax-cpuMin)
)
driver, err := NewDriver(root, root, "", false)
driver, err := NewDriver(root, "", false)
if err != nil {
t.Fatal(err)
}
@@ -76,7 +76,7 @@ func TestCustomLxcConfig(t *testing.T) {
os.MkdirAll(path.Join(root, "containers", "1"), 0777)
driver, err := NewDriver(root, root, "", false)
driver, err := NewDriver(root, "", false)
if err != nil {
t.Fatal(err)
}
@@ -194,7 +194,7 @@ func TestCustomLxcConfigMounts(t *testing.T) {
}
os.MkdirAll(path.Join(root, "containers", "1"), 0777)
driver, err := NewDriver(root, root, "", false)
driver, err := NewDriver(root, "", false)
if err != nil {
t.Fatal(err)
}
@@ -248,7 +248,7 @@ func TestCustomLxcConfigMisc(t *testing.T) {
}
defer os.RemoveAll(root)
os.MkdirAll(path.Join(root, "containers", "1"), 0777)
driver, err := NewDriver(root, root, "", true)
driver, err := NewDriver(root, "", true)
if err != nil {
t.Fatal(err)
@@ -313,7 +313,7 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) {
}
defer os.RemoveAll(root)
os.MkdirAll(path.Join(root, "containers", "1"), 0777)
driver, err := NewDriver(root, root, "", false)
driver, err := NewDriver(root, "", false)
if err != nil {
t.Fatal(err)
}
+17 -25
View File
@@ -64,6 +64,7 @@ func NewDriver(root, initPath string) (*driver, error) {
root,
cgm,
libcontainer.InitPath(reexec.Self(), DriverName),
libcontainer.TmpfsRoot,
)
if err != nil {
return nil, err
@@ -156,38 +157,30 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
startCallback(&c.ProcessConfig, pid)
}
oom := notifyOnOOM(cont)
oomKillNotification, err := cont.NotifyOOM()
if err != nil {
oomKillNotification = nil
log.Warnf("Your kernel does not support OOM notifications: %s", err)
}
waitF := p.Wait
if nss := cont.Config().Namespaces; !nss.Contains(configs.NEWPID) {
if nss := cont.Config().Namespaces; nss.Contains(configs.NEWPID) {
// we need such hack for tracking processes with inerited fds,
// because cmd.Wait() waiting for all streams to be copied
waitF = waitInPIDHost(p, cont)
}
ps, err := waitF()
if err != nil {
execErr, ok := err.(*exec.ExitError)
if !ok {
if err, ok := err.(*exec.ExitError); !ok {
return execdriver.ExitStatus{ExitCode: -1}, err
} else {
ps = err.ProcessState
}
ps = execErr.ProcessState
}
cont.Destroy()
_, oomKill := <-oom
return execdriver.ExitStatus{ExitCode: utils.ExitStatus(ps.Sys().(syscall.WaitStatus)), OOMKilled: oomKill}, nil
}
// notifyOnOOM returns a channel that signals if the container received an OOM notification
// for any process. If it is unable to subscribe to OOM notifications then a closed
// channel is returned as it will be non-blocking and return the correct result when read.
func notifyOnOOM(container libcontainer.Container) <-chan struct{} {
oom, err := container.NotifyOOM()
if err != nil {
log.Warnf("Your kernel does not support OOM notifications: %s", err)
c := make(chan struct{})
close(c)
return c
}
return oom
_, oomKill := <-oomKillNotification
return execdriver.ExitStatus{ExitCode: utils.ExitStatus(ps.Sys().(syscall.WaitStatus)), OOMKilled: oomKill}, nil
}
func waitInPIDHost(p *libcontainer.Process, c libcontainer.Container) func() (*os.ProcessState, error) {
@@ -197,17 +190,16 @@ func waitInPIDHost(p *libcontainer.Process, c libcontainer.Container) func() (*o
return nil, err
}
processes, err := c.Processes()
process, err := os.FindProcess(pid)
s, err := process.Wait()
if err != nil {
execErr, ok := err.(*exec.ExitError)
if !ok {
if err, ok := err.(*exec.ExitError); !ok {
return s, err
} else {
s = err.ProcessState
}
s = execErr.ProcessState
}
processes, err := c.Processes()
if err != nil {
return s, err
}
+2 -45
View File
@@ -23,7 +23,6 @@ package aufs
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
@@ -48,9 +47,6 @@ var (
graphdriver.FsMagicAufs,
}
backingFs = "<unknown>"
enableDirpermLock sync.Once
enableDirperm bool
)
func init() {
@@ -156,7 +152,6 @@ func (a *Driver) Status() [][2]string {
{"Root Dir", a.rootPath()},
{"Backing Filesystem", backingFs},
{"Dirs", fmt.Sprintf("%d", len(ids))},
{"Dirperm1 Supported", fmt.Sprintf("%v", useDirperm())},
}
}
@@ -427,11 +422,7 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro
// Mount options are clipped to page size(4096 bytes). If there are more
// layers then these are remounted individually using append.
offset := 54
if useDirperm() {
offset += len("dirperm1")
}
b := make([]byte, syscall.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel
b := make([]byte, syscall.Getpagesize()-len(mountLabel)-54) // room for xino & mountLabel
bp := copy(b, fmt.Sprintf("br:%s=rw", rw))
firstMount := true
@@ -455,11 +446,7 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro
}
if firstMount {
opts := "dio,xino=/dev/shm/aufs.xino"
if useDirperm() {
opts += ",dirperm1"
}
data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel)
data := label.FormatMountLabel(fmt.Sprintf("%s,dio,xino=/dev/shm/aufs.xino", string(b[:bp])), mountLabel)
if err = mount("none", target, "aufs", 0, data); err != nil {
return
}
@@ -473,33 +460,3 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro
return
}
// useDirperm checks dirperm1 mount option can be used with the current
// version of aufs.
func useDirperm() bool {
enableDirpermLock.Do(func() {
base, err := ioutil.TempDir("", "docker-aufs-base")
if err != nil {
log.Errorf("error checking dirperm1: %v", err)
return
}
defer os.RemoveAll(base)
union, err := ioutil.TempDir("", "docker-aufs-union")
if err != nil {
log.Errorf("error checking dirperm1: %v", err)
return
}
defer os.RemoveAll(union)
opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base)
if err := mount("none", union, "aufs", 0, opts); err != nil {
return
}
enableDirperm = true
if err := Unmount(union); err != nil {
log.Errorf("error checking dirperm1: failed to unmount %v", err)
}
})
return enableDirperm
}
+7 -9
View File
@@ -5,22 +5,20 @@ package btrfs
/*
#include <btrfs/version.h>
// around version 3.16, they did not define lib version yet
#ifndef BTRFS_LIB_VERSION
#define BTRFS_LIB_VERSION -1
#endif
// upstream had removed it, but now it will be coming back
#ifndef BTRFS_BUILD_VERSION
#define BTRFS_BUILD_VERSION "-"
// because around version 3.16, they did not define lib version yet
int my_btrfs_lib_version() {
#ifdef BTRFS_LIB_VERSION
return BTRFS_LIB_VERSION;
#else
return -1;
#endif
}
*/
import "C"
func BtrfsBuildVersion() string {
return string(C.BTRFS_BUILD_VERSION)
}
func BtrfsLibVersion() int {
return int(C.BTRFS_LIB_VERSION)
}
-1
View File
@@ -8,7 +8,6 @@ package btrfs
func BtrfsBuildVersion() string {
return "-"
}
func BtrfsLibVersion() int {
return -1
}
+4 -4
View File
@@ -1,4 +1,4 @@
// +build linux,!btrfs_noversion
// +build linux
package btrfs
@@ -6,8 +6,8 @@ import (
"testing"
)
func TestLibVersion(t *testing.T) {
if BtrfsLibVersion() <= 0 {
t.Errorf("expected output from btrfs lib version > 0")
func TestBuildVersion(t *testing.T) {
if len(BtrfsBuildVersion()) == 0 {
t.Errorf("expected output from btrfs build version, but got empty string")
}
}
+2 -1
View File
@@ -39,8 +39,9 @@ var (
"aufs",
"btrfs",
"devicemapper",
"overlay",
"vfs",
// experimental, has to be enabled manually for now
"overlay",
}
ErrNotSupported = errors.New("driver not supported")
+3 -17
View File
@@ -77,19 +77,11 @@ var (
bridgeIPv4Network *net.IPNet
bridgeIPv6Addr net.IP
globalIPv6Network *net.IPNet
portMapper *portmapper.PortMapper
once sync.Once
defaultBindingIP = net.ParseIP("0.0.0.0")
currentInterfaces = ifaces{c: make(map[string]*networkInterface)}
)
func initPortMapper() {
once.Do(func() {
portMapper = portmapper.New()
})
}
func InitDriver(job *engine.Job) engine.Status {
var (
networkv4 *net.IPNet
@@ -106,7 +98,6 @@ func InitDriver(job *engine.Job) engine.Status {
fixedCIDR = job.Getenv("FixedCIDR")
fixedCIDRv6 = job.Getenv("FixedCIDRv6")
)
initPortMapper()
if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
defaultBindingIP = net.ParseIP(defaultIP)
@@ -243,7 +234,7 @@ func InitDriver(job *engine.Job) engine.Status {
if err != nil {
return job.Error(err)
}
portMapper.SetIptablesChain(chain)
portmapper.SetIptablesChain(chain)
}
bridgeIPv4Network = networkv4
@@ -358,11 +349,6 @@ func setupIPTables(addr net.Addr, icc, ipmasq bool) error {
return nil
}
func RequestPort(ip net.IP, proto string, port int) (int, error) {
initPortMapper()
return portMapper.Allocator.RequestPort(ip, proto, port)
}
// configureBridge attempts to create and configure a network bridge interface named `bridgeIface` on the host
// If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges
// If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing
@@ -600,7 +586,7 @@ func Release(job *engine.Job) engine.Status {
}
for _, nat := range containerInterface.PortMappings {
if err := portMapper.Unmap(nat); err != nil {
if err := portmapper.Unmap(nat); err != nil {
log.Infof("Unable to unmap port %s: %s", nat, err)
}
}
@@ -657,7 +643,7 @@ func AllocatePort(job *engine.Job) engine.Status {
var host net.Addr
for i := 0; i < MaxAllocatedPortAttempts; i++ {
if host, err = portMapper.Map(container, ip, hostPort); err == nil {
if host, err = portmapper.Map(container, ip, hostPort); err == nil {
break
}
// There is no point in immediately retrying to map an explicitly
@@ -16,12 +16,44 @@ const (
DefaultPortRangeEnd = 65535
)
var (
beginPortRange = DefaultPortRangeStart
endPortRange = DefaultPortRangeEnd
)
type portMap struct {
p map[int]struct{}
last int
}
func newPortMap() *portMap {
return &portMap{
p: map[int]struct{}{},
last: endPortRange,
}
}
type protoMap map[string]*portMap
func newProtoMap() protoMap {
return protoMap{
"tcp": newPortMap(),
"udp": newPortMap(),
}
}
type ipMapping map[string]protoMap
var (
ErrAllPortsAllocated = errors.New("all ports are allocated")
ErrUnknownProtocol = errors.New("unknown protocol")
defaultIP = net.ParseIP("0.0.0.0")
)
var (
mutex sync.Mutex
defaultIP = net.ParseIP("0.0.0.0")
globalMap = ipMapping{}
)
type ErrPortAlreadyAllocated struct {
@@ -36,6 +68,32 @@ func NewErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated {
}
}
func init() {
const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range"
portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", beginPortRange, endPortRange)
file, err := os.Open(portRangeKernelParam)
if err != nil {
log.Warnf("port allocator - %s due to error: %v", portRangeFallback, err)
return
}
var start, end int
n, err := fmt.Fscanf(bufio.NewReader(file), "%d\t%d", &start, &end)
if n != 2 || err != nil {
if err == nil {
err = fmt.Errorf("unexpected count of parsed numbers (%d)", n)
}
log.Errorf("port allocator - failed to parse system ephemeral port range from %s - %s: %v", portRangeKernelParam, portRangeFallback, err)
return
}
beginPortRange = start
endPortRange = end
}
func PortRange() (int, int) {
return beginPortRange, endPortRange
}
func (e ErrPortAlreadyAllocated) IP() string {
return e.ip
}
@@ -52,57 +110,12 @@ func (e ErrPortAlreadyAllocated) Error() string {
return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
}
type (
PortAllocator struct {
mutex sync.Mutex
ipMap ipMapping
Begin int
End int
}
portMap struct {
p map[int]struct{}
begin, end int
last int
}
protoMap map[string]*portMap
)
func New() *PortAllocator {
start, end, err := getDynamicPortRange()
if err != nil {
log.Warn(err)
start, end = DefaultPortRangeStart, DefaultPortRangeEnd
}
return &PortAllocator{
ipMap: ipMapping{},
Begin: start,
End: end,
}
}
func getDynamicPortRange() (start int, end int, err error) {
const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range"
portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd)
file, err := os.Open(portRangeKernelParam)
if err != nil {
return 0, 0, fmt.Errorf("port allocator - %s due to error: %v", portRangeFallback, err)
}
n, err := fmt.Fscanf(bufio.NewReader(file), "%d\t%d", &start, &end)
if n != 2 || err != nil {
if err == nil {
err = fmt.Errorf("unexpected count of parsed numbers (%d)", n)
}
return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range from %s - %s: %v", portRangeKernelParam, portRangeFallback, err)
}
return start, end, nil
}
// RequestPort requests new port from global ports pool for specified ip and proto.
// If port is 0 it returns first free port. Otherwise it cheks port availability
// in pool and return that port or error if port is already busy.
func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) {
p.mutex.Lock()
defer p.mutex.Unlock()
func RequestPort(ip net.IP, proto string, port int) (int, error) {
mutex.Lock()
defer mutex.Unlock()
if proto != "tcp" && proto != "udp" {
return 0, ErrUnknownProtocol
@@ -112,14 +125,10 @@ func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, err
ip = defaultIP
}
ipstr := ip.String()
protomap, ok := p.ipMap[ipstr]
protomap, ok := globalMap[ipstr]
if !ok {
protomap = protoMap{
"tcp": p.newPortMap(),
"udp": p.newPortMap(),
}
p.ipMap[ipstr] = protomap
protomap = newProtoMap()
globalMap[ipstr] = protomap
}
mapping := protomap[proto]
if port > 0 {
@@ -138,14 +147,14 @@ func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, err
}
// ReleasePort releases port from global ports pool for specified ip and proto.
func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error {
p.mutex.Lock()
defer p.mutex.Unlock()
func ReleasePort(ip net.IP, proto string, port int) error {
mutex.Lock()
defer mutex.Unlock()
if ip == nil {
ip = defaultIP
}
protomap, ok := p.ipMap[ip.String()]
protomap, ok := globalMap[ip.String()]
if !ok {
return nil
}
@@ -153,29 +162,20 @@ func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error {
return nil
}
func (p *PortAllocator) newPortMap() *portMap {
return &portMap{
p: map[int]struct{}{},
begin: p.Begin,
end: p.End,
last: p.End,
}
}
// ReleaseAll releases all ports for all ips.
func (p *PortAllocator) ReleaseAll() error {
p.mutex.Lock()
p.ipMap = ipMapping{}
p.mutex.Unlock()
func ReleaseAll() error {
mutex.Lock()
globalMap = ipMapping{}
mutex.Unlock()
return nil
}
func (pm *portMap) findPort() (int, error) {
port := pm.last
for i := 0; i <= pm.end-pm.begin; i++ {
for i := 0; i <= endPortRange-beginPortRange; i++ {
port++
if port > pm.end {
port = pm.begin
if port > endPortRange {
port = beginPortRange
}
if _, ok := pm.p[port]; !ok {
@@ -5,23 +5,32 @@ import (
"testing"
)
func TestRequestNewPort(t *testing.T) {
p := New()
func init() {
beginPortRange = DefaultPortRangeStart
endPortRange = DefaultPortRangeEnd
}
port, err := p.RequestPort(defaultIP, "tcp", 0)
func reset() {
ReleaseAll()
}
func TestRequestNewPort(t *testing.T) {
defer reset()
port, err := RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatal(err)
}
if expected := p.Begin; port != expected {
if expected := beginPortRange; port != expected {
t.Fatalf("Expected port %d got %d", expected, port)
}
}
func TestRequestSpecificPort(t *testing.T) {
p := New()
defer reset()
port, err := p.RequestPort(defaultIP, "tcp", 5000)
port, err := RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
@@ -31,9 +40,9 @@ func TestRequestSpecificPort(t *testing.T) {
}
func TestReleasePort(t *testing.T) {
p := New()
defer reset()
port, err := p.RequestPort(defaultIP, "tcp", 5000)
port, err := RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
@@ -41,15 +50,15 @@ func TestReleasePort(t *testing.T) {
t.Fatalf("Expected port 5000 got %d", port)
}
if err := p.ReleasePort(defaultIP, "tcp", 5000); err != nil {
if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
t.Fatal(err)
}
}
func TestReuseReleasedPort(t *testing.T) {
p := New()
defer reset()
port, err := p.RequestPort(defaultIP, "tcp", 5000)
port, err := RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
@@ -57,20 +66,20 @@ func TestReuseReleasedPort(t *testing.T) {
t.Fatalf("Expected port 5000 got %d", port)
}
if err := p.ReleasePort(defaultIP, "tcp", 5000); err != nil {
if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
t.Fatal(err)
}
port, err = p.RequestPort(defaultIP, "tcp", 5000)
port, err = RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
}
func TestReleaseUnreadledPort(t *testing.T) {
p := New()
defer reset()
port, err := p.RequestPort(defaultIP, "tcp", 5000)
port, err := RequestPort(defaultIP, "tcp", 5000)
if err != nil {
t.Fatal(err)
}
@@ -78,7 +87,7 @@ func TestReleaseUnreadledPort(t *testing.T) {
t.Fatalf("Expected port 5000 got %d", port)
}
port, err = p.RequestPort(defaultIP, "tcp", 5000)
port, err = RequestPort(defaultIP, "tcp", 5000)
switch err.(type) {
case ErrPortAlreadyAllocated:
@@ -88,40 +97,42 @@ func TestReleaseUnreadledPort(t *testing.T) {
}
func TestUnknowProtocol(t *testing.T) {
if _, err := New().RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol {
defer reset()
if _, err := RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol {
t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err)
}
}
func TestAllocateAllPorts(t *testing.T) {
p := New()
defer reset()
for i := 0; i <= p.End-p.Begin; i++ {
port, err := p.RequestPort(defaultIP, "tcp", 0)
for i := 0; i <= endPortRange-beginPortRange; i++ {
port, err := RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatal(err)
}
if expected := p.Begin + i; port != expected {
if expected := beginPortRange + i; port != expected {
t.Fatalf("Expected port %d got %d", expected, port)
}
}
if _, err := p.RequestPort(defaultIP, "tcp", 0); err != ErrAllPortsAllocated {
if _, err := RequestPort(defaultIP, "tcp", 0); err != ErrAllPortsAllocated {
t.Fatalf("Expected error %s got %s", ErrAllPortsAllocated, err)
}
_, err := p.RequestPort(defaultIP, "udp", 0)
_, err := RequestPort(defaultIP, "udp", 0)
if err != nil {
t.Fatal(err)
}
// release a port in the middle and ensure we get another tcp port
port := p.Begin + 5
if err := p.ReleasePort(defaultIP, "tcp", port); err != nil {
port := beginPortRange + 5
if err := ReleasePort(defaultIP, "tcp", port); err != nil {
t.Fatal(err)
}
newPort, err := p.RequestPort(defaultIP, "tcp", 0)
newPort, err := RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatal(err)
}
@@ -131,10 +142,10 @@ func TestAllocateAllPorts(t *testing.T) {
// now pm.last == newPort, release it so that it's the only free port of
// the range, and ensure we get it back
if err := p.ReleasePort(defaultIP, "tcp", newPort); err != nil {
if err := ReleasePort(defaultIP, "tcp", newPort); err != nil {
t.Fatal(err)
}
port, err = p.RequestPort(defaultIP, "tcp", 0)
port, err = RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatal(err)
}
@@ -144,34 +155,34 @@ func TestAllocateAllPorts(t *testing.T) {
}
func BenchmarkAllocatePorts(b *testing.B) {
p := New()
defer reset()
for i := 0; i < b.N; i++ {
for i := 0; i <= p.End-p.Begin; i++ {
port, err := p.RequestPort(defaultIP, "tcp", 0)
for i := 0; i <= endPortRange-beginPortRange; i++ {
port, err := RequestPort(defaultIP, "tcp", 0)
if err != nil {
b.Fatal(err)
}
if expected := p.Begin + i; port != expected {
if expected := beginPortRange + i; port != expected {
b.Fatalf("Expected port %d got %d", expected, port)
}
}
p.ReleaseAll()
reset()
}
}
func TestPortAllocation(t *testing.T) {
p := New()
defer reset()
ip := net.ParseIP("192.168.0.1")
ip2 := net.ParseIP("192.168.0.2")
if port, err := p.RequestPort(ip, "tcp", 80); err != nil {
if port, err := RequestPort(ip, "tcp", 80); err != nil {
t.Fatal(err)
} else if port != 80 {
t.Fatalf("Acquire(80) should return 80, not %d", port)
}
port, err := p.RequestPort(ip, "tcp", 0)
port, err := RequestPort(ip, "tcp", 0)
if err != nil {
t.Fatal(err)
}
@@ -179,41 +190,41 @@ func TestPortAllocation(t *testing.T) {
t.Fatalf("Acquire(0) should return a non-zero port")
}
if _, err := p.RequestPort(ip, "tcp", port); err == nil {
if _, err := RequestPort(ip, "tcp", port); err == nil {
t.Fatalf("Acquiring a port already in use should return an error")
}
if newPort, err := p.RequestPort(ip, "tcp", 0); err != nil {
if newPort, err := RequestPort(ip, "tcp", 0); err != nil {
t.Fatal(err)
} else if newPort == port {
t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
}
if _, err := p.RequestPort(ip, "tcp", 80); err == nil {
if _, err := RequestPort(ip, "tcp", 80); err == nil {
t.Fatalf("Acquiring a port already in use should return an error")
}
if _, err := p.RequestPort(ip2, "tcp", 80); err != nil {
if _, err := RequestPort(ip2, "tcp", 80); err != nil {
t.Fatalf("It should be possible to allocate the same port on a different interface")
}
if _, err := p.RequestPort(ip2, "tcp", 80); err == nil {
if _, err := RequestPort(ip2, "tcp", 80); err == nil {
t.Fatalf("Acquiring a port already in use should return an error")
}
if err := p.ReleasePort(ip, "tcp", 80); err != nil {
if err := ReleasePort(ip, "tcp", 80); err != nil {
t.Fatal(err)
}
if _, err := p.RequestPort(ip, "tcp", 80); err != nil {
if _, err := RequestPort(ip, "tcp", 80); err != nil {
t.Fatal(err)
}
port, err = p.RequestPort(ip, "tcp", 0)
port, err = RequestPort(ip, "tcp", 0)
if err != nil {
t.Fatal(err)
}
port2, err := p.RequestPort(ip, "tcp", port+1)
port2, err := RequestPort(ip, "tcp", port+1)
if err != nil {
t.Fatal(err)
}
port3, err := p.RequestPort(ip, "tcp", 0)
port3, err := RequestPort(ip, "tcp", 0)
if err != nil {
t.Fatal(err)
}
@@ -223,17 +234,17 @@ func TestPortAllocation(t *testing.T) {
}
func TestNoDuplicateBPR(t *testing.T) {
p := New()
defer reset()
if port, err := p.RequestPort(defaultIP, "tcp", p.Begin); err != nil {
if port, err := RequestPort(defaultIP, "tcp", beginPortRange); err != nil {
t.Fatal(err)
} else if port != p.Begin {
t.Fatalf("Expected port %d got %d", p.Begin, port)
} else if port != beginPortRange {
t.Fatalf("Expected port %d got %d", beginPortRange, port)
}
if port, err := p.RequestPort(defaultIP, "tcp", 0); err != nil {
if port, err := RequestPort(defaultIP, "tcp", 0); err != nil {
t.Fatal(err)
} else if port == p.Begin {
} else if port == beginPortRange {
t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
}
}
+33 -46
View File
@@ -18,7 +18,15 @@ type mapping struct {
container net.Addr
}
var NewProxy = NewProxyCommand
var (
chain *iptables.Chain
lock sync.Mutex
// udp:ip:port
currentMappings = make(map[string]*mapping)
NewProxy = NewProxyCommand
)
var (
ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
@@ -26,34 +34,13 @@ var (
ErrPortNotMapped = errors.New("port is not mapped")
)
type PortMapper struct {
chain *iptables.Chain
// udp:ip:port
currentMappings map[string]*mapping
lock sync.Mutex
Allocator *portallocator.PortAllocator
func SetIptablesChain(c *iptables.Chain) {
chain = c
}
func New() *PortMapper {
return NewWithPortAllocator(portallocator.New())
}
func NewWithPortAllocator(allocator *portallocator.PortAllocator) *PortMapper {
return &PortMapper{
currentMappings: make(map[string]*mapping),
Allocator: allocator,
}
}
func (pm *PortMapper) SetIptablesChain(c *iptables.Chain) {
pm.chain = c
}
func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) {
pm.lock.Lock()
defer pm.lock.Unlock()
func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) {
lock.Lock()
defer lock.Unlock()
var (
m *mapping
@@ -65,7 +52,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host
switch container.(type) {
case *net.TCPAddr:
proto = "tcp"
if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil {
if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
return nil, err
}
@@ -78,7 +65,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host
proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port)
case *net.UDPAddr:
proto = "udp"
if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil {
if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
return nil, err
}
@@ -96,25 +83,25 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host
// release the allocated port on any further error during return.
defer func() {
if err != nil {
pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort)
portallocator.ReleasePort(hostIP, proto, allocatedHostPort)
}
}()
key := getKey(m.host)
if _, exists := pm.currentMappings[key]; exists {
if _, exists := currentMappings[key]; exists {
return nil, ErrPortMappedForIP
}
containerIP, containerPort := getIPAndPort(m.container)
if err := pm.forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
if err := forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
return nil, err
}
cleanup := func() error {
// need to undo the iptables rules before we return
proxy.Stop()
pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
if err := portallocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
return err
}
@@ -128,35 +115,35 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host
return nil, err
}
m.userlandProxy = proxy
pm.currentMappings[key] = m
currentMappings[key] = m
return m.host, nil
}
func (pm *PortMapper) Unmap(host net.Addr) error {
pm.lock.Lock()
defer pm.lock.Unlock()
func Unmap(host net.Addr) error {
lock.Lock()
defer lock.Unlock()
key := getKey(host)
data, exists := pm.currentMappings[key]
data, exists := currentMappings[key]
if !exists {
return ErrPortNotMapped
}
data.userlandProxy.Stop()
delete(pm.currentMappings, key)
delete(currentMappings, key)
containerIP, containerPort := getIPAndPort(data.container)
hostIP, hostPort := getIPAndPort(data.host)
if err := pm.forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
log.Errorf("Error on iptables delete: %s", err)
}
switch a := host.(type) {
case *net.TCPAddr:
return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port)
return portallocator.ReleasePort(a.IP, "tcp", a.Port)
case *net.UDPAddr:
return pm.Allocator.ReleasePort(a.IP, "udp", a.Port)
return portallocator.ReleasePort(a.IP, "udp", a.Port)
}
return nil
}
@@ -181,9 +168,9 @@ func getIPAndPort(a net.Addr) (net.IP, int) {
return nil, 0
}
func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
if pm.chain == nil {
func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
if chain == nil {
return nil
}
return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
}
+22 -18
View File
@@ -4,6 +4,7 @@ import (
"net"
"testing"
"github.com/docker/docker/daemon/networkdriver/portallocator"
"github.com/docker/docker/pkg/iptables"
)
@@ -12,26 +13,30 @@ func init() {
NewProxy = NewMockProxyCommand
}
func reset() {
chain = nil
currentMappings = make(map[string]*mapping)
}
func TestSetIptablesChain(t *testing.T) {
pm := New()
defer reset()
c := &iptables.Chain{
Name: "TEST",
Bridge: "192.168.1.1",
}
if pm.chain != nil {
if chain != nil {
t.Fatal("chain should be nil at init")
}
pm.SetIptablesChain(c)
if pm.chain == nil {
SetIptablesChain(c)
if chain == nil {
t.Fatal("chain should not be nil after set")
}
}
func TestMapPorts(t *testing.T) {
pm := New()
dstIp1 := net.ParseIP("192.168.0.1")
dstIp2 := net.ParseIP("192.168.0.2")
dstAddr1 := &net.TCPAddr{IP: dstIp1, Port: 80}
@@ -44,34 +49,34 @@ func TestMapPorts(t *testing.T) {
return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String())
}
if host, err := pm.Map(srcAddr1, dstIp1, 80); err != nil {
if host, err := Map(srcAddr1, dstIp1, 80); err != nil {
t.Fatalf("Failed to allocate port: %s", err)
} else if !addrEqual(dstAddr1, host) {
t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s",
dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network())
}
if _, err := pm.Map(srcAddr1, dstIp1, 80); err == nil {
if _, err := Map(srcAddr1, dstIp1, 80); err == nil {
t.Fatalf("Port is in use - mapping should have failed")
}
if _, err := pm.Map(srcAddr2, dstIp1, 80); err == nil {
if _, err := Map(srcAddr2, dstIp1, 80); err == nil {
t.Fatalf("Port is in use - mapping should have failed")
}
if _, err := pm.Map(srcAddr2, dstIp2, 80); err != nil {
if _, err := Map(srcAddr2, dstIp2, 80); err != nil {
t.Fatalf("Failed to allocate port: %s", err)
}
if pm.Unmap(dstAddr1) != nil {
if Unmap(dstAddr1) != nil {
t.Fatalf("Failed to release port")
}
if pm.Unmap(dstAddr2) != nil {
if Unmap(dstAddr2) != nil {
t.Fatalf("Failed to release port")
}
if pm.Unmap(dstAddr2) == nil {
if Unmap(dstAddr2) == nil {
t.Fatalf("Port already released, but no error reported")
}
}
@@ -110,7 +115,6 @@ func TestGetUDPIPAndPort(t *testing.T) {
}
func TestMapAllPortsSingleInterface(t *testing.T) {
pm := New()
dstIp1 := net.ParseIP("0.0.0.0")
srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
@@ -120,26 +124,26 @@ func TestMapAllPortsSingleInterface(t *testing.T) {
defer func() {
for _, val := range hosts {
pm.Unmap(val)
Unmap(val)
}
}()
for i := 0; i < 10; i++ {
start, end := pm.Allocator.Begin, pm.Allocator.End
start, end := portallocator.PortRange()
for i := start; i < end; i++ {
if host, err = pm.Map(srcAddr1, dstIp1, 0); err != nil {
if host, err = Map(srcAddr1, dstIp1, 0); err != nil {
t.Fatal(err)
}
hosts = append(hosts, host)
}
if _, err := pm.Map(srcAddr1, dstIp1, start); err == nil {
if _, err := Map(srcAddr1, dstIp1, start); err == nil {
t.Fatalf("Port %d should be bound but is not", start)
}
for _, val := range hosts {
if err := pm.Unmap(val); err != nil {
if err := Unmap(val); err != nil {
t.Fatal(err)
}
}
+10 -47
View File
@@ -11,18 +11,16 @@ import (
type State struct {
sync.Mutex
Running bool
Paused bool
Restarting bool
OOMKilled bool
removalInProgress bool // Not need for this to be persistent on disk.
Dead bool
Pid int
ExitCode int
Error string // contains last known error when starting the container
StartedAt time.Time
FinishedAt time.Time
waitChan chan struct{}
Running bool
Paused bool
Restarting bool
OOMKilled bool
Pid int
ExitCode int
Error string // contains last known error when starting the container
StartedAt time.Time
FinishedAt time.Time
waitChan chan struct{}
}
func NewState() *State {
@@ -44,14 +42,6 @@ func (s *State) String() string {
return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
}
if s.removalInProgress {
return "Removal In Progress"
}
if s.Dead {
return "Dead"
}
if s.FinishedAt.IsZero() {
return ""
}
@@ -70,11 +60,6 @@ func (s *State) StateString() string {
}
return "running"
}
if s.Dead {
return "dead"
}
return "exited"
}
@@ -232,25 +217,3 @@ func (s *State) IsPaused() bool {
s.Unlock()
return res
}
func (s *State) SetRemovalInProgress() error {
s.Lock()
defer s.Unlock()
if s.removalInProgress {
return fmt.Errorf("Status is already RemovalInProgress")
}
s.removalInProgress = true
return nil
}
func (s *State) ResetRemovalInProgress() {
s.Lock()
s.removalInProgress = false
s.Unlock()
}
func (s *State) SetDead() {
s.Lock()
s.Dead = true
s.Unlock()
}
+3 -6
View File
@@ -189,13 +189,10 @@ func (container *Container) parseVolumeMountConfig() (map[string]*Mount, error)
if _, exists := container.Volumes[path]; exists {
continue
}
realPath, err := container.getResourcePath(path)
if err != nil {
return nil, fmt.Errorf("failed to evaluate the absolute path of symlink")
}
if stat, err := os.Stat(realPath); err == nil {
if stat, err := os.Stat(filepath.Join(container.basefs, path)); err == nil {
if !stat.IsDir() {
return nil, fmt.Errorf("file exists at %s, can't create volume there", realPath)
return nil, fmt.Errorf("file exists at %s, can't create volume there")
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 22 KiB

+48 -94
View File
@@ -8,17 +8,12 @@ page_keywords: Docker, Docker documentation, Windows, requirements, virtualbox,
> Your processor needs to support hardware virtualization.
The Docker Engine uses Linux-specific kernel features, so to run it on Windows
we need to use a lightweight virtual machine (VM). You use the **Windows Docker
Client** to control the virtualized Docker Engine to build, run, and manage
Docker containers.
we need to use a lightweight virtual machine (vm). You use the Windows Docker client to
control the virtualized Docker Engine to build, run, and manage Docker containers.
To make this process easier, we've designed a helper application called
[Boot2Docker](https://github.com/boot2docker/boot2docker) creates a Linux virtual
machine on Windows to run Docker on a Linux operating system.
Although you will be using Windows Docker client, the docker engine hosting the
containers will still be running on Linux. Until the Docker engine for Windows
is developed, you can launch only Linux containers from your Windows machine.
[Boot2Docker](https://github.com/boot2docker/boot2docker) that installs the
virtual machine and runs the Docker daemon.
## Demonstration
@@ -26,77 +21,18 @@ is developed, you can launch only Linux containers from your Windows machine.
## Installation
1. Download the latest release of the
[Docker for Windows Installer](https://github.com/boot2docker/windows-installer/releases/latest).
2. Run the installer, which will install Docker Client or Windows, VirtualBox,
Git for Windows (MSYS-git), the boot2docker Linux ISO, and the Boot2Docker
management tool.
1. Download the latest release of the [Docker for Windows Installer](https://github.com/boot2docker/windows-installer/releases/latest)
2. Run the installer, which will install VirtualBox, MSYS-git, the boot2docker Linux ISO,
and the Boot2Docker management tool.
![](/installation/images/windows-installer.png)
3. Run the **Boot2Docker Start** shortcut from your Desktop or Program Files
Boot2Docker for Windows”.
3. Run the `Boot2Docker Start` shell script from your Desktop or Program Files > Boot2Docker for Windows.
The Start script will ask you to enter an ssh key passphrase - the simplest
(but least secure) is to just hit [Enter].
4. The **Boot2Docker Start** will start a unix shell already configured to manage
Docker running inside the virtual machine. Run `docker version` to see
if it is working correctly:
![](/installation/images/windows-boot2docker-start.png)
![](/installation/images/windows-boot2docker-start.png)
## Running Docker
{{ include "no-remote-sudo.md" }}
**Boot2Docker Start** will automatically start a shell with environment variables
correctly set so you can start using Docker right away:
Let's try the `hello-world` example image. Run
$ docker run hello-world
This should download the very small `hello-world` image and print a
`Hello from Docker.` message.
## Using docker from Windows Command Line Prompt (cmd.exe)
Launch a Windows Command Line Prompt (cmd.exe).
Boot2Docker command requires `ssh.exe` to be in the PATH, therefore we need to
include `bin` folder of the Git installation (which has ssh.exe) to the `%PATH%`
environment variable by running:
set PATH=%PATH%;"c:\Program Files (x86)\Git\bin"
and then we can run the `boot2docker start` command to start the Boot2Docker VM.
(Run `boot2docker init` command if you get an error saying machine does not
exist.) Then copy the instructions for cmd.exe to set the environment variables
to your console window and you are ready to run docker commands such as
`docker ps`:
![](/installation/images/windows-boot2docker-cmd.png)
## Using docker from PowerShell
Launch a PowerShell window, then you need to add `ssh.exe` to your PATH:
$Env:Path = "${Env:Path};c:\Program Files (x86)\Git\bin"
and after running `boot2docker start` command it will print PowerShell commands
to set the environment variables to connect Docker running inside VM. Run these
commands and you are ready to run docker commands such as `docker ps`:
![](/installation/images/windows-boot2docker-powershell.png)
> NOTE: You can alternatively run `boot2docker shellinit | Invoke-Expression`
> command to set the environment variables instead of copying and pasting on
> PowerShell.
# Further Details
The Boot2Docker management tool provides several commands:
$ boot2docker
Usage: boot2docker.exe [<options>] {help|init|up|ssh|save|down|poweroff|reset|restart|config|status|info|ip|shellinit|delete|download|upgrade|version} [<args>]
The `Boot2Docker Start` script will connect you to a shell session in the virtual
machine. If needed, it will initialize a new VM and start it.
## Upgrading
@@ -111,13 +47,46 @@ The Boot2Docker management tool provides several commands:
boot2docker download
boot2docker start
## Running Docker
{{ include "no-remote-sudo.md" }}
Boot2Docker will log you in automatically so you can start using Docker right away.
Let's try the `hello-world` example image. Run
$ docker run hello-world
This should download the very small `hello-world` image and print a `Hello from Docker.` message.
## Login with PUTTY instead of using the CMD
Boot2Docker generates and uses the public/private key pair in your `%HOMEPATH%\.ssh`
directory so to log in you need to use the private key from this same directory.
The private key needs to be converted into the format PuTTY uses.
You can do this with
[puttygen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html):
- Open `puttygen.exe` and load ("File"->"Load" menu) the private key from
`%HOMEPATH%\.ssh\id_boot2docker`
- then click: "Save Private Key".
- Then use the saved file to login with PuTTY using `docker@127.0.0.1:2022`.
# Further Details
The Boot2Docker management tool provides several commands:
$ ./boot2docker
Usage: ./boot2docker [<options>] {help|init|up|ssh|save|down|poweroff|reset|restart|config|status|info|ip|delete|download|version} [<args>]
## Container port redirection
If you are curious, the username for the boot2docker default user is `docker`
and the password is `tcuser`.
If you are curious, the username for the boot2docker default user is `docker` and the password is `tcuser`.
The latest version of `boot2docker` sets up a host only network adaptor which
provides access to the container's ports.
The latest version of `boot2docker` sets up a host only network adaptor which provides access to the container's ports.
If you run a container with an exposed port:
@@ -132,18 +101,3 @@ Typically, it is 192.168.59.103, but it could get changed by Virtualbox's DHCP
implementation.
For further information or to report issues, please see the [Boot2Docker site](http://boot2docker.io)
## Login with PUTTY instead of using the CMD
Boot2Docker generates and uses the public/private key pair in your `%USERPROFILE%\.ssh`
directory so to log in you need to use the private key from this same directory.
The private key needs to be converted into the format PuTTY uses.
You can do this with
[puttygen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html):
- Open `puttygen.exe` and load ("File"->"Load" menu) the private key from
`%USERPROFILE%\.ssh\id_boot2docker`
- then click: "Save Private Key".
- Then use the saved file to login with PuTTY using `docker@127.0.0.1:2022`.
+2 -8
View File
@@ -280,14 +280,8 @@ The cache for `RUN` instructions can be invalidated by `ADD` instructions. See
- [Issue 783](https://github.com/docker/docker/issues/783) is about file
permissions problems that can occur when using the AUFS file system. You
might notice it during an attempt to `rm` a file, for example.
For systems that have recent aufs version (i.e., `dirperm1` mount option can
be set), docker will attempt to fix the issue automatically by mounting
the layers with `dirperm1` option. More details on `dirperm1` option can be
found at [`aufs` man page](http://aufs.sourceforge.net/aufs3/man.html)
If your system doesnt have support for `dirperm1`, the issue describes a workaround.
might notice it during an attempt to `rm` a file, for example. The issue
describes a workaround.
## CMD
+1 -8
View File
@@ -57,14 +57,7 @@ impact on users. This list will be updated as issues are resolved.
* **Unexpected File Permissions in Containers**
An idiosyncrasy in AUFS prevents permissions from propagating predictably
between upper and lower layers. This can cause issues with accessing private
keys, database instances, etc.
For systems that have recent aufs version (i.e., `dirperm1` mount option can
be set), docker will attempt to fix the issue automatically by mounting
the layers with `dirperm1` option. More details on `dirperm1` option can be
found at [`aufs` man page](http://aufs.sourceforge.net/aufs3/man.html)
For complete information and workarounds see
keys, database instances, etc. For complete information and workarounds see
[Github Issue 783](https://github.com/docker/docker/issues/783).
* **Docker Hub incompatible with Safari 8**
+6 -18
View File
@@ -1,8 +1,6 @@
package graph
import (
"compress/gzip"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
@@ -15,7 +13,6 @@ import (
"time"
log "github.com/Sirupsen/logrus"
"github.com/docker/distribution/digest"
"github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/image"
@@ -244,27 +241,18 @@ func (graph *Graph) newTempFile() (*os.File, error) {
return ioutil.TempFile(tmp, "")
}
func bufferToFile(f *os.File, src io.Reader) (int64, digest.Digest, error) {
var (
h = sha256.New()
w = gzip.NewWriter(io.MultiWriter(f, h))
)
_, err := io.Copy(w, src)
w.Close()
func bufferToFile(f *os.File, src io.Reader) (int64, error) {
n, err := io.Copy(f, src)
if err != nil {
return 0, "", err
return n, err
}
if err = f.Sync(); err != nil {
return 0, "", err
}
n, err := f.Seek(0, os.SEEK_CUR)
if err != nil {
return 0, "", err
return n, err
}
if _, err := f.Seek(0, 0); err != nil {
return 0, "", err
return n, err
}
return n, digest.NewDigest("sha256", h), nil
return n, nil
}
// setupInitLayer populates a directory with mountpoints suitable
+8 -1
View File
@@ -1,6 +1,7 @@
package graph
import (
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
@@ -12,6 +13,7 @@ import (
"sync"
log "github.com/Sirupsen/logrus"
"github.com/docker/distribution/digest"
"github.com/docker/docker/engine"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/common"
@@ -462,7 +464,12 @@ func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *
os.Remove(tf.Name())
}()
size, dgst, err := bufferToFile(tf, arch)
h := sha256.New()
size, err := bufferToFile(tf, io.TeeReader(arch, h))
if err != nil {
return "", err
}
dgst := digest.NewDigest("sha256", h)
// Send the layer
log.Debugf("rendered layer for %s of [%d] size", img.ID, size)
+3
View File
@@ -265,6 +265,9 @@ main() {
bundle $SCRIPTDIR/make/$bundle
echo
done
# if we get all the way through successfully, let's delete our autogenerated code!
rm -r autogen
}
main "$@"
+1 -1
View File
@@ -75,7 +75,7 @@ rm -rf src/github.com/docker/distribution
mkdir -p src/github.com/docker/distribution
mv tmp-digest src/github.com/docker/distribution/digest
clone git github.com/docker/libcontainer d00b8369852285d6a830a8d3b966608b2ed89705
clone git github.com/docker/libcontainer fd0087d3acdc4c5865de1829d4accee5e3ebb658
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
rm -rf src/github.com/docker/libcontainer/vendor
eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli' | grep -v 'github.com/Sirupsen/logrus')"
-1
View File
@@ -1958,7 +1958,6 @@ func TestBuildCancelationKillsSleep(t *testing.T) {
name := "testbuildcancelation"
defer deleteImages(name)
defer deleteAllContainers()
// (Note: one year, will never finish)
ctx, err := fakeContext("FROM busybox\nRUN sleep 31536000", nil)
-34
View File
@@ -413,7 +413,6 @@ func TestRunLinkToContainerNetMode(t *testing.T) {
}
func TestRunModeNetContainerHostname(t *testing.T) {
testRequires(t, ExecSupport)
defer deleteAllContainers()
cmd := exec.Command(dockerBinary, "run", "-i", "-d", "--name", "parent", "busybox", "top")
out, _, err := runCommandWithOutput(cmd)
@@ -476,39 +475,6 @@ func TestRunWithVolumesFromExited(t *testing.T) {
logDone("run - regression test for #4979 - volumes-from on exited container")
}
// Volume path is a symlink which also exists on the host, and the host side is a file not a dir
// But the volume call is just a normal volume, not a bind mount
func TestRunCreateVolumesInSymlinkDir(t *testing.T) {
testRequires(t, SameHostDaemon)
testRequires(t, NativeExecDriver)
defer deleteAllContainers()
name := "test-volume-symlink"
dir, err := ioutil.TempDir("", name)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
f, err := os.OpenFile(filepath.Join(dir, "test"), os.O_CREATE, 0700)
if err != nil {
t.Fatal(err)
}
f.Close()
dockerFile := fmt.Sprintf("FROM busybox\nRUN mkdir -p %s\nRUN ln -s %s /test", dir, dir)
if _, err := buildImage(name, dockerFile, false); err != nil {
t.Fatal(err)
}
defer deleteImages(name)
if out, _, err := dockerCmd(t, "run", "-v", "/test/test", name); err != nil {
t.Fatal(err, out)
}
logDone("run - create volume in symlink directory")
}
// Regression test for #4830
func TestRunWithRelativePath(t *testing.T) {
defer deleteAllContainers()
+1 -3
View File
@@ -17,13 +17,11 @@ var (
privateRegistryURL = "127.0.0.1:5000"
dockerBasePath = "/var/lib/docker"
execDriverPath = dockerBasePath + "/execdriver/native"
volumesConfigPath = dockerBasePath + "/volumes"
volumesStoragePath = dockerBasePath + "/vfs/dir"
containerStoragePath = dockerBasePath + "/containers"
runtimePath = "/var/run/docker"
execDriverPath = runtimePath + "/execdriver/native"
workingDirectory string
)
+1 -1
View File
@@ -51,7 +51,7 @@ func (w *BroadcastWriter) Write(p []byte) (n int, err error) {
for {
line, err := w.buf.ReadString('\n')
if err != nil {
w.buf.WriteString(line)
w.buf.Write([]byte(line))
break
}
for stream, writers := range w.streams {
-1
View File
@@ -64,7 +64,6 @@ func (config *Config) Read(p []byte) (n int, err error) {
return read, err
}
func (config *Config) Close() error {
config.Current = config.Size
config.Out.Write(config.Formatter.FormatProg(config.ID, config.Action, &JSONProg{Current: config.Current, Total: config.Size}))
return config.In.Close()
}
+2 -6
View File
@@ -241,6 +241,8 @@ func StdStreams() (stdIn io.ReadCloser, stdOut io.Writer, stdErr io.Writer) {
}
handler.screenBufferInfo = screenBufferInfo
// Set the window size
SetWindowSize(stdoutHandle, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HEIGHT)
buffer = make([]CHAR_INFO, screenBufferInfo.MaximumWindowSize.X*screenBufferInfo.MaximumWindowSize.Y)
stdOut = &terminalWriter{
@@ -281,12 +283,6 @@ func GetHandleInfo(in interface{}) (uintptr, bool) {
isTerminalIn = IsTerminal(inFd)
}
}
if tr, ok := in.(*terminalWriter); ok {
if file, ok := tr.wrappedWriter.(*os.File); ok {
inFd = file.Fd()
isTerminalIn = IsTerminal(inFd)
}
}
return inFd, isTerminalIn
}
+44 -7
View File
@@ -1,6 +1,7 @@
package registry
import (
"crypto/tls"
"encoding/base64"
"encoding/json"
"errors"
@@ -70,7 +71,21 @@ func (auth *RequestAuthorization) getToken() (string, error) {
return auth.tokenCache, nil
}
client := auth.registryEndpoint.HTTPClient()
tlsConfig := tls.Config{
MinVersion: tls.VersionTLS10,
}
if !auth.registryEndpoint.IsSecure {
tlsConfig.InsecureSkipVerify = true
}
client := &http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tlsConfig,
},
CheckRedirect: AddRequiredHeadersToRedirectedRequests,
}
factory := HTTPRequestFactory(nil)
for _, challenge := range auth.registryEndpoint.AuthChallenges {
@@ -237,10 +252,16 @@ func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HT
// loginV1 tries to register/login to the v1 registry server.
func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
var (
status string
reqBody []byte
err error
client = registryEndpoint.HTTPClient()
status string
reqBody []byte
err error
client = &http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
Proxy: http.ProxyFromEnvironment,
},
CheckRedirect: AddRequiredHeadersToRedirectedRequests,
}
reqStatusCode = 0
serverAddress = authConfig.ServerAddress
)
@@ -264,7 +285,7 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.
// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
b := strings.NewReader(string(jsonBody))
req1, err := client.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
if err != nil {
return "", fmt.Errorf("Server Error: %s", err)
}
@@ -350,10 +371,26 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.
// is to be determined.
func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
log.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
tlsConfig := tls.Config{
MinVersion: tls.VersionTLS10,
}
if !registryEndpoint.IsSecure {
tlsConfig.InsecureSkipVerify = true
}
client := &http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tlsConfig,
},
CheckRedirect: AddRequiredHeadersToRedirectedRequests,
}
var (
err error
allErrors []error
client = registryEndpoint.HTTPClient()
)
for _, challenge := range registryEndpoint.AuthChallenges {
-18
View File
@@ -1,7 +1,6 @@
package registry
import (
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
@@ -263,20 +262,3 @@ HeaderLoop:
return RegistryInfo{}, fmt.Errorf("v2 registry endpoint returned status %d: %q", resp.StatusCode, http.StatusText(resp.StatusCode))
}
func (e *Endpoint) HTTPClient() *http.Client {
tlsConfig := tls.Config{
MinVersion: tls.VersionTLS10,
}
if !e.IsSecure {
tlsConfig.InsecureSkipVerify = true
}
return &http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tlsConfig,
},
CheckRedirect: AddRequiredHeadersToRedirectedRequests,
}
}
-4
View File
@@ -511,10 +511,6 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
}
defer res.Body.Close()
if res.StatusCode == 401 {
return nil, errLoginRequired
}
var tokens, endpoints []string
if !validate {
if res.StatusCode != 200 && res.StatusCode != 201 {
+1
View File
@@ -33,6 +33,7 @@ type Config struct {
NetworkDisabled bool
MacAddress string
OnBuild []string
SecurityOpt []string
Labels map[string]string
}
+4 -4
View File
@@ -67,12 +67,12 @@ func generateProfile(out io.Writer) error {
data := &data{
Name: "docker-default",
}
if tunablesExists() {
if tuntablesExists() {
data.Imports = append(data.Imports, "#include <tunables/global>")
} else {
data.Imports = append(data.Imports, "@{PROC}=/proc/")
}
if abstractionsExists() {
if abstrctionsEsists() {
data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
}
if err := compiled.Execute(out, data); err != nil {
@@ -82,13 +82,13 @@ func generateProfile(out io.Writer) error {
}
// check if the tunables/global exist
func tunablesExists() bool {
func tuntablesExists() bool {
_, err := os.Stat("/etc/apparmor.d/tunables/global")
return err == nil
}
// check if abstractions/base exist
func abstractionsExists() bool {
func abstrctionsEsists() bool {
_, err := os.Stat("/etc/apparmor.d/abstractions/base")
return err == nil
}
@@ -173,6 +173,9 @@ func (m *Manager) Freeze(state configs.FreezerState) error {
if err != nil {
return err
}
if !cgroups.PathExists(dir) {
return cgroups.NewNotFoundError("freezer")
}
prevState := m.Cgroups.Freezer
m.Cgroups.Freezer = state
@@ -197,6 +200,9 @@ func (m *Manager) GetPids() ([]int, error) {
if err != nil {
return nil, err
}
if !cgroups.PathExists(dir) {
return nil, cgroups.NewNotFoundError("devices")
}
return cgroups.ReadProcsFile(dir)
}
@@ -220,16 +226,16 @@ func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
}, nil
}
func (raw *data) parent(subsystem, mountpoint string) (string, error) {
func (raw *data) parent(subsystem string) (string, error) {
initPath, err := cgroups.GetInitCgroupDir(subsystem)
if err != nil {
return "", err
}
return filepath.Join(mountpoint, initPath), nil
return filepath.Join(raw.root, subsystem, initPath), nil
}
func (raw *data) path(subsystem string) (string, error) {
mnt, err := cgroups.FindCgroupMountpoint(subsystem)
_, err := cgroups.FindCgroupMountpoint(subsystem)
// If we didn't mount the subsystem, there is no point we make the path.
if err != nil {
return "", err
@@ -240,7 +246,7 @@ func (raw *data) path(subsystem string) (string, error) {
return filepath.Join(raw.root, subsystem, raw.cgroup), nil
}
parent, err := raw.parent(subsystem, mnt)
parent, err := raw.parent(subsystem)
if err != nil {
return "", err
}
@@ -1,7 +1,6 @@
package fs
import (
"fmt"
"strings"
"time"
@@ -42,10 +41,6 @@ func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error {
}
time.Sleep(1 * time.Millisecond)
}
case configs.Undefined:
return nil
default:
return fmt.Errorf("Invalid argument '%s' to freezer.state", string(cgroup.Freezer))
}
return nil
@@ -1,45 +0,0 @@
package fs
import (
"testing"
"github.com/docker/libcontainer/configs"
)
func TestFreezerSetState(t *testing.T) {
helper := NewCgroupTestUtil("freezer", t)
defer helper.cleanup()
helper.writeFileContents(map[string]string{
"freezer.state": string(configs.Frozen),
})
helper.CgroupData.c.Freezer = configs.Thawed
freezer := &FreezerGroup{}
if err := freezer.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
t.Fatal(err)
}
value, err := getCgroupParamString(helper.CgroupPath, "freezer.state")
if err != nil {
t.Fatalf("Failed to parse freezer.state - %s", err)
}
if value != string(configs.Thawed) {
t.Fatal("Got the wrong value, set freezer.state failed.")
}
}
func TestFreezerSetInvalidState(t *testing.T) {
helper := NewCgroupTestUtil("freezer", t)
defer helper.cleanup()
const (
invalidArg configs.FreezerState = "Invalid"
)
helper.CgroupData.c.Freezer = invalidArg
freezer := &FreezerGroup{}
if err := freezer.Set(helper.CgroupPath, helper.CgroupData.c); err == nil {
t.Fatal("Failed to return invalid argument error")
}
}
@@ -43,10 +43,6 @@ var subsystems = map[string]subsystem{
"freezer": &fs.FreezerGroup{},
}
const (
testScopeWait = 4
)
var (
connLock sync.Mutex
theConn *systemd.Conn
@@ -90,41 +86,16 @@ func UseSystemd() bool {
}
}
// Ensure the scope name we use doesn't exist. Use the Pid to
// avoid collisions between multiple libcontainer users on a
// single host.
scope := fmt.Sprintf("libcontainer-%d-systemd-test-default-dependencies.scope", os.Getpid())
testScopeExists := true
for i := 0; i <= testScopeWait; i++ {
if _, err := theConn.StopUnit(scope, "replace"); err != nil {
if dbusError, ok := err.(dbus.Error); ok {
if strings.Contains(dbusError.Name, "org.freedesktop.systemd1.NoSuchUnit") {
testScopeExists = false
break
}
}
}
time.Sleep(time.Millisecond)
}
// Bail out if we can't kill this scope without testing for DefaultDependencies
if testScopeExists {
return hasStartTransientUnit
}
// Assume StartTransientUnit on a scope allows DefaultDependencies
hasTransientDefaultDependencies = true
ddf := newProp("DefaultDependencies", false)
if _, err := theConn.StartTransientUnit(scope, "replace", ddf); err != nil {
if _, err := theConn.StartTransientUnit("docker-systemd-test-default-dependencies.scope", "replace", ddf); err != nil {
if dbusError, ok := err.(dbus.Error); ok {
if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") {
hasTransientDefaultDependencies = false
}
}
}
// Not critical because of the stop unit logic above.
theConn.StopUnit(scope, "replace")
}
return hasStartTransientUnit
}
@@ -218,7 +189,16 @@ func (m *Manager) Apply(pid int) error {
}
paths := make(map[string]string)
for sysname := range subsystems {
for _, sysname := range []string{
"devices",
"memory",
"cpu",
"cpuset",
"cpuacct",
"blkio",
"perf_event",
"freezer",
} {
subsystemPath, err := getSubsystemPath(m.Cgroups, sysname)
if err != nil {
// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
@@ -140,9 +140,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
cmd.ExtraFiles = []*os.File{childPipe}
// NOTE: when running a container with no PID namespace and the parent process spawning the container is
// PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason
// even with the parent still running.
cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
if c.config.ParentDeathSignal > 0 {
cmd.SysProcAttr.Pdeathsig = syscall.Signal(c.config.ParentDeathSignal)
}
@@ -195,13 +193,12 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
return &initConfig{
Config: c.config,
Args: process.Args,
Env: process.Env,
User: process.User,
Cwd: process.Cwd,
Console: process.consolePath,
Capabilities: process.Capabilities,
Config: c.config,
Args: process.Args,
Env: process.Env,
User: process.User,
Cwd: process.Cwd,
Console: process.consolePath,
}
}
+10 -17
View File
@@ -40,14 +40,13 @@ type network struct {
// initConfig is used for transferring parameters from Exec() to Init()
type initConfig struct {
Args []string `json:"args"`
Env []string `json:"env"`
Cwd string `json:"cwd"`
Capabilities []string `json:"capabilities"`
User string `json:"user"`
Config *configs.Config `json:"config"`
Console string `json:"console"`
Networks []*network `json:"network"`
Args []string `json:"args"`
Env []string `json:"env"`
Cwd string `json:"cwd"`
User string `json:"user"`
Config *configs.Config `json:"config"`
Console string `json:"console"`
Networks []*network `json:"network"`
}
type initer interface {
@@ -69,8 +68,7 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) {
}, nil
case initStandard:
return &linuxStandardInit{
parentPid: syscall.Getppid(),
config: config,
config: config,
}, nil
}
return nil, fmt.Errorf("unknown init type %q", t)
@@ -93,7 +91,7 @@ func populateProcessEnvironment(env []string) error {
// finalizeNamespace drops the caps, sets the correct user
// and working dir, and closes any leaked file descriptors
// before executing the command inside the namespace
// before execing the command inside the namespace
func finalizeNamespace(config *initConfig) error {
// Ensure that all non-standard fds we may have accidentally
// inherited are marked close-on-exec so they stay out of the
@@ -101,12 +99,7 @@ func finalizeNamespace(config *initConfig) error {
if err := utils.CloseExecFrom(3); err != nil {
return err
}
capabilities := config.Config.Capabilities
if config.Capabilities != nil {
capabilities = config.Capabilities
}
w, err := newCapWhitelist(capabilities)
w, err := newCapWhitelist(config.Config.Capabilities)
if err != nil {
return err
}
@@ -4,7 +4,6 @@ import (
"bytes"
"io/ioutil"
"os"
"strconv"
"strings"
"testing"
@@ -396,90 +395,6 @@ func TestProcessEnv(t *testing.T) {
}
}
func TestProcessCaps(t *testing.T) {
if testing.Short() {
return
}
root, err := newTestRoot()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(root)
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
defer remove(rootfs)
config := newTemplateConfig(rootfs)
factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
if err != nil {
t.Fatal(err)
}
container, err := factory.Create("test", config)
if err != nil {
t.Fatal(err)
}
defer container.Destroy()
processCaps := append(config.Capabilities, "NET_ADMIN")
var stdout bytes.Buffer
pconfig := libcontainer.Process{
Args: []string{"sh", "-c", "cat /proc/self/status"},
Env: standardEnvironment,
Capabilities: processCaps,
Stdin: nil,
Stdout: &stdout,
}
err = container.Start(&pconfig)
if err != nil {
t.Fatal(err)
}
// Wait for process
waitProcess(&pconfig, t)
outputStatus := string(stdout.Bytes())
if err != nil {
t.Fatal(err)
}
lines := strings.Split(outputStatus, "\n")
effectiveCapsLine := ""
for _, l := range lines {
line := strings.TrimSpace(l)
if strings.Contains(line, "CapEff:") {
effectiveCapsLine = line
break
}
}
if effectiveCapsLine == "" {
t.Fatal("Couldn't find effective caps: ", outputStatus)
}
parts := strings.Split(effectiveCapsLine, ":")
effectiveCapsStr := strings.TrimSpace(parts[1])
effectiveCaps, err := strconv.ParseUint(effectiveCapsStr, 16, 64)
if err != nil {
t.Fatal("Could not parse effective caps", err)
}
var netAdminMask uint64
var netAdminBit uint
netAdminBit = 12 // from capability.h
netAdminMask = 1 << netAdminBit
if effectiveCaps&netAdminMask != netAdminMask {
t.Fatal("CAP_NET_ADMIN is not set as expected")
}
}
func TestFreeze(t *testing.T) {
if testing.Short() {
return
@@ -1,67 +0,0 @@
## nsinit
`nsinit` is a cli application which demonstrates the use of libcontainer.
It is able to spawn new containers or join existing containers.
### How to build?
First add the `libcontainer/vendor` into your GOPATH. It's because libcontainer
vendors all its dependencies, so it can be built predictably.
```
export GOPATH=$GOPATH:/your/path/to/libcontainer/vendor
```
Then get into the nsinit folder and get the imported file. Use `make` command
to make the nsinit binary.
```
cd libcontainer/nsinit
go get
make
```
We have finished compiling the nsinit package, but a root filesystem must be
provided for use along with a container configuration file.
Choose a proper place to run your container. For example we use `/busybox`.
```
mkdir /busybox
curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' | tar -xC /busybox
```
Then you may need to write a configuration file named `container.json` in the
`/busybox` folder. Environment, networking, and different capabilities for
the container are specified in this file. The configuration is used for each
process executed inside the container.
See the `sample_configs` folder for examples of what the container configuration
should look like.
```
cp libcontainer/sample_configs/minimal.json /busybox/container.json
cd /busybox
```
You can customize `container.json` per your needs. After that, nsinit is
ready to work.
To execute `/bin/bash` in the current directory as a container just run the
following **as root**:
```bash
nsinit exec --tty --config container.json /bin/bash
```
If you wish to spawn another process inside the container while your current
bash session is running, run the same command again to get another bash shell
(or change the command). If the original process (PID 1) dies, all other
processes spawned inside the container will be killed and the namespace will
be removed.
You can identify if a process is running in a container by looking to see if
`state.json` is in the root of the directory.
You may also specify an alternate root directory from where the `container.json`
file is read and where the `state.json` file will be saved.
-4
View File
@@ -41,10 +41,6 @@ type Process struct {
// consolePath is the path to the console allocated to the container.
consolePath string
// Capabilities specify the capabilities to keep when executing the process inside the container
// All capbilities not specified will be dropped from the processes capability mask
Capabilities []string
ops processOperations
}
+4 -13
View File
@@ -4,7 +4,6 @@ package libcontainer
import (
"encoding/json"
"errors"
"io"
"os"
"os/exec"
@@ -45,12 +44,8 @@ func (p *setnsProcess) startTime() (string, error) {
return system.GetProcessStartTime(p.pid())
}
func (p *setnsProcess) signal(sig os.Signal) error {
s, ok := sig.(syscall.Signal)
if !ok {
return errors.New("os: unsupported signal type")
}
return syscall.Kill(p.cmd.Process.Pid, s)
func (p *setnsProcess) signal(s os.Signal) error {
return p.cmd.Process.Signal(s)
}
func (p *setnsProcess) start() (err error) {
@@ -240,10 +235,6 @@ func (p *initProcess) createNetworkInterfaces() error {
return nil
}
func (p *initProcess) signal(sig os.Signal) error {
s, ok := sig.(syscall.Signal)
if !ok {
return errors.New("os: unsupported signal type")
}
return syscall.Kill(p.cmd.Process.Pid, s)
func (p *initProcess) signal(s os.Signal) error {
return p.cmd.Process.Signal(s)
}
+11 -12
View File
@@ -186,9 +186,7 @@ func reOpenDevNull(rootfs string) error {
func createDevices(config *configs.Config) error {
oldMask := syscall.Umask(0000)
for _, node := range config.Devices {
// containers running in a user namespace are not allowed to mknod
// devices so we can just bind mount it from the host.
if err := createDeviceNode(config.Rootfs, node, config.Namespaces.Contains(configs.NEWUSER)); err != nil {
if err := createDeviceNode(config.Rootfs, node); err != nil {
syscall.Umask(oldMask)
return err
}
@@ -198,13 +196,20 @@ func createDevices(config *configs.Config) error {
}
// Creates the device node in the rootfs of the container.
func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
func createDeviceNode(rootfs string, node *configs.Device) error {
dest := filepath.Join(rootfs, node.Path)
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
return err
}
if bind {
if err := mknodDevice(dest, node); err != nil {
if os.IsExist(err) {
return nil
}
if err != syscall.EPERM {
return err
}
// containers running in a user namespace are not allowed to mknod
// devices so we can just bind mount it from the host.
f, err := os.Create(dest)
if err != nil && !os.IsExist(err) {
return err
@@ -214,12 +219,6 @@ func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
}
return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
}
if err := mknodDevice(dest, node); err != nil {
if os.IsExist(err) {
return nil
}
return err
}
return nil
}
@@ -13,8 +13,7 @@ import (
)
type linuxStandardInit struct {
parentPid int
config *initConfig
config *initConfig
}
func (l *linuxStandardInit) Init() error {
@@ -86,10 +85,9 @@ func (l *linuxStandardInit) Init() error {
if err := pdeath.Restore(); err != nil {
return err
}
// compare the parent from the inital start of the init process and make sure that it did not change.
// if the parent changes that means it died and we were reparened to something else so we should
// just kill ourself and not cause problems for someone else.
if syscall.Getppid() != l.parentPid {
// Signal self if parent is already dead. Does nothing if running in a new
// PID namespace, as Getppid will always return 0.
if syscall.Getppid() == 1 {
return syscall.Kill(syscall.Getpid(), syscall.SIGKILL)
}
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
+1 -1
View File
@@ -44,6 +44,6 @@ clone git github.com/codegangsta/cli 1.1.0
clone git github.com/coreos/go-systemd v2
clone git github.com/godbus/dbus v2
clone git github.com/Sirupsen/logrus v0.6.6
clone git github.com/syndtr/gocapability 8e4cdcb
clone git github.com/syndtr/gocapability e55e583369
# intentionally not vendoring Docker itself... that'd be a circle :)
@@ -417,6 +417,10 @@ func (c *capsV3) Load() (err error) {
}
func (c *capsV3) Apply(kind CapType) (err error) {
err = initLastCap()
if err != nil {
return
}
if kind&BOUNDS == BOUNDS {
var data [2]capData
err = capget(&c.hdr, &data[0])
@@ -424,7 +428,7 @@ func (c *capsV3) Apply(kind CapType) (err error) {
return
}
if (1<<uint(CAP_SETPCAP))&data[0].effective != 0 {
for i := Cap(0); i <= CAP_LAST_CAP; i++ {
for i := Cap(0); i <= capLastCap; i++ {
if c.Get(BOUNDING, i) {
continue
}