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
103 changed files with 1162 additions and 1969 deletions
+1 -4
View File
@@ -4,10 +4,8 @@
#### 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
+ basic build cancellation
#### Client
+ Windows Support
@@ -15,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
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() {
prevH, prevW := cli.getTtySize()
for {
time.Sleep(time.Millisecond * 250)
h, w := cli.getTtySize()
if prevW != w || prevH != h {
cli.resizeTty(id, isExec)
}
prevH = h
prevW = w
}
}()
} 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:*)
+1 -1
View File
@@ -49,7 +49,7 @@ post-start script
fi
if ! printf "%s" "$DOCKER_OPTS" | grep -qE -e '-H|--host'; then
while ! [ -e /var/run/docker.sock ]; do
initctl status $UPSTART_JOB | grep -qE "(stop|respawn)/" && exit 1
initctl status $UPSTART_JOB | grep -q "stop/" && exit 1
echo "Waiting for /var/run/docker.sock"
sleep 0.1
done
-5
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() {
@@ -1379,7 +1375,6 @@ func (container *Container) startLogging() error {
if err != nil {
return err
}
container.LogPath = pth
dl, err := jsonfilelog.New(pth)
if err != nil {
+20 -3
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"
@@ -287,8 +288,19 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool) err
if err := container.ToDisk(); err != nil {
log.Debugf("saving stopped state to disk %s", err)
}
}
info := daemon.execDriver.Info(container.ID)
if !info.IsRunning() {
log.Debugf("Container %s was supposed to be running but is not.", container.ID)
log.Debugf("Marking as stopped")
container.SetStopped(&execdriver.ExitStatus{ExitCode: -127})
if err := container.ToDisk(); err != nil {
return err
}
}
}
return nil
}
@@ -815,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 != "" {
@@ -994,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)
}
+39 -57
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,68 +157,32 @@ 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
_, oomKill := <-oomKillNotification
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
}
func killCgroupProcs(c libcontainer.Container) {
var procs []*os.Process
if err := c.Pause(); err != nil {
log.Warn(err)
}
pids, err := c.Processes()
if err != nil {
// don't care about childs if we can't get them, this is mostly because cgroup already deleted
log.Warnf("Failed to get processes from container %s: %v", c.ID(), err)
}
for _, pid := range pids {
if p, err := os.FindProcess(pid); err == nil {
procs = append(procs, p)
if err := p.Kill(); err != nil {
log.Warn(err)
}
}
}
if err := c.Resume(); err != nil {
log.Warn(err)
}
for _, p := range procs {
if _, err := p.Wait(); err != nil {
log.Warn(err)
}
}
}
func waitInPIDHost(p *libcontainer.Process, c libcontainer.Container) func() (*os.ProcessState, error) {
return func() (*os.ProcessState, error) {
pid, err := p.Pid()
@@ -228,13 +193,26 @@ func waitInPIDHost(p *libcontainer.Process, c libcontainer.Container) func() (*o
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
}
killCgroupProcs(c)
processes, err := c.Processes()
if err != nil {
return s, err
}
for _, pid := range processes {
process, err := os.FindProcess(pid)
if err != nil {
log.Errorf("Failed to kill process: %d", pid)
continue
}
process.Kill()
}
p.Wait()
return s, err
}
@@ -270,25 +248,29 @@ func (d *driver) Unpause(c *execdriver.Command) error {
func (d *driver) Terminate(c *execdriver.Command) error {
defer d.cleanContainer(c.ID)
container, err := d.factory.Load(c.ID)
if err != nil {
return err
// lets check the start time for the process
active := d.activeContainers[c.ID]
if active == nil {
return fmt.Errorf("active container for %s does not exist", c.ID)
}
defer container.Destroy()
state, err := container.State()
state, err := active.State()
if err != nil {
return err
}
pid := state.InitProcessPid
currentStartTime, err := system.GetProcessStartTime(pid)
if err != nil {
return err
}
if state.InitProcessStartTime == currentStartTime {
err = syscall.Kill(pid, 9)
syscall.Wait4(pid, nil, 0, nil)
}
return err
}
func (d *driver) Info(id string) execdriver.Info {
+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")
+12 -3
View File
@@ -17,20 +17,29 @@ type Syslog struct {
}
func New(tag string) (logger.Logger, error) {
log, err := syslog.New(syslog.LOG_DAEMON, fmt.Sprintf("%s/%s", path.Base(os.Args[0]), tag))
log, err := syslog.New(syslog.LOG_USER, path.Base(os.Args[0]))
if err != nil {
return nil, err
}
return &Syslog{
writer: log,
tag: tag,
}, nil
}
func (s *Syslog) Log(msg *logger.Message) error {
logMessage := fmt.Sprintf("%s: %s", s.tag, string(msg.Line))
if msg.Source == "stderr" {
return s.writer.Err(string(msg.Line))
if err := s.writer.Err(logMessage); err != nil {
return err
}
} else {
if err := s.writer.Info(logMessage); err != nil {
return err
}
}
return s.writer.Info(string(msg.Line))
return nil
}
func (s *Syslog) Close() error {
+3 -25
View File
@@ -7,7 +7,6 @@ import (
"io/ioutil"
"net"
"os"
"os/exec"
"strings"
"sync"
@@ -78,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
@@ -108,14 +99,6 @@ func InitDriver(job *engine.Job) engine.Status {
fixedCIDRv6 = job.Getenv("FixedCIDRv6")
)
// try to modprobe bridge first
// see gh#12177
if out, err := exec.Command("modprobe", "-va", "bridge", "nf_nat").Output(); err != nil {
log.Warnf("Running modprobe bridge nf_nat failed with message: %s, error: %v", out, err)
}
initPortMapper()
if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
defaultBindingIP = net.ParseIP(defaultIP)
}
@@ -251,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
@@ -366,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
@@ -608,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)
}
}
@@ -665,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")
}
}
+20 -61
View File
@@ -4,20 +4,11 @@
FROM docs/base:latest
MAINTAINER Sven Dowideit <SvenDowideit@docker.com> (@SvenDowideit)
# This section ensures we pull the correct version of each
# sub project
ENV COMPOSE_BRANCH 1.2.0
ENV SWARM_BRANCH v0.2.0
ENV MACHINE_BRANCH master
ENV DISTRIB_BRANCH master
# TODO: need the full repo source to get the git version info
COPY . /src
# Reset the /docs dir so we can replace the theme meta with the new repo's git info
# RUN git reset --hard
RUN git reset --hard
# Then copy the desired docs into the /docs/sources/ dir
COPY ./sources/ /docs/sources
@@ -32,77 +23,45 @@ COPY ./mkdocs.yml mkdocs.yml
COPY ./s3_website.json s3_website.json
COPY ./release.sh release.sh
# Docker Distribution
#
#ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/mkdocs.yml /docs/mkdocs-distribution.yml
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/images/notifications.png /docs/sources/registry/images/notifications.png
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/images/registry.png /docs/sources/registry/images/registry.png
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/overview.md /docs/sources/registry/overview.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/registry/overview.md
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/deploying.md /docs/sources/registry/deploying.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/registry/deploying.md
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/configuration.md /docs/sources/registry/configuration.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/registry/configuration.md
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/storagedrivers.md /docs/sources/registry/storagedrivers.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/registry/storagedrivers.md
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/notifications.md /docs/sources/registry/notifications.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/registry/notifications.md
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/spec/api.md /docs/sources/registry/spec/api.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/registry/spec/api.md
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/spec/json.md /docs/sources/registry/spec/json.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/registry/spec/json.md
ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs/spec/auth/token.md /docs/sources/registry/spec/auth/token.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/registry/spec/auth/token.md
# Docker Swarm
#ADD https://raw.githubusercontent.com/docker/swarm/${SWARM_BRANCH}/docs/mkdocs.yml /docs/mkdocs-swarm.yml
ADD https://raw.githubusercontent.com/docker/swarm/${SWARM_BRANCH}/docs/index.md /docs/sources/swarm/index.md
#ADD https://raw.githubusercontent.com/docker/swarm/master/docs/mkdocs.yml /docs/mkdocs-swarm.yml
ADD https://raw.githubusercontent.com/docker/swarm/master/docs/index.md /docs/sources/swarm/index.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/swarm/index.md
ADD https://raw.githubusercontent.com/docker/swarm/${SWARM_BRANCH}/discovery/README.md /docs/sources/swarm/discovery.md
ADD https://raw.githubusercontent.com/docker/swarm/master/discovery/README.md /docs/sources/swarm/discovery.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/swarm/discovery.md
ADD https://raw.githubusercontent.com/docker/swarm/${SWARM_BRANCH}/api/README.md /docs/sources/swarm/API.md
ADD https://raw.githubusercontent.com/docker/swarm/master/api/README.md /docs/sources/swarm/API.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/swarm/API.md
ADD https://raw.githubusercontent.com/docker/swarm/${SWARM_BRANCH}/scheduler/filter/README.md /docs/sources/swarm/scheduler/filter.md
ADD https://raw.githubusercontent.com/docker/swarm/master/scheduler/filter/README.md /docs/sources/swarm/scheduler/filter.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/swarm/scheduler/filter.md
ADD https://raw.githubusercontent.com/docker/swarm/${SWARM_BRANCH}/scheduler/strategy/README.md /docs/sources/swarm/scheduler/strategy.md
ADD https://raw.githubusercontent.com/docker/swarm/master/scheduler/strategy/README.md /docs/sources/swarm/scheduler/strategy.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/swarm/scheduler/strategy.md
# Docker Machine
#ADD https://raw.githubusercontent.com/docker/machine/${MACHINE_BRANCH}/docs/mkdocs.yml /docs/mkdocs-machine.yml
ADD https://raw.githubusercontent.com/docker/machine/${MACHINE_BRANCH}/docs/index.md /docs/sources/machine/index.md
#ADD https://raw.githubusercontent.com/docker/machine/master/docs/mkdocs.yml /docs/mkdocs-machine.yml
ADD https://raw.githubusercontent.com/docker/machine/master/docs/index.md /docs/sources/machine/index.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/machine/index.md
# Docker Compose
#ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/mkdocs.yml /docs/mkdocs-compose.yml
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/index.md /docs/sources/compose/index.md
#ADD https://raw.githubusercontent.com/docker/compose/master/docs/mkdocs.yml /docs/mkdocs-compose.yml
ADD https://raw.githubusercontent.com/docker/compose/master/docs/index.md /docs/sources/compose/index.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/index.md
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/install.md /docs/sources/compose/install.md
ADD https://raw.githubusercontent.com/docker/compose/master/docs/install.md /docs/sources/compose/install.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/install.md
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/cli.md /docs/sources/compose/cli.md
ADD https://raw.githubusercontent.com/docker/compose/master/docs/cli.md /docs/sources/compose/cli.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/cli.md
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/yml.md /docs/sources/compose/yml.md
ADD https://raw.githubusercontent.com/docker/compose/master/docs/yml.md /docs/sources/compose/yml.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/yml.md
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/env.md /docs/sources/compose/env.md
ADD https://raw.githubusercontent.com/docker/compose/master/docs/env.md /docs/sources/compose/env.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/env.md
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/completion.md /docs/sources/compose/completion.md
ADD https://raw.githubusercontent.com/docker/compose/master/docs/completion.md /docs/sources/compose/completion.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/completion.md
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/django.md /docs/sources/compose/django.md
ADD https://raw.githubusercontent.com/docker/compose/master/docs/django.md /docs/sources/compose/django.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/django.md
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/rails.md /docs/sources/compose/rails.md
ADD https://raw.githubusercontent.com/docker/compose/master/docs/rails.md /docs/sources/compose/rails.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/rails.md
ADD https://raw.githubusercontent.com/docker/compose/${COMPOSE_BRANCH}/docs/wordpress.md /docs/sources/compose/wordpress.md
ADD https://raw.githubusercontent.com/docker/compose/master/docs/wordpress.md /docs/sources/compose/wordpress.md
RUN sed -i.old '1s;^;no_version_dropdown: true;' /docs/sources/compose/wordpress.md
# Then build everything together, ready for mkdocs
RUN /docs/build.sh
RUN /docs/build.sh
+5 -11
View File
@@ -2,7 +2,7 @@
% Docker Community
% JUNE 2014
# NAME
docker-login - Register or log in to a Docker registry.
docker-login - Register or log in to a Docker registry server, if no server is specified "https://index.docker.io/v1/" is the default.
# SYNOPSIS
**docker login**
@@ -13,14 +13,9 @@ docker-login - Register or log in to a Docker registry.
[SERVER]
# DESCRIPTION
Register or log in to a Docker Registry Service located on the specified
`SERVER`. You can specify a URL or a `hostname` for the `SERVER` value. If you
do not specify a `SERVER`, the command uses Docker's public registry located at
`https://registry-1.docker.io/` by default. To get a username/password for Docker's public registry, create an account on Docker Hub.
You can log into any public or private repository for which you have
credentials. When you log in, the command stores encoded credentials in
`$HOME/.dockercfg` on Linux or `%USERPROFILE%/.dockercfg` on Windows.
Register or Login to a docker registry server, if no server is
specified "https://index.docker.io/v1/" is the default. If you want to
login to a private registry you can specify this by adding the server name.
# OPTIONS
**-e**, **--email**=""
@@ -37,7 +32,7 @@ credentials. When you log in, the command stores encoded credentials in
# EXAMPLES
## Login to a registry on your localhost
## Login to a local registry
# docker login localhost:8080
@@ -48,4 +43,3 @@ credentials. When you log in, the command stores encoded credentials in
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work.
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2015, updated by Mary Anthony for v2 <mary@docker.com>
+5 -7
View File
@@ -2,24 +2,23 @@
% Docker Community
% JUNE 2014
# NAME
docker-logout - Log out from a Docker Registry Service.
docker-logout - Log out from a Docker registry, if no server is specified "https://index.docker.io/v1/" is the default.
# SYNOPSIS
**docker logout**
[SERVER]
# DESCRIPTION
Log out of a Docker Registry Service located on the specified `SERVER`. You can
specify a URL or a `hostname` for the `SERVER` value. If you do not specify a
`SERVER`, the command attempts to log you out of Docker's public registry
located at `https://registry-1.docker.io/` by default.
Log the user out from a Docker registry, if no server is
specified "https://index.docker.io/v1/" is the default. If you want to
log out from a private registry you can specify this by adding the server name.
# OPTIONS
There are no available options.
# EXAMPLES
## Log out from a registry on your localhost
## Log out from a local registry
# docker logout localhost:8080
@@ -29,4 +28,3 @@ There are no available options.
# HISTORY
June 2014, Originally compiled by Daniel, Dao Quang Minh (daniel at nitrous dot io)
July 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2015, updated by Mary Anthony for v2 <mary@docker.com>
+4 -8
View File
@@ -2,7 +2,7 @@
% Docker Community
% JUNE 2014
# NAME
docker-pull - Pull an image or a repository from a registry
docker-pull - Pull an image or a repository from the registry
# SYNOPSIS
**docker pull**
@@ -12,12 +12,10 @@ NAME[:TAG]
# DESCRIPTION
This command pulls down an image or a repository from a registry. If
This command pulls down an image or a repository from the registry. If
there is more than one image for a repository (e.g., fedora) then all
images for that repository name are pulled down including any tags.
If you do not specify a `REGISTRY_HOST`, the command uses Docker's public
registry located at `registry-1.docker.io` by default.
It is also possible to specify a non-default registry to pull from.
# OPTIONS
**-a**, **--all-tags**=*true*|*false*
@@ -47,7 +45,7 @@ registry located at `registry-1.docker.io` by default.
fedora heisenbug 105182bb5e8b 5 days ago 372.7 MB
fedora latest 105182bb5e8b 5 days ago 372.7 MB
# Pull an image, manually specifying path to Docker's public registry and tag
# Pull an image, manually specifying path to the registry and tag
# Note that if the image is previously downloaded then the status would be
# 'Status: Image is up to date for registry.hub.docker.com/fedora:20'
@@ -69,5 +67,3 @@ April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work.
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
August 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2015, updated by John Willis <john.willis@docker.com>
April 2015, updated by Mary Anthony for v2 <mary@docker.com>
+12 -12
View File
@@ -2,18 +2,18 @@
% Docker Community
% JUNE 2014
# NAME
docker-push - Push an image or a repository to a registry
docker-push - Push an image or a repository to the registry
# SYNOPSIS
**docker push**
[**--help**]
NAME[:TAG] | [REGISTRY_HOST[:REGISTRY_PORT]/]NAME[:TAG]
NAME[:TAG]
# DESCRIPTION
This command pushes an image or a repository to a registry. If you do not
specify a `REGISTRY_HOST`, the command uses Docker's public registry located at
`registry-1.docker.io` by default.
Push an image or a repository to a registry. The default registry is the Docker
Hub located at [hub.docker.com](https://hub.docker.com/). However the
image can be pushed to another, perhaps private, registry as demonstrated in
the example below.
# OPTIONS
**--help**
@@ -28,10 +28,12 @@ and then committing it to a new image name:
# docker commit c16378f943fe rhel-httpd
Now, push the image to the registry using the image ID. In this example the
registry is on host named `registry-host` and listening on port `5000`. To do
this, tag the image with the host name or IP address, and the port of the
registry:
Now push the image to the registry using the image ID. In this example
the registry is on host named registry-host and listening on port 5000.
Default Docker commands will push to the default `hub.docker.com`
registry. Instead, push to the local registry, which is on a host called
registry-host*. To do this, tag the image with the host name or IP
address, and the port of the registry:
# docker tag rhel-httpd registry-host:5000/myadmin/rhel-httpd
# docker push registry-host:5000/myadmin/rhel-httpd
@@ -47,5 +49,3 @@ listed.
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work.
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2015, updated by Mary Anthony for v2 <mary@docker.com>
+4 -4
View File
@@ -13,9 +13,10 @@ IMAGE [IMAGE...]
# DESCRIPTION
Removes one or more images from the host node. This does not remove images from
a registry. You cannot remove an image of a running container unless you use the
**-f** option. To see all images on a host use the **docker images** command.
This will remove one or more images from the host node. This does not
remove images from a registry. You cannot remove an image of a running
container unless you use the **-f** option. To see all images on a host
use the **docker images** command.
# OPTIONS
**-f**, **--force**=*true*|*false*
@@ -39,4 +40,3 @@ Here is an example of removing and image:
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work.
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2015, updated by Mary Anthony for v2 <mary@docker.com>
+8 -9
View File
@@ -14,9 +14,10 @@ TERM
# DESCRIPTION
Search Docker Hub for an image with that matches the specified `TERM`. The table
of images returned displays the name, description (truncated by default), number
of stars awarded, whether the image is official, and whether it is automated.
Search an index for an image with that matches the term TERM. The table
of images returned displays the name, description (truncated by default),
number of stars awarded, whether the image is official, and whether it
is automated.
*Note* - Search queries will only return up to 25 results
@@ -35,9 +36,9 @@ of stars awarded, whether the image is official, and whether it is automated.
# EXAMPLES
## Search Docker Hub for ranked images
## Search the registry for ranked images
Search a registry for the term 'fedora' and only display those images
Search the registry for the term 'fedora' and only display those images
ranked 3 or higher:
$ sudo docker search -s 3 fedora
@@ -47,9 +48,9 @@ ranked 3 or higher:
mattdm/fedora-small A small Fedora image on which to build. Co... 8
goldmann/wildfly A WildFly application server running on a ... 3 [OK]
## Search Docker Hub for automated images
## Search the registry for automated images
Search Docker Hub for the term 'fedora' and only display automated images
Search the registry for the term 'fedora' and only display automated images
ranked 1 or higher:
$ sudo docker search -s 1 -t fedora
@@ -61,5 +62,3 @@ ranked 1 or higher:
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work.
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2015, updated by Mary Anthony for v2 <mary@docker.com>
+3 -8
View File
@@ -8,14 +8,11 @@ docker-tag - Tag an image into a repository
**docker tag**
[**-f**|**--force**[=*false*]]
[**--help**]
IMAGE[:TAG] [REGISTRY_HOST/][USERNAME/]NAME[:TAG]
IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
# DESCRIPTION
Assigns a new alias to an image in a registry. An alias refers to the
entire image name including the optional `TAG` after the ':'.
If you do not specify a `REGISTRY_HOST`, the command uses Docker's public
registry located at `registry-1.docker.io` by default.
This will give a new alias to an image in the repository. This refers to the
entire image name including the optional TAG after the ':'.
# "OPTIONS"
**-f**, **--force**=*true*|*false*
@@ -61,5 +58,3 @@ April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work.
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
July 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2015, updated by Mary Anthony for v2 <mary@docker.com>
+4 -4
View File
@@ -172,10 +172,10 @@ inside it)
Load an image from a tar archive
**docker-login(1)**
Register or login to a Docker Registry Service
Register or Login to a Docker registry server
**docker-logout(1)**
Log the user out of a Docker Registry Service
Log the user out of a Docker registry server
**docker-logs(1)**
Fetch the logs of a container
@@ -190,10 +190,10 @@ inside it)
List containers
**docker-pull(1)**
Pull an image or a repository from a Docker Registry Service
Pull an image or a repository from a Docker registry server
**docker-push(1)**
Push an image or a repository to a Docker Registry Service
Push an image or a repository to a Docker registry server
**docker-restart(1)**
Restart a running container
+3 -12
View File
@@ -132,19 +132,10 @@ pages:
- ['swarm/scheduler/filter.md', 'Reference', 'Swarm filters']
- ['swarm/API.md', 'Reference', 'Swarm API']
- ['reference/api/index.md', '**HIDDEN**']
- ['registry/overview.md', 'Reference', 'Docker Registry 2.0']
- ['registry/deploying.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp; Deploy a registry' ]
- ['registry/configuration.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp; Configure a registry' ]
- ['registry/storagedrivers.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp; Storage driver model' ]
- ['registry/notifications.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp; Work with notifications' ]
- ['registry/spec/api.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp; Registry Service API v2' ]
- ['registry/spec/json.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp; JSON format' ]
- ['registry/spec/auth/token.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp; Authenticate via central service' ]
- ['reference/api/hub_registry_spec.md', 'Reference', 'Docker Hub and Registry 1.0']
- ['reference/api/registry_api.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp;Docker Registry API v1']
- ['reference/api/registry_api_client_libraries.md', 'Reference', '&nbsp;&nbsp;&nbsp;&nbsp;&blacksquare;&nbsp;Docker Registry 1.0 API Client Libraries']
#- ['reference/image-spec-v1.md', 'Reference', 'Docker Image Specification v1.0.0']
- ['reference/api/docker-io_api.md', 'Reference', 'Docker Hub API']
- ['reference/api/registry_api.md', 'Reference', 'Docker Registry API']
- ['reference/api/registry_api_client_libraries.md', 'Reference', 'Docker Registry API Client Libraries']
- ['reference/api/hub_registry_spec.md', 'Reference', 'Docker Hub and Registry Spec']
#- ['reference/image-spec-v1.md', 'Reference', 'Docker Image Specification v1.0.0']
- ['reference/api/docker_remote_api.md', 'Reference', 'Docker Remote API']
- ['reference/api/docker_remote_api_v1.18.md', 'Reference', 'Docker Remote API v1.18']
+4 -11
View File
@@ -22,17 +22,10 @@ EOF
}
create_robots_txt() {
if [ "$AWS_S3_BUCKET" == "docs.docker.com" ]; then
cat > ./sources/robots.txt <<-'EOF'
User-agent: *
Allow: /
EOF
else
cat > ./sources/robots.txt <<-'EOF'
User-agent: *
Disallow: /
EOF
fi
cat > ./sources/robots.txt <<'EOF'
User-agent: *
Disallow: /
EOF
}
setup_s3() {
+1 -2
View File
@@ -39,8 +39,7 @@
{ "Condition": { "KeyPrefixEquals": "installation/openSUSE/" }, "Redirect": { "HostName": "$BUCKET", "ReplaceKeyPrefixWith": "installation/SUSE/" } },
{ "Condition": { "KeyPrefixEquals": "contributing/contributing/" }, "Redirect": { "HostName": "$BUCKET", "ReplaceKeyPrefixWith": "project/who-written-for/" } },
{ "Condition": { "KeyPrefixEquals": "contributing/devenvironment/" }, "Redirect": { "HostName": "$BUCKET", "ReplaceKeyPrefixWith": "project/set-up-prereqs/" } },
{ "Condition": { "KeyPrefixEquals": "contributing/docs_style-guide/" }, "Redirect": { "HostName": "$BUCKET", "ReplaceKeyPrefixWith": "project/doc-style/" } },
{ "Condition": { "KeyPrefixEquals": "registry/" }, "Redirect": { "HostName": "$BUCKET", "ReplaceKeyPrefixWith": "registry/overview/" } }
{ "Condition": { "KeyPrefixEquals": "contributing/docs_style-guide/" }, "Redirect": { "HostName": "$BUCKET", "ReplaceKeyPrefixWith": "project/doc-style/" } }
]
}
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

