From 2394c9c8dca18831eea5f2b3d67da9ea3c5d18f2 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 3 Jul 2013 17:11:00 +0000 Subject: [PATCH 01/17] add last version Upstream-commit: 64450ae3f89b8f9b5288224c5a7d109a166cf22a Component: engine --- components/engine/commands.go | 9 +++++++++ components/engine/utils/utils.go | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/components/engine/commands.go b/components/engine/commands.go index 6e1e5e88c2..e275bdd671 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -407,6 +407,15 @@ func (cli *DockerCli) CmdVersion(args ...string) error { if out.GoVersion != "" { fmt.Fprintf(cli.out, "Go version: %s\n", out.GoVersion) } + + release := utils.GetReleaseVersion() + if release != "" { + fmt.Fprintf(cli.out, "Last stable version: %s", release) + if VERSION != release || out.Version != release { + fmt.Fprintf(cli.out, ", please update docker") + } + fmt.Fprintf(cli.out, "\n") + } return nil } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index 2f2a52867e..b4a41ea420 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -687,3 +687,25 @@ func ParseHost(host string, port int, addr string) string { } return fmt.Sprintf("tcp://%s:%d", host, port) } + +func GetReleaseVersion() string { + type githubTag struct { + Name string `json:"name"` + } + + resp, err := http.Get("https://api.github.com/repos/dotcloud/docker/tags") + if err != nil { + return "" + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "" + } + var tags []githubTag + err = json.Unmarshal(body, &tags) + if err != nil || len(tags) == 0 { + return "" + } + return strings.TrimPrefix(tags[0].Name, "v") +} From 9c8c35f4bd8616581c8840eb4b72b08630d89ab5 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 20:56:41 +0000 Subject: [PATCH 02/17] handle -dev Upstream-commit: 0afed3eded0950b5899729714fb11d6a7322301e Component: engine --- components/engine/commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 5f3913f9c2..7b70faa198 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -436,7 +436,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error { release := utils.GetReleaseVersion() if release != "" { fmt.Fprintf(cli.out, "Last stable version: %s", release) - if VERSION != release || out.Version != release { + if strings.Trim(VERSION, "-dev") != release || strings.Trim(out.Version, "-dev") != release { fmt.Fprintf(cli.out, ", please update docker") } fmt.Fprintf(cli.out, "\n") From 1ccfbf8698584ad97e67a104b38d9bba7a89155f Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Mon, 29 Jul 2013 09:45:19 -0700 Subject: [PATCH 03/17] testing, issue #1331: Add registry functional test to docker-ci Upstream-commit: 17ffb0ac84874656a61a477fa29048cb82b28748 Component: engine --- components/engine/testing/README.rst | 4 ++++ components/engine/testing/Vagrantfile | 4 +++- .../engine/testing/buildbot/credentials.cfg | 5 +++++ components/engine/testing/buildbot/master.cfg | 20 +++++++++++++++++-- .../engine/testing/buildbot/requirements.txt | 1 + .../testing/buildbot/setup_credentials.sh | 17 ++++++++++++++++ .../testing/functionaltests/test_registry.sh | 11 ++++++++++ 7 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 components/engine/testing/buildbot/credentials.cfg create mode 100755 components/engine/testing/buildbot/setup_credentials.sh create mode 100755 components/engine/testing/functionaltests/test_registry.sh diff --git a/components/engine/testing/README.rst b/components/engine/testing/README.rst index 3b11092f9f..ce5aa837a4 100644 --- a/components/engine/testing/README.rst +++ b/components/engine/testing/README.rst @@ -40,6 +40,10 @@ Deployment export SMTP_USER=xxxxxxxxxxxx export SMTP_PWD=xxxxxxxxxxxx + # Define docker registry functional test credentials + export REGISTRY_USER=xxxxxxxxxxxx + export REGISTRY_PWD=xxxxxxxxxxxx + # Checkout docker git clone git://github.com/dotcloud/docker.git diff --git a/components/engine/testing/Vagrantfile b/components/engine/testing/Vagrantfile index 47257201dc..e76a951508 100644 --- a/components/engine/testing/Vagrantfile +++ b/components/engine/testing/Vagrantfile @@ -29,7 +29,9 @@ Vagrant::Config.run do |config| "chown #{USER}.#{USER} /data; cd /data; " \ "#{CFG_PATH}/setup.sh #{USER} #{CFG_PATH} #{ENV['BUILDBOT_PWD']} " \ "#{ENV['IRC_PWD']} #{ENV['IRC_CHANNEL']} #{ENV['SMTP_USER']} " \ - "#{ENV['SMTP_PWD']} #{ENV['EMAIL_RCP']}; " + "#{ENV['SMTP_PWD']} #{ENV['EMAIL_RCP']}; " \ + "#{CFG_PATH}/setup_credentials.sh #{USER} " \ + "#{ENV['REGISTRY_USER']} #{ENV['REGISTRY_PWD']}; " # Install docker dependencies pkg_cmd << "apt-get install -q -y python-software-properties; " \ "add-apt-repository -y ppa:dotcloud/docker-golang/ubuntu; apt-get update -qq; " \ diff --git a/components/engine/testing/buildbot/credentials.cfg b/components/engine/testing/buildbot/credentials.cfg new file mode 100644 index 0000000000..fbdd35d578 --- /dev/null +++ b/components/engine/testing/buildbot/credentials.cfg @@ -0,0 +1,5 @@ +# Credentials for tests. Buildbot source this file on tests +# when needed. + +# Docker registry credentials. Format: 'username:password' +export DOCKER_CREDS='' diff --git a/components/engine/testing/buildbot/master.cfg b/components/engine/testing/buildbot/master.cfg index 61912808ec..29926dbe5f 100644 --- a/components/engine/testing/buildbot/master.cfg +++ b/components/engine/testing/buildbot/master.cfg @@ -19,6 +19,7 @@ TEST_USER = 'buildbot' # Credential to authenticate build triggers TEST_PWD = 'docker' # Credential to authenticate build triggers BUILDER_NAME = 'docker' GITHUB_DOCKER = 'github.com/dotcloud/docker' +BUILDBOT_PATH = '/data/buildbot' DOCKER_PATH = '/data/docker' BUILDER_PATH = '/data/buildbot/slave/{0}/build'.format(BUILDER_NAME) DOCKER_BUILD_PATH = BUILDER_PATH + '/src/github.com/dotcloud/docker' @@ -41,16 +42,19 @@ c['db'] = {'db_url':"sqlite:///state.sqlite"} c['slaves'] = [BuildSlave('buildworker', BUILDBOT_PWD)] c['slavePortnum'] = PORT_MASTER + # Schedulers c['schedulers'] = [ForceScheduler(name='trigger', builderNames=[BUILDER_NAME, - 'coverage'])] + 'registry','coverage'])] c['schedulers'] += [SingleBranchScheduler(name="all", change_filter=filter.ChangeFilter(branch='master'), treeStableTimer=None, builderNames=[BUILDER_NAME])] -c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['coverage'], +c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['coverage','registry'], hour=0, minute=30)] + # Builders +# Docker commit test factory = BuildFactory() factory.addStep(ShellCommand(description='Docker',logEnviron=False,usePTY=True, command=["sh", "-c", Interpolate("cd ..; rm -rf build; export GOPATH={0}; " @@ -58,6 +62,7 @@ factory.addStep(ShellCommand(description='Docker',logEnviron=False,usePTY=True, "go test -v".format(BUILDER_PATH,GITHUB_DOCKER,DOCKER_BUILD_PATH))])) c['builders'] = [BuilderConfig(name=BUILDER_NAME,slavenames=['buildworker'], factory=factory)] + # Docker coverage test coverage_cmd = ('GOPATH=`pwd` go get -d github.com/dotcloud/docker\n' 'GOPATH=`pwd` go get github.com/axw/gocov/gocov\n' @@ -69,6 +74,17 @@ factory.addStep(ShellCommand(description='Coverage',logEnviron=False,usePTY=True c['builders'] += [BuilderConfig(name='coverage',slavenames=['buildworker'], factory=factory)] +# Registry Functionaltest builder +factory = BuildFactory() +factory.addStep(ShellCommand(description='registry', logEnviron=False, + command='. {0}/master/credentials.cfg; ' + '{1}/testing/functionaltests/test_registry.sh'.format(BUILDBOT_PATH, + DOCKER_PATH), usePTY=True)) + +c['builders'] += [BuilderConfig(name='registry',slavenames=['buildworker'], + factory=factory)] + + # Status authz_cfg = authz.Authz(auth=auth.BasicAuth([(TEST_USER, TEST_PWD)]), forceBuild='auth') diff --git a/components/engine/testing/buildbot/requirements.txt b/components/engine/testing/buildbot/requirements.txt index 0e451b017d..4e183ba062 100644 --- a/components/engine/testing/buildbot/requirements.txt +++ b/components/engine/testing/buildbot/requirements.txt @@ -4,3 +4,4 @@ buildbot==0.8.7p1 buildbot_slave==0.8.7p1 nose==1.2.1 requests==1.1.0 +flask==0.10.1 diff --git a/components/engine/testing/buildbot/setup_credentials.sh b/components/engine/testing/buildbot/setup_credentials.sh new file mode 100755 index 0000000000..f093815d60 --- /dev/null +++ b/components/engine/testing/buildbot/setup_credentials.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Setup of test credentials. Called by Vagrantfile +export PATH="/bin:sbin:/usr/bin:/usr/sbin:/usr/local/bin" + +USER=$1 +REGISTRY_USER=$2 +REGISTRY_PWD=$3 + +BUILDBOT_PATH="/data/buildbot" +DOCKER_PATH="/data/docker" + +function run { su $USER -c "$1"; } + +run "cp $DOCKER_PATH/testing/buildbot/credentials.cfg $BUILDBOT_PATH/master" +cd $BUILDBOT_PATH/master +run "sed -i -E 's#(export DOCKER_CREDS=).+#\1\"$REGISTRY_USER:$REGISTRY_PWD\"#' credentials.cfg" diff --git a/components/engine/testing/functionaltests/test_registry.sh b/components/engine/testing/functionaltests/test_registry.sh new file mode 100755 index 0000000000..095a731631 --- /dev/null +++ b/components/engine/testing/functionaltests/test_registry.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Cleanup +rm -rf docker-registry + +# Get latest docker registry +git clone https://github.com/dotcloud/docker-registry.git + +# Configure and run registry tests +cd docker-registry; cp config_sample.yml config.yml +cd test; python -m unittest workflow From dc883510210f5a92f0a21ad468df47e382116799 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 29 Jul 2013 11:03:09 -0700 Subject: [PATCH 04/17] Make sure ADD will create everything in 0755 Upstream-commit: f7542664e3efb35b6b153dc2b2cc9cdac181989a Component: engine --- components/engine/archive.go | 2 +- components/engine/docs/sources/use/builder.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/archive.go b/components/engine/archive.go index 01af86006f..bb79cd34d4 100644 --- a/components/engine/archive.go +++ b/components/engine/archive.go @@ -173,7 +173,7 @@ func CopyWithTar(src, dst string) error { } // Create dst, copy src's content into it utils.Debugf("Creating dest directory: %s", dst) - if err := os.MkdirAll(dst, 0700); err != nil && !os.IsExist(err) { + if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) { return err } utils.Debugf("Calling TarUntar(%s, %s)", src, dst) diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index aa2fd6b92a..9f5ee8b795 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/docs/sources/use/builder.rst @@ -182,7 +182,7 @@ The copy obeys the following rules: written at ````. * If ```` doesn't exist, it is created along with all missing directories in its path. All new files and directories are created - with mode 0700, uid and gid 0. + with mode 0755, uid and gid 0. 3.8 ENTRYPOINT -------------- From fb6e560276b5e8f6bd32f112d4e3bbdd39302c53 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 30 Jul 2013 13:47:29 +0200 Subject: [PATCH 05/17] add ufw doc Upstream-commit: 7d0b8c726c0d178291063751ac735d6caebfb45a Component: engine --- .../docs/sources/installation/ubuntulinux.rst | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/components/engine/docs/sources/installation/ubuntulinux.rst b/components/engine/docs/sources/installation/ubuntulinux.rst index ed592d3a9d..299b7a29e9 100644 --- a/components/engine/docs/sources/installation/ubuntulinux.rst +++ b/components/engine/docs/sources/installation/ubuntulinux.rst @@ -19,6 +19,8 @@ Docker has the following dependencies * Linux kernel 3.8 (read more about :ref:`kernel`) * AUFS file system support (we are working on BTRFS support as an alternative) +Please read :ref:`ufw`, if you plan to use `UFW (Uncomplicated Firewall) `_ + .. _ubuntu_precise: Ubuntu Precise 12.04 (LTS) (64-bit) @@ -135,3 +137,35 @@ Verify it worked **Done!**, now continue with the :ref:`hello_world` example. + + +.. _ufw: + +Docker and UFW +^^^^^^^^^^^^^^ + +Docker uses a bridge to manage containers networking, by default UFW drop all `forwarding`, a first step is to enable forwarding: + +.. code-block:: bash + + sudo nano /etc/default/ufw + ---- + # Change: + # DEFAULT_FORWARD_POLICY="DROP" + # to + DEFAULT_FORWARD_POLICY="ACCEPT" + +Then reload UFW: + +.. code-block:: bash + + sudo ufw reload + + +UFW's default set of rules denied all `incoming`, so if you want to be able to reach your containers from another host, +you should allow incoming connexions on the docker port (default 4243): + +.. code-block:: bash + + sudo ufw allow 4243/tcp + From c7d245ec6d1824f5980ac9918eb086ed02cc4e1b Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 30 Jul 2013 16:39:35 +0000 Subject: [PATCH 06/17] Add check that the request is good Upstream-commit: e4752c8c1a09fc3cc96dbb9be7183b271db3d6b7 Component: engine --- components/engine/utils/utils.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index 190dc175cd..def5ae5a50 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -736,6 +736,9 @@ func GetReleaseVersion() string { return "" } defer resp.Body.Close() + if resp.ContentLength > 24 || resp.StatusCode != 200 { + return "" + } body, err := ioutil.ReadAll(resp.Body) if err != nil { return "" From 554c357a3ee4b84a513a3e7458c062c12714fb33 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 30 Jul 2013 17:18:19 +0000 Subject: [PATCH 07/17] update http://get.docker.io/latest Upstream-commit: e66e0289abc213164dca1e1eadfb0380b6e81904 Component: engine --- components/engine/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/Makefile b/components/engine/Makefile index 6050000582..dd365dc30e 100644 --- a/components/engine/Makefile +++ b/components/engine/Makefile @@ -50,6 +50,7 @@ release: $(BINRELEASE) s3cmd -P put $(BINRELEASE) s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-$(RELEASE_VERSION).tgz s3cmd -P put docker-latest.tgz s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-latest.tgz s3cmd -P put $(SRCRELEASE)/bin/docker s3://get.docker.io/builds/`uname -s`/`uname -m`/docker + echo $(RELEASE_VERSION) > latest ; s3cmd -P put latest s3://get.docker.io/latest ; rm latest srcrelease: $(SRCRELEASE) deps: $(DOCKER_DIR) @@ -65,7 +66,6 @@ $(BINRELEASE): $(SRCRELEASE) rm -f $(BINRELEASE) cd $(SRCRELEASE); make; cp -R bin docker-$(RELEASE_VERSION); tar -f ../$(BINRELEASE) -zv -c docker-$(RELEASE_VERSION) cd $(SRCRELEASE); cp -R bin docker-latest; tar -f ../docker-latest.tgz -zv -c docker-latest - clean: @rm -rf $(dir $(DOCKER_BIN)) ifeq ($(GOPATH), $(BUILD_DIR)) From 7aa4663949e8f79b0adb7e8f8d71126631c7596a Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 24 Jul 2013 03:01:24 +0000 Subject: [PATCH 08/17] Return registy status code in error Added Details map to the JSONMessage Upstream-commit: 3043c2641990d94298c6377b7ef14709263a4709 Component: engine --- components/engine/api.go | 12 +++++++---- components/engine/commands.go | 29 +++++++++++++++++--------- components/engine/registry/registry.go | 24 ++++++++++----------- components/engine/utils/error.go | 18 ++++++++++++++++ components/engine/utils/utils.go | 26 ++++++++++++++++------- 5 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 components/engine/utils/error.go diff --git a/components/engine/api.go b/components/engine/api.go index 4ad2ba461a..5869669df0 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -388,7 +388,7 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht if image != "" { //pull if err := srv.ImagePull(image, tag, w, sf, &auth.AuthConfig{}); err != nil { if sf.Used() { - w.Write(sf.FormatError(err)) + w.Write(sf.FormatError(err, 0)) return nil } return err @@ -396,7 +396,7 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht } else { //import if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil { if sf.Used() { - w.Write(sf.FormatError(err)) + w.Write(sf.FormatError(err, 0)) return nil } return err @@ -441,7 +441,7 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht imgID, err := srv.ImageInsert(name, url, path, w, sf) if err != nil { if sf.Used() { - w.Write(sf.FormatError(err)) + w.Write(sf.FormatError(err, 0)) return nil } } @@ -472,7 +472,11 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http sf := utils.NewStreamFormatter(version > 1.0) if err := srv.ImagePush(name, w, sf, authConfig); err != nil { if sf.Used() { - w.Write(sf.FormatError(err)) + var code int + if httpErr, ok := err.(*utils.HTTPRequestError); ok { + code = httpErr.StatusCode + } + w.Write(sf.FormatError(err, code)) return nil } return err diff --git a/components/engine/commands.go b/components/engine/commands.go index 95ddca1f1d..9ad2c367ae 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -30,7 +30,8 @@ import ( const VERSION = "0.5.0-dev" var ( - GITCOMMIT string + GITCOMMIT string + AuthRequiredError error = fmt.Errorf("Authentication is required.") ) func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) { @@ -814,10 +815,6 @@ func (cli *DockerCli) CmdPush(args ...string) error { return nil } - if err := cli.checkIfLogged("push"); err != nil { - return err - } - // If we're not using a custom registry, we know the restrictions // applied to repository names and can warn the user in advance. // Custom repositories can have different rules, and we must also @@ -826,13 +823,22 @@ func (cli *DockerCli) CmdPush(args ...string) error { return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in / (ex: %s/%s)", cli.configFile.Configs[auth.IndexServerAddress()].Username, name) } - buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()]) - if err != nil { - return err + v := url.Values{} + push := func() error { + buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()]) + if err != nil { + return err + } + + return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out) } - v := url.Values{} - if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out); err != nil { + if err := push(); err != nil { + if err == AuthRequiredError { + if err = cli.checkIfLogged("push"); err == nil { + return push() + } + } return err } return nil @@ -1559,6 +1565,9 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e } else if err != nil { return err } + if jm.Error != nil && jm.Error.Code == 401 { + return AuthRequiredError + } jm.Display(out) } } else { diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index 4e9dd8895f..ed6f4c7df8 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -147,7 +147,7 @@ func (r *Registry) GetRemoteHistory(imgID, registry string, token []string) ([]s res, err := doWithCookies(r.client, req) if err != nil || res.StatusCode != 200 { if res != nil { - return nil, fmt.Errorf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgID) + return nil, utils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res) } return nil, err } @@ -197,7 +197,7 @@ func (r *Registry) GetRemoteImageJSON(imgID, registry string, token []string) ([ } defer res.Body.Close() if res.StatusCode != 200 { - return nil, -1, fmt.Errorf("HTTP code %d", res.StatusCode) + return nil, -1, utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d", res.StatusCode), res) } imageSize, err := strconv.Atoi(res.Header.Get("X-Docker-Size")) @@ -289,12 +289,12 @@ func (r *Registry) GetRepositoryData(indexEp, remote string) (*RepositoryData, e } defer res.Body.Close() if res.StatusCode == 401 { - return nil, fmt.Errorf("Please login first (HTTP code %d)", res.StatusCode) + return nil, utils.NewHTTPRequestError(fmt.Sprintf("Please login first (HTTP code %d)", res.StatusCode), res) } // TODO: Right now we're ignoring checksums in the response body. // In the future, we need to use them to check image validity. if res.StatusCode != 200 { - return nil, fmt.Errorf("HTTP code: %d", res.StatusCode) + return nil, utils.NewHTTPRequestError(fmt.Sprintf("HTTP code: %d", res.StatusCode), res) } var tokens []string @@ -391,7 +391,7 @@ func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regis if res.StatusCode != 200 { errBody, err := ioutil.ReadAll(res.Body) if err != nil { - return fmt.Errorf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err) + return utils.NewHTTPRequestError(fmt.Sprint("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res) } var jsonBody map[string]string if err := json.Unmarshal(errBody, &jsonBody); err != nil { @@ -399,7 +399,7 @@ func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regis } else if jsonBody["error"] == "Image already exists" { return ErrAlreadyExists } - return fmt.Errorf("HTTP code %d while uploading metadata: %s", res.StatusCode, errBody) + return utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata: %s", res.StatusCode, errBody), res) } return nil } @@ -427,9 +427,9 @@ func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registr if res.StatusCode != 200 { errBody, err := ioutil.ReadAll(res.Body) if err != nil { - return "", fmt.Errorf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err) + return utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res) } - return "", fmt.Errorf("Received HTTP code %d while uploading layer: %s", res.StatusCode, errBody) + return utils.NewHTTPRequestError(fmt.Sprintf("Received HTTP code %d while uploading layer: %s", res.StatusCode, errBody), res) } return tarsumLayer.Sum(jsonRaw), nil } @@ -463,7 +463,7 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token } res.Body.Close() if res.StatusCode != 200 && res.StatusCode != 201 { - return fmt.Errorf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote) + return utils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote), res) } return nil } @@ -540,7 +540,7 @@ func (r *Registry) PushImageJSONIndex(indexEp, remote string, imgList []*ImgData if err != nil { return nil, err } - return nil, fmt.Errorf("Error: Status %d trying to push repository %s: %s", res.StatusCode, remote, errBody) + return nil, utils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %s", res.StatusCode, remote, errBody), res) } if res.Header.Get("X-Docker-Token") != "" { tokens = res.Header["X-Docker-Token"] @@ -564,7 +564,7 @@ func (r *Registry) PushImageJSONIndex(indexEp, remote string, imgList []*ImgData if err != nil { return nil, err } - return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody) + return nil, utils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody), res) } } @@ -586,7 +586,7 @@ func (r *Registry) SearchRepositories(term string) (*SearchResults, error) { } defer res.Body.Close() if res.StatusCode != 200 { - return nil, fmt.Errorf("Unexepected status code %d", res.StatusCode) + return nil, utils.NewHTTPRequestError(fmt.Sprintf("Unexepected status code %d", res.StatusCode), res) } rawData, err := ioutil.ReadAll(res.Body) if err != nil { diff --git a/components/engine/utils/error.go b/components/engine/utils/error.go new file mode 100644 index 0000000000..7e3c846ebc --- /dev/null +++ b/components/engine/utils/error.go @@ -0,0 +1,18 @@ +package utils + +import ( + "net/http" +) + +type HTTPRequestError struct { + Message string + StatusCode int +} + +func (e *HTTPRequestError) Error() string { + return e.Message +} + +func NewHTTPRequestError(msg string, resp *http.Response) error { + return &HTTPRequestError{Message: msg, StatusCode: resp.StatusCode} +} diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index c70e80b72e..659d960d0a 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -607,12 +607,22 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher { return &WriteFlusher{w: w, flusher: flusher} } +type JSONError struct { + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` +} + type JSONMessage struct { - Status string `json:"status,omitempty"` - Progress string `json:"progress,omitempty"` - Error string `json:"error,omitempty"` - ID string `json:"id,omitempty"` - Time int64 `json:"time,omitempty"` + Status string `json:"status,omitempty"` + Progress string `json:"progress,omitempty"` + ErrorMessage string `json:"error,omitempty"` //deprecated + ID string `json:"id,omitempty"` + Time int64 `json:"time,omitempty"` + Error *JSONError `json:"errorDetail,omitempty"` +} + +func (e *JSONError) Error() string { + return e.Message } func (jm *JSONMessage) Display(out io.Writer) error { @@ -621,8 +631,8 @@ func (jm *JSONMessage) Display(out io.Writer) error { } if jm.Progress != "" { fmt.Fprintf(out, "%s %s\r", jm.Status, jm.Progress) - } else if jm.Error != "" { - return fmt.Errorf(jm.Error) + } else if jm.Error != nil { + return jm.Error } else if jm.ID != "" { fmt.Fprintf(out, "%s: %s\n", jm.ID, jm.Status) } else { @@ -656,7 +666,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte func (sf *StreamFormatter) FormatError(err error) []byte { sf.used = true if sf.json { - if b, err := json.Marshal(&JSONMessage{Error: err.Error()}); err == nil { + if b, err := json.Marshal(&JSONMessage{Error: &JSONError{Code: code, Message: err.Error()}, ErrorMessage: err.Error()}); err == nil { return b } return []byte("{\"error\":\"format error\"}") From 30e6d541d0b27da785ccbb43ffb848adc62488b9 Mon Sep 17 00:00:00 2001 From: Nolan Date: Tue, 30 Jul 2013 13:23:34 -0500 Subject: [PATCH 09/17] Add hostname to the container environment. Upstream-commit: 9a604acc23d30d00ae907acdf756cbcdf0e4cf83 Component: engine --- components/engine/container.go | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/container.go b/components/engine/container.go index d610c3c7d4..ccc7ab3e9f 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -652,6 +652,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { "-e", "HOME=/", "-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "-e", "container=lxc", + "-e", "HOSTNAME="+container.Config.Hostname, ) for _, elem := range container.Config.Env { From d69f94df65007e156adc9bc9fed2c65e1dd958aa Mon Sep 17 00:00:00 2001 From: Thatcher Peskens Date: Wed, 31 Jul 2013 12:17:42 -0700 Subject: [PATCH 10/17] Solved the logo being squished in Safari Upstream-commit: e0c24ccfc37b64eb919c5675d8a8f383201fa7cb Component: engine --- components/engine/docs/theme/docker/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/docs/theme/docker/layout.html b/components/engine/docs/theme/docker/layout.html index 0b22f22fab..ca26f44dc0 100755 --- a/components/engine/docs/theme/docker/layout.html +++ b/components/engine/docs/theme/docker/layout.html @@ -79,7 +79,7 @@
- +
From da0dfa9ebd66eff0cbd7708bcd77e21dca48936d Mon Sep 17 00:00:00 2001 From: Tobias Schmidt Date: Fri, 2 Aug 2013 12:24:38 +0700 Subject: [PATCH 11/17] Move note about officially supported kernel It seems this a general note about kernel issues and not specific to Cgroups or namespaces. Upstream-commit: 2424480e2ce14d848c041c53cc041aab91308081 Component: engine --- .../engine/docs/sources/installation/kernel.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/components/engine/docs/sources/installation/kernel.rst b/components/engine/docs/sources/installation/kernel.rst index 58730f8191..7c5715a62d 100644 --- a/components/engine/docs/sources/installation/kernel.rst +++ b/components/engine/docs/sources/installation/kernel.rst @@ -15,12 +15,11 @@ In short, Docker has the following kernel requirements: - Cgroups and namespaces must be enabled. - - The officially supported kernel is the one recommended by the - :ref:`ubuntu_linux` installation path. It is the one that most developers - will use, and the one that receives the most attention from the core - contributors. If you decide to go with a different kernel and hit a bug, - please try to reproduce it with the official kernels first. +The officially supported kernel is the one recommended by the +:ref:`ubuntu_linux` installation path. It is the one that most developers +will use, and the one that receives the most attention from the core +contributors. If you decide to go with a different kernel and hit a bug, +please try to reproduce it with the official kernels first. If you cannot or do not want to use the "official" kernels, here is some technical background about the features (both optional and From ac12d9b409aec16a0cfbade7ed576e6bf8832b82 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 2 Aug 2013 16:18:54 +0000 Subject: [PATCH 12/17] Add no cache for docker build Add a new flag to disable the image cache when building images. Upstream-commit: 3a123bc479457c4dfa14e39b7c42d9a9dccf8c32 Component: engine --- components/engine/api.go | 7 ++- components/engine/buildfile.go | 55 +++++++++++-------- components/engine/buildfile_test.go | 2 +- components/engine/commands.go | 4 ++ .../sources/api/docker_remote_api_v1.4.rst | 1 + .../sources/commandline/command/build.rst | 1 + 6 files changed, 44 insertions(+), 26 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index b8b7897c32..95b9c98de8 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -793,6 +793,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ remoteURL := r.FormValue("remote") repoName := r.FormValue("t") rawSuppressOutput := r.FormValue("q") + rawNoCache := r.FormValue("nocache") repoName, tag := utils.ParseRepositoryTag(repoName) var context io.Reader @@ -839,8 +840,12 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ if err != nil { return err } + noCache, err := getBoolParam(rawNoCache) + if err != nil { + return err + } - b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput) + b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache) id, err := b.Build(context) if err != nil { fmt.Fprintf(w, "Error build: %s\n", err) diff --git a/components/engine/buildfile.go b/components/engine/buildfile.go index 736725e915..159d7ba704 100644 --- a/components/engine/buildfile.go +++ b/components/engine/buildfile.go @@ -26,11 +26,12 @@ type buildFile struct { builder *Builder srv *Server - image string - maintainer string - config *Config - context string - verbose bool + image string + maintainer string + config *Config + context string + verbose bool + utilizeCache bool tmpContainers map[string]struct{} tmpImages map[string]struct{} @@ -94,15 +95,17 @@ func (b *buildFile) CmdRun(args string) error { utils.Debugf("Command to be executed: %v", b.config.Cmd) - if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { - return err - } else if cache != nil { - fmt.Fprintf(b.out, " ---> Using cache\n") - utils.Debugf("[BUILDER] Use cached version") - b.image = cache.ID - return nil - } else { - utils.Debugf("[BUILDER] Cache miss") + if b.utilizeCache { + if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { + return err + } else if cache != nil { + fmt.Fprintf(b.out, " ---> Using cache\n") + utils.Debugf("[BUILDER] Use cached version") + b.image = cache.ID + return nil + } else { + utils.Debugf("[BUILDER] Cache miss") + } } cid, err := b.run() @@ -397,16 +400,19 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment} defer func(cmd []string) { b.config.Cmd = cmd }(cmd) - if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { - return err - } else if cache != nil { - fmt.Fprintf(b.out, " ---> Using cache\n") - utils.Debugf("[BUILDER] Use cached version") - b.image = cache.ID - return nil - } else { - utils.Debugf("[BUILDER] Cache miss") + if b.utilizeCache { + if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { + return err + } else if cache != nil { + fmt.Fprintf(b.out, " ---> Using cache\n") + utils.Debugf("[BUILDER] Use cached version") + b.image = cache.ID + return nil + } else { + utils.Debugf("[BUILDER] Cache miss") + } } + container, err := b.builder.Create(b.config) if err != nil { return err @@ -500,7 +506,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) { return "", fmt.Errorf("An error occured during the build\n") } -func NewBuildFile(srv *Server, out io.Writer, verbose bool) BuildFile { +func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache bool) BuildFile { return &buildFile{ builder: NewBuilder(srv.runtime), runtime: srv.runtime, @@ -510,5 +516,6 @@ func NewBuildFile(srv *Server, out io.Writer, verbose bool) BuildFile { tmpContainers: make(map[string]struct{}), tmpImages: make(map[string]struct{}), verbose: verbose, + utilizeCache: utilizeCache, } } diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 78e53b8419..eda047d8ea 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -227,7 +227,7 @@ func buildImage(context testContextTemplate, t *testing.T) *Image { ip := runtime.networkManager.bridgeNetwork.IP dockerfile := constructDockerfile(context.dockerfile, ip, port) - buildfile := NewBuildFile(srv, ioutil.Discard, false) + buildfile := NewBuildFile(srv, ioutil.Discard, false, true) id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t)) if err != nil { t.Fatal(err) diff --git a/components/engine/commands.go b/components/engine/commands.go index 95ddca1f1d..7a1935ee61 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -160,6 +160,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH") tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success") suppressOutput := cmd.Bool("q", false, "Suppress verbose build output") + noCache := cmd.Bool("no-cache", false, "Do not use cache when building the image") if err := cmd.Parse(args); err != nil { return nil @@ -208,6 +209,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error { if isRemote { v.Set("remote", cmd.Arg(0)) } + if *noCache { + v.Set("nocache", "1") + } req, err := http.NewRequest("POST", fmt.Sprintf("/v%g/build?%s", APIVERSION, v.Encode()), body) if err != nil { return err diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.4.rst b/components/engine/docs/sources/api/docker_remote_api_v1.4.rst index 6ee0b35fa2..6830bacde0 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.4.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.4.rst @@ -928,6 +928,7 @@ Build an image from Dockerfile via stdin :query t: tag to be applied to the resulting image in case of success :query q: suppress verbose build output + :query nocache: do not use the cache when building the image :statuscode 200: no error :statuscode 500: server error diff --git a/components/engine/docs/sources/commandline/command/build.rst b/components/engine/docs/sources/commandline/command/build.rst index 45b6d2ec8e..fcd78dd1ab 100644 --- a/components/engine/docs/sources/commandline/command/build.rst +++ b/components/engine/docs/sources/commandline/command/build.rst @@ -12,6 +12,7 @@ Build a new container image from the source code at PATH -t="": Tag to be applied to the resulting image in case of success. -q=false: Suppress verbose build output. + -no-cache: Do not use the cache when building the image. When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context From b7d9cc36c6037696ffb6d7f0a495f9150123bec6 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 2 Aug 2013 19:12:38 +0000 Subject: [PATCH 13/17] Add unit tests for build no cache Upstream-commit: b9f06959244e3f77eb212c7c234b06eb7b750999 Component: engine --- components/engine/buildfile_test.go | 102 ++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index eda047d8ea..0e2d9ecefc 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -195,21 +195,23 @@ func mkTestingFileServer(files [][2]string) (*httptest.Server, error) { func TestBuild(t *testing.T) { for _, ctx := range testContexts { - buildImage(ctx, t) + buildImage(ctx, t, nil, true) } } -func buildImage(context testContextTemplate, t *testing.T) *Image { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } - defer nuke(runtime) +func buildImage(context testContextTemplate, t *testing.T, srv *Server, useCache bool) *Image { + if srv == nil { + runtime, err := newTestRuntime() + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) - srv := &Server{ - runtime: runtime, - pullingPool: make(map[string]struct{}), - pushingPool: make(map[string]struct{}), + srv = &Server{ + runtime: runtime, + pullingPool: make(map[string]struct{}), + pushingPool: make(map[string]struct{}), + } } httpServer, err := mkTestingFileServer(context.remoteFiles) @@ -224,10 +226,10 @@ func buildImage(context testContextTemplate, t *testing.T) *Image { } port := httpServer.URL[idx+1:] - ip := runtime.networkManager.bridgeNetwork.IP + ip := srv.runtime.networkManager.bridgeNetwork.IP dockerfile := constructDockerfile(context.dockerfile, ip, port) - buildfile := NewBuildFile(srv, ioutil.Discard, false, true) + buildfile := NewBuildFile(srv, ioutil.Discard, false, useCache) id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t)) if err != nil { t.Fatal(err) @@ -245,7 +247,7 @@ func TestVolume(t *testing.T) { from {IMAGE} volume /test cmd Hello world - `, nil, nil}, t) + `, nil, nil}, t, nil, true) if len(img.Config.Volumes) == 0 { t.Fail() @@ -261,7 +263,7 @@ func TestBuildMaintainer(t *testing.T) { img := buildImage(testContextTemplate{` from {IMAGE} maintainer dockerio - `, nil, nil}, t) + `, nil, nil}, t, nil, true) if img.Author != "dockerio" { t.Fail() @@ -273,7 +275,7 @@ func TestBuildEnv(t *testing.T) { from {IMAGE} env port 4243 `, - nil, nil}, t) + nil, nil}, t, nil, true) hasEnv := false for _, envVar := range img.Config.Env { if envVar == "port=4243" { @@ -291,7 +293,7 @@ func TestBuildCmd(t *testing.T) { from {IMAGE} cmd ["/bin/echo", "Hello World"] `, - nil, nil}, t) + nil, nil}, t, nil, true) if img.Config.Cmd[0] != "/bin/echo" { t.Log(img.Config.Cmd[0]) @@ -308,7 +310,7 @@ func TestBuildExpose(t *testing.T) { from {IMAGE} expose 4243 `, - nil, nil}, t) + nil, nil}, t, nil, true) if img.Config.PortSpecs[0] != "4243" { t.Fail() @@ -320,8 +322,70 @@ func TestBuildEntrypoint(t *testing.T) { from {IMAGE} entrypoint ["/bin/echo"] `, - nil, nil}, t) + nil, nil}, t, nil, true) if img.Config.Entrypoint[0] != "/bin/echo" { } } + +func TestBuildImageWithCache(t *testing.T) { + runtime, err := newTestRuntime() + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) + + srv := &Server{ + runtime: runtime, + pullingPool: make(map[string]struct{}), + pushingPool: make(map[string]struct{}), + } + + template := testContextTemplate{` + from {IMAGE} + maintainer dockerio + `, + nil, nil} + + img := buildImage(template, t, srv, true) + imageId := img.ID + + img = nil + img = buildImage(template, t, srv, true) + + if imageId != img.ID { + t.Logf("Image ids should match: %s != %s", imageId, img.ID) + t.Fail() + } +} + +func TestBuildImageWithoutCache(t *testing.T) { + runtime, err := newTestRuntime() + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) + + srv := &Server{ + runtime: runtime, + pullingPool: make(map[string]struct{}), + pushingPool: make(map[string]struct{}), + } + + template := testContextTemplate{` + from {IMAGE} + maintainer dockerio + `, + nil, nil} + + img := buildImage(template, t, srv, true) + imageId := img.ID + + img = nil + img = buildImage(template, t, srv, false) + + if imageId == img.ID { + t.Logf("Image ids should not match: %s == %s", imageId, img.ID) + t.Fail() + } +} From 48643d0043c3ed12e37db9aa4f4ea315edf889b8 Mon Sep 17 00:00:00 2001 From: Jonathan Rudenberg Date: Fri, 2 Aug 2013 19:18:02 -0300 Subject: [PATCH 14/17] Revert "Bind daemon to 0.0.0.0 in Vagrant. Fixes #1304" This reverts commit bdc79ac8b2dfad302f9e144711067a566726cfa2. Upstream-commit: 07fee445593982ef9063a184c05ba93ecacd2e65 Component: engine --- components/engine/Vagrantfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/engine/Vagrantfile b/components/engine/Vagrantfile index 7258af5bf7..aadabb8711 100644 --- a/components/engine/Vagrantfile +++ b/components/engine/Vagrantfile @@ -20,8 +20,6 @@ Vagrant::Config.run do |config| pkg_cmd = "apt-get update -qq; apt-get install -q -y python-software-properties; " \ "add-apt-repository -y ppa:dotcloud/lxc-docker; apt-get update -qq; " \ "apt-get install -q -y lxc-docker; " - # Listen on all interfaces so that the daemon is accessible from the host - pkg_cmd << "sed -i -E 's| /usr/bin/docker -d| /usr/bin/docker -d -H 0.0.0.0|' /etc/init/docker.conf;" # Add X.org Ubuntu backported 3.8 kernel pkg_cmd << "add-apt-repository -y ppa:ubuntu-x-swat/r-lts-backport; " \ "apt-get update -qq; apt-get install -q -y linux-image-3.8.0-19-generic; " From 71b5dff29912802026a8b6fb58d4150c4c9ef224 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 2 Aug 2013 15:23:36 -0700 Subject: [PATCH 15/17] Consider empty /etc/resolv.conf as local dns + add unit test Upstream-commit: 3e9575e275c40acb04c505fa14c1ac63ba490b75 Component: engine --- components/engine/api.go | 7 +++++- components/engine/builder.go | 7 +++++- components/engine/utils/utils.go | 24 ++++++++++++++----- components/engine/utils/utils_test.go | 34 +++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index 95b9c98de8..a48b11c771 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -488,7 +488,12 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r return err } - if len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns() { + resolvConf, err := utils.GetResolvConf() + if err != nil { + return err + } + + if len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns)) config.Dns = defaultDns } diff --git a/components/engine/builder.go b/components/engine/builder.go index 420370b1e6..82ad1c1271 100644 --- a/components/engine/builder.go +++ b/components/engine/builder.go @@ -80,7 +80,12 @@ func (builder *Builder) Create(config *Config) (*Container, error) { return nil, err } - if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns() { + resolvConf, err := utils.GetResolvConf() + if err != nil { + return nil, err + } + + if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { //"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns builder.runtime.Dns = defaultDns } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index def5ae5a50..74b0e12c36 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -688,17 +688,29 @@ func IsGIT(str string) bool { return strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "github.com/") } -func CheckLocalDns() bool { +// GetResolvConf opens and read the content of /etc/resolv.conf. +// It returns it as byte slice. +func GetResolvConf() ([]byte, error) { resolv, err := ioutil.ReadFile("/etc/resolv.conf") if err != nil { Debugf("Error openning resolv.conf: %s", err) - return false + return nil, err } - for _, ip := range []string{ - "127.0.0.1", - "127.0.1.1", + return resolv, nil +} + +// CheckLocalDns looks into the /etc/resolv.conf, +// it returns true if there is a local nameserver or if there is no nameserver. +func CheckLocalDns(resolvConf []byte) bool { + if !bytes.Contains(resolvConf, []byte("nameserver")) { + return true + } + + for _, ip := range [][]byte{ + []byte("127.0.0.1"), + []byte("127.0.1.1"), } { - if strings.Contains(string(resolv), ip) { + if bytes.Contains(resolvConf, ip) { return true } } diff --git a/components/engine/utils/utils_test.go b/components/engine/utils/utils_test.go index 5c480b9438..882165ac54 100644 --- a/components/engine/utils/utils_test.go +++ b/components/engine/utils/utils_test.go @@ -303,3 +303,37 @@ func TestParseRepositoryTag(t *testing.T) { t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "tag", repo, tag) } } + +func TestGetResolvConf(t *testing.T) { + resolvConfUtils, err := GetResolvConf() + if err != nil { + t.Fatal(err) + } + resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") + if err != nil { + t.Fatal(err) + } + if string(resolvConfUtils) != string(resolvConfSystem) { + t.Fatalf("/etc/resolv.conf and GetResolvConf have different content.") + } +} + +func TestCheclLocalDns(t *testing.T) { + for resolv, result := range map[string]bool{`# Dynamic +nameserver 10.0.2.3 +search dotcloud.net`: false, + `# Dynamic +nameserver 127.0.0.1 +search dotcloud.net`: true, + `# Dynamic +nameserver 127.0.1.1 +search dotcloud.net`: true, + `# Dynamic +`: true, + ``: true, + } { + if CheckLocalDns([]byte(resolv)) != result { + t.Fatalf("Wrong local dns detection: {%s} should be %v", resolv, result) + } + } +} From 46b9c958878fd716ab26af38c3192dffe1c5cc1b Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 2 Aug 2013 15:58:10 -0700 Subject: [PATCH 16/17] Fix TestEnv Upstream-commit: dde8f74ceae83f26386ec29e42f615fdc7945e80 Component: engine --- components/engine/container_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/container_test.go b/components/engine/container_test.go index a1ac0bd33a..f29ae9e4ea 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -960,6 +960,7 @@ func TestEnv(t *testing.T) { "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "HOME=/", "container=lxc", + "HOSTNAME=" + container.ShortID(), } sort.Strings(goodEnv) if len(goodEnv) != len(actualEnv) { From 3029f070e629c91386d2bb05829f925f1749aff3 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 30 Jul 2013 22:48:20 +0000 Subject: [PATCH 17/17] Return JSONError for HTTPResponse error Upstream-commit: dae585c6e4c19817b2dbd106171728a0bb564ccc Component: engine --- components/engine/api.go | 12 ++++-------- components/engine/commands.go | 2 +- components/engine/registry/registry.go | 4 ++-- components/engine/utils/error.go | 18 ------------------ components/engine/utils/utils.go | 13 ++++++++++++- components/engine/utils_test.go | 2 +- 6 files changed, 20 insertions(+), 31 deletions(-) delete mode 100644 components/engine/utils/error.go diff --git a/components/engine/api.go b/components/engine/api.go index 5869669df0..4ad2ba461a 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -388,7 +388,7 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht if image != "" { //pull if err := srv.ImagePull(image, tag, w, sf, &auth.AuthConfig{}); err != nil { if sf.Used() { - w.Write(sf.FormatError(err, 0)) + w.Write(sf.FormatError(err)) return nil } return err @@ -396,7 +396,7 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht } else { //import if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil { if sf.Used() { - w.Write(sf.FormatError(err, 0)) + w.Write(sf.FormatError(err)) return nil } return err @@ -441,7 +441,7 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht imgID, err := srv.ImageInsert(name, url, path, w, sf) if err != nil { if sf.Used() { - w.Write(sf.FormatError(err, 0)) + w.Write(sf.FormatError(err)) return nil } } @@ -472,11 +472,7 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http sf := utils.NewStreamFormatter(version > 1.0) if err := srv.ImagePush(name, w, sf, authConfig); err != nil { if sf.Used() { - var code int - if httpErr, ok := err.(*utils.HTTPRequestError); ok { - code = httpErr.StatusCode - } - w.Write(sf.FormatError(err, code)) + w.Write(sf.FormatError(err)) return nil } return err diff --git a/components/engine/commands.go b/components/engine/commands.go index 9ad2c367ae..355e91f4bf 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -31,7 +31,7 @@ const VERSION = "0.5.0-dev" var ( GITCOMMIT string - AuthRequiredError error = fmt.Errorf("Authentication is required.") + AuthRequiredError = fmt.Errorf("Authentication is required.") ) func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) { diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index ed6f4c7df8..5b8480d183 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -427,9 +427,9 @@ func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registr if res.StatusCode != 200 { errBody, err := ioutil.ReadAll(res.Body) if err != nil { - return utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res) + return "", utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res) } - return utils.NewHTTPRequestError(fmt.Sprintf("Received HTTP code %d while uploading layer: %s", res.StatusCode, errBody), res) + return "", utils.NewHTTPRequestError(fmt.Sprintf("Received HTTP code %d while uploading layer: %s", res.StatusCode, errBody), res) } return tarsumLayer.Sum(jsonRaw), nil } diff --git a/components/engine/utils/error.go b/components/engine/utils/error.go deleted file mode 100644 index 7e3c846ebc..0000000000 --- a/components/engine/utils/error.go +++ /dev/null @@ -1,18 +0,0 @@ -package utils - -import ( - "net/http" -) - -type HTTPRequestError struct { - Message string - StatusCode int -} - -func (e *HTTPRequestError) Error() string { - return e.Message -} - -func NewHTTPRequestError(msg string, resp *http.Response) error { - return &HTTPRequestError{Message: msg, StatusCode: resp.StatusCode} -} diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index 659d960d0a..2323829f65 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -625,6 +625,13 @@ func (e *JSONError) Error() string { return e.Message } +func NewHTTPRequestError(msg string, res *http.Response) error { + return &JSONError{ + Message: msg, + Code: res.StatusCode, + } +} + func (jm *JSONMessage) Display(out io.Writer) error { if jm.Time != 0 { fmt.Fprintf(out, "[%s] ", time.Unix(jm.Time, 0)) @@ -666,7 +673,11 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte func (sf *StreamFormatter) FormatError(err error) []byte { sf.used = true if sf.json { - if b, err := json.Marshal(&JSONMessage{Error: &JSONError{Code: code, Message: err.Error()}, ErrorMessage: err.Error()}); err == nil { + jsonError, ok := err.(*JSONError) + if !ok { + jsonError = &JSONError{Message: err.Error()} + } + if b, err := json.Marshal(&JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil { return b } return []byte("{\"error\":\"format error\"}") diff --git a/components/engine/utils_test.go b/components/engine/utils_test.go index c4adeb4a74..91df6183fc 100644 --- a/components/engine/utils_test.go +++ b/components/engine/utils_test.go @@ -191,7 +191,7 @@ func TestMergeConfig(t *testing.T) { if len(configUser.Volumes) != 3 { t.Fatalf("Expected 3 volumes, /test1, /test2 and /test3, found %d", len(configUser.Volumes)) } - for v, _ := range configUser.Volumes { + for v := range configUser.Volumes { if v != "/test1" && v != "/test2" && v != "/test3" { t.Fatalf("Expected /test1 or /test2 or /test3, found %s", v) }