+49 -102
View File
@@ -8,95 +8,31 @@ 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
<iframe width="640" height="480" src="//www.youtube.com/embed/TjMU3bDX4vo?rel=0" frameborder="0" allowfullscreen></iframe>
<iframe width="640" height="480" src="//www.youtube.com/embed/oSHN8_uiZd4?rel=0" frameborder="0" allowfullscreen></iframe>
## 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,25 +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`.
## References
If you have Docker hosts running and if you don't wish to do a
Boot2Docker installation, you can install the docker.exe using
unofficial Windows package manager Chocolately. For information
on how to do this, see [Docker package on Chocolatey](http://chocolatey.org/packages/docker).
@@ -359,10 +359,7 @@ Return low-level information on the container `id`
"MaximumRetryCount": 2,
"Name": "on-failure"
},
"LogConfig": {
"Config": null,
"Type": "json-file"
},
"LogConfig": { "Type": "json-file", Config: {} },
"SecurityOpt": null,
"VolumesFrom": null,
"Ulimits": [{}]
@@ -2,7 +2,7 @@ page_title: Registry Documentation
page_description: Documentation for docker Registry and Registry API
page_keywords: docker, registry, api, hub
# The Docker Hub and the Registry 1.0 spec
# The Docker Hub and the Registry spec
## The three roles
@@ -28,9 +28,9 @@ The Docker Hub is authoritative for that information.
There is only one instance of the Docker Hub, run and
managed by Docker Inc.
### Docker Registry 1.0
### Registry
The 1.0 registry has the following characteristics:
The registry has the following characteristics:
- It stores the images and the graph for a set of repositories
- It does not have user accounts data
+2 -2
View File
@@ -2,11 +2,11 @@ page_title: Registry API
page_description: API Documentation for Docker Registry
page_keywords: API, Docker, index, registry, REST, documentation
# Docker Registry API v1
# Docker Registry API
## Introduction
- This is the REST API for the Docker Registry 1.0
- This is the REST API for the Docker Registry
- It stores the images and the graph for a set of repositories
- It does not have user accounts data
- It has no notion of user accounts or authorization
@@ -2,7 +2,7 @@ page_title: Registry API Client Libraries
page_description: Various client libraries available to use with the Docker registry API
page_keywords: API, Docker, index, registry, REST, documentation, clients, C#, Erlang, Go, Groovy, Java, JavaScript, Perl, PHP, Python, Ruby, Rust, Scala
# Docker Registry 1.0 API Client Libraries
# Docker Registry API Client Libraries
These libraries have not been tested by the Docker maintainers for
compatibility. Please file issues with the library owners. If you find
+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)
+181 -198
View File
@@ -20,230 +20,213 @@ command_exists() {
command -v "$@" > /dev/null 2>&1
}
echo_docker_as_nonroot() {
your_user=your-user
[ "$user" != 'root' ] && your_user="$user"
# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-EOF", spaces are kept in the output
cat <<-EOF
case "$(uname -m)" in
*64)
;;
*)
echo >&2 'Error: you are not using a 64bit platform.'
echo >&2 'Docker currently only supports 64bit platforms.'
exit 1
;;
esac
If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:
if command_exists docker || command_exists lxc-docker; then
echo >&2 'Warning: "docker" or "lxc-docker" command appears to already exist.'
echo >&2 'Please ensure that you do not already have docker installed.'
echo >&2 'You may press Ctrl+C now to abort this process and rectify this situation.'
( set -x; sleep 20 )
fi
sudo usermod -aG docker $your_user
user="$(id -un 2>/dev/null || true)"
Remember that you will have to log out and back in for this to take effect!
EOF
}
do_install() {
case "$(uname -m)" in
*64)
;;
*)
cat >&2 <<-'EOF'
Error: you are not using a 64bit platform.
Docker currently only supports 64bit platforms.
EOF
exit 1
;;
esac
if command_exists docker || command_exists lxc-docker; then
cat >&2 <<-'EOF'
Warning: "docker" or "lxc-docker" command appears to already exist.
Please ensure that you do not already have docker installed.
You may press Ctrl+C now to abort this process and rectify this situation.
EOF
( set -x; sleep 20 )
sh_c='sh -c'
if [ "$user" != 'root' ]; then
if command_exists sudo; then
sh_c='sudo -E sh -c'
elif command_exists su; then
sh_c='su -c'
else
echo >&2 'Error: this installer needs the ability to run commands as root.'
echo >&2 'We are unable to find either "sudo" or "su" available to make this happen.'
exit 1
fi
fi
user="$(id -un 2>/dev/null || true)"
curl=''
if command_exists curl; then
curl='curl -sSL'
elif command_exists wget; then
curl='wget -qO-'
elif command_exists busybox && busybox --list-modules | grep -q wget; then
curl='busybox wget -qO-'
fi
sh_c='sh -c'
if [ "$user" != 'root' ]; then
if command_exists sudo; then
sh_c='sudo -E sh -c'
elif command_exists su; then
sh_c='su -c'
# perform some very rudimentary platform detection
lsb_dist=''
if command_exists lsb_release; then
lsb_dist="$(lsb_release -si)"
fi
if [ -z "$lsb_dist" ] && [ -r /etc/lsb-release ]; then
lsb_dist="$(. /etc/lsb-release && echo "$DISTRIB_ID")"
fi
if [ -z "$lsb_dist" ] && [ -r /etc/debian_version ]; then
lsb_dist='debian'
fi
if [ -z "$lsb_dist" ] && [ -r /etc/fedora-release ]; then
lsb_dist='fedora'
fi
if [ -z "$lsb_dist" ] && [ -r /etc/os-release ]; then
lsb_dist="$(. /etc/os-release && echo "$ID")"
fi
lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"
case "$lsb_dist" in
amzn|fedora)
if [ "$lsb_dist" = 'amzn' ]; then
(
set -x
$sh_c 'sleep 3; yum -y -q install docker'
)
else
cat >&2 <<-'EOF'
Error: this installer needs the ability to run commands as root.
We are unable to find either "sudo" or "su" available to make this happen.
EOF
exit 1
(
set -x
$sh_c 'sleep 3; yum -y -q install docker-io'
)
fi
fi
if command_exists docker && [ -e /var/run/docker.sock ]; then
(
set -x
$sh_c 'docker version'
) || true
fi
your_user=your-user
[ "$user" != 'root' ] && your_user="$user"
echo
echo 'If you would like to use Docker as a non-root user, you should now consider'
echo 'adding your user to the "docker" group with something like:'
echo
echo ' sudo usermod -aG docker' $your_user
echo
echo 'Remember that you will have to log out and back in for this to take effect!'
echo
exit 0
;;
curl=''
if command_exists curl; then
curl='curl -sSL'
elif command_exists wget; then
curl='wget -qO-'
elif command_exists busybox && busybox --list-modules | grep -q wget; then
curl='busybox wget -qO-'
fi
ubuntu|debian|linuxmint)
export DEBIAN_FRONTEND=noninteractive
# perform some very rudimentary platform detection
lsb_dist=''
if command_exists lsb_release; then
lsb_dist="$(lsb_release -si)"
fi
if [ -z "$lsb_dist" ] && [ -r /etc/lsb-release ]; then
lsb_dist="$(. /etc/lsb-release && echo "$DISTRIB_ID")"
fi
if [ -z "$lsb_dist" ] && [ -r /etc/debian_version ]; then
lsb_dist='debian'
fi
if [ -z "$lsb_dist" ] && [ -r /etc/fedora-release ]; then
lsb_dist='fedora'
fi
if [ -z "$lsb_dist" ] && [ -r /etc/os-release ]; then
lsb_dist="$(. /etc/os-release && echo "$ID")"
fi
lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"
case "$lsb_dist" in
amzn|fedora|centos)
if [ "$lsb_dist" = 'amzn' ]; then
(
set -x
$sh_c 'sleep 3; yum -y -q install docker'
)
else
(
set -x
$sh_c 'sleep 3; yum -y -q install docker-io'
)
did_apt_get_update=
apt_get_update() {
if [ -z "$did_apt_get_update" ]; then
( set -x; $sh_c 'sleep 3; apt-get update' )
did_apt_get_update=1
fi
if command_exists docker && [ -e /var/run/docker.sock ]; then
(
set -x
$sh_c 'docker version'
) || true
fi
echo_docker_as_nonroot
exit 0
;;
}
ubuntu|debian|linuxmint)
export DEBIAN_FRONTEND=noninteractive
# aufs is preferred over devicemapper; try to ensure the driver is available.
if ! grep -q aufs /proc/filesystems && ! $sh_c 'modprobe aufs'; then
if uname -r | grep -q -- '-generic' && dpkg -l 'linux-image-*-generic' | grep -q '^ii' 2>/dev/null; then
kern_extras="linux-image-extra-$(uname -r) linux-image-extra-virtual"
did_apt_get_update=
apt_get_update() {
if [ -z "$did_apt_get_update" ]; then
( set -x; $sh_c 'sleep 3; apt-get update' )
did_apt_get_update=1
fi
}
apt_get_update
( set -x; $sh_c 'sleep 3; apt-get install -y -q '"$kern_extras" ) || true
# aufs is preferred over devicemapper; try to ensure the driver is available.
if ! grep -q aufs /proc/filesystems && ! $sh_c 'modprobe aufs'; then
if uname -r | grep -q -- '-generic' && dpkg -l 'linux-image-*-generic' | grep -q '^ii' 2>/dev/null; then
kern_extras="linux-image-extra-$(uname -r) linux-image-extra-virtual"
apt_get_update
( set -x; $sh_c 'sleep 3; apt-get install -y -q '"$kern_extras" ) || true
if ! grep -q aufs /proc/filesystems && ! $sh_c 'modprobe aufs'; then
echo >&2 'Warning: tried to install '"$kern_extras"' (for AUFS)'
echo >&2 ' but we still have no AUFS. Docker may not work. Proceeding anyways!'
( set -x; sleep 10 )
fi
else
echo >&2 'Warning: current kernel is not supported by the linux-image-extra-virtual'
echo >&2 ' package. We have no AUFS support. Consider installing the packages'
echo >&2 ' linux-image-virtual kernel and linux-image-extra-virtual for AUFS support.'
if ! grep -q aufs /proc/filesystems && ! $sh_c 'modprobe aufs'; then
echo >&2 'Warning: tried to install '"$kern_extras"' (for AUFS)'
echo >&2 ' but we still have no AUFS. Docker may not work. Proceeding anyways!'
( set -x; sleep 10 )
fi
else
echo >&2 'Warning: current kernel is not supported by the linux-image-extra-virtual'
echo >&2 ' package. We have no AUFS support. Consider installing the packages'
echo >&2 ' linux-image-virtual kernel and linux-image-extra-virtual for AUFS support.'
( set -x; sleep 10 )
fi
fi
# install apparmor utils if they're missing and apparmor is enabled in the kernel
# otherwise Docker will fail to start
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = 'Y' ]; then
if command -v apparmor_parser &> /dev/null; then
echo 'apparmor is enabled in the kernel and apparmor utils were already installed'
else
echo 'apparmor is enabled in the kernel, but apparmor_parser missing'
apt_get_update
( set -x; $sh_c 'sleep 3; apt-get install -y -q apparmor' )
fi
# install apparmor utils if they're missing and apparmor is enabled in the kernel
# otherwise Docker will fail to start
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = 'Y' ]; then
if command -v apparmor_parser &> /dev/null; then
echo 'apparmor is enabled in the kernel and apparmor utils were already installed'
else
echo 'apparmor is enabled in the kernel, but apparmor_parser missing'
apt_get_update
( set -x; $sh_c 'sleep 3; apt-get install -y -q apparmor' )
fi
fi
if [ ! -e /usr/lib/apt/methods/https ]; then
apt_get_update
( set -x; $sh_c 'sleep 3; apt-get install -y -q apt-transport-https ca-certificates' )
fi
if [ -z "$curl" ]; then
apt_get_update
( set -x; $sh_c 'sleep 3; apt-get install -y -q curl ca-certificates' )
curl='curl -sSL'
if [ ! -e /usr/lib/apt/methods/https ]; then
apt_get_update
( set -x; $sh_c 'sleep 3; apt-get install -y -q apt-transport-https ca-certificates' )
fi
if [ -z "$curl" ]; then
apt_get_update
( set -x; $sh_c 'sleep 3; apt-get install -y -q curl ca-certificates' )
curl='curl -sSL'
fi
(
set -x
if [ "https://get.docker.com/" = "$url" ]; then
$sh_c "apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9"
elif [ "https://test.docker.com/" = "$url" ]; then
$sh_c "apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 740B314AE3941731B942C66ADF4FD13717AAD7D6"
else
$sh_c "$curl ${url}gpg | apt-key add -"
fi
$sh_c "echo deb ${url}ubuntu docker main > /etc/apt/sources.list.d/docker.list"
$sh_c 'sleep 3; apt-get update; apt-get install -y -q lxc-docker'
)
if command_exists docker && [ -e /var/run/docker.sock ]; then
(
set -x
if [ "https://get.docker.com/" = "$url" ]; then
$sh_c "apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9"
elif [ "https://test.docker.com/" = "$url" ]; then
$sh_c "apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 740B314AE3941731B942C66ADF4FD13717AAD7D6"
else
$sh_c "$curl ${url}gpg | apt-key add -"
fi
$sh_c "echo deb ${url}ubuntu docker main > /etc/apt/sources.list.d/docker.list"
$sh_c 'sleep 3; apt-get update; apt-get install -y -q lxc-docker'
)
if command_exists docker && [ -e /var/run/docker.sock ]; then
(
set -x
$sh_c 'docker version'
) || true
fi
echo_docker_as_nonroot
exit 0
;;
$sh_c 'docker version'
) || true
fi
your_user=your-user
[ "$user" != 'root' ] && your_user="$user"
echo
echo 'If you would like to use Docker as a non-root user, you should now consider'
echo 'adding your user to the "docker" group with something like:'
echo
echo ' sudo usermod -aG docker' $your_user
echo
echo 'Remember that you will have to log out and back in for this to take effect!'
echo
exit 0
;;
gentoo)
if [ "$url" = "https://test.docker.com/" ]; then
# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-'EOF'", spaces are kept in the output
cat >&2 <<-'EOF'
gentoo)
if [ "$url" = "https://test.docker.com/" ]; then
echo >&2
echo >&2 ' You appear to be trying to install the latest nightly build in Gentoo.'
echo >&2 ' The portage tree should contain the latest stable release of Docker, but'
echo >&2 ' if you want something more recent, you can always use the live ebuild'
echo >&2 ' provided in the "docker" overlay available via layman. For more'
echo >&2 ' instructions, please see the following URL:'
echo >&2 ' https://github.com/tianon/docker-overlay#using-this-overlay'
echo >&2 ' After adding the "docker" overlay, you should be able to:'
echo >&2 ' emerge -av =app-emulation/docker-9999'
echo >&2
exit 1
fi
You appear to be trying to install the latest nightly build in Gentoo.'
The portage tree should contain the latest stable release of Docker, but'
if you want something more recent, you can always use the live ebuild'
provided in the "docker" overlay available via layman. For more'
instructions, please see the following URL:'
(
set -x
$sh_c 'sleep 3; emerge app-emulation/docker'
)
exit 0
;;
esac
https://github.com/tianon/docker-overlay#using-this-overlay'
cat >&2 <<'EOF'
After adding the "docker" overlay, you should be able to:'
Either your platform is not easily detectable, is not supported by this
installer script (yet - PRs welcome! [hack/install.sh]), or does not yet have
a package for Docker. Please visit the following URL for more detailed
installation instructions:
emerge -av =app-emulation/docker-9999'
https://docs.docker.com/en/latest/installation/
EOF
exit 1
fi
(
set -x
$sh_c 'sleep 3; emerge app-emulation/docker'
)
exit 0
;;
esac
# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-'EOF'", spaces are kept in the output
cat >&2 <<-'EOF'
Either your platform is not easily detectable, is not supported by this
installer script (yet - PRs welcome! [hack/install.sh]), or does not yet have
a package for Docker. Please visit the following URL for more detailed
installation instructions:
https://docs.docker.com/en/latest/installation/
EOF
exit 1
}
# wrapped up in a function so that we have some protection against only getting
# half the file during "curl | sh"
do_install
EOF
exit 1
+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 bd8ec36106086f72b66e1be85a81202b93503e44
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)
-88
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()
@@ -3237,35 +3203,6 @@ func TestRunNetHost(t *testing.T) {
logDone("run - net host mode")
}
func TestRunNetContainerWhichHost(t *testing.T) {
testRequires(t, SameHostDaemon)
defer deleteAllContainers()
hostNet, err := os.Readlink("/proc/1/ns/net")
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(dockerBinary, "run", "-d", "--net=host", "--name=test", "busybox", "top")
out, _, err := runCommandWithOutput(cmd)
if err != nil {
t.Fatal(err, out)
}
cmd = exec.Command(dockerBinary, "run", "--net=container:test", "busybox", "readlink", "/proc/self/ns/net")
out, _, err = runCommandWithOutput(cmd)
if err != nil {
t.Fatal(err, out)
}
out = strings.Trim(out, "\n")
if hostNet != out {
t.Fatalf("Container should have host network namespace")
}
logDone("run - net container mode, where container in host mode")
}
func TestRunAllowPortRangeThroughPublish(t *testing.T) {
defer deleteAllContainers()
@@ -3411,28 +3348,3 @@ func TestRunVolumesFromRestartAfterRemoved(t *testing.T) {
logDone("run - can restart a volumes-from container after producer is removed")
}
func TestRunPidHostWithChildIsKillable(t *testing.T) {
defer deleteAllContainers()
name := "ibuildthecloud"
if out, err := exec.Command(dockerBinary, "run", "-d", "--pid=host", "--name", name, "busybox", "sh", "-c", "sleep 30; echo hi").CombinedOutput(); err != nil {
t.Fatal(err, out)
}
time.Sleep(1 * time.Second)
errchan := make(chan error)
go func() {
if out, err := exec.Command(dockerBinary, "kill", name).CombinedOutput(); err != nil {
errchan <- fmt.Errorf("%v:\n%s", err, out)
}
close(errchan)
}()
select {
case err := <-errchan:
if err != nil {
t.Fatal(err)
}
case <-time.After(5 * time.Second):
t.Fatal("Kill container timed out")
}
logDone("run - can kill container with pid-host and some childs of pid 1")
}
+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()
}
+58 -59
View File
@@ -5,7 +5,6 @@ import (
"io"
"os"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/term/winconsole"
)
@@ -22,49 +21,34 @@ type Winsize struct {
y uint16
}
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
switch {
case os.Getenv("ConEmuANSI") == "ON":
// The ConEmu shell emulates ANSI well by default.
return os.Stdin, os.Stdout, os.Stderr
case os.Getenv("MSYSTEM") != "":
// MSYS (mingw) does not emulate ANSI well.
return winconsole.WinConsoleStreams()
default:
return winconsole.WinConsoleStreams()
}
}
// GetFdInfo returns file descriptor and bool indicating whether the file is a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
return winconsole.GetHandleInfo(in)
}
// GetWinsize retrieves the window size of the terminal connected to the passed file descriptor.
// GetWinsize gets the window size of the given terminal
func GetWinsize(fd uintptr) (*Winsize, error) {
ws := &Winsize{}
var info *winconsole.CONSOLE_SCREEN_BUFFER_INFO
info, err := winconsole.GetConsoleScreenBufferInfo(fd)
if err != nil {
return nil, err
}
// TODO(azlinux): Set the pixel width / height of the console (currently unused by any caller)
return &Winsize{
Width: uint16(info.Window.Right - info.Window.Left + 1),
Height: uint16(info.Window.Bottom - info.Window.Top + 1),
x: 0,
y: 0}, nil
ws.Width = uint16(info.Window.Right - info.Window.Left + 1)
ws.Height = uint16(info.Window.Bottom - info.Window.Top + 1)
ws.x = 0 // todo azlinux -- this is the pixel size of the Window, and not currently used by any caller
ws.y = 0
return ws, nil
}
// SetWinsize sets the size of the given terminal connected to the passed file descriptor.
// SetWinsize sets the terminal connected to the given file descriptor to a
// given size.
func SetWinsize(fd uintptr, ws *Winsize) error {
// TODO(azlinux): Implement SetWinsize
logrus.Debugf("[windows] SetWinsize: WARNING -- Unsupported method invoked")
return nil
}
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
return winconsole.IsConsole(fd)
_, e := winconsole.GetConsoleMode(fd)
return e == nil
}
// RestoreTerminal restores the terminal connected to the given file descriptor to a
@@ -73,7 +57,7 @@ func RestoreTerminal(fd uintptr, state *State) error {
return winconsole.SetConsoleMode(fd, state.mode)
}
// SaveState saves the state of the terminal connected to the given file descriptor.
// SaveState saves the state of the given console
func SaveState(fd uintptr) (*State, error) {
mode, e := winconsole.GetConsoleMode(fd)
if e != nil {
@@ -82,57 +66,72 @@ func SaveState(fd uintptr) (*State, error) {
return &State{mode}, nil
}
// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
// DisableEcho disbales the echo for given file descriptor and returns previous state
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
func DisableEcho(fd uintptr, state *State) error {
mode := state.mode
mode &^= winconsole.ENABLE_ECHO_INPUT
mode |= winconsole.ENABLE_PROCESSED_INPUT | winconsole.ENABLE_LINE_INPUT
// TODO(azlinux): Core code registers a goroutine to catch os.Interrupt and reset the terminal state.
return winconsole.SetConsoleMode(fd, mode)
state.mode &^= (winconsole.ENABLE_ECHO_INPUT)
state.mode |= (winconsole.ENABLE_PROCESSED_INPUT | winconsole.ENABLE_LINE_INPUT)
return winconsole.SetConsoleMode(fd, state.mode)
}
// SetRawTerminal puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func SetRawTerminal(fd uintptr) (*State, error) {
state, err := MakeRaw(fd)
oldState, err := MakeRaw(fd)
if err != nil {
return nil, err
}
// TODO(azlinux): Core code registers a goroutine to catch os.Interrupt and reset the terminal state.
return state, err
// TODO (azlinux): implement handling interrupt and restore state of terminal
return oldState, err
}
// MakeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
var state *State
state, err := SaveState(fd)
if err != nil {
return nil, err
}
// See
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
mode := state.mode
// Disable these modes
mode &^= winconsole.ENABLE_ECHO_INPUT
mode &^= winconsole.ENABLE_LINE_INPUT
mode &^= winconsole.ENABLE_MOUSE_INPUT
mode &^= winconsole.ENABLE_WINDOW_INPUT
mode &^= winconsole.ENABLE_PROCESSED_INPUT
// Enable these modes
mode |= winconsole.ENABLE_EXTENDED_FLAGS
mode |= winconsole.ENABLE_INSERT_MODE
mode |= winconsole.ENABLE_QUICK_EDIT_MODE
err = winconsole.SetConsoleMode(fd, mode)
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
// All three input modes, along with processed output mode, are designed to work together.
// It is best to either enable or disable all of these modes as a group.
// When all are enabled, the application is said to be in "cooked" mode, which means that most of the processing is handled for the application.
// When all are disabled, the application is in "raw" mode, which means that input is unfiltered and any processing is left to the application.
state.mode = 0
err = winconsole.SetConsoleMode(fd, state.mode)
if err != nil {
return nil, err
}
return state, nil
}
// GetFdInfo returns file descriptor and bool indicating whether the file is a terminal
func GetFdInfo(in interface{}) (uintptr, bool) {
return winconsole.GetHandleInfo(in)
}
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
var shouldEmulateANSI bool
switch {
case os.Getenv("ConEmuANSI") == "ON":
// ConEmu shell, ansi emulated by default and ConEmu does an extensively
// good emulation.
shouldEmulateANSI = false
case os.Getenv("MSYSTEM") != "":
// MSYS (mingw) cannot fully emulate well and still shows escape characters
// mostly because it's still running on cmd.exe window.
shouldEmulateANSI = true
default:
shouldEmulateANSI = true
}
if shouldEmulateANSI {
return winconsole.StdStreams()
}
return os.Stdin, os.Stdout, os.Stderr
}
+82 -93
View File
@@ -12,22 +12,18 @@ import (
"sync"
"syscall"
"unsafe"
"github.com/Sirupsen/logrus"
)
const (
// Consts for Get/SetConsoleMode function
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
ENABLE_PROCESSED_INPUT = 0x0001
ENABLE_LINE_INPUT = 0x0002
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
ENABLE_ECHO_INPUT = 0x0004
ENABLE_WINDOW_INPUT = 0x0008
ENABLE_MOUSE_INPUT = 0x0010
ENABLE_INSERT_MODE = 0x0020
ENABLE_LINE_INPUT = 0x0002
ENABLE_MOUSE_INPUT = 0x0010
ENABLE_PROCESSED_INPUT = 0x0001
ENABLE_QUICK_EDIT_MODE = 0x0040
ENABLE_EXTENDED_FLAGS = 0x0080
ENABLE_WINDOW_INPUT = 0x0008
// If parameter is a screen buffer handle, additional values
ENABLE_PROCESSED_OUTPUT = 0x0001
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
@@ -101,27 +97,27 @@ const (
VK_HOME = 0x24 // HOME key
VK_LEFT = 0x25 // LEFT ARROW key
VK_UP = 0x26 // UP ARROW key
VK_RIGHT = 0x27 // RIGHT ARROW key
VK_DOWN = 0x28 // DOWN ARROW key
VK_SELECT = 0x29 // SELECT key
VK_PRINT = 0x2A // PRINT key
VK_EXECUTE = 0x2B // EXECUTE key
VK_SNAPSHOT = 0x2C // PRINT SCREEN key
VK_INSERT = 0x2D // INS key
VK_DELETE = 0x2E // DEL key
VK_HELP = 0x2F // HELP key
VK_F1 = 0x70 // F1 key
VK_F2 = 0x71 // F2 key
VK_F3 = 0x72 // F3 key
VK_F4 = 0x73 // F4 key
VK_F5 = 0x74 // F5 key
VK_F6 = 0x75 // F6 key
VK_F7 = 0x76 // F7 key
VK_F8 = 0x77 // F8 key
VK_F9 = 0x78 // F9 key
VK_F10 = 0x79 // F10 key
VK_F11 = 0x7A // F11 key
VK_F12 = 0x7B // F12 key
VK_RIGHT = 0x27 //RIGHT ARROW key
VK_DOWN = 0x28 //DOWN ARROW key
VK_SELECT = 0x29 //SELECT key
VK_PRINT = 0x2A //PRINT key
VK_EXECUTE = 0x2B //EXECUTE key
VK_SNAPSHOT = 0x2C //PRINT SCREEN key
VK_INSERT = 0x2D //INS key
VK_DELETE = 0x2E //DEL key
VK_HELP = 0x2F //HELP key
VK_F1 = 0x70 //F1 key
VK_F2 = 0x71 //F2 key
VK_F3 = 0x72 //F3 key
VK_F4 = 0x73 //F4 key
VK_F5 = 0x74 //F5 key
VK_F6 = 0x75 //F6 key
VK_F7 = 0x76 //F7 key
VK_F8 = 0x77 //F8 key
VK_F9 = 0x78 //F9 key
VK_F10 = 0x79 //F10 key
VK_F11 = 0x7A //F11 key
VK_F12 = 0x7B //F12 key
)
var kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
@@ -144,12 +140,7 @@ var (
// types for calling various windows API
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
type (
SHORT int16
BOOL int32
WORD uint16
WCHAR uint16
DWORD uint32
SHORT int16
SMALL_RECT struct {
Left SHORT
Top SHORT
@@ -162,6 +153,11 @@ type (
Y SHORT
}
BOOL int32
WORD uint16
WCHAR uint16
DWORD uint32
CONSOLE_SCREEN_BUFFER_INFO struct {
Size COORD
CursorPosition COORD
@@ -196,10 +192,6 @@ type (
}
)
// TODO(azlinux): Basic type clean-up
// -- Convert all uses of uintptr to syscall.Handle to be consistent with Windows syscall
// -- Convert, as appropriate, types to use defined Windows types (e.g., DWORD instead of uint32)
// Implements the TerminalEmulator interface
type WindowsTerminal struct {
outMutex sync.Mutex
@@ -219,14 +211,14 @@ func getStdHandle(stdhandle int) uintptr {
return uintptr(handle)
}
func WinConsoleStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
func StdStreams() (stdIn io.ReadCloser, stdOut io.Writer, stdErr io.Writer) {
handler := &WindowsTerminal{
inputBuffer: make([]byte, MAX_INPUT_BUFFER),
inputEscapeSequence: []byte(KEY_ESC_CSI),
inputEvents: make([]INPUT_RECORD, MAX_INPUT_EVENTS),
}
if IsConsole(os.Stdin.Fd()) {
if IsTerminal(os.Stdin.Fd()) {
stdIn = &terminalReader{
wrappedReader: os.Stdin,
emulator: handler,
@@ -237,7 +229,7 @@ func WinConsoleStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
stdIn = os.Stdin
}
if IsConsole(os.Stdout.Fd()) {
if IsTerminal(os.Stdout.Fd()) {
stdoutHandle := getStdHandle(syscall.STD_OUTPUT_HANDLE)
// Save current screen buffer info
@@ -249,6 +241,8 @@ func WinConsoleStreams() (stdIn io.ReadCloser, stdOut, 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{
@@ -261,7 +255,7 @@ func WinConsoleStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
stdOut = os.Stdout
}
if IsConsole(os.Stderr.Fd()) {
if IsTerminal(os.Stderr.Fd()) {
stdErr = &terminalWriter{
wrappedWriter: os.Stderr,
emulator: handler,
@@ -275,21 +269,19 @@ func WinConsoleStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
return stdIn, stdOut, stdErr
}
// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
// GetHandleInfo returns file descriptor and bool indicating whether the file is a terminal
func GetHandleInfo(in interface{}) (uintptr, bool) {
var inFd uintptr
var isTerminalIn bool
switch t := in.(type) {
case *terminalReader:
in = t.wrappedReader
case *terminalWriter:
in = t.wrappedWriter
}
if file, ok := in.(*os.File); ok {
inFd = file.Fd()
isTerminalIn = IsConsole(inFd)
isTerminalIn = IsTerminal(inFd)
}
if tr, ok := in.(*terminalReader); ok {
if file, ok := tr.wrappedReader.(*os.File); ok {
inFd = file.Fd()
isTerminalIn = IsTerminal(inFd)
}
}
return inFd, isTerminalIn
}
@@ -322,12 +314,12 @@ func SetConsoleMode(handle uintptr, mode uint32) error {
// SetCursorVisible sets the cursor visbility
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx
func SetCursorVisible(handle uintptr, isVisible BOOL) (bool, error) {
var cursorInfo *CONSOLE_CURSOR_INFO = &CONSOLE_CURSOR_INFO{}
if err := getError(getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)); err != nil {
var cursorInfo CONSOLE_CURSOR_INFO
if err := getError(getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(&cursorInfo)), 0)); err != nil {
return false, err
}
cursorInfo.Visible = isVisible
if err := getError(setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)); err != nil {
if err := getError(setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(&cursorInfo)), 0)); err != nil {
return false, err
}
return true, nil
@@ -412,25 +404,25 @@ func getNumberOfChars(fromCoord COORD, toCoord COORD, screenSize COORD) uint32 {
var buffer []CHAR_INFO
func clearDisplayRect(handle uintptr, attributes WORD, fromCoord COORD, toCoord COORD) (uint32, error) {
func clearDisplayRect(handle uintptr, fillChar rune, attributes WORD, fromCoord COORD, toCoord COORD, windowSize COORD) (uint32, error) {
var writeRegion SMALL_RECT
writeRegion.Left = fromCoord.X
writeRegion.Top = fromCoord.Y
writeRegion.Left = fromCoord.X
writeRegion.Right = toCoord.X
writeRegion.Bottom = toCoord.Y
// allocate and initialize buffer
width := toCoord.X - fromCoord.X + 1
height := toCoord.Y - fromCoord.Y + 1
size := uint32(width) * uint32(height)
size := width * height
if size > 0 {
buffer := make([]CHAR_INFO, size)
for i := range buffer {
buffer[i] = CHAR_INFO{WCHAR(' '), attributes}
for i := 0; i < int(size); i++ {
buffer[i].UnicodeChar = WCHAR(fillChar)
buffer[i].Attributes = attributes
}
// Write to buffer
r, err := writeConsoleOutput(handle, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, &writeRegion)
r, err := writeConsoleOutput(handle, buffer[:size], windowSize, COORD{X: 0, Y: 0}, &writeRegion)
if !r {
if err != nil {
return 0, err
@@ -441,18 +433,18 @@ func clearDisplayRect(handle uintptr, attributes WORD, fromCoord COORD, toCoord
return uint32(size), nil
}
func clearDisplayRange(handle uintptr, attributes WORD, fromCoord COORD, toCoord COORD) (uint32, error) {
func clearDisplayRange(handle uintptr, fillChar rune, attributes WORD, fromCoord COORD, toCoord COORD, windowSize COORD) (uint32, error) {
nw := uint32(0)
// start and end on same line
if fromCoord.Y == toCoord.Y {
return clearDisplayRect(handle, attributes, fromCoord, toCoord)
return clearDisplayRect(handle, fillChar, attributes, fromCoord, toCoord, windowSize)
}
// TODO(azlinux): if full screen, optimize
// spans more than one line
if fromCoord.Y < toCoord.Y {
// from start position till end of line for first line
n, err := clearDisplayRect(handle, attributes, fromCoord, COORD{X: toCoord.X, Y: fromCoord.Y})
n, err := clearDisplayRect(handle, fillChar, attributes, fromCoord, COORD{X: windowSize.X - 1, Y: fromCoord.Y}, windowSize)
if err != nil {
return nw, err
}
@@ -460,14 +452,14 @@ func clearDisplayRange(handle uintptr, attributes WORD, fromCoord COORD, toCoord
// lines between
linesBetween := toCoord.Y - fromCoord.Y - 1
if linesBetween > 0 {
n, err = clearDisplayRect(handle, attributes, COORD{X: 0, Y: fromCoord.Y + 1}, COORD{X: toCoord.X, Y: toCoord.Y - 1})
n, err = clearDisplayRect(handle, fillChar, attributes, COORD{X: 0, Y: fromCoord.Y + 1}, COORD{X: windowSize.X - 1, Y: toCoord.Y - 1}, windowSize)
if err != nil {
return nw, err
}
nw += n
}
// lines at end
n, err = clearDisplayRect(handle, attributes, COORD{X: 0, Y: toCoord.Y}, toCoord)
n, err = clearDisplayRect(handle, fillChar, attributes, COORD{X: 0, Y: toCoord.Y}, toCoord, windowSize)
if err != nil {
return nw, err
}
@@ -497,7 +489,7 @@ func setConsoleCursorPosition(handle uintptr, isRelative bool, column int16, lin
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683207(v=vs.85).aspx
func getNumberOfConsoleInputEvents(handle uintptr) (uint16, error) {
var n DWORD
var n WORD
if err := getError(getNumberOfConsoleInputEventsProc.Call(handle, uintptr(unsafe.Pointer(&n)))); err != nil {
return 0, err
}
@@ -506,7 +498,7 @@ func getNumberOfConsoleInputEvents(handle uintptr) (uint16, error) {
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx
func readConsoleInputKey(handle uintptr, inputBuffer []INPUT_RECORD) (int, error) {
var nr DWORD
var nr WORD
if err := getError(readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&inputBuffer[0])), uintptr(len(inputBuffer)), uintptr(unsafe.Pointer(&nr)))); err != nil {
return 0, err
}
@@ -595,7 +587,6 @@ func (term *WindowsTerminal) HandleOutputCommand(handle uintptr, command []byte)
n = len(command)
parsedCommand := parseAnsiCommand(command)
logrus.Debugf("[windows] HandleOutputCommand: %v", parsedCommand)
// console settings changes need to happen in atomic way
term.outMutex.Lock()
@@ -641,17 +632,16 @@ func (term *WindowsTerminal) HandleOutputCommand(handle uintptr, command []byte)
return n, err
}
if line > int16(screenBufferInfo.Window.Bottom) {
line = int16(screenBufferInfo.Window.Bottom) + 1
line = int16(screenBufferInfo.Window.Bottom)
}
column, err := parseInt16OrDefault(parsedCommand.getParam(1), 1)
if err != nil {
return n, err
}
if column > int16(screenBufferInfo.Window.Right) {
column = int16(screenBufferInfo.Window.Right) + 1
column = int16(screenBufferInfo.Window.Right)
}
// The numbers are not 0 based, but 1 based
logrus.Debugf("[windows] HandleOutputCommmand: Moving cursor to (%v,%v)", column-1, line-1)
if err := setConsoleCursorPosition(handle, false, column-1, line-1); err != nil {
return n, err
}
@@ -719,9 +709,9 @@ func (term *WindowsTerminal) HandleOutputCommand(handle uintptr, command []byte)
switch value {
case 0:
start = screenBufferInfo.CursorPosition
// end of the buffer
end.X = screenBufferInfo.Size.X - 1
end.Y = screenBufferInfo.Size.Y - 1
// end of the screen
end.X = screenBufferInfo.MaximumWindowSize.X - 1
end.Y = screenBufferInfo.MaximumWindowSize.Y - 1
// cursor
cursor = screenBufferInfo.CursorPosition
case 1:
@@ -737,21 +727,20 @@ func (term *WindowsTerminal) HandleOutputCommand(handle uintptr, command []byte)
// start of the screen
start.X = 0
start.Y = 0
// end of the buffer
end.X = screenBufferInfo.Size.X - 1
end.Y = screenBufferInfo.Size.Y - 1
// end of the screen
end.X = screenBufferInfo.MaximumWindowSize.X - 1
end.Y = screenBufferInfo.MaximumWindowSize.Y - 1
// cursor
cursor.X = 0
cursor.Y = 0
}
if _, err := clearDisplayRange(uintptr(handle), term.screenBufferInfo.Attributes, start, end); err != nil {
if _, err := clearDisplayRange(uintptr(handle), ' ', term.screenBufferInfo.Attributes, start, end, screenBufferInfo.MaximumWindowSize); err != nil {
return n, err
}
// remember the the cursor position is 1 based
if err := setConsoleCursorPosition(handle, false, int16(cursor.X), int16(cursor.Y)); err != nil {
return n, err
}
case "K":
// [K
// Clears all characters from the cursor position to the end of the line (including the character at the cursor position).
@@ -771,7 +760,7 @@ func (term *WindowsTerminal) HandleOutputCommand(handle uintptr, command []byte)
// start is where cursor is
start = screenBufferInfo.CursorPosition
// end of line
end.X = screenBufferInfo.Size.X - 1
end.X = screenBufferInfo.MaximumWindowSize.X - 1
end.Y = screenBufferInfo.CursorPosition.Y
// cursor remains the same
cursor = screenBufferInfo.CursorPosition
@@ -787,15 +776,15 @@ func (term *WindowsTerminal) HandleOutputCommand(handle uintptr, command []byte)
case 2:
// start of the line
start.X = 0
start.Y = screenBufferInfo.CursorPosition.Y - 1
start.Y = screenBufferInfo.MaximumWindowSize.Y - 1
// end of the line
end.X = screenBufferInfo.Size.X - 1
end.Y = screenBufferInfo.CursorPosition.Y - 1
end.X = screenBufferInfo.MaximumWindowSize.X - 1
end.Y = screenBufferInfo.MaximumWindowSize.Y - 1
// cursor
cursor.X = 0
cursor.Y = screenBufferInfo.CursorPosition.Y - 1
cursor.Y = screenBufferInfo.MaximumWindowSize.Y - 1
}
if _, err := clearDisplayRange(uintptr(handle), term.screenBufferInfo.Attributes, start, end); err != nil {
if _, err := clearDisplayRange(uintptr(handle), ' ', term.screenBufferInfo.Attributes, start, end, screenBufferInfo.MaximumWindowSize); err != nil {
return n, err
}
// remember the the cursor position is 1 based
@@ -1042,12 +1031,12 @@ func (term *WindowsTerminal) HandleInputSequence(fd uintptr, command []byte) (n
}
func marshal(c COORD) uintptr {
return uintptr(*((*DWORD)(unsafe.Pointer(&c))))
// works only on intel-endian machines
return uintptr(uint32(uint32(uint16(c.Y))<<16 | uint32(uint16(c.X))))
}
// IsConsole returns true if the given file descriptor is a terminal.
// -- The code assumes that GetConsoleMode will return an error for file descriptors that are not a console.
func IsConsole(fd uintptr) bool {
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
_, e := GetConsoleMode(fd)
return e == nil
}
-16
View File
@@ -1,7 +1,6 @@
package winconsole
import (
"fmt"
"io"
"strconv"
"strings"
@@ -207,21 +206,6 @@ func (c *ansiCommand) getParam(index int) string {
return ""
}
func (ac *ansiCommand) String() string {
return fmt.Sprintf("0x%v \"%v\" (\"%v\")",
bytesToHex(ac.CommandBytes),
ac.Command,
strings.Join(ac.Parameters, "\",\""))
}
func bytesToHex(b []byte) string {
hex := make([]string, len(b))
for i, ch := range b {
hex[i] = fmt.Sprintf("%X", ch)
}
return strings.Join(hex, "")
}
func parseInt16OrDefault(s string, defaultValue int16) (n int16, err error) {
if s == "" {
return defaultValue, nil
+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
}
@@ -1,2 +1 @@
bundles
nsinit/nsinit
-2
View File
@@ -29,5 +29,3 @@ local:
validate:
hack/validate.sh
binary: all
docker run --rm --privileged -v $(CURDIR)/bundles:/go/bin dockercore/libcontainer make direct-install
-3
View File
@@ -141,9 +141,6 @@ container.Resume()
It is able to spawn new containers or join existing containers. A root
filesystem must be provided for use along with a container configuration file.
To build `nsinit`, run `make binary`. It will save the binary into
`bundles/nsinit`.
To use `nsinit`, cd into a Linux rootfs and copy a `container.json` file into
the directory with your specified configuration. Environment, networking,
and different capabilities for the container are specified in this file.
+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")
}
}
@@ -3,6 +3,7 @@
package systemd
import (
"bytes"
"fmt"
"io/ioutil"
"os"
@@ -42,10 +43,6 @@ var subsystems = map[string]subsystem{
"freezer": &fs.FreezerGroup{},
}
const (
testScopeWait = 4
)
var (
connLock sync.Mutex
theConn *systemd.Conn
@@ -89,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
}
@@ -217,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
@@ -246,21 +227,6 @@ func writeFile(dir, file, data string) error {
return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
}
func join(c *configs.Cgroup, subsystem string, pid int) (string, error) {
path, err := getSubsystemPath(c, subsystem)
if err != nil {
return "", err
}
if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
return "", err
}
if err := writeFile(path, "cgroup.procs", strconv.Itoa(pid)); err != nil {
return "", err
}
return path, nil
}
func joinCpu(c *configs.Cgroup, pid int) error {
path, err := getSubsystemPath(c, "cpu")
if err != nil {
@@ -280,11 +246,16 @@ func joinCpu(c *configs.Cgroup, pid int) error {
}
func joinFreezer(c *configs.Cgroup, pid int) error {
if _, err := join(c, "freezer", pid); err != nil {
path, err := getSubsystemPath(c, "freezer")
if err != nil {
return err
}
return nil
if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
return err
}
return ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700)
}
func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
@@ -312,15 +283,21 @@ func (m *Manager) Freeze(state configs.FreezerState) error {
return err
}
prevState := m.Cgroups.Freezer
m.Cgroups.Freezer = state
freezer := subsystems["freezer"]
err = freezer.Set(path, m.Cgroups)
if err != nil {
m.Cgroups.Freezer = prevState
if err := ioutil.WriteFile(filepath.Join(path, "freezer.state"), []byte(state), 0); err != nil {
return err
}
for {
state_, err := ioutil.ReadFile(filepath.Join(path, "freezer.state"))
if err != nil {
return err
}
if string(state) == string(bytes.TrimSpace(state_)) {
break
}
time.Sleep(1 * time.Millisecond)
}
m.Cgroups.Freezer = state
return nil
}
@@ -369,16 +346,29 @@ func getUnitName(c *configs.Cgroup) string {
// because systemd will re-write the device settings if it needs to re-apply the cgroup context.
// This happens at least for v208 when any sibling unit is started.
func joinDevices(c *configs.Cgroup, pid int) error {
path, err := join(c, "devices", pid)
path, err := getSubsystemPath(c, "devices")
if err != nil {
return err
}
devices := subsystems["devices"]
if err := devices.Set(path, c); err != nil {
if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
return err
}
if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
return err
}
if !c.AllowAllDevices {
if err := writeFile(path, "devices.deny", "a"); err != nil {
return err
}
}
for _, dev := range c.AllowedDevices {
if err := writeFile(path, "devices.allow", dev.CgroupString()); err != nil {
return err
}
}
return nil
}
@@ -16,17 +16,6 @@ const (
NEWUSER NamespaceType = "NEWUSER"
)
func NamespaceTypes() []NamespaceType {
return []NamespaceType{
NEWNET,
NEWPID,
NEWNS,
NEWUTS,
NEWIPC,
NEWUSER,
}
}
// Namespace defines configuration for each namespace. It specifies an
// alternate path that is able to be joined via setns.
type Namespace struct {
@@ -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,
}
}
@@ -306,11 +303,5 @@ func (c *linuxContainer) currentState() (*State, error) {
for _, ns := range c.config.Namespaces {
state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid())
}
for _, nsType := range configs.NamespaceTypes() {
if _, ok := state.NamespacePaths[nsType]; !ok {
ns := configs.Namespace{Type: nsType}
state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid())
}
}
return state, nil
}
@@ -130,8 +130,7 @@ func TestGetContainerState(t *testing.T) {
{Type: configs.NEWNS},
{Type: configs.NEWNET, Path: expectedNetworkPath},
{Type: configs.NEWUTS},
// emulate host for IPC
//{Type: configs.NEWIPC},
{Type: configs.NEWIPC},
},
},
initProcess: &mockProcess{
+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,12 +4,10 @@ import (
"bytes"
"io/ioutil"
"os"
"strconv"
"strings"
"testing"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/cgroups/systemd"
"github.com/docker/libcontainer/configs"
)
@@ -397,102 +395,7 @@ 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) {
testFreeze(t, false)
}
func TestSystemdFreeze(t *testing.T) {
if !systemd.UseSystemd() {
t.Skip("Systemd is unsupported")
}
testFreeze(t, true)
}
func testFreeze(t *testing.T, systemd bool) {
if testing.Short() {
return
}
@@ -509,9 +412,6 @@ func testFreeze(t *testing.T, systemd bool) {
defer remove(rootfs)
config := newTemplateConfig(rootfs)
if systemd {
config.Cgroups.Slice = "system.slice"
}
factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
if err != nil {
@@ -574,77 +474,3 @@ func testFreeze(t *testing.T, systemd bool) {
t.Fatal(s.String())
}
}
func TestContainerState(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)
l, err := os.Readlink("/proc/1/ns/ipc")
if err != nil {
t.Fatal(err)
}
config := newTemplateConfig(rootfs)
config.Namespaces = configs.Namespaces([]configs.Namespace{
{Type: configs.NEWNS},
{Type: configs.NEWUTS},
// host for IPC
//{Type: configs.NEWIPC},
{Type: configs.NEWPID},
{Type: configs.NEWNET},
})
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()
stdinR, stdinW, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
p := &libcontainer.Process{
Args: []string{"cat"},
Env: standardEnvironment,
Stdin: stdinR,
}
err = container.Start(p)
if err != nil {
t.Fatal(err)
}
stdinR.Close()
defer p.Signal(os.Kill)
st, err := container.State()
if err != nil {
t.Fatal(err)
}
l1, err := os.Readlink(st.NamespacePaths[configs.NEWIPC])
if err != nil {
t.Fatal(err)
}
if l1 != l {
t.Fatal("Container using non-host ipc namespace")
}
stdinW.Close()
p.Wait()
}
@@ -68,14 +68,9 @@ func copyBusybox(dest string) error {
}
func newContainer(config *configs.Config) (libcontainer.Container, error) {
cgm := libcontainer.Cgroupfs
if config.Cgroups != nil && config.Cgroups.Slice == "system.slice" {
cgm = libcontainer.SystemdCgroups
}
factory, err := libcontainer.New(".",
libcontainer.InitArgs(os.Args[0], "init", "--"),
cgm,
libcontainer.Cgroupfs,
)
if err != nil {
return nil, err
@@ -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.
+1 -1
View File
@@ -13,7 +13,7 @@ func main() {
app.Version = "2"
app.Author = "libcontainer maintainers"
app.Flags = []cli.Flag{
cli.StringFlag{Name: "root", Value: "/var/run/nsinit", Usage: "root directory for containers"},
cli.StringFlag{Name: "root", Value: ".", Usage: "root directory for containers"},
cli.StringFlag{Name: "log-file", Value: "", Usage: "set the log file to output logs to"},
cli.BoolFlag{Name: "debug", Usage: "enable debug output in the logs"},
}
-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
}

Some files were not shown because too many files have changed in this diff Show More