mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-19 20:26:54 +00:00
Compare commits
1 Commits
master
..
v1.8.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f1c66a50e |
+4
-17
@@ -1,12 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## 1.8.1 (2015-08-12)
|
||||
|
||||
### Distribution
|
||||
|
||||
* Fix a bug where pushing multiple tags would result in invalid images
|
||||
|
||||
## 1.8.0 (2015-08-11)
|
||||
## 1.8.0 (2015-07-24)
|
||||
|
||||
### Distribution
|
||||
|
||||
@@ -42,7 +36,7 @@
|
||||
+ Add logging driver for GELF via UDP
|
||||
+ Allow to copy files from host to containers with `docker cp`
|
||||
+ Promote volume drivers from experimental to master
|
||||
+ Add rollover options to json-file log driver, and --log-driver-opts flag
|
||||
+ Add rollover log driver, and --log-driver-opts flag
|
||||
+ Add memory swappiness tuning options
|
||||
* Remove cgroup read-only flag when privileged
|
||||
* Make /proc, /sys, & /dev readonly for readonly containers
|
||||
@@ -53,7 +47,7 @@
|
||||
* Remove RC4 from the list of registry cipher suites
|
||||
* Add syslog-facility option
|
||||
* LXC execdriver compatibility with recent LXC versions
|
||||
* Mark LXC execriver as deprecated (to be removed with the migration to runc)
|
||||
|
||||
|
||||
### Plugins
|
||||
|
||||
@@ -63,7 +57,7 @@
|
||||
### Bug fixes
|
||||
|
||||
- Add missing 'Names' field to /containers/json API output
|
||||
- Make `docker rmi` of dangling images safe while pulling
|
||||
- Make `docker rmi --dangling` safe when pulling
|
||||
- Devicemapper: Change default basesize to 100G
|
||||
- Go Scheduler issue with sync.Mutex and gcc
|
||||
- Fix issue where Search API endpoint would panic due to empty AuthConfig
|
||||
@@ -89,13 +83,6 @@
|
||||
- Remove panic in nat package on invalid hostport
|
||||
- Fix container linking in Fedora 22
|
||||
- Fix error caused using default gateways outside of the allocated range
|
||||
- Format times in inspect command with a template as RFC3339Nano
|
||||
- Make registry client to accept 2xx and 3xx http status responses as successful
|
||||
- Fix race issue that caused the daemon to crash with certain layer downloads failed in a specific order.
|
||||
- Fix error when the docker ps format was not valid.
|
||||
- Remove redundant ip forward check.
|
||||
- Fix issue trying to push images to repository mirrors.
|
||||
- Fix error cleaning up network entrypoints when there is an initialization issue.
|
||||
|
||||
## 1.7.1 (2015-07-14)
|
||||
|
||||
|
||||
+3
-4
@@ -40,7 +40,6 @@ RUN apt-get update && apt-get install -y \
|
||||
createrepo \
|
||||
curl \
|
||||
dpkg-sig \
|
||||
gcc-mingw-w64 \
|
||||
git \
|
||||
iptables \
|
||||
libapparmor-dev \
|
||||
@@ -128,7 +127,7 @@ RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint
|
||||
RUN gem install --no-rdoc --no-ri fpm --version 1.3.2
|
||||
|
||||
# Install registry
|
||||
ENV REGISTRY_COMMIT ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||
ENV REGISTRY_COMMIT 2317f721a3d8428215a2b65da4ae85212ed473b4
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||
@@ -138,7 +137,7 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_COMMIT 8e8122eb5528f621afcd4e2854c47302f17392f7
|
||||
ENV NOTARY_COMMIT 77bced079e83d80f40c1f0a544b1a8a3b97fb052
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
@@ -148,7 +147,7 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 139850f3f3b17357bab5ba3edfb745fb14043764
|
||||
ENV DOCKER_PY_COMMIT 8a87001d09852058f08a807ab6e8491d57ca1e88
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT
|
||||
|
||||
+1
-1
@@ -309,7 +309,7 @@ made through a pull request.
|
||||
# - If the pull request has some small problems that need to be changed, make
|
||||
# a comment adressing the issues.
|
||||
# - If the changes needed to a PR are small, you can add a "LGTM once the
|
||||
# following comments are addressed..." this will reduce needless back and
|
||||
# following comments are adressed..." this will reduce needless back and
|
||||
# forth.
|
||||
# - If the PR only needs a few changes before being merged, any MAINTAINER can
|
||||
# make a replacement PR that incorporates the existing commits and fixes the
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
Docker: the container engine [](https://github.com/docker/docker/releases/latest)
|
||||
============================
|
||||
Docker: the Linux container engine
|
||||
==================================
|
||||
|
||||
Docker is an open source project to pack, ship and run any application
|
||||
as a lightweight container.
|
||||
|
||||
Docker containers are both *hardware-agnostic* and *platform-agnostic*.
|
||||
This means they can run anywhere, from your laptop to the largest
|
||||
cloud compute instance and everything in between - and they don't require
|
||||
EC2 compute instance and everything in between - and they don't require
|
||||
you to use a particular language, framework or packaging system. That
|
||||
makes them great building blocks for deploying and scaling web apps,
|
||||
databases, and backend services without depending on a particular stack
|
||||
or provider.
|
||||
|
||||
Docker began as an open-source implementation of the deployment engine which
|
||||
powers [dotCloud](https://www.dotcloud.com), a popular Platform-as-a-Service.
|
||||
powers [dotCloud](https://dotcloud.com), a popular Platform-as-a-Service.
|
||||
It benefits directly from the experience accumulated over several years
|
||||
of large-scale operation and support of hundreds of thousands of
|
||||
applications and databases.
|
||||
@@ -174,14 +174,13 @@ Under the hood, Docker is built on the following components:
|
||||
capabilities of the Linux kernel
|
||||
* The [Go](https://golang.org) programming language
|
||||
* The [Docker Image Specification](https://github.com/docker/docker/blob/master/image/spec/v1.md)
|
||||
* The [Libcontainer Specification](https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md)
|
||||
* The [Libcontainer Specification](https://github.com/docker/libcontainer/blob/master/SPEC.md)
|
||||
|
||||
Contributing to Docker [](https://godoc.org/github.com/docker/docker)
|
||||
Contributing to Docker
|
||||
======================
|
||||
|
||||
| **Master** (Linux) | **Experimental** (linux) | **Windows** | **FreeBSD** |
|
||||
|------------------|----------------------|---------|---------|
|
||||
| [](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master/) | [](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master%20%28experimental%29/) | [/badge/icon)](http://jenkins.dockerproject.org/job/Docker%20Master%20(windows)/) | [/badge/icon)](http://jenkins.dockerproject.org/job/Docker%20Master%20(freebsd)/) |
|
||||
[](https://godoc.org/github.com/docker/docker)
|
||||
[](https://jenkins.dockerproject.org/job/Docker%20Master/)
|
||||
|
||||
Want to hack on Docker? Awesome! We have [instructions to help you get
|
||||
started contributing code or documentation](https://docs.docker.com/project/who-written-for/).
|
||||
@@ -248,7 +247,7 @@ We are always open to suggestions on process improvements, and are always lookin
|
||||
<tr>
|
||||
<td>Stack Overflow</td>
|
||||
<td>
|
||||
Stack Overflow has over 7000 Docker questions listed. We regularly
|
||||
Stack Overflow has over 7000K Docker questions listed. We regularly
|
||||
monitor <a href="https://stackoverflow.com/search?tab=newest&q=docker" target="_blank">Docker questions</a>
|
||||
and so do many other knowledgeable Docker users.
|
||||
</td>
|
||||
@@ -294,7 +293,3 @@ Docker on Mac and Windows
|
||||
|
||||
If you know of another project underway that should be listed here, please help
|
||||
us keep this list up-to-date by submitting a PR.
|
||||
|
||||
Awesome-Docker
|
||||
==============
|
||||
You can find more projects, tools and articles related to Docker on the [awesome-docker list](https://github.com/veggiemonk/awesome-docker). Add your project there.
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ We won't accept patches expanding the surface of `docker exec`, which we intend
|
||||
|
||||
## 2.2 Dockerfile syntax
|
||||
|
||||
The Dockerfile syntax as we know it is simple, and has proven successful in supporting all our
|
||||
The Dockerfile syntax as we know it is simple, and has proven succesful in supporting all our
|
||||
[official images](https://github.com/docker-library/official-images). Although this is *not* a
|
||||
definitive move, we temporarily won't accept more patches to the Dockerfile syntax for several
|
||||
reasons:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJsonContentType(t *testing.T) {
|
||||
if !MatchesContentType("application/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if !MatchesContentType("application/json; charset=utf-8", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if MatchesContentType("dockerapplication/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
+24
-95
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/progressreader"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/ulimit"
|
||||
"github.com/docker/docker/pkg/units"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
@@ -59,8 +60,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
|
||||
flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
|
||||
flCPUShares := cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
||||
flCPUPeriod := cmd.Int64([]string{"-cpu-period"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||
flCPUQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||
flCpuPeriod := cmd.Int64([]string{"-cpu-period"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||
flCpuQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||
flCPUSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||
flCPUSetMems := cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
|
||||
flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
||||
@@ -114,9 +115,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
}
|
||||
|
||||
// Resolve the FROM lines in the Dockerfile to trusted digest references
|
||||
// using Notary. On a successful build, we must tag the resolved digests
|
||||
// to the original name specified in the Dockerfile.
|
||||
newDockerfile, resolvedTags, err := rewriteDockerfileFrom(filepath.Join(contextDir, relDockerfile), cli.trustedReference)
|
||||
// using Notary.
|
||||
newDockerfile, err := rewriteDockerfileFrom(filepath.Join(contextDir, relDockerfile), cli.trustedReference)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to process Dockerfile: %v", err)
|
||||
}
|
||||
@@ -242,8 +242,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
v.Set("cpusetcpus", *flCPUSetCpus)
|
||||
v.Set("cpusetmems", *flCPUSetMems)
|
||||
v.Set("cpushares", strconv.FormatInt(*flCPUShares, 10))
|
||||
v.Set("cpuquota", strconv.FormatInt(*flCPUQuota, 10))
|
||||
v.Set("cpuperiod", strconv.FormatInt(*flCPUPeriod, 10))
|
||||
v.Set("cpuquota", strconv.FormatInt(*flCpuQuota, 10))
|
||||
v.Set("cpuperiod", strconv.FormatInt(*flCpuPeriod, 10))
|
||||
v.Set("memory", strconv.FormatInt(memory, 10))
|
||||
v.Set("memswap", strconv.FormatInt(memorySwap, 10))
|
||||
v.Set("cgroupparent", *flCgroupParent)
|
||||
@@ -251,11 +251,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
v.Set("dockerfile", relDockerfile)
|
||||
|
||||
ulimitsVar := flUlimits.GetList()
|
||||
ulimitsJSON, err := json.Marshal(ulimitsVar)
|
||||
ulimitsJson, err := json.Marshal(ulimitsVar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Set("ulimits", string(ulimitsJSON))
|
||||
v.Set("ulimits", string(ulimitsJson))
|
||||
|
||||
headers := http.Header(make(map[string][]string))
|
||||
buf, err := json.Marshal(cli.configFile.AuthConfigs)
|
||||
@@ -291,26 +291,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
}
|
||||
return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Since the build was successful, now we must tag any of the resolved
|
||||
// images from the above Dockerfile rewrite.
|
||||
for _, resolved := range resolvedTags {
|
||||
if err := cli.tagTrusted(resolved.repoInfo, resolved.digestRef, resolved.tagRef); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isUNC returns true if the path is UNC (one starting \\). It always returns
|
||||
// false on Linux.
|
||||
func isUNC(path string) bool {
|
||||
return runtime.GOOS == "windows" && strings.HasPrefix(path, `\\`)
|
||||
return err
|
||||
}
|
||||
|
||||
// getDockerfileRelPath uses the given context directory for a `docker build`
|
||||
@@ -321,29 +302,6 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
||||
return "", "", fmt.Errorf("unable to get absolute context directory: %v", err)
|
||||
}
|
||||
|
||||
// The context dir might be a symbolic link, so follow it to the actual
|
||||
// target directory.
|
||||
//
|
||||
// FIXME. We use isUNC (always false on non-Windows platforms) to workaround
|
||||
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||
// paths (those starting with \\). This hack means that when using links
|
||||
// on UNC paths, they will not be followed.
|
||||
if !isUNC(absContextDir) {
|
||||
absContextDir, err = filepath.EvalSymlinks(absContextDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("unable to evaluate symlinks in context path: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
stat, err := os.Lstat(absContextDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("unable to stat context directory %q: %v", absContextDir, err)
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
return "", "", fmt.Errorf("context must be a directory: %s", absContextDir)
|
||||
}
|
||||
|
||||
absDockerfile := givenDockerfile
|
||||
if absDockerfile == "" {
|
||||
// No -f/--file was specified so use the default relative to the
|
||||
@@ -366,22 +324,15 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
||||
absDockerfile = filepath.Join(absContextDir, absDockerfile)
|
||||
}
|
||||
|
||||
// Evaluate symlinks in the path to the Dockerfile too.
|
||||
//
|
||||
// FIXME. We use isUNC (always false on non-Windows platforms) to workaround
|
||||
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||
// paths (those starting with \\). This hack means that when using links
|
||||
// on UNC paths, they will not be followed.
|
||||
if !isUNC(absDockerfile) {
|
||||
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
||||
}
|
||||
// Verify that 'filename' is within the build context
|
||||
absDockerfile, err = symlink.FollowSymlinkInScope(absDockerfile, absContextDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", givenDockerfile, givenContextDir)
|
||||
}
|
||||
|
||||
if _, err := os.Lstat(absDockerfile); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", "", fmt.Errorf("Cannot locate Dockerfile: %q", absDockerfile)
|
||||
return "", "", fmt.Errorf("Cannot locate Dockerfile: absDockerfile: %q", absDockerfile)
|
||||
}
|
||||
return "", "", fmt.Errorf("unable to stat Dockerfile: %v", err)
|
||||
}
|
||||
@@ -390,10 +341,6 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
||||
return "", "", fmt.Errorf("unable to get relative Dockerfile path: %v", err)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||
return "", "", fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", givenDockerfile, givenContextDir)
|
||||
}
|
||||
|
||||
return absContextDir, relDockerfile, nil
|
||||
}
|
||||
|
||||
@@ -445,7 +392,7 @@ func getContextFromReader(r io.Reader, dockerfileName string) (absContextDir, re
|
||||
}
|
||||
|
||||
if err := archive.Untar(buf, absContextDir, nil); err != nil {
|
||||
return "", "", fmt.Errorf("unable to extract stdin to temporary context directory: %v", err)
|
||||
return "", "", fmt.Errorf("unable to extract stdin to temporary context direcotry: %v", err)
|
||||
}
|
||||
|
||||
return getDockerfileRelPath(absContextDir, dockerfileName)
|
||||
@@ -482,7 +429,7 @@ func getContextFromURL(out io.Writer, remoteURL, dockerfileName string) (absCont
|
||||
In: response.Body,
|
||||
Out: out,
|
||||
Formatter: streamformatter.NewStreamFormatter(),
|
||||
Size: response.ContentLength,
|
||||
Size: int(response.ContentLength),
|
||||
NewLines: true,
|
||||
ID: "",
|
||||
Action: fmt.Sprintf("Downloading build context from remote url: %s", remoteURL),
|
||||
@@ -520,21 +467,14 @@ func (td *trustedDockerfile) Close() error {
|
||||
return os.Remove(td.File.Name())
|
||||
}
|
||||
|
||||
// resolvedTag records the repository, tag, and resolved digest reference
|
||||
// from a Dockerfile rewrite.
|
||||
type resolvedTag struct {
|
||||
repoInfo *registry.RepositoryInfo
|
||||
digestRef, tagRef registry.Reference
|
||||
}
|
||||
|
||||
// rewriteDockerfileFrom rewrites the given Dockerfile by resolving images in
|
||||
// "FROM <image>" instructions to a digest reference. `translator` is a
|
||||
// function that takes a repository name and tag reference and returns a
|
||||
// trusted digest reference.
|
||||
func rewriteDockerfileFrom(dockerfileName string, translator func(string, registry.Reference) (registry.Reference, error)) (newDockerfile *trustedDockerfile, resolvedTags []*resolvedTag, err error) {
|
||||
func rewriteDockerfileFrom(dockerfileName string, translator func(string, registry.Reference) (registry.Reference, error)) (newDockerfile *trustedDockerfile, err error) {
|
||||
dockerfile, err := os.Open(dockerfileName)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to open Dockerfile: %v", err)
|
||||
return nil, fmt.Errorf("unable to open Dockerfile: %v", err)
|
||||
}
|
||||
defer dockerfile.Close()
|
||||
|
||||
@@ -543,7 +483,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
|
||||
// Make a tempfile to store the rewritten Dockerfile.
|
||||
tempFile, err := ioutil.TempFile("", "trusted-dockerfile-")
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to make temporary trusted Dockerfile: %v", err)
|
||||
return nil, fmt.Errorf("unable to make temporary trusted Dockerfile: %v", err)
|
||||
}
|
||||
|
||||
trustedFile := &trustedDockerfile{
|
||||
@@ -567,34 +507,23 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
|
||||
// Replace the line with a resolved "FROM repo@digest"
|
||||
repo, tag := parsers.ParseRepositoryTag(matches[1])
|
||||
if tag == "" {
|
||||
tag = tags.DefaultTag
|
||||
tag = tags.DEFAULTTAG
|
||||
}
|
||||
|
||||
repoInfo, err := registry.ParseRepositoryInfo(repo)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to parse repository info: %v", err)
|
||||
}
|
||||
|
||||
ref := registry.ParseReference(tag)
|
||||
|
||||
if !ref.HasDigest() && isTrusted() {
|
||||
trustedRef, err := translator(repo, ref)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.ImageName(repo)))
|
||||
resolvedTags = append(resolvedTags, &resolvedTag{
|
||||
repoInfo: repoInfo,
|
||||
digestRef: trustedRef,
|
||||
tagRef: ref,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
n, err := fmt.Fprintln(tempFile, line)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trustedFile.size += int64(n)
|
||||
@@ -602,7 +531,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
|
||||
|
||||
tempFile.Seek(0, os.SEEK_SET)
|
||||
|
||||
return trustedFile, resolvedTags, scanner.Err()
|
||||
return trustedFile, scanner.Err()
|
||||
}
|
||||
|
||||
// replaceDockerfileTarWrapper wraps the given input tar archive stream and
|
||||
|
||||
@@ -58,8 +58,6 @@ type DockerCli struct {
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
// Initialize calls the init function that will setup the configuration for the client
|
||||
// such as the TLS, tcp and other parameters used to run the client.
|
||||
func (cli *DockerCli) Initialize() error {
|
||||
if cli.init == nil {
|
||||
return nil
|
||||
@@ -79,8 +77,6 @@ func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PsFormat returns the format string specified in the configuration.
|
||||
// String contains columns and format specification, for example {{ID}\t{{Name}}.
|
||||
func (cli *DockerCli) PsFormat() string {
|
||||
return cli.configFile.PsFormat
|
||||
}
|
||||
@@ -98,7 +94,6 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
|
||||
}
|
||||
|
||||
cli.init = func() error {
|
||||
|
||||
clientFlags.PostParse()
|
||||
|
||||
hosts := clientFlags.Common.Hosts
|
||||
|
||||
+11
-31
@@ -15,7 +15,6 @@ import (
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
type copyDirection int
|
||||
@@ -102,7 +101,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
|
||||
// client, a `:` could be part of an absolute Windows path, in which case it
|
||||
// is immediately proceeded by a backslash.
|
||||
func splitCpArg(arg string) (container, path string) {
|
||||
if system.IsAbs(arg) {
|
||||
if filepath.IsAbs(arg) {
|
||||
// Explicit local absolute path, e.g., `C:\foo` or `/foo`.
|
||||
return "", arg
|
||||
}
|
||||
@@ -233,20 +232,6 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
||||
// Prepare destination copy info by stat-ing the container path.
|
||||
dstInfo := archive.CopyInfo{Path: dstPath}
|
||||
dstStat, err := cli.statContainerPath(dstContainer, dstPath)
|
||||
|
||||
// If the destination is a symbolic link, we should evaluate it.
|
||||
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
|
||||
linkTarget := dstStat.LinkTarget
|
||||
if !system.IsAbs(linkTarget) {
|
||||
// Join with the parent directory.
|
||||
dstParent, _ := archive.SplitPathDirEntry(dstPath)
|
||||
linkTarget = filepath.Join(dstParent, linkTarget)
|
||||
}
|
||||
|
||||
dstInfo.Path = linkTarget
|
||||
dstStat, err = cli.statContainerPath(dstContainer, linkTarget)
|
||||
}
|
||||
|
||||
// Ignore any error and assume that the parent directory of the destination
|
||||
// path exists, in which case the copy may still succeed. If there is any
|
||||
// type of conflict (e.g., non-directory overwriting an existing directory
|
||||
@@ -257,26 +242,15 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
||||
dstInfo.Exists, dstInfo.IsDir = true, dstStat.Mode.IsDir()
|
||||
}
|
||||
|
||||
var (
|
||||
content io.Reader
|
||||
resolvedDstPath string
|
||||
)
|
||||
|
||||
var content io.Reader
|
||||
if srcPath == "-" {
|
||||
// Use STDIN.
|
||||
content = os.Stdin
|
||||
resolvedDstPath = dstInfo.Path
|
||||
if !dstInfo.IsDir {
|
||||
return fmt.Errorf("destination %q must be a directory", fmt.Sprintf("%s:%s", dstContainer, dstPath))
|
||||
}
|
||||
} else {
|
||||
// Prepare source copy info.
|
||||
srcInfo, err := archive.CopyInfoSourcePath(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcArchive, err := archive.TarResource(srcInfo)
|
||||
srcArchive, err := archive.TarResource(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -288,6 +262,12 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
||||
// it to the specified directory in the container we get the disired
|
||||
// copy behavior.
|
||||
|
||||
// Prepare source copy info.
|
||||
srcInfo, err := archive.CopyInfoStatPath(srcPath, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// See comments in the implementation of `archive.PrepareArchiveCopy`
|
||||
// for exactly what goes into deciding how and whether the source
|
||||
// archive needs to be altered for the correct copy behavior when it is
|
||||
@@ -300,12 +280,12 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
||||
}
|
||||
defer preparedArchive.Close()
|
||||
|
||||
resolvedDstPath = dstDir
|
||||
dstPath = dstDir
|
||||
content = preparedArchive
|
||||
}
|
||||
|
||||
query := make(url.Values, 2)
|
||||
query.Set("path", filepath.ToSlash(resolvedDstPath)) // Normalize the paths used in the API.
|
||||
query.Set("path", filepath.ToSlash(dstPath)) // Normalize the paths used in the API.
|
||||
// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
|
||||
query.Set("noOverwriteDirNonDir", "true")
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
|
||||
repos, tag := parsers.ParseRepositoryTag(image)
|
||||
// pull only the image tagged 'latest' if no tag was specified
|
||||
if tag == "" {
|
||||
tag = tags.DefaultTag
|
||||
tag = tags.DEFAULTTAG
|
||||
}
|
||||
v.Set("fromImage", repos)
|
||||
v.Set("tag", tag)
|
||||
@@ -96,7 +96,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
|
||||
|
||||
repo, tag := parsers.ParseRepositoryTag(config.Image)
|
||||
if tag == "" {
|
||||
tag = tags.DefaultTag
|
||||
tag = tags.DEFAULTTAG
|
||||
}
|
||||
|
||||
ref := registry.ParseReference(tag)
|
||||
|
||||
@@ -2,6 +2,7 @@ package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
@@ -21,7 +22,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var (
|
||||
output = cli.out
|
||||
output io.Writer = cli.out
|
||||
err error
|
||||
)
|
||||
if *outfile != "" {
|
||||
|
||||
@@ -167,7 +167,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
|
||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
//
|
||||
// Usage: docker images [OPTIONS] [REPOSITORY]
|
||||
func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
cmd := Cli.Subcmd("images", []string{"[REPOSITORY[:TAG]]"}, "List images", true)
|
||||
cmd := Cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true)
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
|
||||
all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
|
||||
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
|
||||
|
||||
@@ -23,7 +23,6 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
||||
cmd := Cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
|
||||
flChanges := opts.NewListOpts(nil)
|
||||
cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
|
||||
message := cmd.String([]string{"m", "-message"}, "", "Set commit message for imported image")
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
@@ -36,7 +35,6 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
||||
|
||||
v.Set("fromSrc", src)
|
||||
v.Set("repo", repository)
|
||||
v.Set("message", *message)
|
||||
for _, change := range flChanges.GetAll() {
|
||||
v.Add("changes", change)
|
||||
}
|
||||
|
||||
+4
-4
@@ -61,8 +61,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", info.DockerRootDir)
|
||||
}
|
||||
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Http Proxy: %s\n", info.HTTPProxy)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Https Proxy: %s\n", info.HTTPSProxy)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Http Proxy: %s\n", info.HttpProxy)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Https Proxy: %s\n", info.HttpsProxy)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "No Proxy: %s\n", info.NoProxy)
|
||||
|
||||
if info.IndexServerAddress != "" {
|
||||
@@ -82,12 +82,12 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
|
||||
}
|
||||
if !info.IPv4Forwarding {
|
||||
fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled\n")
|
||||
fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
|
||||
}
|
||||
if !info.BridgeNfIptables {
|
||||
fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-iptables is disabled\n")
|
||||
}
|
||||
if !info.BridgeNfIP6tables {
|
||||
if !info.BridgeNfIp6tables {
|
||||
fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-ip6tables is disabled\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,13 @@ func (cli *DockerCli) CmdLogout(args ...string) error {
|
||||
|
||||
if _, ok := cli.configFile.AuthConfigs[serverAddress]; !ok {
|
||||
fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
|
||||
delete(cli.configFile.AuthConfigs, serverAddress)
|
||||
|
||||
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
|
||||
delete(cli.configFile.AuthConfigs, serverAddress)
|
||||
if err := cli.configFile.Save(); err != nil {
|
||||
return fmt.Errorf("Failed to save docker config: %v", err)
|
||||
if err := cli.configFile.Save(); err != nil {
|
||||
return fmt.Errorf("Failed to save docker config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
nwclient "github.com/docker/libnetwork/client"
|
||||
)
|
||||
|
||||
// CmdNetwork is used to create, display and configure network endpoints.
|
||||
func (cli *DockerCli) CmdNetwork(args ...string) error {
|
||||
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
|
||||
args = append([]string{"network"}, args...)
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
||||
|
||||
f := *format
|
||||
if len(f) == 0 {
|
||||
if len(cli.PsFormat()) > 0 && !*quiet {
|
||||
if len(cli.PsFormat()) > 0 {
|
||||
f = cli.PsFormat()
|
||||
} else {
|
||||
f = "table"
|
||||
|
||||
+4
-10
@@ -64,9 +64,6 @@ func (c *containerContext) Image() string {
|
||||
if c.c.Image == "" {
|
||||
return "<no image>"
|
||||
}
|
||||
if c.trunc {
|
||||
return stringutils.Truncate(c.c.Image, 12)
|
||||
}
|
||||
return c.c.Image
|
||||
}
|
||||
|
||||
@@ -173,11 +170,9 @@ func customFormat(ctx Context, containers []types.Container) {
|
||||
format += "\t{{.Size}}"
|
||||
}
|
||||
|
||||
tmpl, err := template.New("").Parse(format)
|
||||
tmpl, err := template.New("ps template").Parse(format)
|
||||
if err != nil {
|
||||
buffer.WriteString(fmt.Sprintf("Template parsing error: %v\n", err))
|
||||
buffer.WriteTo(ctx.Output)
|
||||
return
|
||||
buffer.WriteString(fmt.Sprintf("Invalid `docker ps` format: %v\n", err))
|
||||
}
|
||||
|
||||
for _, container := range containers {
|
||||
@@ -186,9 +181,8 @@ func customFormat(ctx Context, containers []types.Container) {
|
||||
c: container,
|
||||
}
|
||||
if err := tmpl.Execute(buffer, containerCtx); err != nil {
|
||||
buffer = bytes.NewBufferString(fmt.Sprintf("Template parsing error: %v\n", err))
|
||||
buffer.WriteTo(ctx.Output)
|
||||
return
|
||||
buffer = bytes.NewBufferString(fmt.Sprintf("Invalid `docker ps` format: %v\n", err))
|
||||
break
|
||||
}
|
||||
if table && len(header) == 0 {
|
||||
header = containerCtx.fullHeader()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ps
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -11,8 +10,8 @@ import (
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
)
|
||||
|
||||
func TestContainerPsContext(t *testing.T) {
|
||||
containerID := stringid.GenerateRandomID()
|
||||
func TestContainerContextID(t *testing.T) {
|
||||
containerId := stringid.GenerateRandomID()
|
||||
unix := time.Now().Unix()
|
||||
|
||||
var ctx containerContext
|
||||
@@ -23,12 +22,12 @@ func TestContainerPsContext(t *testing.T) {
|
||||
expHeader string
|
||||
call func() string
|
||||
}{
|
||||
{types.Container{ID: containerID}, true, stringid.TruncateID(containerID), idHeader, ctx.ID},
|
||||
{types.Container{ID: containerId}, true, stringid.TruncateID(containerId), idHeader, ctx.ID},
|
||||
{types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", namesHeader, ctx.Names},
|
||||
{types.Container{Image: "ubuntu"}, true, "ubuntu", imageHeader, ctx.Image},
|
||||
{types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image},
|
||||
{types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command},
|
||||
{types.Container{Created: unix}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
|
||||
{types.Container{Created: int(unix)}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
|
||||
{types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports},
|
||||
{types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status},
|
||||
{types.Container{SizeRw: 10}, true, "10 B", sizeHeader, ctx.Size},
|
||||
@@ -87,16 +86,3 @@ func TestContainerPsContext(t *testing.T) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerPsFormatError(t *testing.T) {
|
||||
out := bytes.NewBufferString("")
|
||||
ctx := Context{
|
||||
Format: "{{InvalidFunction}}",
|
||||
Output: out,
|
||||
}
|
||||
|
||||
customFormat(ctx, make([]types.Container, 0))
|
||||
if out.String() != "Template parsing error: template: :1: function \"InvalidFunction\" not defined\n" {
|
||||
t.Fatalf("Expected format error, got `%v`\n", out.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,22 +14,14 @@ const (
|
||||
defaultQuietFormat = "{{.ID}}"
|
||||
)
|
||||
|
||||
// Context contains information required by the formatter to print the output as desired.
|
||||
type Context struct {
|
||||
// Output is the output stream to which the formatted string is written.
|
||||
Output io.Writer
|
||||
// Format is used to choose raw, table or custom format for the output.
|
||||
Format string
|
||||
// Size when set to true will display the size of the output.
|
||||
Size bool
|
||||
// Quiet when set to true will simply print minimal information.
|
||||
Quiet bool
|
||||
// Trunc when set to true will truncate the output of certain fields such as Container ID.
|
||||
Trunc bool
|
||||
Size bool
|
||||
Quiet bool
|
||||
Trunc bool
|
||||
}
|
||||
|
||||
// Format helps to format the output using the parameters set in the Context.
|
||||
// Currently Format allow to display in raw, table or custom format the output.
|
||||
func Format(ctx Context, containers []types.Container) {
|
||||
switch ctx.Format {
|
||||
case tableFormatKey:
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
||||
|
||||
taglessRemote, tag := parsers.ParseRepositoryTag(remote)
|
||||
if tag == "" && !*allTags {
|
||||
tag = tags.DefaultTag
|
||||
tag = tags.DEFAULTTAG
|
||||
fmt.Fprintf(cli.out, "Using default tag: %s\n", tag)
|
||||
} else if tag != "" && *allTags {
|
||||
return fmt.Errorf("tag can't be used with --all-tags/-a")
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// CmdRestart restarts one or more running containers.
|
||||
//
|
||||
// Usage: docker restart [OPTIONS] CONTAINER [CONTAINER...]
|
||||
// Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
|
||||
func (cli *DockerCli) CmdRestart(args ...string) error {
|
||||
cmd := Cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
|
||||
nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
|
||||
|
||||
+2
-2
@@ -63,11 +63,11 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(hostConfig.DNS) > 0 {
|
||||
if len(hostConfig.Dns) > 0 {
|
||||
// check the DNS settings passed via --dns against
|
||||
// localhost regexp to warn if they are trying to
|
||||
// set a DNS to a localhost address
|
||||
for _, dnsIP := range hostConfig.DNS {
|
||||
for _, dnsIP := range hostConfig.Dns {
|
||||
if dns.IsLocalhost(dnsIP) {
|
||||
fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
||||
break
|
||||
|
||||
+3
-2
@@ -2,6 +2,7 @@ package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
@@ -16,13 +17,13 @@ import (
|
||||
// Usage: docker save [OPTIONS] IMAGE [IMAGE...]
|
||||
func (cli *DockerCli) CmdSave(args ...string) error {
|
||||
cmd := Cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
|
||||
outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
|
||||
outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var (
|
||||
output = cli.out
|
||||
output io.Writer = cli.out
|
||||
err error
|
||||
)
|
||||
if *outfile != "" {
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
nwclient "github.com/docker/libnetwork/client"
|
||||
)
|
||||
|
||||
// CmdService is used to manage network services.
|
||||
// service command is user to publish, attach and list a service from a container.
|
||||
func (cli *DockerCli) CmdService(args ...string) error {
|
||||
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
|
||||
args = append([]string{"service"}, args...)
|
||||
|
||||
+8
-26
@@ -25,8 +25,6 @@ type containerStats struct {
|
||||
MemoryPercentage float64
|
||||
NetworkRx float64
|
||||
NetworkTx float64
|
||||
BlockRead float64
|
||||
BlockWrite float64
|
||||
mu sync.RWMutex
|
||||
err error
|
||||
}
|
||||
@@ -65,10 +63,9 @@ func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
|
||||
memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
|
||||
cpuPercent = 0.0
|
||||
)
|
||||
previousCPU = v.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem = v.PreCPUStats.SystemUsage
|
||||
previousCPU = v.PreCpuStats.CpuUsage.TotalUsage
|
||||
previousSystem = v.PreCpuStats.SystemUsage
|
||||
cpuPercent = calculateCPUPercent(previousCPU, previousSystem, v)
|
||||
blkRead, blkWrite := calculateBlockIO(v.BlkioStats)
|
||||
s.mu.Lock()
|
||||
s.CPUPercentage = cpuPercent
|
||||
s.Memory = float64(v.MemoryStats.Usage)
|
||||
@@ -76,8 +73,6 @@ func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
|
||||
s.MemoryPercentage = memPercent
|
||||
s.NetworkRx = float64(v.Network.RxBytes)
|
||||
s.NetworkTx = float64(v.Network.TxBytes)
|
||||
s.BlockRead = float64(blkRead)
|
||||
s.BlockWrite = float64(blkWrite)
|
||||
s.mu.Unlock()
|
||||
u <- nil
|
||||
if !streamStats {
|
||||
@@ -115,13 +110,12 @@ func (s *containerStats) Display(w io.Writer) error {
|
||||
if s.err != nil {
|
||||
return s.err
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%.2f%%\t%s / %s\t%.2f%%\t%s / %s\t%s / %s\n",
|
||||
fmt.Fprintf(w, "%s\t%.2f%%\t%s/%s\t%.2f%%\t%s/%s\n",
|
||||
s.Name,
|
||||
s.CPUPercentage,
|
||||
units.HumanSize(s.Memory), units.HumanSize(s.MemoryLimit),
|
||||
s.MemoryPercentage,
|
||||
units.HumanSize(s.NetworkRx), units.HumanSize(s.NetworkTx),
|
||||
units.HumanSize(s.BlockRead), units.HumanSize(s.BlockWrite))
|
||||
units.HumanSize(s.NetworkRx), units.HumanSize(s.NetworkTx))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -148,7 +142,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
|
||||
fmt.Fprint(cli.out, "\033[2J")
|
||||
fmt.Fprint(cli.out, "\033[H")
|
||||
}
|
||||
io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE / LIMIT\tMEM %\tNET I/O\tBLOCK I/O\n")
|
||||
io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O\n")
|
||||
}
|
||||
for _, n := range names {
|
||||
s := &containerStats{Name: n}
|
||||
@@ -196,25 +190,13 @@ func calculateCPUPercent(previousCPU, previousSystem uint64, v *types.Stats) flo
|
||||
var (
|
||||
cpuPercent = 0.0
|
||||
// calculate the change for the cpu usage of the container in between readings
|
||||
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage - previousCPU)
|
||||
cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCPU)
|
||||
// calculate the change for the entire system between readings
|
||||
systemDelta = float64(v.CPUStats.SystemUsage - previousSystem)
|
||||
systemDelta = float64(v.CpuStats.SystemUsage - previousSystem)
|
||||
)
|
||||
|
||||
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead uint64, blkWrite uint64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
case "read":
|
||||
blkRead = blkRead + bioEntry.Value
|
||||
case "write":
|
||||
blkWrite = blkWrite + bioEntry.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
func TestDisplay(t *testing.T) {
|
||||
@@ -17,8 +15,6 @@ func TestDisplay(t *testing.T) {
|
||||
MemoryPercentage: 100.0 / 2048.0 * 100.0,
|
||||
NetworkRx: 100 * 1024 * 1024,
|
||||
NetworkTx: 800 * 1024 * 1024,
|
||||
BlockRead: 100 * 1024 * 1024,
|
||||
BlockWrite: 800 * 1024 * 1024,
|
||||
mu: sync.RWMutex{},
|
||||
}
|
||||
var b bytes.Buffer
|
||||
@@ -26,21 +22,8 @@ func TestDisplay(t *testing.T) {
|
||||
t.Fatalf("c.Display() gave error: %s", err)
|
||||
}
|
||||
got := b.String()
|
||||
want := "app\t30.00%\t104.9 MB / 2.147 GB\t4.88%\t104.9 MB / 838.9 MB\t104.9 MB / 838.9 MB\n"
|
||||
want := "app\t30.00%\t104.9 MB/2.147 GB\t4.88%\t104.9 MB/838.9 MB\n"
|
||||
if got != want {
|
||||
t.Fatalf("c.Display() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculBlockIO(t *testing.T) {
|
||||
blkio := types.BlkioStats{
|
||||
IoServiceBytesRecursive: []types.BlkioStatEntry{{8, 0, "read", 1234}, {8, 1, "read", 4567}, {8, 0, "write", 123}, {8, 1, "write", 456}},
|
||||
}
|
||||
blkRead, blkWrite := calculateBlockIO(blkio)
|
||||
if blkRead != 5801 {
|
||||
t.Fatalf("blkRead = %d, want 5801", blkRead)
|
||||
}
|
||||
if blkWrite != 579 {
|
||||
t.Fatalf("blkWrite = %d, want 579", blkWrite)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-28
@@ -13,7 +13,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -177,16 +176,11 @@ func convertTarget(t client.Target) (target, error) {
|
||||
}
|
||||
|
||||
func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
|
||||
aliasMap := map[string]string{
|
||||
"root": "offline",
|
||||
"snapshot": "tagging",
|
||||
"targets": "tagging",
|
||||
}
|
||||
baseRetriever := passphrase.PromptRetrieverWithInOut(cli.in, cli.out, aliasMap)
|
||||
baseRetriever := passphrase.PromptRetrieverWithInOut(cli.in, cli.out)
|
||||
env := map[string]string{
|
||||
"root": os.Getenv("DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE"),
|
||||
"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE"),
|
||||
"targets": os.Getenv("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE"),
|
||||
"root": os.Getenv("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE"),
|
||||
"targets": os.Getenv("DOCKER_CONTENT_TRUST_TARGET_PASSPHRASE"),
|
||||
"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_SNAPSHOT_PASSPHRASE"),
|
||||
}
|
||||
return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
|
||||
if v := env[alias]; v != "" {
|
||||
@@ -317,22 +311,6 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
|
||||
return nil
|
||||
}
|
||||
|
||||
func selectKey(keys map[string]string) string {
|
||||
if len(keys) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
keyIDs := []string{}
|
||||
for k := range keys {
|
||||
keyIDs = append(keyIDs, k)
|
||||
}
|
||||
|
||||
// TODO(dmcgowan): let user choose if multiple keys, now pick consistently
|
||||
sort.Strings(keyIDs)
|
||||
|
||||
return keyIDs[0]
|
||||
}
|
||||
|
||||
func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) {
|
||||
r, w := io.Pipe()
|
||||
out := io.MultiWriter(in, w)
|
||||
@@ -431,13 +409,16 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string,
|
||||
|
||||
ks := repo.KeyStoreManager
|
||||
keys := ks.RootKeyStore().ListKeys()
|
||||
var rootKey string
|
||||
|
||||
rootKey := selectKey(keys)
|
||||
if rootKey == "" {
|
||||
if len(keys) == 0 {
|
||||
rootKey, err = ks.GenRootKey("ecdsa")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// TODO(dmcgowan): let user choose
|
||||
rootKey = keys[0]
|
||||
}
|
||||
|
||||
cryptoService, err := ks.GetRootCryptoService(rootKey)
|
||||
|
||||
+39
-43
@@ -30,7 +30,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errConnectionFailed = errors.New("Cannot connect to the Docker daemon. Is the docker daemon running on this host?")
|
||||
errConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
)
|
||||
|
||||
type serverResponse struct {
|
||||
@@ -94,14 +94,13 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
|
||||
if resp != nil {
|
||||
serverResp.statusCode = resp.StatusCode
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if types.IsTimeout(err) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
|
||||
return serverResp, errConnectionFailed
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return serverResp, errConnectionRefused
|
||||
}
|
||||
|
||||
if cli.tlsConfig == nil && strings.Contains(err.Error(), "malformed HTTP response") {
|
||||
return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
|
||||
if cli.tlsConfig == nil {
|
||||
return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?\n* Is your docker daemon up and running?", err)
|
||||
}
|
||||
if cli.tlsConfig != nil && strings.Contains(err.Error(), "remote error: bad certificate") {
|
||||
return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
|
||||
@@ -126,51 +125,48 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
|
||||
return serverResp, nil
|
||||
}
|
||||
|
||||
// cmdAttempt builds the corresponding registry Auth Header from the given
|
||||
// authConfig. It returns the servers body, status, error response
|
||||
func (cli *DockerCli) cmdAttempt(authConfig cliconfig.AuthConfig, method, path string, in io.Reader, out io.Writer) (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
|
||||
serverResp, 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(serverResp.body, serverResp.header.Get("Content-Type"), 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(), "401 Unauthorized") ||
|
||||
strings.Contains(err.Error(), "status code 401") {
|
||||
serverResp.statusCode = http.StatusUnauthorized
|
||||
}
|
||||
}
|
||||
return serverResp.body, serverResp.statusCode, err
|
||||
}
|
||||
|
||||
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 cliconfig.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
|
||||
serverResp, 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(serverResp.body, serverResp.header.Get("Content-Type"), 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(), "401 Unauthorized") ||
|
||||
strings.Contains(err.Error(), "status code 401") {
|
||||
serverResp.statusCode = http.StatusUnauthorized
|
||||
}
|
||||
}
|
||||
return serverResp.body, serverResp.statusCode, err
|
||||
}
|
||||
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := registry.ResolveAuthConfig(cli.configFile, index)
|
||||
body, statusCode, err := cli.cmdAttempt(authConfig, method, path, in, out)
|
||||
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 = registry.ResolveAuthConfig(cli.configFile, index)
|
||||
return cli.cmdAttempt(authConfig, method, path, in, out)
|
||||
return cmdAttempt(authConfig)
|
||||
}
|
||||
return body, statusCode, err
|
||||
}
|
||||
@@ -281,7 +277,7 @@ func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
|
||||
serverResp, err := cli.call("GET", "/containers/"+containerID+"/json", nil, nil)
|
||||
if err != nil {
|
||||
// If we can't connect, then the daemon probably died.
|
||||
if err != errConnectionFailed {
|
||||
if err != errConnectionRefused {
|
||||
return false, -1, err
|
||||
}
|
||||
return false, -1, nil
|
||||
@@ -303,7 +299,7 @@ func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
|
||||
serverResp, err := cli.call("GET", "/exec/"+execID+"/json", nil, nil)
|
||||
if err != nil {
|
||||
// If we can't connect, then the daemon probably died.
|
||||
if err != errConnectionFailed {
|
||||
if err != errConnectionRefused {
|
||||
return false, -1, err
|
||||
}
|
||||
return false, -1, nil
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"github.com/docker/docker/utils"
|
||||
)
|
||||
|
||||
var versionTemplate = `Client:
|
||||
var VersionTemplate = `Client:
|
||||
Version: {{.Client.Version}}
|
||||
API version: {{.Client.APIVersion}}
|
||||
API version: {{.Client.ApiVersion}}
|
||||
Go version: {{.Client.GoVersion}}
|
||||
Git commit: {{.Client.GitCommit}}
|
||||
Built: {{.Client.BuildTime}}
|
||||
@@ -24,14 +24,14 @@ var versionTemplate = `Client:
|
||||
|
||||
Server:
|
||||
Version: {{.Server.Version}}
|
||||
API version: {{.Server.APIVersion}}
|
||||
API version: {{.Server.ApiVersion}}
|
||||
Go version: {{.Server.GoVersion}}
|
||||
Git commit: {{.Server.GitCommit}}
|
||||
Built: {{.Server.BuildTime}}
|
||||
OS/Arch: {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}}
|
||||
Experimental: {{.Server.Experimental}}{{end}}{{end}}`
|
||||
|
||||
type versionData struct {
|
||||
type VersionData struct {
|
||||
Client types.Version
|
||||
ServerOK bool
|
||||
Server types.Version
|
||||
@@ -49,7 +49,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
if *tmplStr == "" {
|
||||
*tmplStr = versionTemplate
|
||||
*tmplStr = VersionTemplate
|
||||
}
|
||||
|
||||
var tmpl *template.Template
|
||||
@@ -58,10 +58,10 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
|
||||
Status: "Template parsing error: " + err.Error()}
|
||||
}
|
||||
|
||||
vd := versionData{
|
||||
vd := VersionData{
|
||||
Client: types.Version{
|
||||
Version: dockerversion.VERSION,
|
||||
APIVersion: api.Version,
|
||||
ApiVersion: api.Version,
|
||||
GoVersion: runtime.Version(),
|
||||
GitCommit: dockerversion.GITCOMMIT,
|
||||
BuildTime: dockerversion.BUILDTIME,
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/parsers/filters"
|
||||
)
|
||||
|
||||
// CmdVolume is the parent subcommand for all volume commands
|
||||
//
|
||||
// Usage: docker volume <COMMAND> <OPTS>
|
||||
func (cli *DockerCli) CmdVolume(args ...string) error {
|
||||
description := "Manage Docker volumes\n\nCommands:\n"
|
||||
commands := [][]string{
|
||||
{"create", "Create a volume"},
|
||||
{"inspect", "Return low-level information on a volume"},
|
||||
{"ls", "List volumes"},
|
||||
{"rm", "Remove a volume"},
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
description += fmt.Sprintf(" %-25.25s%s\n", cmd[0], cmd[1])
|
||||
}
|
||||
|
||||
description += "\nRun 'docker volume COMMAND --help' for more information on a command."
|
||||
cmd := Cli.Subcmd("volume", []string{"[COMMAND]"}, description, true)
|
||||
cmd.Require(flag.Exact, 0)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
return cli.CmdVolumeLs(args...)
|
||||
}
|
||||
|
||||
// CmdVolumeLs outputs a list of Docker volumes.
|
||||
//
|
||||
// Usage: docker volume ls [OPTIONS]
|
||||
func (cli *DockerCli) CmdVolumeLs(args ...string) error {
|
||||
cmd := Cli.Subcmd("volume ls", nil, "List volumes", true)
|
||||
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display volume names")
|
||||
flFilter := opts.NewListOpts(nil)
|
||||
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')")
|
||||
|
||||
cmd.Require(flag.Exact, 0)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
volFilterArgs := filters.Args{}
|
||||
for _, f := range flFilter.GetAll() {
|
||||
var err error
|
||||
volFilterArgs, err = filters.ParseFlag(f, volFilterArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
if len(volFilterArgs) > 0 {
|
||||
filterJSON, err := filters.ToParam(volFilterArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Set("filters", filterJSON)
|
||||
}
|
||||
|
||||
resp, err := cli.call("GET", "/volumes?"+v.Encode(), nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var volumes types.VolumesListResponse
|
||||
if err := json.NewDecoder(resp.body).Decode(&volumes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
if !*quiet {
|
||||
fmt.Fprintf(w, "DRIVER \tVOLUME NAME")
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
|
||||
for _, vol := range volumes.Volumes {
|
||||
if *quiet {
|
||||
fmt.Fprintln(w, vol.Name)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\n", vol.Driver, vol.Name)
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdVolumeInspect displays low-level information on one or more volumes.
|
||||
//
|
||||
// Usage: docker volume inspect [OPTIONS] VOLUME [VOLUME...]
|
||||
func (cli *DockerCli) CmdVolumeInspect(args ...string) error {
|
||||
cmd := Cli.Subcmd("volume inspect", []string{"[VOLUME NAME]"}, "Return low-level information on a volume", true)
|
||||
tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template.")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Require(flag.Min, 1)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var tmpl *template.Template
|
||||
if *tmplStr != "" {
|
||||
var err error
|
||||
tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var status = 0
|
||||
var volumes []*types.Volume
|
||||
for _, name := range cmd.Args() {
|
||||
resp, err := cli.call("GET", "/volumes/"+name, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var volume types.Volume
|
||||
if err := json.NewDecoder(resp.body).Decode(&volume); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
|
||||
if tmpl == nil {
|
||||
volumes = append(volumes, &volume)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := tmpl.Execute(cli.out, &volume); err != nil {
|
||||
if err := tmpl.Execute(cli.out, &volume); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
io.WriteString(cli.out, "\n")
|
||||
}
|
||||
|
||||
if tmpl != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(volumes, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(cli.out, bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
io.WriteString(cli.out, "\n")
|
||||
|
||||
if status != 0 {
|
||||
return Cli.StatusError{StatusCode: status}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdVolumeCreate creates a new container from a given image.
|
||||
//
|
||||
// Usage: docker volume create [OPTIONS]
|
||||
func (cli *DockerCli) CmdVolumeCreate(args ...string) error {
|
||||
cmd := Cli.Subcmd("volume create", nil, "Create a volume", true)
|
||||
flDriver := cmd.String([]string{"d", "-driver"}, "local", "Specify volume driver name")
|
||||
flName := cmd.String([]string{"-name"}, "", "Specify volume name")
|
||||
|
||||
flDriverOpts := opts.NewMapOpts(nil, nil)
|
||||
cmd.Var(flDriverOpts, []string{"o", "-opt"}, "Set driver specific options")
|
||||
|
||||
cmd.Require(flag.Exact, 0)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
volReq := &types.VolumeCreateRequest{
|
||||
Driver: *flDriver,
|
||||
DriverOpts: flDriverOpts.GetAll(),
|
||||
}
|
||||
|
||||
if *flName != "" {
|
||||
volReq.Name = *flName
|
||||
}
|
||||
|
||||
resp, err := cli.call("POST", "/volumes", volReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var vol types.Volume
|
||||
if err := json.NewDecoder(resp.body).Decode(&vol); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "%s\n", vol.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdVolumeRm removes one or more containers.
|
||||
//
|
||||
// Usage: docker volume rm VOLUME [VOLUME...]
|
||||
func (cli *DockerCli) CmdVolumeRm(args ...string) error {
|
||||
cmd := Cli.Subcmd("volume rm", []string{"[NAME]"}, "Remove a volume", true)
|
||||
cmd.Require(flag.Min, 1)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var status = 0
|
||||
for _, name := range cmd.Args() {
|
||||
_, err := cli.call("DELETE", "/volumes/"+name, nil, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
|
||||
if status != 0 {
|
||||
return Cli.StatusError{StatusCode: status}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+12
-17
@@ -16,26 +16,22 @@ import (
|
||||
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// Version of Current REST API
|
||||
Version version.Version = "1.21"
|
||||
// Current REST API version
|
||||
Version version.Version = "1.20"
|
||||
|
||||
// MinVersion represents Minimun REST API version supported
|
||||
// Minimun REST API version supported
|
||||
MinVersion version.Version = "1.12"
|
||||
|
||||
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
|
||||
// Default filename with Docker commands, read by docker build
|
||||
DefaultDockerfileName string = "Dockerfile"
|
||||
)
|
||||
|
||||
// byPrivatePort is temporary type used to sort types.Port by PrivatePort
|
||||
type byPrivatePort []types.Port
|
||||
type ByPrivatePort []types.Port
|
||||
|
||||
func (r byPrivatePort) Len() int { return len(r) }
|
||||
func (r byPrivatePort) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
func (r byPrivatePort) Less(i, j int) bool { return r[i].PrivatePort < r[j].PrivatePort }
|
||||
func (r ByPrivatePort) Len() int { return len(r) }
|
||||
func (r ByPrivatePort) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
func (r ByPrivatePort) Less(i, j int) bool { return r[i].PrivatePort < r[j].PrivatePort }
|
||||
|
||||
// DisplayablePorts returns formatted string representing open ports of container
|
||||
// e.g. "0.0.0.0:80->9090/tcp, 9988/tcp"
|
||||
// it's used by command 'docker ps'
|
||||
func DisplayablePorts(ports []types.Port) string {
|
||||
var (
|
||||
result = []string{}
|
||||
@@ -45,7 +41,7 @@ func DisplayablePorts(ports []types.Port) string {
|
||||
)
|
||||
firstInGroupMap = make(map[string]int)
|
||||
lastInGroupMap = make(map[string]int)
|
||||
sort.Sort(byPrivatePort(ports))
|
||||
sort.Sort(ByPrivatePort(ports))
|
||||
for _, port := range ports {
|
||||
var (
|
||||
current = port.PrivatePort
|
||||
@@ -73,18 +69,18 @@ func DisplayablePorts(ports []types.Port) string {
|
||||
lastInGroupMap[portKey] = current
|
||||
continue
|
||||
}
|
||||
result = append(result, formGroup(portKey, firstInGroup, lastInGroup))
|
||||
result = append(result, FormGroup(portKey, firstInGroup, lastInGroup))
|
||||
firstInGroupMap[portKey] = current
|
||||
lastInGroupMap[portKey] = current
|
||||
}
|
||||
for portKey, firstInGroup := range firstInGroupMap {
|
||||
result = append(result, formGroup(portKey, firstInGroup, lastInGroupMap[portKey]))
|
||||
result = append(result, FormGroup(portKey, firstInGroup, lastInGroupMap[portKey]))
|
||||
}
|
||||
result = append(result, hostMappings...)
|
||||
return strings.Join(result, ", ")
|
||||
}
|
||||
|
||||
func formGroup(key string, start, last int) string {
|
||||
func FormGroup(key string, start, last int) string {
|
||||
var (
|
||||
group string
|
||||
parts = strings.Split(key, "/")
|
||||
@@ -106,7 +102,6 @@ func formGroup(key string, start, last int) string {
|
||||
return fmt.Sprintf("%s/%s", group, groupType)
|
||||
}
|
||||
|
||||
// MatchesContentType validates the content type against the expected one
|
||||
func MatchesContentType(contentType, expectedType string) bool {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,282 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"os"
|
||||
)
|
||||
|
||||
type ports struct {
|
||||
ports []types.Port
|
||||
expected string
|
||||
}
|
||||
|
||||
// DisplayablePorts
|
||||
func TestDisplayablePorts(t *testing.T) {
|
||||
cases := []ports{
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
PrivatePort: 9988,
|
||||
Type: "tcp",
|
||||
},
|
||||
},
|
||||
"9988/tcp"},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
PrivatePort: 9988,
|
||||
Type: "udp",
|
||||
},
|
||||
},
|
||||
"9988/udp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
IP: "0.0.0.0",
|
||||
PrivatePort: 9988,
|
||||
Type: "tcp",
|
||||
},
|
||||
},
|
||||
"0.0.0.0:0->9988/tcp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
PrivatePort: 9988,
|
||||
PublicPort: 8899,
|
||||
Type: "tcp",
|
||||
},
|
||||
},
|
||||
"9988/tcp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
IP: "4.3.2.1",
|
||||
PrivatePort: 9988,
|
||||
PublicPort: 8899,
|
||||
Type: "tcp",
|
||||
},
|
||||
},
|
||||
"4.3.2.1:8899->9988/tcp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
IP: "4.3.2.1",
|
||||
PrivatePort: 9988,
|
||||
PublicPort: 9988,
|
||||
Type: "tcp",
|
||||
},
|
||||
},
|
||||
"4.3.2.1:9988->9988/tcp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
PrivatePort: 9988,
|
||||
Type: "udp",
|
||||
}, {
|
||||
PrivatePort: 9988,
|
||||
Type: "udp",
|
||||
},
|
||||
},
|
||||
"9988/udp, 9988/udp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
IP: "1.2.3.4",
|
||||
PublicPort: 9998,
|
||||
PrivatePort: 9998,
|
||||
Type: "udp",
|
||||
}, {
|
||||
IP: "1.2.3.4",
|
||||
PublicPort: 9999,
|
||||
PrivatePort: 9999,
|
||||
Type: "udp",
|
||||
},
|
||||
},
|
||||
"1.2.3.4:9998-9999->9998-9999/udp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
IP: "1.2.3.4",
|
||||
PublicPort: 8887,
|
||||
PrivatePort: 9998,
|
||||
Type: "udp",
|
||||
}, {
|
||||
IP: "1.2.3.4",
|
||||
PublicPort: 8888,
|
||||
PrivatePort: 9999,
|
||||
Type: "udp",
|
||||
},
|
||||
},
|
||||
"1.2.3.4:8887->9998/udp, 1.2.3.4:8888->9999/udp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
PrivatePort: 9998,
|
||||
Type: "udp",
|
||||
}, {
|
||||
PrivatePort: 9999,
|
||||
Type: "udp",
|
||||
},
|
||||
},
|
||||
"9998-9999/udp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
IP: "1.2.3.4",
|
||||
PrivatePort: 6677,
|
||||
PublicPort: 7766,
|
||||
Type: "tcp",
|
||||
}, {
|
||||
PrivatePort: 9988,
|
||||
PublicPort: 8899,
|
||||
Type: "udp",
|
||||
},
|
||||
},
|
||||
"9988/udp, 1.2.3.4:7766->6677/tcp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
IP: "1.2.3.4",
|
||||
PrivatePort: 9988,
|
||||
PublicPort: 8899,
|
||||
Type: "udp",
|
||||
}, {
|
||||
IP: "1.2.3.4",
|
||||
PrivatePort: 9988,
|
||||
PublicPort: 8899,
|
||||
Type: "tcp",
|
||||
}, {
|
||||
IP: "4.3.2.1",
|
||||
PrivatePort: 2233,
|
||||
PublicPort: 3322,
|
||||
Type: "tcp",
|
||||
},
|
||||
},
|
||||
"4.3.2.1:3322->2233/tcp, 1.2.3.4:8899->9988/udp, 1.2.3.4:8899->9988/tcp",
|
||||
},
|
||||
{
|
||||
[]types.Port{
|
||||
{
|
||||
PrivatePort: 9988,
|
||||
PublicPort: 8899,
|
||||
Type: "udp",
|
||||
}, {
|
||||
IP: "1.2.3.4",
|
||||
PrivatePort: 6677,
|
||||
PublicPort: 7766,
|
||||
Type: "tcp",
|
||||
}, {
|
||||
IP: "4.3.2.1",
|
||||
PrivatePort: 2233,
|
||||
PublicPort: 3322,
|
||||
Type: "tcp",
|
||||
},
|
||||
},
|
||||
"9988/udp, 4.3.2.1:3322->2233/tcp, 1.2.3.4:7766->6677/tcp",
|
||||
},
|
||||
}
|
||||
|
||||
for _, port := range cases {
|
||||
actual := DisplayablePorts(port.ports)
|
||||
if port.expected != actual {
|
||||
t.Fatalf("Expected %s, got %s.", port.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MatchesContentType
|
||||
func TestJsonContentType(t *testing.T) {
|
||||
if !MatchesContentType("application/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if !MatchesContentType("application/json; charset=utf-8", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if MatchesContentType("dockerapplication/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// LoadOrCreateTrustKey
|
||||
func TestLoadOrCreateTrustKeyInvalidKeyFile(t *testing.T) {
|
||||
tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpKeyFolderPath)
|
||||
|
||||
tmpKeyFile, err := ioutil.TempFile(tmpKeyFolderPath, "keyfile")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := LoadOrCreateTrustKey(tmpKeyFile.Name()); err == nil {
|
||||
t.Fatalf("expected an error, got nothing.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLoadOrCreateTrustKeyCreateKey(t *testing.T) {
|
||||
tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpKeyFolderPath)
|
||||
|
||||
// Without the need to create the folder hierarchy
|
||||
tmpKeyFile := filepath.Join(tmpKeyFolderPath, "keyfile")
|
||||
|
||||
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(tmpKeyFile); err != nil {
|
||||
t.Fatalf("Expected to find a file %s, got %v", tmpKeyFile, err)
|
||||
}
|
||||
|
||||
// With the need to create the folder hierarchy as tmpKeyFie is in a path
|
||||
// where some folder do not exists.
|
||||
tmpKeyFile = filepath.Join(tmpKeyFolderPath, "folder/hierarchy/keyfile")
|
||||
|
||||
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(tmpKeyFile); err != nil {
|
||||
t.Fatalf("Expected to find a file %s, got %v", tmpKeyFile, err)
|
||||
}
|
||||
|
||||
// With no path at all
|
||||
defer os.Remove("keyfile")
|
||||
if key, err := LoadOrCreateTrustKey("keyfile"); err != nil || key == nil {
|
||||
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||
}
|
||||
|
||||
if _, err := os.Stat("keyfile"); err != nil {
|
||||
t.Fatalf("Expected to find a file keyfile, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadOrCreateTrustKeyLoadValidKey(t *testing.T) {
|
||||
tmpKeyFile := filepath.Join("fixtures", "keyfile")
|
||||
|
||||
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||
t.Fatalf("expected a key file, got : %v and %v", err, key)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
keyID: AWX2:I27X:WQFX:IOMK:CNAK:O7PW:VYNB:ZLKC:CVAE:YJP2:SI4A:XXAY
|
||||
|
||||
MHcCAQEEILHTRWdcpKWsnORxSFyBnndJ4ROU41hMtr/GCiLVvwBQoAoGCCqGSM49
|
||||
AwEHoUQDQgAElpVFbQ2V2UQKajqdE3fVxJ+/pE/YuEFOxWbOxF2be19BY209/iky
|
||||
NzeFFK7SLpQ4CBJ7zDVXOHsMzrkY/GquGA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
@@ -1,26 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
)
|
||||
|
||||
func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var config *cliconfig.AuthConfig
|
||||
err := json.NewDecoder(r.Body).Decode(&config)
|
||||
r.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status, err := s.daemon.RegistryService.Auth(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, &types.AuthResponse{
|
||||
Status: status,
|
||||
})
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
if version.LessThan("1.20") && runtime.GOOS != "windows" {
|
||||
return getContainersByNameDownlevel(w, s, vars["name"])
|
||||
}
|
||||
|
||||
containerJSON, err := s.daemon.ContainerInspect(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, containerJSON)
|
||||
}
|
||||
|
||||
func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := &daemon.ContainersConfig{
|
||||
All: boolValue(r, "all"),
|
||||
Size: boolValue(r, "size"),
|
||||
Since: r.Form.Get("since"),
|
||||
Before: r.Form.Get("before"),
|
||||
Filters: r.Form.Get("filters"),
|
||||
}
|
||||
|
||||
if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
|
||||
limit, err := strconv.Atoi(tmpLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Limit = limit
|
||||
}
|
||||
|
||||
containers, err := s.daemon.Containers(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, containers)
|
||||
}
|
||||
|
||||
func (s *Server) getContainersStats(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
stream := boolValueOrDefault(r, "stream", true)
|
||||
var out io.Writer
|
||||
if !stream {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
out = w
|
||||
} else {
|
||||
out = ioutils.NewWriteFlusher(w)
|
||||
}
|
||||
|
||||
var closeNotifier <-chan bool
|
||||
if notifier, ok := w.(http.CloseNotifier); ok {
|
||||
closeNotifier = notifier.CloseNotify()
|
||||
}
|
||||
|
||||
config := &daemon.ContainerStatsConfig{
|
||||
Stream: stream,
|
||||
OutStream: out,
|
||||
Stop: closeNotifier,
|
||||
}
|
||||
|
||||
return s.daemon.ContainerStats(vars["name"], config)
|
||||
}
|
||||
|
||||
func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
// Validate args here, because we can't return not StatusOK after job.Run() call
|
||||
stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr")
|
||||
if !(stdout || stderr) {
|
||||
return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
||||
}
|
||||
|
||||
var since time.Time
|
||||
if r.Form.Get("since") != "" {
|
||||
s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
since = time.Unix(s, 0)
|
||||
}
|
||||
|
||||
var closeNotifier <-chan bool
|
||||
if notifier, ok := w.(http.CloseNotifier); ok {
|
||||
closeNotifier = notifier.CloseNotify()
|
||||
}
|
||||
|
||||
c, err := s.daemon.Get(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outStream := ioutils.NewWriteFlusher(w)
|
||||
// write an empty chunk of data (this is to ensure that the
|
||||
// HTTP Response is sent immediately, even if the container has
|
||||
// not yet produced any data)
|
||||
outStream.Write(nil)
|
||||
|
||||
logsConfig := &daemon.ContainerLogsConfig{
|
||||
Follow: boolValue(r, "follow"),
|
||||
Timestamps: boolValue(r, "timestamps"),
|
||||
Since: since,
|
||||
Tail: r.Form.Get("tail"),
|
||||
UseStdout: stdout,
|
||||
UseStderr: stderr,
|
||||
OutStream: outStream,
|
||||
Stop: closeNotifier,
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerLogs(c, logsConfig); err != nil {
|
||||
fmt.Fprintf(w, "Error running logs job: %s\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) getContainersExport(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
return s.daemon.ContainerExport(vars["name"], w)
|
||||
}
|
||||
|
||||
func (s *Server) postContainersStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
// If contentLength is -1, we can assumed chunked encoding
|
||||
// or more technically that the length is unknown
|
||||
// https://golang.org/src/pkg/net/http/request.go#L139
|
||||
// net/http otherwise seems to swallow any headers related to chunked encoding
|
||||
// including r.TransferEncoding
|
||||
// allow a nil body for backwards compatibility
|
||||
var hostConfig *runconfig.HostConfig
|
||||
if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
|
||||
if err := checkForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := runconfig.DecodeHostConfig(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostConfig = c
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil {
|
||||
if err.Error() == "Container already started" {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainersStop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
seconds, _ := strconv.Atoi(r.Form.Get("t"))
|
||||
|
||||
if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil {
|
||||
if err.Error() == "Container already stopped" {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainersKill(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sig uint64
|
||||
name := vars["name"]
|
||||
|
||||
// If we have a signal, look at it. Otherwise, do nothing
|
||||
if sigStr := r.Form.Get("signal"); sigStr != "" {
|
||||
// Check if we passed the signal as a number:
|
||||
// The largest legal signal is 31, so let's parse on 5 bits
|
||||
sigN, err := strconv.ParseUint(sigStr, 10, 5)
|
||||
if err != nil {
|
||||
// The signal is not a number, treat it as a string (either like
|
||||
// "KILL" or like "SIGKILL")
|
||||
syscallSig, ok := signal.SignalMap[strings.TrimPrefix(sigStr, "SIG")]
|
||||
if !ok {
|
||||
return fmt.Errorf("Invalid signal: %s", sigStr)
|
||||
}
|
||||
sig = uint64(syscallSig)
|
||||
} else {
|
||||
sig = sigN
|
||||
}
|
||||
|
||||
if sig == 0 {
|
||||
return fmt.Errorf("Invalid signal: %s", sigStr)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerKill(name, sig); err != nil {
|
||||
_, isStopped := err.(daemon.ErrContainerNotRunning)
|
||||
// Return error that's not caused because the container is stopped.
|
||||
// Return error if the container is not running and the api is >= 1.20
|
||||
// to keep backwards compatibility.
|
||||
if version.GreaterThanOrEqualTo("1.20") || !isStopped {
|
||||
return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainersRestart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
timeout, _ := strconv.Atoi(r.Form.Get("t"))
|
||||
|
||||
if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainersPause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerPause(vars["name"]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainersUnpause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerUnpause(vars["name"]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainersWait(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, &types.ContainerWaitResponse{
|
||||
StatusCode: status,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) getContainersChanges(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
changes, err := s.daemon.ContainerChanges(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, changes)
|
||||
}
|
||||
|
||||
func (s *Server) getContainersTop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, procList)
|
||||
}
|
||||
|
||||
func (s *Server) postContainerRename(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
newName := r.Form.Get("name")
|
||||
if err := s.daemon.ContainerRename(name, newName); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainersCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
warnings []string
|
||||
name = r.Form.Get("name")
|
||||
)
|
||||
|
||||
config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adjustCPUShares := version.LessThan("1.19")
|
||||
|
||||
container, warnings, err := s.daemon.ContainerCreate(name, config, hostConfig, adjustCPUShares)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusCreated, &types.ContainerCreateResponse{
|
||||
ID: container.ID,
|
||||
Warnings: warnings,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) deleteContainers(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
config := &daemon.ContainerRmConfig{
|
||||
ForceRemove: boolValue(r, "force"),
|
||||
RemoveVolume: boolValue(r, "v"),
|
||||
RemoveLink: boolValue(r, "link"),
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerRm(name, config); err != nil {
|
||||
// Force a 404 for the empty string
|
||||
if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
|
||||
return fmt.Errorf("no such id: \"\"")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainersResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
width, err := strconv.Atoi(r.Form.Get("w"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.daemon.ContainerResize(vars["name"], height, width)
|
||||
}
|
||||
|
||||
func (s *Server) postContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
cont, err := s.daemon.Get(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inStream, outStream, err := hijackServer(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeStreams(inStream, outStream)
|
||||
|
||||
if _, ok := r.Header["Upgrade"]; ok {
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||
} else {
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
}
|
||||
|
||||
attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
|
||||
InStream: inStream,
|
||||
OutStream: outStream,
|
||||
UseStdin: boolValue(r, "stdin"),
|
||||
UseStdout: boolValue(r, "stdout"),
|
||||
UseStderr: boolValue(r, "stderr"),
|
||||
Logs: boolValue(r, "logs"),
|
||||
Stream: boolValue(r, "stream"),
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerAttachWithLogs(cont, attachWithLogsConfig); err != nil {
|
||||
fmt.Fprintf(outStream, "Error attaching: %s\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) wsContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
cont, err := s.daemon.Get(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h := websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
|
||||
InStream: ws,
|
||||
OutStream: ws,
|
||||
ErrStream: ws,
|
||||
Logs: boolValue(r, "logs"),
|
||||
Stream: boolValue(r, "stream"),
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerWsAttachWithLogs(cont, wsAttachWithLogsConfig); err != nil {
|
||||
logrus.Errorf("Error attaching websocket: %s", err)
|
||||
}
|
||||
})
|
||||
ws := websocket.Server{Handler: h, Handshake: nil}
|
||||
ws.ServeHTTP(w, r)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
)
|
||||
|
||||
// postContainersCopy is deprecated in favor of getContainersArchive.
|
||||
func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
if err := checkForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := types.CopyConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.Resource == "" {
|
||||
return fmt.Errorf("Path cannot be empty")
|
||||
}
|
||||
|
||||
data, err := s.daemon.ContainerCopy(vars["name"], cfg.Resource)
|
||||
if err != nil {
|
||||
if strings.Contains(strings.ToLower(err.Error()), "no such id") {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("Could not find the file %s in container %s", cfg.Resource, vars["name"])
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer data.Close()
|
||||
|
||||
w.Header().Set("Content-Type", "application/x-tar")
|
||||
if _, err := io.Copy(w, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// // Encode the stat to JSON, base64 encode, and place in a header.
|
||||
func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
||||
statJSON, err := json.Marshal(stat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.Set(
|
||||
"X-Docker-Container-Path-Stat",
|
||||
base64.StdEncoding.EncodeToString(statJSON),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) headContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
v, err := archiveFormValues(r, vars)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, err := s.daemon.ContainerStatPath(v.name, v.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return setContainerPathStatHeader(stat, w.Header())
|
||||
}
|
||||
|
||||
func (s *Server) getContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
v, err := archiveFormValues(r, vars)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tarArchive, stat, err := s.daemon.ContainerArchivePath(v.name, v.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tarArchive.Close()
|
||||
|
||||
if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/x-tar")
|
||||
_, err = io.Copy(w, tarArchive)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) putContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
v, err := archiveFormValues(r, vars)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
noOverwriteDirNonDir := boolValue(r, "noOverwriteDirNonDir")
|
||||
return s.daemon.ContainerExtractToDir(v.name, v.path, noOverwriteDirNonDir, r.Body)
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/autogen/dockerversion"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/utils"
|
||||
)
|
||||
|
||||
func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
v := &types.Version{
|
||||
Version: dockerversion.VERSION,
|
||||
APIVersion: api.Version,
|
||||
GitCommit: dockerversion.GITCOMMIT,
|
||||
GoVersion: runtime.Version(),
|
||||
Os: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
BuildTime: dockerversion.BUILDTIME,
|
||||
}
|
||||
|
||||
if version.GreaterThanOrEqualTo("1.19") {
|
||||
v.Experimental = utils.ExperimentalBuild()
|
||||
}
|
||||
|
||||
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
||||
v.KernelVersion = kernelVersion.String()
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, v)
|
||||
}
|
||||
|
||||
func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
info, err := s.daemon.SystemInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, info)
|
||||
}
|
||||
|
||||
func (s *Server) getEvents(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
var since int64 = -1
|
||||
if r.Form.Get("since") != "" {
|
||||
s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
since = s
|
||||
}
|
||||
|
||||
var until int64 = -1
|
||||
if r.Form.Get("until") != "" {
|
||||
u, err := strconv.ParseInt(r.Form.Get("until"), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
until = u
|
||||
}
|
||||
|
||||
timer := time.NewTimer(0)
|
||||
timer.Stop()
|
||||
if until > 0 {
|
||||
dur := time.Unix(until, 0).Sub(time.Now())
|
||||
timer = time.NewTimer(dur)
|
||||
}
|
||||
|
||||
ef, err := filters.FromParam(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isFiltered := func(field string, filter []string) bool {
|
||||
if len(field) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(filter) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range filter {
|
||||
if v == field {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(field, ":") {
|
||||
image := strings.Split(field, ":")
|
||||
if image[0] == v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
d := s.daemon
|
||||
es := d.EventsService
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
outStream := ioutils.NewWriteFlusher(w)
|
||||
outStream.Write(nil) // make sure response is sent immediately
|
||||
enc := json.NewEncoder(outStream)
|
||||
|
||||
getContainerID := func(cn string) string {
|
||||
c, err := d.Get(cn)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return c.ID
|
||||
}
|
||||
|
||||
sendEvent := func(ev *jsonmessage.JSONMessage) error {
|
||||
//incoming container filter can be name,id or partial id, convert and replace as a full container id
|
||||
for i, cn := range ef["container"] {
|
||||
ef["container"][i] = getContainerID(cn)
|
||||
}
|
||||
|
||||
if isFiltered(ev.Status, ef["event"]) || (isFiltered(ev.ID, ef["image"]) &&
|
||||
isFiltered(ev.From, ef["image"])) || isFiltered(ev.ID, ef["container"]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return enc.Encode(ev)
|
||||
}
|
||||
|
||||
current, l := es.Subscribe()
|
||||
if since == -1 {
|
||||
current = nil
|
||||
}
|
||||
defer es.Evict(l)
|
||||
for _, ev := range current {
|
||||
if ev.Time < since {
|
||||
continue
|
||||
}
|
||||
if err := sendEvent(ev); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var closeNotify <-chan bool
|
||||
if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
||||
closeNotify = closeNotifier.CloseNotify()
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case ev := <-l:
|
||||
jev, ok := ev.(*jsonmessage.JSONMessage)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if err := sendEvent(jev); err != nil {
|
||||
return err
|
||||
}
|
||||
case <-timer.C:
|
||||
return nil
|
||||
case <-closeNotify:
|
||||
logrus.Debug("Client disconnected, stop sending events")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
func (s *Server) getExecByID(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter 'id'")
|
||||
}
|
||||
|
||||
eConfig, err := s.daemon.ContainerExecInspect(vars["id"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, eConfig)
|
||||
}
|
||||
|
||||
func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
execConfig := &runconfig.ExecConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
execConfig.Container = name
|
||||
|
||||
if len(execConfig.Cmd) == 0 {
|
||||
return fmt.Errorf("No exec command specified")
|
||||
}
|
||||
|
||||
// Register an instance of Exec in container.
|
||||
id, err := s.daemon.ContainerExecCreate(execConfig)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error setting up exec command in container %s: %s", name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{
|
||||
ID: id,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
|
||||
func (s *Server) postContainerExecStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
execName = vars["name"]
|
||||
stdin, inStream io.ReadCloser
|
||||
stdout, stderr, outStream io.Writer
|
||||
)
|
||||
|
||||
execStartCheck := &types.ExecStartCheck{}
|
||||
if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !execStartCheck.Detach {
|
||||
var err error
|
||||
// Setting up the streaming http interface.
|
||||
inStream, outStream, err = hijackServer(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeStreams(inStream, outStream)
|
||||
|
||||
if _, ok := r.Header["Upgrade"]; ok {
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||
} else {
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
}
|
||||
|
||||
stdin = inStream
|
||||
stdout = outStream
|
||||
if !execStartCheck.Tty {
|
||||
stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
|
||||
stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
|
||||
}
|
||||
} else {
|
||||
outStream = w
|
||||
}
|
||||
|
||||
// Now run the user process in container.
|
||||
if err := s.daemon.ContainerExecStart(execName, stdin, stdout, stderr); err != nil {
|
||||
fmt.Fprintf(outStream, "Error running exec in container: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postContainerExecResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
width, err := strconv.Atoi(r.Form.Get("w"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.daemon.ContainerExecResize(vars["name"], height, width)
|
||||
}
|
||||
+1
-2
@@ -3,7 +3,6 @@ package server
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -44,7 +43,7 @@ func archiveFormValues(r *http.Request, vars map[string]string) (archiveOptions,
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
path := filepath.FromSlash(r.Form.Get("path"))
|
||||
path := r.Form.Get("path")
|
||||
|
||||
switch {
|
||||
case name == "":
|
||||
|
||||
@@ -1,425 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/ulimit"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
)
|
||||
|
||||
func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cname := r.Form.Get("container")
|
||||
|
||||
pause := boolValue(r, "pause")
|
||||
if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
|
||||
pause = true
|
||||
}
|
||||
|
||||
c, _, err := runconfig.DecodeContainerConfig(r.Body)
|
||||
if err != nil && err != io.EOF { //Do not fail if body is empty.
|
||||
return err
|
||||
}
|
||||
|
||||
commitCfg := &builder.CommitConfig{
|
||||
Pause: pause,
|
||||
Repo: r.Form.Get("repo"),
|
||||
Tag: r.Form.Get("tag"),
|
||||
Author: r.Form.Get("author"),
|
||||
Comment: r.Form.Get("comment"),
|
||||
Changes: r.Form["changes"],
|
||||
Config: c,
|
||||
}
|
||||
|
||||
imgID, err := builder.Commit(cname, s.daemon, commitCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
|
||||
ID: imgID,
|
||||
})
|
||||
}
|
||||
|
||||
// Creates an image from Pull or from Import
|
||||
func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
image = r.Form.Get("fromImage")
|
||||
repo = r.Form.Get("repo")
|
||||
tag = r.Form.Get("tag")
|
||||
message = r.Form.Get("message")
|
||||
)
|
||||
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||
authConfig := &cliconfig.AuthConfig{}
|
||||
if authEncoded != "" {
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting to be empty
|
||||
authConfig = &cliconfig.AuthConfig{}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
output = ioutils.NewWriteFlusher(w)
|
||||
)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if image != "" { //pull
|
||||
if tag == "" {
|
||||
image, tag = parsers.ParseRepositoryTag(image)
|
||||
}
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
metaHeaders[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
imagePullConfig := &graph.ImagePullConfig{
|
||||
MetaHeaders: metaHeaders,
|
||||
AuthConfig: authConfig,
|
||||
OutStream: output,
|
||||
}
|
||||
|
||||
err = s.daemon.Repositories().Pull(image, tag, imagePullConfig)
|
||||
} else { //import
|
||||
if tag == "" {
|
||||
repo, tag = parsers.ParseRepositoryTag(repo)
|
||||
}
|
||||
|
||||
src := r.Form.Get("fromSrc")
|
||||
|
||||
// 'err' MUST NOT be defined within this block, we need any error
|
||||
// generated from the download to be available to the output
|
||||
// stream processing below
|
||||
var newConfig *runconfig.Config
|
||||
newConfig, err = builder.BuildFromConfig(s.daemon, &runconfig.Config{}, r.Form["changes"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.daemon.Repositories().Import(src, repo, tag, message, r.Body, output, newConfig)
|
||||
}
|
||||
if err != nil {
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
sf := streamformatter.NewJSONStreamFormatter()
|
||||
output.Write(sf.FormatError(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postImagesPush(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
metaHeaders[k] = v
|
||||
}
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
authConfig := &cliconfig.AuthConfig{}
|
||||
|
||||
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||
if authEncoded != "" {
|
||||
// the new format is to handle the authConfig as a header
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||
// to increase compatibility to existing api it is defaulting to be empty
|
||||
authConfig = &cliconfig.AuthConfig{}
|
||||
}
|
||||
} else {
|
||||
// the old format is supported for compatibility if there was no authConfig header
|
||||
if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
|
||||
return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
imagePushConfig := &graph.ImagePushConfig{
|
||||
MetaHeaders: metaHeaders,
|
||||
AuthConfig: authConfig,
|
||||
Tag: r.Form.Get("tag"),
|
||||
OutStream: output,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if err := s.daemon.Repositories().Push(name, imagePushConfig); err != nil {
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
sf := streamformatter.NewJSONStreamFormatter()
|
||||
output.Write(sf.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/x-tar")
|
||||
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
var names []string
|
||||
if name, ok := vars["name"]; ok {
|
||||
names = []string{name}
|
||||
} else {
|
||||
names = r.Form["names"]
|
||||
}
|
||||
|
||||
if err := s.daemon.Repositories().ImageExport(names, output); err != nil {
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
sf := streamformatter.NewJSONStreamFormatter()
|
||||
output.Write(sf.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
return s.daemon.Repositories().Load(r.Body, w)
|
||||
}
|
||||
|
||||
func (s *Server) deleteImages(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
|
||||
if name == "" {
|
||||
return fmt.Errorf("image name cannot be blank")
|
||||
}
|
||||
|
||||
force := boolValue(r, "force")
|
||||
prune := !boolValue(r, "noprune")
|
||||
|
||||
list, err := s.daemon.ImageDelete(name, force, prune)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, list)
|
||||
}
|
||||
|
||||
func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
imageInspect, err := s.daemon.Repositories().Lookup(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, imageInspect)
|
||||
}
|
||||
|
||||
func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var (
|
||||
authConfigs = map[string]cliconfig.AuthConfig{}
|
||||
authConfigsEncoded = r.Header.Get("X-Registry-Config")
|
||||
buildConfig = builder.NewBuildConfig()
|
||||
)
|
||||
|
||||
if authConfigsEncoded != "" {
|
||||
authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
|
||||
if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting
|
||||
// to be empty.
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
|
||||
buildConfig.Remove = true
|
||||
} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
|
||||
buildConfig.Remove = true
|
||||
} else {
|
||||
buildConfig.Remove = boolValue(r, "rm")
|
||||
}
|
||||
if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
|
||||
buildConfig.Pull = true
|
||||
}
|
||||
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
buildConfig.Stdout = output
|
||||
buildConfig.Context = r.Body
|
||||
|
||||
buildConfig.RemoteURL = r.FormValue("remote")
|
||||
buildConfig.DockerfileName = r.FormValue("dockerfile")
|
||||
buildConfig.RepoName = r.FormValue("t")
|
||||
buildConfig.SuppressOutput = boolValue(r, "q")
|
||||
buildConfig.NoCache = boolValue(r, "nocache")
|
||||
buildConfig.ForceRemove = boolValue(r, "forcerm")
|
||||
buildConfig.AuthConfigs = authConfigs
|
||||
buildConfig.MemorySwap = int64ValueOrZero(r, "memswap")
|
||||
buildConfig.Memory = int64ValueOrZero(r, "memory")
|
||||
buildConfig.CPUShares = int64ValueOrZero(r, "cpushares")
|
||||
buildConfig.CPUPeriod = int64ValueOrZero(r, "cpuperiod")
|
||||
buildConfig.CPUQuota = int64ValueOrZero(r, "cpuquota")
|
||||
buildConfig.CPUSetCpus = r.FormValue("cpusetcpus")
|
||||
buildConfig.CPUSetMems = r.FormValue("cpusetmems")
|
||||
buildConfig.CgroupParent = r.FormValue("cgroupparent")
|
||||
|
||||
var buildUlimits = []*ulimit.Ulimit{}
|
||||
ulimitsJSON := r.FormValue("ulimits")
|
||||
if ulimitsJSON != "" {
|
||||
if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil {
|
||||
return err
|
||||
}
|
||||
buildConfig.Ulimits = buildUlimits
|
||||
}
|
||||
|
||||
// Job cancellation. Note: not all job types support this.
|
||||
if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
||||
finished := make(chan struct{})
|
||||
defer close(finished)
|
||||
go func() {
|
||||
select {
|
||||
case <-finished:
|
||||
case <-closeNotifier.CloseNotify():
|
||||
logrus.Infof("Client disconnected, cancelling job: build")
|
||||
buildConfig.Cancel()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if err := builder.Build(s.daemon, buildConfig); err != nil {
|
||||
// Do not write the error in the http output if it's still empty.
|
||||
// This prevents from writing a 200(OK) when there is an interal error.
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
sf := streamformatter.NewJSONStreamFormatter()
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: The filter parameter could just be a match filter
|
||||
images, err := s.daemon.Repositories().Images(r.Form.Get("filters"), r.Form.Get("filter"), boolValue(r, "all"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, images)
|
||||
}
|
||||
|
||||
func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
history, err := s.daemon.Repositories().History(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, history)
|
||||
}
|
||||
|
||||
func (s *Server) postImagesTag(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
repo := r.Form.Get("repo")
|
||||
tag := r.Form.Get("tag")
|
||||
force := boolValue(r, "force")
|
||||
name := vars["name"]
|
||||
if err := s.daemon.Repositories().Tag(repo, tag, name, force); err != nil {
|
||||
return err
|
||||
}
|
||||
s.daemon.EventsService.Log("tag", utils.ImageReference(repo, tag), "")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) getImagesSearch(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
config *cliconfig.AuthConfig
|
||||
authEncoded = r.Header.Get("X-Registry-Auth")
|
||||
headers = map[string][]string{}
|
||||
)
|
||||
|
||||
if authEncoded != "" {
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
|
||||
// for a search it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting to be empty
|
||||
config = &cliconfig.AuthConfig{}
|
||||
}
|
||||
}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
headers[k] = v
|
||||
}
|
||||
}
|
||||
query, err := s.daemon.RegistryService.Search(r.Form.Get("term"), config, headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, query.Results)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func profilerSetup(mainRouter *mux.Router, path string) {
|
||||
func ProfilerSetup(mainRouter *mux.Router, path string) {
|
||||
var r = mainRouter.PathPrefix(path).Subrouter()
|
||||
r.HandleFunc("/vars", expVars)
|
||||
r.HandleFunc("/pprof/", pprof.Index)
|
||||
|
||||
+1347
-34
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
// +build experimental,!windows
|
||||
// +build experimental
|
||||
|
||||
package server
|
||||
|
||||
func (s *Server) registerSubRouter() {
|
||||
httpHandler := s.daemon.NetworkAPIRouter()
|
||||
httpHandler := s.daemon.NetworkApiRouter()
|
||||
|
||||
subrouter := s.router.PathPrefix("/v{version:[0-9.]+}/networks").Subrouter()
|
||||
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build freebsd linux
|
||||
// +build linux
|
||||
|
||||
package server
|
||||
|
||||
@@ -8,12 +8,19 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/pkg/sockets"
|
||||
"github.com/docker/docker/pkg/systemd"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork/portallocator"
|
||||
)
|
||||
|
||||
systemdActivation "github.com/coreos/go-systemd/activation"
|
||||
systemdDaemon "github.com/coreos/go-systemd/daemon"
|
||||
const (
|
||||
// See http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269
|
||||
linuxMinCpuShares = 2
|
||||
linuxMaxCpuShares = 262144
|
||||
)
|
||||
|
||||
// newServer sets up the required serverClosers and does protocol specific checking.
|
||||
@@ -24,7 +31,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
||||
)
|
||||
switch proto {
|
||||
case "fd":
|
||||
ls, err = listenFD(addr)
|
||||
ls, err = systemd.ListenFD(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -33,7 +40,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
||||
// won't be ready.
|
||||
<-s.start
|
||||
case "tcp":
|
||||
l, err := s.initTCPSocket(addr)
|
||||
l, err := s.initTcpSocket(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -49,7 +56,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
||||
}
|
||||
var res []serverCloser
|
||||
for _, l := range ls {
|
||||
res = append(res, &HTTPServer{
|
||||
res = append(res, &HttpServer{
|
||||
&http.Server{
|
||||
Addr: addr,
|
||||
Handler: s.router,
|
||||
@@ -60,14 +67,11 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// AcceptConnections allows clients to connect to the API server.
|
||||
// Referenced Daemon is notified about this server, and waits for the
|
||||
// daemon acknowledgement before the incoming connections are accepted.
|
||||
func (s *Server) AcceptConnections(d *daemon.Daemon) {
|
||||
// Tell the init daemon we are accepting requests
|
||||
s.daemon = d
|
||||
s.registerSubRouter()
|
||||
go systemdDaemon.SdNotify("READY=1")
|
||||
go systemd.SdNotify("READY=1")
|
||||
// close the lock so the listeners start accepting connections
|
||||
select {
|
||||
case <-s.start:
|
||||
@@ -103,43 +107,17 @@ func allocateDaemonPort(addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
|
||||
// is only relevant on non-Windows daemons.
|
||||
func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
|
||||
containerJSONRaw, err := s.daemon.ContainerInspectPre120(namevar)
|
||||
if err != nil {
|
||||
return err
|
||||
func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) {
|
||||
if version.LessThan("1.19") {
|
||||
if hostConfig.CpuShares > 0 {
|
||||
// Handle unsupported CpuShares
|
||||
if hostConfig.CpuShares < linuxMinCpuShares {
|
||||
logrus.Warnf("Changing requested CpuShares of %d to minimum allowed of %d", hostConfig.CpuShares, linuxMinCpuShares)
|
||||
hostConfig.CpuShares = linuxMinCpuShares
|
||||
} else if hostConfig.CpuShares > linuxMaxCpuShares {
|
||||
logrus.Warnf("Changing requested CpuShares of %d to maximum allowed of %d", hostConfig.CpuShares, linuxMaxCpuShares)
|
||||
hostConfig.CpuShares = linuxMaxCpuShares
|
||||
}
|
||||
}
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, containerJSONRaw)
|
||||
}
|
||||
|
||||
// listenFD returns the specified socket activated files as a slice of
|
||||
// net.Listeners or all of the activated files if "*" is given.
|
||||
func listenFD(addr string) ([]net.Listener, error) {
|
||||
// socket activation
|
||||
listeners, err := systemdActivation.Listeners(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if listeners == nil || len(listeners) == 0 {
|
||||
return nil, fmt.Errorf("No sockets found")
|
||||
}
|
||||
|
||||
// default to all fds just like unix:// and tcp://
|
||||
if addr == "" {
|
||||
addr = "*"
|
||||
}
|
||||
|
||||
fdNum, _ := strconv.Atoi(addr)
|
||||
fdOffset := fdNum - 3
|
||||
if (addr != "*") && (len(listeners) < int(fdOffset)+1) {
|
||||
return nil, fmt.Errorf("Too few socket activated files passed in")
|
||||
}
|
||||
|
||||
if addr == "*" {
|
||||
return listeners, nil
|
||||
}
|
||||
|
||||
return []net.Listener{listeners[fdOffset]}, nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// +build linux
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
func TestAdjustCpuSharesOldApi(t *testing.T) {
|
||||
apiVersion := version.Version("1.18")
|
||||
hostConfig := &runconfig.HostConfig{
|
||||
CpuShares: linuxMinCpuShares - 1,
|
||||
}
|
||||
adjustCpuShares(apiVersion, hostConfig)
|
||||
if hostConfig.CpuShares != linuxMinCpuShares {
|
||||
t.Errorf("Expected CpuShares to be %d", linuxMinCpuShares)
|
||||
}
|
||||
|
||||
hostConfig.CpuShares = linuxMaxCpuShares + 1
|
||||
adjustCpuShares(apiVersion, hostConfig)
|
||||
if hostConfig.CpuShares != linuxMaxCpuShares {
|
||||
t.Errorf("Expected CpuShares to be %d", linuxMaxCpuShares)
|
||||
}
|
||||
|
||||
hostConfig.CpuShares = 0
|
||||
adjustCpuShares(apiVersion, hostConfig)
|
||||
if hostConfig.CpuShares != 0 {
|
||||
t.Error("Expected CpuShares to be unchanged")
|
||||
}
|
||||
|
||||
hostConfig.CpuShares = 1024
|
||||
adjustCpuShares(apiVersion, hostConfig)
|
||||
if hostConfig.CpuShares != 1024 {
|
||||
t.Error("Expected CpuShares to be unchanged")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdjustCpuSharesNoAdjustment(t *testing.T) {
|
||||
apiVersion := version.Version("1.19")
|
||||
hostConfig := &runconfig.HostConfig{
|
||||
CpuShares: linuxMinCpuShares - 1,
|
||||
}
|
||||
adjustCpuShares(apiVersion, hostConfig)
|
||||
if hostConfig.CpuShares != linuxMinCpuShares-1 {
|
||||
t.Errorf("Expected CpuShares to be %d", linuxMinCpuShares-1)
|
||||
}
|
||||
|
||||
hostConfig.CpuShares = linuxMaxCpuShares + 1
|
||||
adjustCpuShares(apiVersion, hostConfig)
|
||||
if hostConfig.CpuShares != linuxMaxCpuShares+1 {
|
||||
t.Errorf("Expected CpuShares to be %d", linuxMaxCpuShares+1)
|
||||
}
|
||||
|
||||
hostConfig.CpuShares = 0
|
||||
adjustCpuShares(apiVersion, hostConfig)
|
||||
if hostConfig.CpuShares != 0 {
|
||||
t.Error("Expected CpuShares to be unchanged")
|
||||
}
|
||||
|
||||
hostConfig.CpuShares = 1024
|
||||
adjustCpuShares(apiVersion, hostConfig)
|
||||
if hostConfig.CpuShares != 1024 {
|
||||
t.Error("Expected CpuShares to be unchanged")
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !experimental windows
|
||||
// +build !experimental
|
||||
|
||||
package server
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
// NewServer sets up the required Server and does protocol specific checking.
|
||||
@@ -17,7 +19,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
||||
)
|
||||
switch proto {
|
||||
case "tcp":
|
||||
l, err := s.initTCPSocket(addr)
|
||||
l, err := s.initTcpSocket(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -29,7 +31,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
||||
|
||||
var res []serverCloser
|
||||
for _, l := range ls {
|
||||
res = append(res, &HTTPServer{
|
||||
res = append(res, &HttpServer{
|
||||
&http.Server{
|
||||
Addr: addr,
|
||||
Handler: s.router,
|
||||
@@ -41,7 +43,6 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
||||
|
||||
}
|
||||
|
||||
// AcceptConnections allows router to start listening for the incoming requests.
|
||||
func (s *Server) AcceptConnections(d *daemon.Daemon) {
|
||||
s.daemon = d
|
||||
s.registerSubRouter()
|
||||
@@ -57,8 +58,5 @@ func allocateDaemonPort(addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
|
||||
// is only relevant on non-Windows daemons.
|
||||
func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
|
||||
return nil
|
||||
func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) {
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
)
|
||||
|
||||
func (s *Server) getVolumesList(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
volumes, err := s.daemon.Volumes(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, &types.VolumesListResponse{Volumes: volumes})
|
||||
}
|
||||
|
||||
func (s *Server) getVolumeByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v, err := s.daemon.VolumeInspect(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, v)
|
||||
}
|
||||
|
||||
func (s *Server) postVolumesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req types.VolumeCreateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
volume, err := s.daemon.VolumeCreate(req.Name, req.Driver, req.DriverOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusCreated, volume)
|
||||
}
|
||||
|
||||
func (s *Server) deleteVolumes(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.daemon.VolumeRm(vars["name"]); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return nil
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// IsTimeout takes an error returned from (generally) the http package and determines if it is a timeout error.
|
||||
func IsTimeout(err error) bool {
|
||||
switch e := err.(type) {
|
||||
case net.Error:
|
||||
return e.Timeout()
|
||||
case *url.Error:
|
||||
if t, ok := e.Err.(net.Error); ok {
|
||||
return t.Timeout()
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
+12
-19
@@ -1,10 +1,9 @@
|
||||
// Package types is used for API stability in the types and response to the
|
||||
// This package is used for API stability in the types and response to the
|
||||
// consumers of the API stats endpoint.
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// ThrottlingData stores CPU throttling stats of one running container
|
||||
type ThrottlingData struct {
|
||||
// Number of periods with throttling active
|
||||
Periods uint64 `json:"periods"`
|
||||
@@ -14,8 +13,8 @@ type ThrottlingData struct {
|
||||
ThrottledTime uint64 `json:"throttled_time"`
|
||||
}
|
||||
|
||||
// CPUUsage stores All CPU stats aggregated since container inception.
|
||||
type CPUUsage struct {
|
||||
// All CPU stats are aggregated since container inception.
|
||||
type CpuUsage struct {
|
||||
// Total CPU time consumed.
|
||||
// Units: nanoseconds.
|
||||
TotalUsage uint64 `json:"total_usage"`
|
||||
@@ -30,14 +29,12 @@ type CPUUsage struct {
|
||||
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||
}
|
||||
|
||||
// CPUStats aggregates and wraps all CPU related info of container
|
||||
type CPUStats struct {
|
||||
CPUUsage CPUUsage `json:"cpu_usage"`
|
||||
type CpuStats struct {
|
||||
CpuUsage CpuUsage `json:"cpu_usage"`
|
||||
SystemUsage uint64 `json:"system_cpu_usage"`
|
||||
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||
}
|
||||
|
||||
// MemoryStats aggregates All memory stats since container inception
|
||||
type MemoryStats struct {
|
||||
// current res_counter usage for memory
|
||||
Usage uint64 `json:"usage"`
|
||||
@@ -51,7 +48,6 @@ type MemoryStats struct {
|
||||
Limit uint64 `json:"limit"`
|
||||
}
|
||||
|
||||
// BlkioStatEntry is one small entity to store a piece of Blkio stats
|
||||
// TODO Windows: This can be factored out
|
||||
type BlkioStatEntry struct {
|
||||
Major uint64 `json:"major"`
|
||||
@@ -60,7 +56,6 @@ type BlkioStatEntry struct {
|
||||
Value uint64 `json:"value"`
|
||||
}
|
||||
|
||||
// BlkioStats stores All IO service stats for data read and write
|
||||
// TODO Windows: This can be factored out
|
||||
type BlkioStats struct {
|
||||
// number of bytes tranferred to and from the block device
|
||||
@@ -74,9 +69,8 @@ type BlkioStats struct {
|
||||
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
||||
}
|
||||
|
||||
// NetworkStats aggregates All network stats of one container
|
||||
// TODO Windows: This will require refactoring
|
||||
type NetworkStats struct {
|
||||
type Network struct {
|
||||
RxBytes uint64 `json:"rx_bytes"`
|
||||
RxPackets uint64 `json:"rx_packets"`
|
||||
RxErrors uint64 `json:"rx_errors"`
|
||||
@@ -87,12 +81,11 @@ type NetworkStats struct {
|
||||
TxDropped uint64 `json:"tx_dropped"`
|
||||
}
|
||||
|
||||
// Stats is Ultimate struct aggregating all types of stats of one container
|
||||
type Stats struct {
|
||||
Read time.Time `json:"read"`
|
||||
Network NetworkStats `json:"network,omitempty"`
|
||||
PreCPUStats CPUStats `json:"precpu_stats,omitempty"`
|
||||
CPUStats CPUStats `json:"cpu_stats,omitempty"`
|
||||
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||
Read time.Time `json:"read"`
|
||||
Network Network `json:"network,omitempty"`
|
||||
PreCpuStats CpuStats `json:"precpu_stats,omitempty"`
|
||||
CpuStats CpuStats `json:"cpu_stats,omitempty"`
|
||||
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||
}
|
||||
|
||||
+35
-81
@@ -19,41 +19,35 @@ type ContainerCreateResponse struct {
|
||||
Warnings []string `json:"Warnings"`
|
||||
}
|
||||
|
||||
// ContainerExecCreateResponse contains response of Remote API:
|
||||
// POST "/containers/{name:.*}/exec"
|
||||
// POST /containers/{name:.*}/exec
|
||||
type ContainerExecCreateResponse struct {
|
||||
// ID is the exec ID.
|
||||
ID string `json:"Id"`
|
||||
}
|
||||
|
||||
// AuthResponse contains response of Remote API:
|
||||
// POST "/auth"
|
||||
// POST /auth
|
||||
type AuthResponse struct {
|
||||
// Status is the authentication status
|
||||
Status string `json:"Status"`
|
||||
}
|
||||
|
||||
// ContainerWaitResponse contains response of Remote API:
|
||||
// POST "/containers/"+containerID+"/wait"
|
||||
type ContainerWaitResponse struct {
|
||||
// StatusCode is the status code of the wait job
|
||||
StatusCode int `json:"StatusCode"`
|
||||
}
|
||||
|
||||
// ContainerCommitResponse contains response of Remote API:
|
||||
// POST "/commit?container="+containerID
|
||||
type ContainerCommitResponse struct {
|
||||
ID string `json:"Id"`
|
||||
}
|
||||
|
||||
// ContainerChange contains response of Remote API:
|
||||
// GET "/containers/{name:.*}/changes"
|
||||
type ContainerChange struct {
|
||||
Kind int
|
||||
Path string
|
||||
}
|
||||
|
||||
// ImageHistory contains response of Remote API:
|
||||
// GET "/images/{name:.*}/history"
|
||||
type ImageHistory struct {
|
||||
ID string `json:"Id"`
|
||||
@@ -64,40 +58,35 @@ type ImageHistory struct {
|
||||
Comment string
|
||||
}
|
||||
|
||||
// ImageDelete contains response of Remote API:
|
||||
// DELETE "/images/{name:.*}"
|
||||
type ImageDelete struct {
|
||||
Untagged string `json:",omitempty"`
|
||||
Deleted string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Image contains response of Remote API:
|
||||
// GET "/images/json"
|
||||
type Image struct {
|
||||
ID string `json:"Id"`
|
||||
ParentID string `json:"ParentId"`
|
||||
ParentId string
|
||||
RepoTags []string
|
||||
RepoDigests []string
|
||||
Created int64
|
||||
Size int64
|
||||
VirtualSize int64
|
||||
Created int
|
||||
Size int
|
||||
VirtualSize int
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// GraphDriverData returns Image's graph driver config info
|
||||
// when calling inspect command
|
||||
type GraphDriverData struct {
|
||||
Name string
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
// ImageInspect contains response of Remote API:
|
||||
// GET "/images/{name:.*}/json"
|
||||
type ImageInspect struct {
|
||||
ID string `json:"Id"`
|
||||
Id string
|
||||
Parent string
|
||||
Comment string
|
||||
Created string
|
||||
Created time.Time
|
||||
Container string
|
||||
ContainerConfig *runconfig.Config
|
||||
DockerVersion string
|
||||
@@ -110,8 +99,7 @@ type ImageInspect struct {
|
||||
GraphDriver GraphDriverData
|
||||
}
|
||||
|
||||
// Port stores open ports info of container
|
||||
// e.g. {"PrivatePort": 8080, "PublicPort": 80, "Type": "tcp"}
|
||||
// GET "/containers/json"
|
||||
type Port struct {
|
||||
IP string `json:",omitempty"`
|
||||
PrivatePort int
|
||||
@@ -119,17 +107,15 @@ type Port struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
// Container contains response of Remote API:
|
||||
// GET "/containers/json"
|
||||
type Container struct {
|
||||
ID string `json:"Id"`
|
||||
Names []string
|
||||
Image string
|
||||
Command string
|
||||
Created int64
|
||||
Created int
|
||||
Ports []Port
|
||||
SizeRw int64 `json:",omitempty"`
|
||||
SizeRootFs int64 `json:",omitempty"`
|
||||
SizeRw int `json:",omitempty"`
|
||||
SizeRootFs int `json:",omitempty"`
|
||||
Labels map[string]string
|
||||
Status string
|
||||
HostConfig struct {
|
||||
@@ -137,35 +123,32 @@ type Container struct {
|
||||
}
|
||||
}
|
||||
|
||||
// CopyConfig contains request body of Remote API:
|
||||
// POST "/containers/"+containerID+"/copy"
|
||||
type CopyConfig struct {
|
||||
Resource string
|
||||
}
|
||||
|
||||
// ContainerPathStat is used to encode the header from
|
||||
// GET "/containers/{name:.*}/archive"
|
||||
// "Name" is the file or directory name.
|
||||
// GET /containers/{name:.*}/archive
|
||||
// "name" is the file or directory name.
|
||||
// "path" is the absolute path to the resource in the container.
|
||||
type ContainerPathStat struct {
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
Mode os.FileMode `json:"mode"`
|
||||
Mtime time.Time `json:"mtime"`
|
||||
LinkTarget string `json:"linkTarget"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Size int64 `json:"size"`
|
||||
Mode os.FileMode `json:"mode"`
|
||||
Mtime time.Time `json:"mtime"`
|
||||
}
|
||||
|
||||
// ContainerProcessList contains response of Remote API:
|
||||
// GET "/containers/{name:.*}/top"
|
||||
type ContainerProcessList struct {
|
||||
Processes [][]string
|
||||
Titles []string
|
||||
}
|
||||
|
||||
// Version contains response of Remote API:
|
||||
// GET "/version"
|
||||
type Version struct {
|
||||
Version string
|
||||
APIVersion version.Version `json:"ApiVersion"`
|
||||
ApiVersion version.Version
|
||||
GitCommit string
|
||||
GoVersion string
|
||||
Os string
|
||||
@@ -175,7 +158,6 @@ type Version struct {
|
||||
BuildTime string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Info contains response of Remote API:
|
||||
// GET "/info"
|
||||
type Info struct {
|
||||
ID string
|
||||
@@ -185,11 +167,11 @@ type Info struct {
|
||||
DriverStatus [][2]string
|
||||
MemoryLimit bool
|
||||
SwapLimit bool
|
||||
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
|
||||
CPUCfsQuota bool `json:"CpuCfsQuota"`
|
||||
CpuCfsPeriod bool
|
||||
CpuCfsQuota bool
|
||||
IPv4Forwarding bool
|
||||
BridgeNfIptables bool
|
||||
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
|
||||
BridgeNfIp6tables bool
|
||||
Debug bool
|
||||
NFd int
|
||||
OomKillDisable bool
|
||||
@@ -207,15 +189,15 @@ type Info struct {
|
||||
NCPU int
|
||||
MemTotal int64
|
||||
DockerRootDir string
|
||||
HTTPProxy string `json:"HttpProxy"`
|
||||
HTTPSProxy string `json:"HttpsProxy"`
|
||||
HttpProxy string
|
||||
HttpsProxy string
|
||||
NoProxy string
|
||||
Name string
|
||||
Labels []string
|
||||
ExperimentalBuild bool
|
||||
}
|
||||
|
||||
// ExecStartCheck is a temp struct used by execStart
|
||||
// This struct is a temp struct used by execStart
|
||||
// Config fields is part of ExecConfig in runconfig package
|
||||
type ExecStartCheck struct {
|
||||
// ExecStart will first check if it's detached
|
||||
@@ -224,10 +206,7 @@ type ExecStartCheck struct {
|
||||
Tty bool
|
||||
}
|
||||
|
||||
// ContainerState stores container's running state
|
||||
// it's part of ContainerJSONBase and will return by "inspect" command
|
||||
type ContainerState struct {
|
||||
Status string
|
||||
Running bool
|
||||
Paused bool
|
||||
Restarting bool
|
||||
@@ -236,15 +215,14 @@ type ContainerState struct {
|
||||
Pid int
|
||||
ExitCode int
|
||||
Error string
|
||||
StartedAt string
|
||||
FinishedAt string
|
||||
StartedAt time.Time
|
||||
FinishedAt time.Time
|
||||
}
|
||||
|
||||
// ContainerJSONBase contains response of Remote API:
|
||||
// GET "/containers/{name:.*}/json"
|
||||
type ContainerJSONBase struct {
|
||||
ID string `json:"Id"`
|
||||
Created string
|
||||
Id string
|
||||
Created time.Time
|
||||
Path string
|
||||
Args []string
|
||||
State *ContainerState
|
||||
@@ -266,15 +244,13 @@ type ContainerJSONBase struct {
|
||||
GraphDriver GraphDriverData
|
||||
}
|
||||
|
||||
// ContainerJSON is newly used struct along with MountPoint
|
||||
type ContainerJSON struct {
|
||||
*ContainerJSONBase
|
||||
Mounts []MountPoint
|
||||
Config *runconfig.Config
|
||||
}
|
||||
|
||||
// ContainerJSONPre120 is a backcompatibility struct along with ContainerConfig.
|
||||
// Note this is not used by the Windows daemon.
|
||||
// backcompatibility struct along with ContainerConfig
|
||||
type ContainerJSONPre120 struct {
|
||||
*ContainerJSONBase
|
||||
Volumes map[string]string
|
||||
@@ -282,15 +258,14 @@ type ContainerJSONPre120 struct {
|
||||
Config *ContainerConfig
|
||||
}
|
||||
|
||||
// ContainerConfig is a backcompatibility struct used in ContainerJSONPre120
|
||||
type ContainerConfig struct {
|
||||
*runconfig.Config
|
||||
|
||||
// backward compatibility, they now live in HostConfig
|
||||
Memory int64
|
||||
MemorySwap int64
|
||||
CPUShares int64 `json:"CpuShares"`
|
||||
CPUSet string `json:"CpuSet"`
|
||||
CpuShares int64
|
||||
Cpuset string
|
||||
}
|
||||
|
||||
// MountPoint represents a mount point configuration inside the container.
|
||||
@@ -299,27 +274,6 @@ type MountPoint struct {
|
||||
Source string
|
||||
Destination string
|
||||
Driver string `json:",omitempty"`
|
||||
Mode string
|
||||
Mode string // this is internally named `Relabel`
|
||||
RW bool
|
||||
}
|
||||
|
||||
// Volume represents the configuration of a volume for the remote API
|
||||
type Volume struct {
|
||||
Name string // Name is the name of the volume
|
||||
Driver string // Driver is the Driver name used to create the volume
|
||||
Mountpoint string // Mountpoint is the location on disk of the volume
|
||||
}
|
||||
|
||||
// VolumesListResponse contains the response for the remote API:
|
||||
// GET "/volumes"
|
||||
type VolumesListResponse struct {
|
||||
Volumes []*Volume // Volumes is the list of volumes being returned
|
||||
}
|
||||
|
||||
// VolumeCreateRequest contains the response for the remote API:
|
||||
// POST "/volumes"
|
||||
type VolumeCreateRequest struct {
|
||||
Name string // Name is the requested name of the volume
|
||||
Driver string // Driver is the name of the driver that should be used to create the volume
|
||||
DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
|
||||
}
|
||||
|
||||
+1
-13
@@ -38,7 +38,6 @@ func NewBFlags() *BFlags {
|
||||
}
|
||||
|
||||
// AddBool adds a bool flag to BFlags
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) AddBool(name string, def bool) *Flag {
|
||||
flag := bf.addFlag(name, boolType)
|
||||
if flag == nil {
|
||||
@@ -53,7 +52,6 @@ func (bf *BFlags) AddBool(name string, def bool) *Flag {
|
||||
}
|
||||
|
||||
// AddString adds a string flag to BFlags
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) AddString(name string, def string) *Flag {
|
||||
flag := bf.addFlag(name, stringType)
|
||||
if flag == nil {
|
||||
@@ -63,9 +61,6 @@ func (bf *BFlags) AddString(name string, def string) *Flag {
|
||||
return flag
|
||||
}
|
||||
|
||||
// addFlag is a generic func used by the other AddXXX() func
|
||||
// to add a new flag to the BFlags struct.
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) addFlag(name string, flagType FlagType) *Flag {
|
||||
if _, ok := bf.flags[name]; ok {
|
||||
bf.Err = fmt.Errorf("Duplicate flag defined: %s", name)
|
||||
@@ -99,14 +94,7 @@ func (fl *Flag) IsTrue() bool {
|
||||
return fl.Value == "true"
|
||||
}
|
||||
|
||||
// Parse parses and checks if the BFlags is valid.
|
||||
// Any error noticed during the AddXXX() funcs will be generated/returned
|
||||
// here. We do this because an error during AddXXX() is more like a
|
||||
// compile time error so it doesn't matter too much when we stop our
|
||||
// processing as long as we do stop it, so this allows the code
|
||||
// around AddXXX() to be just:
|
||||
// defFlag := AddString("desription", "")
|
||||
// w/o needing to add an if-statement around each one.
|
||||
// Parse parses and checks if the BFlags is valid
|
||||
func (bf *BFlags) Parse() error {
|
||||
// If there was an error while defining the possible flags
|
||||
// go ahead and bubble it back up here since we didn't do it
|
||||
|
||||
+36
-18
@@ -10,7 +10,7 @@ package builder
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@@ -20,8 +20,6 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/nat"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
@@ -196,11 +194,7 @@ func from(b *builder, args []string, attributes map[string]bool, original string
|
||||
|
||||
name := args[0]
|
||||
|
||||
// Windows cannot support a container with no base image.
|
||||
if name == NoBaseImageSpecifier {
|
||||
if runtime.GOOS == "windows" {
|
||||
return fmt.Errorf("Windows does not support FROM scratch")
|
||||
}
|
||||
b.image = ""
|
||||
b.noBaseImage = true
|
||||
return nil
|
||||
@@ -273,15 +267,39 @@ func workdir(b *builder, args []string, attributes map[string]bool, original str
|
||||
return err
|
||||
}
|
||||
|
||||
// This is from the Dockerfile and will not necessarily be in platform
|
||||
// specific semantics, hence ensure it is converted.
|
||||
workdir := filepath.FromSlash(args[0])
|
||||
// Note that workdir passed comes from the Dockerfile. Hence it is in
|
||||
// Linux format using forward-slashes, even on Windows. However,
|
||||
// b.Config.WorkingDir is in platform-specific notation (in other words
|
||||
// on Windows will use `\`
|
||||
workdir := args[0]
|
||||
|
||||
if !system.IsAbs(workdir) {
|
||||
current := filepath.FromSlash(b.Config.WorkingDir)
|
||||
workdir = filepath.Join(string(os.PathSeparator), current, workdir)
|
||||
isAbs := false
|
||||
if runtime.GOOS == "windows" {
|
||||
// Alternate processing for Windows here is necessary as we can't call
|
||||
// filepath.IsAbs(workDir) as that would verify Windows style paths,
|
||||
// along with drive-letters (eg c:\pathto\file.txt). We (arguably
|
||||
// correctly or not) check for both forward and back slashes as this
|
||||
// is what the 1.4.2 GoLang implementation of IsAbs() does in the
|
||||
// isSlash() function.
|
||||
isAbs = workdir[0] == '\\' || workdir[0] == '/'
|
||||
} else {
|
||||
isAbs = filepath.IsAbs(workdir)
|
||||
}
|
||||
|
||||
if !isAbs {
|
||||
current := b.Config.WorkingDir
|
||||
if runtime.GOOS == "windows" {
|
||||
// Convert to Linux format before join
|
||||
current = strings.Replace(current, "\\", "/", -1)
|
||||
}
|
||||
// Must use path.Join so works correctly on Windows, not filepath
|
||||
workdir = path.Join("/", current, workdir)
|
||||
}
|
||||
|
||||
// Convert to platform specific format
|
||||
if runtime.GOOS == "windows" {
|
||||
workdir = strings.Replace(workdir, "/", "\\", -1)
|
||||
}
|
||||
b.Config.WorkingDir = workdir
|
||||
|
||||
return b.commit("", b.Config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
|
||||
@@ -330,7 +348,7 @@ func run(b *builder, args []string, attributes map[string]bool, original string)
|
||||
b.Config.Cmd = config.Cmd
|
||||
runconfig.Merge(b.Config, config)
|
||||
|
||||
defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
|
||||
defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
||||
|
||||
logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd)
|
||||
|
||||
@@ -383,7 +401,7 @@ func cmd(b *builder, args []string, attributes map[string]bool, original string)
|
||||
}
|
||||
}
|
||||
|
||||
b.Config.Cmd = stringutils.NewStrSlice(cmdSlice...)
|
||||
b.Config.Cmd = runconfig.NewCommand(cmdSlice...)
|
||||
|
||||
if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil {
|
||||
return err
|
||||
@@ -414,16 +432,16 @@ func entrypoint(b *builder, args []string, attributes map[string]bool, original
|
||||
switch {
|
||||
case attributes["json"]:
|
||||
// ENTRYPOINT ["echo", "hi"]
|
||||
b.Config.Entrypoint = stringutils.NewStrSlice(parsed...)
|
||||
b.Config.Entrypoint = runconfig.NewEntrypoint(parsed...)
|
||||
case len(parsed) == 0:
|
||||
// ENTRYPOINT []
|
||||
b.Config.Entrypoint = nil
|
||||
default:
|
||||
// ENTRYPOINT echo hi
|
||||
if runtime.GOOS != "windows" {
|
||||
b.Config.Entrypoint = stringutils.NewStrSlice("/bin/sh", "-c", parsed[0])
|
||||
b.Config.Entrypoint = runconfig.NewEntrypoint("/bin/sh", "-c", parsed[0])
|
||||
} else {
|
||||
b.Config.Entrypoint = stringutils.NewStrSlice("cmd", "/S /C", parsed[0])
|
||||
b.Config.Entrypoint = runconfig.NewEntrypoint("cmd", "/S /C", parsed[0])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-22
@@ -24,7 +24,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
@@ -282,18 +281,11 @@ func (b *builder) readDockerfile() error {
|
||||
// features.
|
||||
func (b *builder) dispatch(stepN int, ast *parser.Node) error {
|
||||
cmd := ast.Value
|
||||
|
||||
// To ensure the user is give a decent error message if the platform
|
||||
// on which the daemon is running does not support a builder command.
|
||||
if err := platformSupports(strings.ToLower(cmd)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attrs := ast.Attributes
|
||||
original := ast.Original
|
||||
flags := ast.Flags
|
||||
strs := []string{}
|
||||
msg := fmt.Sprintf("Step %d : %s", stepN+1, strings.ToUpper(cmd))
|
||||
msg := fmt.Sprintf("Step %d : %s", stepN, strings.ToUpper(cmd))
|
||||
|
||||
if len(ast.Flags) > 0 {
|
||||
msg += " " + strings.Join(ast.Flags, " ")
|
||||
@@ -357,16 +349,3 @@ func (b *builder) dispatch(stepN int, ast *parser.Node) error {
|
||||
|
||||
return fmt.Errorf("Unknown instruction: %s", strings.ToUpper(cmd))
|
||||
}
|
||||
|
||||
// platformSupports is a short-term function to give users a quality error
|
||||
// message if a Dockerfile uses a command not supported on the platform.
|
||||
func platformSupports(command string) error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return nil
|
||||
}
|
||||
switch command {
|
||||
case "expose", "volume", "user":
|
||||
return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+30
-28
@@ -33,7 +33,6 @@ import (
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/progressreader"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
@@ -74,7 +73,7 @@ func (b *builder) readContext(context io.Reader) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (b *builder) commit(id string, autoCmd *stringutils.StrSlice, comment string) error {
|
||||
func (b *builder) commit(id string, autoCmd *runconfig.Command, comment string) error {
|
||||
if b.disableCommit {
|
||||
return nil
|
||||
}
|
||||
@@ -85,11 +84,11 @@ func (b *builder) commit(id string, autoCmd *stringutils.StrSlice, comment strin
|
||||
if id == "" {
|
||||
cmd := b.Config.Cmd
|
||||
if runtime.GOOS != "windows" {
|
||||
b.Config.Cmd = stringutils.NewStrSlice("/bin/sh", "-c", "#(nop) "+comment)
|
||||
b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", "#(nop) "+comment)
|
||||
} else {
|
||||
b.Config.Cmd = stringutils.NewStrSlice("cmd", "/S /C", "REM (nop) "+comment)
|
||||
b.Config.Cmd = runconfig.NewCommand("cmd", "/S /C", "REM (nop) "+comment)
|
||||
}
|
||||
defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
|
||||
defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
||||
|
||||
hit, err := b.probeCache()
|
||||
if err != nil {
|
||||
@@ -216,11 +215,11 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
||||
|
||||
cmd := b.Config.Cmd
|
||||
if runtime.GOOS != "windows" {
|
||||
b.Config.Cmd = stringutils.NewStrSlice("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
|
||||
b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
|
||||
} else {
|
||||
b.Config.Cmd = stringutils.NewStrSlice("cmd", "/S /C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest))
|
||||
b.Config.Cmd = runconfig.NewCommand("cmd", "/S /C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest))
|
||||
}
|
||||
defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
|
||||
defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
||||
|
||||
hit, err := b.probeCache()
|
||||
if err != nil {
|
||||
@@ -231,7 +230,7 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
||||
return nil
|
||||
}
|
||||
|
||||
container, _, err := b.Daemon.ContainerCreate("", b.Config, nil, true)
|
||||
container, _, err := b.Daemon.Create(b.Config, nil, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -242,12 +241,20 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
||||
}
|
||||
defer container.Unmount()
|
||||
|
||||
if err := container.PrepareStorage(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ci := range copyInfos {
|
||||
if err := b.addContext(container, ci.origPath, ci.destPath, ci.decompress); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := container.CleanupStorage(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest)); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -270,7 +277,7 @@ func calcCopyInfo(b *builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
||||
|
||||
// Twiddle the destPath when its a relative path - meaning, make it
|
||||
// relative to the WORKINGDIR
|
||||
if !system.IsAbs(destPath) {
|
||||
if !filepath.IsAbs(destPath) {
|
||||
hasSlash := strings.HasSuffix(destPath, string(os.PathSeparator))
|
||||
destPath = filepath.Join(string(os.PathSeparator), filepath.FromSlash(b.Config.WorkingDir), destPath)
|
||||
|
||||
@@ -323,7 +330,7 @@ func calcCopyInfo(b *builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
||||
In: resp.Body,
|
||||
Out: b.OutOld,
|
||||
Formatter: b.StreamFormatter,
|
||||
Size: resp.ContentLength,
|
||||
Size: int(resp.ContentLength),
|
||||
NewLines: true,
|
||||
ID: "",
|
||||
Action: "Downloading",
|
||||
@@ -348,11 +355,8 @@ func calcCopyInfo(b *builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
||||
}
|
||||
}
|
||||
|
||||
// Windows does not support UtimesNano.
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := system.UtimesNano(tmpFileName, times); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := system.UtimesNano(tmpFileName, times); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ci.origPath = filepath.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
|
||||
@@ -363,7 +367,7 @@ func calcCopyInfo(b *builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.FromSlash(u.Path) // Ensure in platform semantics
|
||||
path := u.Path
|
||||
if strings.HasSuffix(path, string(os.PathSeparator)) {
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
@@ -535,11 +539,7 @@ func (b *builder) processImageFrom(img *image.Image) error {
|
||||
|
||||
// Process ONBUILD triggers if they exist
|
||||
if nTriggers := len(b.Config.OnBuild); nTriggers != 0 {
|
||||
word := "trigger"
|
||||
if nTriggers > 1 {
|
||||
word = "triggers"
|
||||
}
|
||||
fmt.Fprintf(b.ErrStream, "# Executing %d build %s...\n", nTriggers, word)
|
||||
fmt.Fprintf(b.ErrStream, "# Executing %d build triggers\n", nTriggers)
|
||||
}
|
||||
|
||||
// Copy the ONBUILD triggers, and remove them from the config, since the config will be committed.
|
||||
@@ -547,7 +547,7 @@ func (b *builder) processImageFrom(img *image.Image) error {
|
||||
b.Config.OnBuild = []string{}
|
||||
|
||||
// parse the ONBUILD triggers by invoking the parser
|
||||
for _, step := range onBuildTriggers {
|
||||
for stepN, step := range onBuildTriggers {
|
||||
ast, err := parser.Parse(strings.NewReader(step))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -561,6 +561,8 @@ func (b *builder) processImageFrom(img *image.Image) error {
|
||||
return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", n.Value)
|
||||
}
|
||||
|
||||
fmt.Fprintf(b.OutStream, "Trigger %d, %s\n", stepN, step)
|
||||
|
||||
if err := b.dispatch(i, n); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -605,9 +607,9 @@ func (b *builder) create() (*daemon.Container, error) {
|
||||
b.Config.Image = b.image
|
||||
|
||||
hostConfig := &runconfig.HostConfig{
|
||||
CPUShares: b.cpuShares,
|
||||
CPUPeriod: b.cpuPeriod,
|
||||
CPUQuota: b.cpuQuota,
|
||||
CpuShares: b.cpuShares,
|
||||
CpuPeriod: b.cpuPeriod,
|
||||
CpuQuota: b.cpuQuota,
|
||||
CpusetCpus: b.cpuSetCpus,
|
||||
CpusetMems: b.cpuSetMems,
|
||||
CgroupParent: b.cgroupParent,
|
||||
@@ -619,7 +621,7 @@ func (b *builder) create() (*daemon.Container, error) {
|
||||
config := *b.Config
|
||||
|
||||
// Create the container
|
||||
c, warnings, err := b.Daemon.ContainerCreate("", b.Config, hostConfig, true)
|
||||
c, warnings, err := b.Daemon.Create(b.Config, hostConfig, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -636,7 +638,7 @@ func (b *builder) create() (*daemon.Container, error) {
|
||||
c.Path = s[0]
|
||||
c.Args = s[1:]
|
||||
} else {
|
||||
config.Cmd = stringutils.NewStrSlice()
|
||||
config.Cmd = runconfig.NewCommand()
|
||||
}
|
||||
|
||||
return c, nil
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build freebsd linux
|
||||
// +build linux
|
||||
|
||||
package builder
|
||||
|
||||
+4
-10
@@ -7,7 +7,6 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -140,7 +139,7 @@ func Build(d *daemon.Daemon, buildConfig *Config) error {
|
||||
}
|
||||
defer f.Body.Close()
|
||||
ct := f.Header.Get("Content-Type")
|
||||
clen := f.ContentLength
|
||||
clen := int(f.ContentLength)
|
||||
contentType, bodyReader, err := inspectResponse(ct, f.Body, clen)
|
||||
|
||||
defer bodyReader.Close()
|
||||
@@ -183,11 +182,11 @@ func Build(d *daemon.Daemon, buildConfig *Config) error {
|
||||
|
||||
builder := &builder{
|
||||
Daemon: d,
|
||||
OutStream: &streamformatter.StdoutFormatter{
|
||||
OutStream: &streamformatter.StdoutFormater{
|
||||
Writer: buildConfig.Stdout,
|
||||
StreamFormatter: sf,
|
||||
},
|
||||
ErrStream: &streamformatter.StderrFormatter{
|
||||
ErrStream: &streamformatter.StderrFormater{
|
||||
Writer: buildConfig.Stdout,
|
||||
StreamFormatter: sf,
|
||||
},
|
||||
@@ -280,11 +279,6 @@ func Commit(name string, d *daemon.Daemon, c *CommitConfig) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// It is not possible to commit a running container on Windows
|
||||
if runtime.GOOS == "windows" && container.IsRunning() {
|
||||
return "", fmt.Errorf("Windows does not support commit of a running container")
|
||||
}
|
||||
|
||||
if c.Config == nil {
|
||||
c.Config = &runconfig.Config{}
|
||||
}
|
||||
@@ -322,7 +316,7 @@ func Commit(name string, d *daemon.Daemon, c *CommitConfig) (string, error) {
|
||||
// - an io.Reader for the response body
|
||||
// - an error value which will be non-nil either when something goes wrong while
|
||||
// reading bytes from r or when the detected content-type is not acceptable.
|
||||
func inspectResponse(ct string, r io.ReadCloser, clen int64) (string, io.ReadCloser, error) {
|
||||
func inspectResponse(ct string, r io.ReadCloser, clen int) (string, io.ReadCloser, error) {
|
||||
plen := clen
|
||||
if plen <= 0 || plen > maxPreambleLength {
|
||||
plen = maxPreambleLength
|
||||
|
||||
+4
-4
@@ -31,7 +31,7 @@ func TestInspectEmptyResponse(t *testing.T) {
|
||||
func TestInspectResponseBinary(t *testing.T) {
|
||||
ct := "application/octet-stream"
|
||||
br := ioutil.NopCloser(bytes.NewReader(binaryContext))
|
||||
contentType, bReader, err := inspectResponse(ct, br, int64(len(binaryContext)))
|
||||
contentType, bReader, err := inspectResponse(ct, br, len(binaryContext))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func TestResponseUnsupportedContentType(t *testing.T) {
|
||||
content := []byte(textPlainDockerfile)
|
||||
ct := "application/json"
|
||||
br := ioutil.NopCloser(bytes.NewReader(content))
|
||||
contentType, bReader, err := inspectResponse(ct, br, int64(len(textPlainDockerfile)))
|
||||
contentType, bReader, err := inspectResponse(ct, br, len(textPlainDockerfile))
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Should have returned an error on content-type 'application/json'")
|
||||
@@ -77,7 +77,7 @@ func TestInspectResponseTextSimple(t *testing.T) {
|
||||
content := []byte(textPlainDockerfile)
|
||||
ct := "text/plain"
|
||||
br := ioutil.NopCloser(bytes.NewReader(content))
|
||||
contentType, bReader, err := inspectResponse(ct, br, int64(len(content)))
|
||||
contentType, bReader, err := inspectResponse(ct, br, len(content))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -96,7 +96,7 @@ func TestInspectResponseTextSimple(t *testing.T) {
|
||||
func TestInspectResponseEmptyContentType(t *testing.T) {
|
||||
content := []byte(textPlainDockerfile)
|
||||
br := ioutil.NopCloser(bytes.NewReader(content))
|
||||
contentType, bodyReader, err := inspectResponse("", br, int64(len(content)))
|
||||
contentType, bodyReader, err := inspectResponse("", br, len(content))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -232,11 +232,6 @@ func parseString(rest string) (*Node, map[string]bool, error) {
|
||||
|
||||
// parseJSON converts JSON arrays to an AST.
|
||||
func parseJSON(rest string) (*Node, map[string]bool, error) {
|
||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||
if !strings.HasPrefix(rest, "[") {
|
||||
return nil, nil, fmt.Errorf(`Error parsing "%s" as a JSON array`, rest)
|
||||
}
|
||||
|
||||
var myJSON []interface{}
|
||||
if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -42,7 +42,7 @@ var (
|
||||
func init() {
|
||||
// Dispatch Table. see line_parsers.go for the parse functions.
|
||||
// The command is parsed and mapped to the line parser. The line parser
|
||||
// receives the arguments but not the command, and returns an AST after
|
||||
// recieves the arguments but not the command, and returns an AST after
|
||||
// reformulating the arguments according to the rules in the parser
|
||||
// functions. Errors are propagated up by Parse() and the resulting AST can
|
||||
// be incorporated directly into the existing AST as a next.
|
||||
|
||||
@@ -3,8 +3,6 @@ MAINTAINER Seongyeol Lim <seongyeol37@gmail.com>
|
||||
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
ADD . /
|
||||
ADD null /
|
||||
COPY nullfile /tmp
|
||||
ADD [ "vimrc", "/tmp" ]
|
||||
COPY [ "bashrc", "/tmp" ]
|
||||
COPY [ "test file", "/tmp" ]
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
(maintainer "Seongyeol Lim <seongyeol37@gmail.com>")
|
||||
(copy "." "/go/src/github.com/docker/docker")
|
||||
(add "." "/")
|
||||
(add "null" "/")
|
||||
(copy "nullfile" "/tmp")
|
||||
(add "vimrc" "/tmp")
|
||||
(copy "bashrc" "/tmp")
|
||||
(copy "test file" "/tmp")
|
||||
|
||||
@@ -49,45 +49,3 @@ func TestShellParser(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEnv(t *testing.T) {
|
||||
sw := &shellWord{
|
||||
word: "",
|
||||
envs: nil,
|
||||
pos: 0,
|
||||
}
|
||||
|
||||
sw.envs = []string{}
|
||||
if sw.getEnv("foo") != "" {
|
||||
t.Fatalf("2 - 'foo' should map to ''")
|
||||
}
|
||||
|
||||
sw.envs = []string{"foo"}
|
||||
if sw.getEnv("foo") != "" {
|
||||
t.Fatalf("3 - 'foo' should map to ''")
|
||||
}
|
||||
|
||||
sw.envs = []string{"foo="}
|
||||
if sw.getEnv("foo") != "" {
|
||||
t.Fatalf("4 - 'foo' should map to ''")
|
||||
}
|
||||
|
||||
sw.envs = []string{"foo=bar"}
|
||||
if sw.getEnv("foo") != "bar" {
|
||||
t.Fatalf("5 - 'foo' should map to 'bar'")
|
||||
}
|
||||
|
||||
sw.envs = []string{"foo=bar", "car=hat"}
|
||||
if sw.getEnv("foo") != "bar" {
|
||||
t.Fatalf("6 - 'foo' should map to 'bar'")
|
||||
}
|
||||
if sw.getEnv("car") != "hat" {
|
||||
t.Fatalf("7 - 'car' should map to 'hat'")
|
||||
}
|
||||
|
||||
// Make sure we grab the first 'car' in the list
|
||||
sw.envs = []string{"foo=bar", "car=hat", "car=bike"}
|
||||
if sw.getEnv("car") != "hat" {
|
||||
t.Fatalf("8 - 'car' should map to 'hat'")
|
||||
}
|
||||
}
|
||||
|
||||
+59
-106
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -15,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// ConfigFileName is the name of config file
|
||||
// ConfigFile is the name of config file
|
||||
ConfigFileName = "config.json"
|
||||
oldConfigfile = ".dockercfg"
|
||||
|
||||
@@ -71,88 +70,6 @@ func NewConfigFile(fn string) *ConfigFile {
|
||||
}
|
||||
}
|
||||
|
||||
// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
|
||||
// auth config information with given directory and populates the receiver object
|
||||
func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
|
||||
b, err := ioutil.ReadAll(configData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
|
||||
arr := strings.Split(string(b), "\n")
|
||||
if len(arr) < 2 {
|
||||
return fmt.Errorf("The Auth config file is empty")
|
||||
}
|
||||
authConfig := AuthConfig{}
|
||||
origAuth := strings.Split(arr[0], " = ")
|
||||
if len(origAuth) != 2 {
|
||||
return fmt.Errorf("Invalid Auth config file")
|
||||
}
|
||||
authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
origEmail := strings.Split(arr[1], " = ")
|
||||
if len(origEmail) != 2 {
|
||||
return fmt.Errorf("Invalid Auth config file")
|
||||
}
|
||||
authConfig.Email = origEmail[1]
|
||||
authConfig.ServerAddress = defaultIndexserver
|
||||
configFile.AuthConfigs[defaultIndexserver] = authConfig
|
||||
} else {
|
||||
for k, authConfig := range configFile.AuthConfigs {
|
||||
authConfig.Username, authConfig.Password, err = DecodeAuth(authConfig.Auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authConfig.Auth = ""
|
||||
authConfig.ServerAddress = k
|
||||
configFile.AuthConfigs[k] = authConfig
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadFromReader reads the configuration data given and sets up the auth config
|
||||
// information with given directory and populates the receiver object
|
||||
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
|
||||
if err := json.NewDecoder(configData).Decode(&configFile); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
for addr, ac := range configFile.AuthConfigs {
|
||||
ac.Username, ac.Password, err = DecodeAuth(ac.Auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ac.Auth = ""
|
||||
ac.ServerAddress = addr
|
||||
configFile.AuthConfigs[addr] = ac
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
|
||||
// a non-nested reader
|
||||
func LegacyLoadFromReader(configData io.Reader) (*ConfigFile, error) {
|
||||
configFile := ConfigFile{
|
||||
AuthConfigs: make(map[string]AuthConfig),
|
||||
}
|
||||
err := configFile.LegacyLoadFromReader(configData)
|
||||
return &configFile, err
|
||||
}
|
||||
|
||||
// LoadFromReader is a convenience function that creates a ConfigFile object from
|
||||
// a reader
|
||||
func LoadFromReader(configData io.Reader) (*ConfigFile, error) {
|
||||
configFile := ConfigFile{
|
||||
AuthConfigs: make(map[string]AuthConfig),
|
||||
}
|
||||
err := configFile.LoadFromReader(configData)
|
||||
return &configFile, err
|
||||
}
|
||||
|
||||
// Load reads the configuration files in the given directory, and sets up
|
||||
// the auth config information and return values.
|
||||
// FIXME: use the internal golang config parser
|
||||
@@ -173,8 +90,22 @@ func Load(configDir string) (*ConfigFile, error) {
|
||||
return &configFile, err
|
||||
}
|
||||
defer file.Close()
|
||||
err = configFile.LoadFromReader(file)
|
||||
return &configFile, err
|
||||
|
||||
if err := json.NewDecoder(file).Decode(&configFile); err != nil {
|
||||
return &configFile, err
|
||||
}
|
||||
|
||||
for addr, ac := range configFile.AuthConfigs {
|
||||
ac.Username, ac.Password, err = DecodeAuth(ac.Auth)
|
||||
if err != nil {
|
||||
return &configFile, err
|
||||
}
|
||||
ac.Auth = ""
|
||||
ac.ServerAddress = addr
|
||||
configFile.AuthConfigs[addr] = ac
|
||||
}
|
||||
|
||||
return &configFile, nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
// if file is there but we can't stat it for any reason other
|
||||
// than it doesn't exist then stop
|
||||
@@ -186,18 +117,49 @@ func Load(configDir string) (*ConfigFile, error) {
|
||||
if _, err := os.Stat(confFile); err != nil {
|
||||
return &configFile, nil //missing file is not an error
|
||||
}
|
||||
file, err := os.Open(confFile)
|
||||
|
||||
b, err := ioutil.ReadFile(confFile)
|
||||
if err != nil {
|
||||
return &configFile, err
|
||||
}
|
||||
defer file.Close()
|
||||
err = configFile.LegacyLoadFromReader(file)
|
||||
return &configFile, err
|
||||
|
||||
if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
|
||||
arr := strings.Split(string(b), "\n")
|
||||
if len(arr) < 2 {
|
||||
return &configFile, fmt.Errorf("The Auth config file is empty")
|
||||
}
|
||||
authConfig := AuthConfig{}
|
||||
origAuth := strings.Split(arr[0], " = ")
|
||||
if len(origAuth) != 2 {
|
||||
return &configFile, fmt.Errorf("Invalid Auth config file")
|
||||
}
|
||||
authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1])
|
||||
if err != nil {
|
||||
return &configFile, err
|
||||
}
|
||||
origEmail := strings.Split(arr[1], " = ")
|
||||
if len(origEmail) != 2 {
|
||||
return &configFile, fmt.Errorf("Invalid Auth config file")
|
||||
}
|
||||
authConfig.Email = origEmail[1]
|
||||
authConfig.ServerAddress = defaultIndexserver
|
||||
configFile.AuthConfigs[defaultIndexserver] = authConfig
|
||||
} else {
|
||||
for k, authConfig := range configFile.AuthConfigs {
|
||||
authConfig.Username, authConfig.Password, err = DecodeAuth(authConfig.Auth)
|
||||
if err != nil {
|
||||
return &configFile, err
|
||||
}
|
||||
authConfig.Auth = ""
|
||||
authConfig.ServerAddress = k
|
||||
configFile.AuthConfigs[k] = authConfig
|
||||
}
|
||||
}
|
||||
return &configFile, nil
|
||||
}
|
||||
|
||||
// SaveToWriter encodes and writes out all the authorization information to
|
||||
// the given writer
|
||||
func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
|
||||
// Save encodes and writes out all the authorization information
|
||||
func (configFile *ConfigFile) Save() error {
|
||||
// Encode sensitive data into a new/temp struct
|
||||
tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
|
||||
for k, authConfig := range configFile.AuthConfigs {
|
||||
@@ -218,25 +180,16 @@ func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Save encodes and writes out all the authorization information
|
||||
func (configFile *ConfigFile) Save() error {
|
||||
if configFile.Filename() == "" {
|
||||
return fmt.Errorf("Can't save config with empty filename")
|
||||
}
|
||||
|
||||
if err := system.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile(configFile.filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
|
||||
if err := ioutil.WriteFile(configFile.filename, data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
return configFile.SaveToWriter(f)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filename returns the name of the configuration file
|
||||
|
||||
+92
-362
@@ -4,41 +4,15 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
)
|
||||
|
||||
func TestEmptyConfigDir(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
SetConfigDir(tmpHome)
|
||||
|
||||
config, err := Load("")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading on empty config dir: %q", err)
|
||||
}
|
||||
|
||||
expectedConfigFilename := filepath.Join(tmpHome, ConfigFileName)
|
||||
if config.Filename() != expectedConfigFilename {
|
||||
t.Fatalf("Expected config filename %s, got %s", expectedConfigFilename, config.Filename())
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||
}
|
||||
|
||||
func TestMissingFile(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
@@ -46,15 +20,19 @@ func TestMissingFile(t *testing.T) {
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||
err = config.Save()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save: %q", err)
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||
if !strings.Contains(string(buf), `"auths":`) {
|
||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveFileToDirs(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
||||
|
||||
tmpHome += "/.docker"
|
||||
|
||||
@@ -64,38 +42,32 @@ func TestSaveFileToDirs(t *testing.T) {
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||
err = config.Save()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save: %q", err)
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||
if !strings.Contains(string(buf), `"auths":`) {
|
||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyFile(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||
if err := ioutil.WriteFile(fn, []byte(""), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(fn, []byte(""), 0600)
|
||||
|
||||
_, err = Load(tmpHome)
|
||||
_, err := Load(tmpHome)
|
||||
if err == nil {
|
||||
t.Fatalf("Was supposed to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyJson(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||
if err := ioutil.WriteFile(fn, []byte("{}"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(fn, []byte("{}"), 0600)
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
@@ -103,276 +75,36 @@ func TestEmptyJson(t *testing.T) {
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||
}
|
||||
|
||||
func TestOldInvalidsAuth(t *testing.T) {
|
||||
invalids := map[string]string{
|
||||
`username = test`: "The Auth config file is empty",
|
||||
`username
|
||||
password
|
||||
email`: "Invalid Auth config file",
|
||||
`username = test
|
||||
email`: "Invalid auth configuration file",
|
||||
`username = am9lam9lOmhlbGxv
|
||||
email`: "Invalid Auth config file",
|
||||
}
|
||||
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
homeKey := homedir.Key()
|
||||
homeVal := homedir.Get()
|
||||
|
||||
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||
os.Setenv(homeKey, tmpHome)
|
||||
|
||||
for content, expectedError := range invalids {
|
||||
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||
if err := ioutil.WriteFile(fn, []byte(content), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err == nil || err.Error() != expectedError {
|
||||
t.Fatalf("Should have failed, got: %q, %q", config, err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestOldValidAuth(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
homeKey := homedir.Key()
|
||||
homeVal := homedir.Get()
|
||||
|
||||
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||
os.Setenv(homeKey, tmpHome)
|
||||
|
||||
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||
js := `username = am9lam9lOmhlbGxv
|
||||
email = user@example.com`
|
||||
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// defaultIndexserver is https://index.docker.io/v1/
|
||||
ac := config.AuthConfigs["https://index.docker.io/v1/"]
|
||||
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||
|
||||
if !strings.Contains(configStr, "user@example.com") {
|
||||
t.Fatalf("Should have save in new form: %s", configStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOldJsonInvalid(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
homeKey := homedir.Key()
|
||||
homeVal := homedir.Get()
|
||||
|
||||
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||
os.Setenv(homeKey, tmpHome)
|
||||
|
||||
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||
js := `{"https://index.docker.io/v1/":{"auth":"test","email":"user@example.com"}}`
|
||||
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err == nil || err.Error() != "Invalid auth configuration file" {
|
||||
t.Fatalf("Expected an error got : %v, %v", config, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOldJson(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
homeKey := homedir.Key()
|
||||
homeVal := homedir.Get()
|
||||
|
||||
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||
os.Setenv(homeKey, tmpHome)
|
||||
|
||||
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
|
||||
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||
}
|
||||
|
||||
ac := config.AuthConfigs["https://index.docker.io/v1/"]
|
||||
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||
|
||||
if !strings.Contains(configStr, "user@example.com") {
|
||||
t.Fatalf("Should have save in new form: %s", configStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewJson(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
|
||||
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||
}
|
||||
|
||||
ac := config.AuthConfigs["https://index.docker.io/v1/"]
|
||||
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||
|
||||
if !strings.Contains(configStr, "user@example.com") {
|
||||
t.Fatalf("Should have save in new form: %s", configStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonWithPsFormat(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||
js := `{
|
||||
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
|
||||
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
|
||||
}`
|
||||
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||
}
|
||||
|
||||
if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
|
||||
t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||
if !strings.Contains(configStr, `"psFormat":`) ||
|
||||
!strings.Contains(configStr, "{{.ID}}") {
|
||||
t.Fatalf("Should have save in new form: %s", configStr)
|
||||
}
|
||||
}
|
||||
|
||||
// Save it and make sure it shows up in new form
|
||||
func saveConfigAndValidateNewFormat(t *testing.T, config *ConfigFile, homeFolder string) string {
|
||||
err := config.Save()
|
||||
err = config.Save()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save: %q", err)
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(filepath.Join(homeFolder, ConfigFileName))
|
||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||
if !strings.Contains(string(buf), `"auths":`) {
|
||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func TestConfigDir(t *testing.T) {
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
func TestOldJson(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
|
||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
if ConfigDir() == tmpHome {
|
||||
t.Fatalf("Expected ConfigDir to be different than %s by default, but was the same", tmpHome)
|
||||
}
|
||||
homeKey := homedir.Key()
|
||||
homeVal := homedir.Get()
|
||||
|
||||
// Update configDir
|
||||
SetConfigDir(tmpHome)
|
||||
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||
os.Setenv(homeKey, tmpHome)
|
||||
|
||||
if ConfigDir() != tmpHome {
|
||||
t.Fatalf("Expected ConfigDir to %s, but was %s", tmpHome, ConfigDir())
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigFile(t *testing.T) {
|
||||
configFilename := "configFilename"
|
||||
configFile := NewConfigFile(configFilename)
|
||||
|
||||
if configFile.Filename() != configFilename {
|
||||
t.Fatalf("Expected %s, got %s", configFilename, configFile.Filename())
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonReaderNoFile(t *testing.T) {
|
||||
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
|
||||
|
||||
config, err := LoadFromReader(strings.NewReader(js))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||
}
|
||||
|
||||
ac := config.AuthConfigs["https://index.docker.io/v1/"]
|
||||
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestOldJsonReaderNoFile(t *testing.T) {
|
||||
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
|
||||
ioutil.WriteFile(fn, []byte(js), 0600)
|
||||
|
||||
config, err := LegacyLoadFromReader(strings.NewReader(js))
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||
}
|
||||
@@ -381,14 +113,59 @@ func TestOldJsonReaderNoFile(t *testing.T) {
|
||||
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
err = config.Save()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save: %q", err)
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||
if !strings.Contains(string(buf), `"auths":`) ||
|
||||
!strings.Contains(string(buf), "user@example.com") {
|
||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonWithPsFormatNoFile(t *testing.T) {
|
||||
func TestNewJson(t *testing.T) {
|
||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
|
||||
ioutil.WriteFile(fn, []byte(js), 0600)
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||
}
|
||||
|
||||
ac := config.AuthConfigs["https://index.docker.io/v1/"]
|
||||
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||
}
|
||||
|
||||
// Now save it and make sure it shows up in new form
|
||||
err = config.Save()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save: %q", err)
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||
if !strings.Contains(string(buf), `"auths":`) ||
|
||||
!strings.Contains(string(buf), "user@example.com") {
|
||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonWithPsFormat(t *testing.T) {
|
||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||
js := `{
|
||||
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
|
||||
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
|
||||
}`
|
||||
config, err := LoadFromReader(strings.NewReader(js))
|
||||
ioutil.WriteFile(fn, []byte(js), 0600)
|
||||
|
||||
config, err := Load(tmpHome)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||
}
|
||||
@@ -397,62 +174,15 @@ func TestJsonWithPsFormatNoFile(t *testing.T) {
|
||||
t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestJsonSaveWithNoFile(t *testing.T) {
|
||||
js := `{
|
||||
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
|
||||
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
|
||||
}`
|
||||
config, err := LoadFromReader(strings.NewReader(js))
|
||||
// Now save it and make sure it shows up in new form
|
||||
err = config.Save()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error. File should not have been able to save with no file name.")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save: %q", err)
|
||||
}
|
||||
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a temp dir: %q", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||
f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
err = config.SaveToWriter(f)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed saving to file: %q", err)
|
||||
}
|
||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||
if !strings.Contains(string(buf), `"auths":`) ||
|
||||
!strings.Contains(string(buf), "user@example.com") {
|
||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||
}
|
||||
|
||||
}
|
||||
func TestLegacyJsonSaveWithNoFile(t *testing.T) {
|
||||
|
||||
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
|
||||
config, err := LegacyLoadFromReader(strings.NewReader(js))
|
||||
err = config.Save()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error. File should not have been able to save with no file name.")
|
||||
}
|
||||
|
||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a temp dir: %q", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpHome)
|
||||
|
||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||
f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
err = config.SaveToWriter(f)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed saving to file: %q", err)
|
||||
}
|
||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||
if !strings.Contains(string(buf), `"auths":`) ||
|
||||
!strings.Contains(string(buf), "user@example.com") {
|
||||
if !strings.Contains(string(buf), `"psFormat":`) ||
|
||||
!strings.Contains(string(buf), "{{.ID}}") {
|
||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <tunables/global>
|
||||
|
||||
profile docker-default flags=(attach_disconnected,mediate_deleted) {
|
||||
#include <abstractions/base>
|
||||
|
||||
network,
|
||||
capability,
|
||||
file,
|
||||
umount,
|
||||
|
||||
deny @{PROC}/sys/fs/** wklx,
|
||||
deny @{PROC}/sysrq-trigger rwklx,
|
||||
deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
|
||||
deny @{PROC}/sys/kernel/*/** wklx,
|
||||
|
||||
deny mount,
|
||||
|
||||
deny /sys/[^f]*/** wklx,
|
||||
deny /sys/f[^s]*/** wklx,
|
||||
deny /sys/fs/[^c]*/** wklx,
|
||||
deny /sys/fs/c[^g]*/** wklx,
|
||||
deny /sys/fs/cg[^r]*/** wklx,
|
||||
deny /sys/firmware/efi/efivars/** rwklx,
|
||||
deny /sys/kernel/security/** rwklx,
|
||||
}
|
||||
+28
-167
@@ -1,6 +1,6 @@
|
||||
@{DOCKER_GRAPH_PATH}=/var/lib/docker
|
||||
|
||||
profile /usr/bin/docker (attach_disconnected, complain) {
|
||||
profile /usr/bin/docker (attach_disconnected) {
|
||||
# Prevent following links to these files during container setup.
|
||||
deny /etc/** mkl,
|
||||
deny /dev/** kl,
|
||||
@@ -21,190 +21,51 @@ profile /usr/bin/docker (attach_disconnected, complain) {
|
||||
ipc rw,
|
||||
network,
|
||||
capability,
|
||||
owner /** rw,
|
||||
/var/lib/docker/** rwl,
|
||||
|
||||
# For non-root client use:
|
||||
/dev/urandom r,
|
||||
/run/docker.sock rw,
|
||||
/proc/** r,
|
||||
/sys/kernel/mm/hugepages/ r,
|
||||
/etc/localtime r,
|
||||
file,
|
||||
|
||||
ptrace peer=@{profile_name},
|
||||
ptrace (read) peer=docker-default,
|
||||
deny ptrace (trace) peer=docker-default,
|
||||
deny ptrace peer=/usr/bin/docker///bin/ps,
|
||||
|
||||
/usr/bin/docker pix,
|
||||
/sbin/xtables-multi rCx,
|
||||
/sbin/xtables-multi rCix,
|
||||
/sbin/iptables rCx,
|
||||
/sbin/modprobe rCx,
|
||||
/sbin/auplink rCx,
|
||||
/sbin/mke2fs rCx,
|
||||
/sbin/tune2fs rCx,
|
||||
/sbin/blkid rCx,
|
||||
/bin/kmod rCx,
|
||||
/usr/bin/xz rCx,
|
||||
/bin/ps rCx,
|
||||
/bin/cat rCx,
|
||||
/sbin/zfs rCx,
|
||||
|
||||
# Transitions
|
||||
change_profile -> docker-*,
|
||||
change_profile -> unconfined,
|
||||
|
||||
profile /bin/cat (complain) {
|
||||
/etc/ld.so.cache r,
|
||||
/lib/** r,
|
||||
/dev/null rw,
|
||||
/proc r,
|
||||
/bin/cat mr,
|
||||
|
||||
# For reading in 'docker stats':
|
||||
/proc/[0-9]*/net/dev r,
|
||||
profile /sbin/iptables {
|
||||
signal (receive) peer=/usr/bin/docker,
|
||||
capability net_admin,
|
||||
}
|
||||
profile /bin/ps (complain) {
|
||||
/etc/ld.so.cache r,
|
||||
/etc/localtime r,
|
||||
/etc/passwd r,
|
||||
/etc/nsswitch.conf r,
|
||||
/lib/** r,
|
||||
/proc/[0-9]*/** r,
|
||||
/dev/null rw,
|
||||
/bin/ps mr,
|
||||
profile /sbin/auplink flags=(attach_disconnected) {
|
||||
signal (receive) peer=/usr/bin/docker,
|
||||
capability sys_admin,
|
||||
capability dac_override,
|
||||
|
||||
# We don't need ptrace so we'll deny and ignore the error.
|
||||
deny ptrace (read, trace),
|
||||
@{DOCKER_GRAPH_PATH}/aufs/** rw,
|
||||
# For user namespaces:
|
||||
@{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/** rw,
|
||||
|
||||
# Quiet dac_override denials
|
||||
deny capability dac_override,
|
||||
deny capability dac_read_search,
|
||||
deny capability sys_ptrace,
|
||||
|
||||
/dev/tty r,
|
||||
/proc/stat r,
|
||||
/proc/cpuinfo r,
|
||||
/proc/meminfo r,
|
||||
/proc/uptime r,
|
||||
/sys/devices/system/cpu/online r,
|
||||
/proc/sys/kernel/pid_max r,
|
||||
/proc/ r,
|
||||
/proc/tty/drivers r,
|
||||
# The following may be removed via delegates
|
||||
/sys/fs/aufs/** r,
|
||||
/lib/** r,
|
||||
/apparmor/.null r,
|
||||
/dev/null rw,
|
||||
/etc/ld.so.cache r,
|
||||
/sbin/auplink rm,
|
||||
/proc/fs/aufs/** rw,
|
||||
/proc/[0-9]*/mounts rw,
|
||||
}
|
||||
profile /sbin/iptables (complain) {
|
||||
signal (receive) peer=/usr/bin/docker,
|
||||
capability net_admin,
|
||||
}
|
||||
profile /sbin/auplink flags=(attach_disconnected, complain) {
|
||||
signal (receive) peer=/usr/bin/docker,
|
||||
capability sys_admin,
|
||||
capability dac_override,
|
||||
|
||||
@{DOCKER_GRAPH_PATH}/aufs/** rw,
|
||||
@{DOCKER_GRAPH_PATH}/tmp/** rw,
|
||||
# For user namespaces:
|
||||
@{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/** rw,
|
||||
|
||||
/sys/fs/aufs/** r,
|
||||
/lib/** r,
|
||||
/apparmor/.null r,
|
||||
/dev/null rw,
|
||||
/etc/ld.so.cache r,
|
||||
/sbin/auplink rm,
|
||||
/proc/fs/aufs/** rw,
|
||||
/proc/[0-9]*/mounts rw,
|
||||
}
|
||||
profile /sbin/modprobe /bin/kmod (complain) {
|
||||
signal (receive) peer=/usr/bin/docker,
|
||||
capability sys_module,
|
||||
/etc/ld.so.cache r,
|
||||
/lib/** r,
|
||||
/dev/null rw,
|
||||
/apparmor/.null rw,
|
||||
/sbin/modprobe rm,
|
||||
/bin/kmod rm,
|
||||
/proc/cmdline r,
|
||||
/sys/module/** r,
|
||||
/etc/modprobe.d{/,/**} r,
|
||||
profile /sbin/modprobe {
|
||||
signal (receive) peer=/usr/bin/docker,
|
||||
capability sys_module,
|
||||
file,
|
||||
}
|
||||
# xz works via pipes, so we do not need access to the filesystem.
|
||||
profile /usr/bin/xz (complain) {
|
||||
signal (receive) peer=/usr/bin/docker,
|
||||
/etc/ld.so.cache r,
|
||||
/lib/** r,
|
||||
/usr/bin/xz rm,
|
||||
deny /proc/** rw,
|
||||
deny /sys/** rw,
|
||||
}
|
||||
profile /sbin/xtables-multi (attach_disconnected, complain) {
|
||||
/etc/ld.so.cache r,
|
||||
/lib/** r,
|
||||
/sbin/xtables-multi rm,
|
||||
/apparmor/.null w,
|
||||
/dev/null rw,
|
||||
capability net_raw,
|
||||
capability net_admin,
|
||||
network raw,
|
||||
}
|
||||
profile /sbin/zfs (attach_disconnected, complain) {
|
||||
file,
|
||||
capability,
|
||||
}
|
||||
profile /sbin/mke2fs (complain) {
|
||||
/sbin/mke2fs rm,
|
||||
|
||||
/lib/** r,
|
||||
|
||||
/apparmor/.null w,
|
||||
|
||||
/etc/ld.so.cache r,
|
||||
/etc/mke2fs.conf r,
|
||||
/etc/mtab r,
|
||||
|
||||
/dev/dm-* rw,
|
||||
/dev/urandom r,
|
||||
/dev/null rw,
|
||||
|
||||
/proc/swaps r,
|
||||
/proc/[0-9]*/mounts r,
|
||||
}
|
||||
profile /sbin/tune2fs (complain) {
|
||||
/sbin/tune2fs rm,
|
||||
|
||||
/lib/** r,
|
||||
|
||||
/apparmor/.null w,
|
||||
|
||||
/etc/blkid.conf r,
|
||||
/etc/mtab r,
|
||||
/etc/ld.so.cache r,
|
||||
|
||||
/dev/null rw,
|
||||
/dev/.blkid.tab r,
|
||||
/dev/dm-* rw,
|
||||
|
||||
/proc/swaps r,
|
||||
/proc/[0-9]*/mounts r,
|
||||
}
|
||||
profile /sbin/blkid (complain) {
|
||||
/sbin/blkid rm,
|
||||
|
||||
/lib/** r,
|
||||
/apparmor/.null w,
|
||||
|
||||
/etc/ld.so.cache r,
|
||||
/etc/blkid.conf r,
|
||||
|
||||
/dev/null rw,
|
||||
/dev/.blkid.tab rl,
|
||||
/dev/.blkid.tab* rwl,
|
||||
/dev/dm-* r,
|
||||
|
||||
/sys/devices/virtual/block/** r,
|
||||
|
||||
capability mknod,
|
||||
|
||||
mount -> @{DOCKER_GRAPH_PATH}/**,
|
||||
profile /usr/bin/xz {
|
||||
signal (receive) peer=/usr/bin/docker,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
FROM debian:jessie
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
FROM debian:stretch
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||
#
|
||||
|
||||
FROM debian:wheezy-backports
|
||||
FROM debian:wheezy
|
||||
RUN echo deb http://http.debian.net/debian wheezy-backports main > /etc/apt/sources.list.d/wheezy-backports.list
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools/wheezy-backports build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
|
||||
@@ -22,13 +22,6 @@ for version in "${versions[@]}"; do
|
||||
suite="${version##*-}"
|
||||
from="${distro}:${suite}"
|
||||
|
||||
case "$from" in
|
||||
debian:wheezy)
|
||||
# add -backports, like our users have to
|
||||
from+='-backports'
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$version"
|
||||
echo "$version -> FROM $from"
|
||||
cat > "$version/Dockerfile" <<-EOF
|
||||
@@ -39,6 +32,13 @@ for version in "${versions[@]}"; do
|
||||
FROM $from
|
||||
EOF
|
||||
|
||||
case "$from" in
|
||||
debian:wheezy)
|
||||
# add -backports, like our users have to
|
||||
echo "RUN echo deb http://http.debian.net/debian $suite-backports main > /etc/apt/sources.list.d/$suite-backports.list" >> "$version/Dockerfile"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo >> "$version/Dockerfile"
|
||||
|
||||
extraBuildTags=
|
||||
@@ -50,6 +50,7 @@ for version in "${versions[@]}"; do
|
||||
build-essential # "essential for building Debian packages"
|
||||
curl ca-certificates # for downloading Go
|
||||
debhelper # for easy ".deb" building
|
||||
dh-apparmor # for apparmor debhelper
|
||||
dh-systemd # for systemd debhelper integration
|
||||
git # for "git commit" info in "docker -v"
|
||||
libapparmor-dev # for "sys/apparmor.h"
|
||||
@@ -73,12 +74,6 @@ for version in "${versions[@]}"; do
|
||||
extraBuildTags+=' exclude_graphdriver_btrfs'
|
||||
fi
|
||||
|
||||
if [ "$suite" = 'wheezy' ]; then
|
||||
# pull btrfs-toold from backports
|
||||
backports="/$suite-backports"
|
||||
packages=( "${packages[@]/btrfs-tools/btrfs-tools$backports}" )
|
||||
fi
|
||||
|
||||
echo "RUN apt-get update && apt-get install -y ${packages[*]} --no-install-recommends && rm -rf /var/lib/apt/lists/*" >> "$version/Dockerfile"
|
||||
|
||||
echo >> "$version/Dockerfile"
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||
#
|
||||
|
||||
FROM ubuntu:precise
|
||||
FROM ubuntu-debootstrap:precise
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion build-essential curl ca-certificates debhelper git libapparmor-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y bash-completion build-essential curl ca-certificates debhelper dh-apparmor git libapparmor-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||
#
|
||||
|
||||
FROM ubuntu:trusty
|
||||
FROM ubuntu-debootstrap:trusty
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||
#
|
||||
|
||||
FROM ubuntu:vivid
|
||||
FROM ubuntu-debootstrap:vivid
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||
#
|
||||
|
||||
FROM ubuntu:wily
|
||||
FROM ubuntu-debootstrap:wily
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
@@ -6,7 +6,7 @@ FROM centos:7
|
||||
|
||||
RUN yum groupinstall -y "Development Tools"
|
||||
RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"!
|
||||
#
|
||||
|
||||
FROM fedora:20
|
||||
|
||||
RUN yum install -y @development-tools fedora-packager
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
ENV AUTO_GOPATH 1
|
||||
ENV DOCKER_BUILDTAGS selinux
|
||||
@@ -5,7 +5,7 @@
|
||||
FROM fedora:21
|
||||
|
||||
RUN yum install -y @development-tools fedora-packager
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
FROM fedora:22
|
||||
|
||||
RUN yum install -y @development-tools fedora-packager
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
|
||||
@@ -58,8 +58,6 @@ for version in "${versions[@]}"; do
|
||||
device-mapper-devel # for "libdevmapper.h"
|
||||
glibc-static
|
||||
libselinux-devel # for "libselinux.so"
|
||||
selinux-policy
|
||||
selinux-policy-devel
|
||||
sqlite-devel # for "sqlite3.h"
|
||||
tar # older versions of dev-tools do not have tar
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
FROM oraclelinux:6
|
||||
|
||||
RUN yum groupinstall -y "Development Tools"
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
FROM oraclelinux:7
|
||||
|
||||
RUN yum groupinstall -y "Development Tools"
|
||||
RUN yum install -y --enablerepo=ol7_optional_latest btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
RUN yum install -y --enablerepo=ol7_optional_latest btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
|
||||
@@ -191,17 +191,11 @@ if [ "$kernelMajor" -lt 3 ] || [ "$kernelMajor" -eq 3 -a "$kernelMinor" -le 18 ]
|
||||
check_flags RESOURCE_COUNTERS
|
||||
fi
|
||||
|
||||
if [ "$kernelMajor" -lt 3 ] || [ "$kernelMajor" -eq 3 -a "$kernelMinor" -le 13 ]; then
|
||||
netprio=NETPRIO_CGROUP
|
||||
else
|
||||
netprio=CGROUP_NET_PRIO
|
||||
fi
|
||||
|
||||
flags=(
|
||||
BLK_CGROUP IOSCHED_CFQ BLK_DEV_THROTTLING
|
||||
BLK_CGROUP IOSCHED_CFQ
|
||||
CGROUP_PERF
|
||||
CGROUP_HUGETLB
|
||||
NET_CLS_CGROUP $netprio
|
||||
NET_CLS_CGROUP NETPRIO_CGROUP
|
||||
CFS_BANDWIDTH FAIR_GROUP_SCHED RT_GROUP_SCHED
|
||||
)
|
||||
check_flags "${flags[@]}"
|
||||
|
||||
Regular → Executable
+92
-255
@@ -14,22 +14,6 @@
|
||||
# - copy this file to e.g. ~/.docker-completion.sh and add the line
|
||||
# below to your .bashrc after bash completion features are loaded
|
||||
# . ~/.docker-completion.sh
|
||||
#
|
||||
# Configuration:
|
||||
#
|
||||
# You can tailor completion for the "events", "history", "inspect", "run",
|
||||
# "rmi" and "save" commands by settings the following environment
|
||||
# variables:
|
||||
#
|
||||
# DOCKER_COMPLETION_SHOW_IMAGE_IDS
|
||||
# "none" - Show names only (default)
|
||||
# "non-intermediate" - Show names and ids, but omit intermediate image IDs
|
||||
# "all" - Show names and ids, including intermediate image IDs
|
||||
#
|
||||
# DOCKER_COMPLETION_SHOW_TAGS
|
||||
# "yes" - include tags in completion options (default)
|
||||
# "no" - don't include tags in completion options
|
||||
|
||||
#
|
||||
# Note:
|
||||
# Currently, the completions will not work if the docker daemon is not
|
||||
@@ -43,7 +27,7 @@
|
||||
# This order should be applied to lists, alternatives and code blocks.
|
||||
|
||||
__docker_q() {
|
||||
docker ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@"
|
||||
docker ${host:+-H "$host"} 2>/dev/null "$@"
|
||||
}
|
||||
|
||||
__docker_containers_all() {
|
||||
@@ -86,40 +70,6 @@ __docker_container_ids() {
|
||||
COMPREPLY=( $(compgen -W "${containers[*]}" -- "$cur") )
|
||||
}
|
||||
|
||||
__docker_images() {
|
||||
local images_args=""
|
||||
|
||||
case "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" in
|
||||
all)
|
||||
images_args="--no-trunc -a"
|
||||
;;
|
||||
non-intermediate)
|
||||
images_args="--no-trunc"
|
||||
;;
|
||||
esac
|
||||
|
||||
local repo_print_command
|
||||
if [ "${DOCKER_COMPLETION_SHOW_TAGS:-yes}" = "yes" ]; then
|
||||
repo_print_command='print $1; print $1":"$2'
|
||||
else
|
||||
repo_print_command='print $1'
|
||||
fi
|
||||
|
||||
local awk_script
|
||||
case "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" in
|
||||
all|non-intermediate)
|
||||
awk_script='NR>1 { print $3; if ($1 != "<none>") { '"$repo_print_command"' } }'
|
||||
;;
|
||||
none|*)
|
||||
awk_script='NR>1 && $1 != "<none>" { '"$repo_print_command"' }'
|
||||
;;
|
||||
esac
|
||||
|
||||
local images=$(__docker_q images $images_args | awk "$awk_script")
|
||||
COMPREPLY=( $(compgen -W "$images" -- "$cur") )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_image_repos() {
|
||||
local repos="$(__docker_q images | awk 'NR>1 && $1 != "<none>" { print $1 }')"
|
||||
COMPREPLY=( $(compgen -W "$repos" -- "$cur") )
|
||||
@@ -131,10 +81,16 @@ __docker_image_repos_and_tags() {
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_image_repos_and_tags_and_ids() {
|
||||
local images="$(__docker_q images -a --no-trunc | awk 'NR>1 { print $3; if ($1 != "<none>") { print $1; print $1":"$2 } }')"
|
||||
COMPREPLY=( $(compgen -W "$images" -- "$cur") )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_containers_and_images() {
|
||||
__docker_containers_all
|
||||
local containers=( "${COMPREPLY[@]}" )
|
||||
__docker_images
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
COMPREPLY+=( "${containers[@]}" )
|
||||
}
|
||||
|
||||
@@ -183,7 +139,7 @@ __docker_value_of_option() {
|
||||
local counter=$((command_pos + 1))
|
||||
while [ $counter -lt $cword ]; do
|
||||
case ${words[$counter]} in
|
||||
@($option_glob) )
|
||||
$option_glob )
|
||||
echo ${words[$counter + 1]}
|
||||
break
|
||||
;;
|
||||
@@ -273,12 +229,11 @@ __docker_log_driver_options() {
|
||||
# see docs/reference/logging/index.md
|
||||
local fluentd_options="fluentd-address fluentd-tag"
|
||||
local gelf_options="gelf-address gelf-tag"
|
||||
local json_file_options="max-file max-size"
|
||||
local syslog_options="syslog-address syslog-facility syslog-tag"
|
||||
|
||||
case $(__docker_value_of_option --log-driver) in
|
||||
'')
|
||||
COMPREPLY=( $( compgen -W "$fluentd_options $gelf_options $json_file_options $syslog_options" -S = -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "$fluentd_options $gelf_options $syslog_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
fluentd)
|
||||
COMPREPLY=( $( compgen -W "$fluentd_options" -S = -- "$cur" ) )
|
||||
@@ -286,9 +241,6 @@ __docker_log_driver_options() {
|
||||
gelf)
|
||||
COMPREPLY=( $( compgen -W "$gelf_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
json-file)
|
||||
COMPREPLY=( $( compgen -W "$json_file_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
syslog)
|
||||
COMPREPLY=( $( compgen -W "$syslog_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
@@ -343,10 +295,6 @@ __docker_complete_log_driver_options() {
|
||||
return 1
|
||||
}
|
||||
|
||||
__docker_log_levels() {
|
||||
COMPREPLY=( $( compgen -W "debug info warn error fatal" -- "$cur" ) )
|
||||
}
|
||||
|
||||
# a selection of the available signals that is most likely of interest in the
|
||||
# context of docker containers.
|
||||
__docker_signals() {
|
||||
@@ -364,34 +312,61 @@ __docker_signals() {
|
||||
COMPREPLY=( $( compgen -W "${signals[*]} ${signals[*]#SIG}" -- "$( echo $cur | tr '[:lower:]' '[:upper:]')" ) )
|
||||
}
|
||||
|
||||
# global options that may appear after the docker command
|
||||
_docker_docker() {
|
||||
local boolean_options="
|
||||
$global_boolean_options
|
||||
--help
|
||||
--daemon -d
|
||||
--debug -D
|
||||
--help -h
|
||||
--icc
|
||||
--ip-forward
|
||||
--ip-masq
|
||||
--iptables
|
||||
--ipv6
|
||||
--selinux-enabled
|
||||
--tls
|
||||
--tlsverify
|
||||
--userland-proxy=false
|
||||
--version -v
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--config)
|
||||
--exec-root|--graph|-g)
|
||||
_filedir -d
|
||||
return
|
||||
;;
|
||||
--log-level|-l)
|
||||
__docker_log_levels
|
||||
--log-driver)
|
||||
__docker_log_drivers
|
||||
return
|
||||
;;
|
||||
$(__docker_to_extglob "$global_options_with_args") )
|
||||
--log-level|-l)
|
||||
COMPREPLY=( $( compgen -W "debug info warn error fatal" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
--log-opt)
|
||||
__docker_log_driver_options
|
||||
return
|
||||
;;
|
||||
--pidfile|-p|--tlscacert|--tlscert|--tlskey)
|
||||
_filedir
|
||||
return
|
||||
;;
|
||||
--storage-driver|-s)
|
||||
COMPREPLY=( $( compgen -W "aufs devicemapper btrfs overlay" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
||||
return
|
||||
;;
|
||||
$main_options_with_args_glob )
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
__docker_complete_log_driver_options && return
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "$boolean_options $global_options_with_args" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "$boolean_options $main_options_with_args" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag $(__docker_to_extglob "$global_options_with_args") )
|
||||
local counter="$(__docker_pos_first_nonflag $main_options_with_args_glob)"
|
||||
if [ $cword -eq $counter ]; then
|
||||
COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) )
|
||||
fi
|
||||
@@ -503,138 +478,6 @@ _docker_create() {
|
||||
_docker_run
|
||||
}
|
||||
|
||||
_docker_daemon() {
|
||||
local boolean_options="
|
||||
$global_boolean_options
|
||||
--help
|
||||
--icc=false
|
||||
--ip-forward=false
|
||||
--ip-masq=false
|
||||
--iptables=false
|
||||
--ipv6
|
||||
--selinux-enabled
|
||||
--userland-proxy=false
|
||||
"
|
||||
local options_with_args="
|
||||
$global_options_with_args
|
||||
--api-cors-header
|
||||
--bip
|
||||
--bridge -b
|
||||
--default-gateway
|
||||
--default-gateway-v6
|
||||
--default-ulimit
|
||||
--dns
|
||||
--dns-search
|
||||
--exec-driver -e
|
||||
--exec-opt
|
||||
--exec-root
|
||||
--fixed-cidr
|
||||
--fixed-cidr-v6
|
||||
--graph -g
|
||||
--group -G
|
||||
--insecure-registry
|
||||
--ip
|
||||
--label
|
||||
--log-driver
|
||||
--log-opt
|
||||
--mtu
|
||||
--pidfile -p
|
||||
--registry-mirror
|
||||
--storage-driver -s
|
||||
--storage-opt
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--exec-root|--graph|-g)
|
||||
_filedir -d
|
||||
return
|
||||
;;
|
||||
--log-driver)
|
||||
__docker_log_drivers
|
||||
return
|
||||
;;
|
||||
--pidfile|-p|--tlscacert|--tlscert|--tlskey)
|
||||
_filedir
|
||||
return
|
||||
;;
|
||||
--storage-driver|-s)
|
||||
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
||||
return
|
||||
;;
|
||||
--storage-opt)
|
||||
local devicemapper_options="
|
||||
dm.basesize
|
||||
dm.blkdiscard
|
||||
dm.blocksize
|
||||
dm.fs
|
||||
dm.loopdatasize
|
||||
dm.loopmetadatasize
|
||||
dm.mkfsarg
|
||||
dm.mountopt
|
||||
dm.override_udev_sync_check
|
||||
dm.thinpooldev
|
||||
"
|
||||
local zfs_options="zfs.fsname"
|
||||
|
||||
case $(__docker_value_of_option '--storage-driver|-s') in
|
||||
'')
|
||||
COMPREPLY=( $( compgen -W "$devicemapper_options $zfs_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
devicemapper)
|
||||
COMPREPLY=( $( compgen -W "$devicemapper_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
zfs)
|
||||
COMPREPLY=( $( compgen -W "$zfs_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
compopt -o nospace
|
||||
return
|
||||
;;
|
||||
--log-level|-l)
|
||||
__docker_log_levels
|
||||
return
|
||||
;;
|
||||
--log-opt)
|
||||
__docker_log_driver_options
|
||||
return
|
||||
;;
|
||||
$(__docker_to_extglob "$options_with_args") )
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
__docker_complete_log_driver_options && return
|
||||
|
||||
# completions for --storage-opt
|
||||
case "${words[$cword-2]}$prev=" in
|
||||
*dm.blkdiscard=*)
|
||||
COMPREPLY=( $( compgen -W "false true" -- "${cur#=}" ) )
|
||||
return
|
||||
;;
|
||||
*dm.fs=*)
|
||||
COMPREPLY=( $( compgen -W "ext4 xfs" -- "${cur#=}" ) )
|
||||
return
|
||||
;;
|
||||
*dm.override_udev_sync_check=*)
|
||||
COMPREPLY=( $( compgen -W "false true" -- "${cur#=}" ) )
|
||||
return
|
||||
;;
|
||||
*dm.thinpooldev=*)
|
||||
_filedir
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_diff() {
|
||||
case "$cur" in
|
||||
-*)
|
||||
@@ -699,7 +542,7 @@ _docker_events() {
|
||||
;;
|
||||
*image=*)
|
||||
cur="${cur#=}"
|
||||
__docker_images
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
return
|
||||
;;
|
||||
esac
|
||||
@@ -720,7 +563,7 @@ _docker_exec() {
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--detach -d --help --interactive -i --privileged -t --tty -u --user" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--detach -d --help --interactive -i -t --tty -u --user" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_containers_running
|
||||
@@ -757,7 +600,7 @@ _docker_history() {
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_images
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
@@ -842,17 +685,8 @@ _docker_inspect() {
|
||||
COMPREPLY=( $( compgen -W "--format -f --type --help" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
case $(__docker_value_of_option --type) in
|
||||
'')
|
||||
__docker_containers_and_images
|
||||
;;
|
||||
container)
|
||||
__docker_containers_all
|
||||
;;
|
||||
image)
|
||||
__docker_images
|
||||
;;
|
||||
esac
|
||||
__docker_containers_and_images
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -965,21 +799,16 @@ _docker_ps() {
|
||||
__docker_containers_all
|
||||
;;
|
||||
--filter|-f)
|
||||
COMPREPLY=( $( compgen -S = -W "ancestor exited id label name status" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -S = -W "exited id label name status" -- "$cur" ) )
|
||||
compopt -o nospace
|
||||
return
|
||||
;;
|
||||
--format|-n)
|
||||
-n)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "${words[$cword-2]}$prev=" in
|
||||
*ancestor=*)
|
||||
cur="${cur#=}"
|
||||
__docker_images
|
||||
return
|
||||
;;
|
||||
*id=*)
|
||||
cur="${cur#=}"
|
||||
__docker_container_ids
|
||||
@@ -998,7 +827,7 @@ _docker_ps() {
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--all -a --before --filter -f --format --help --latest -l -n --no-trunc --quiet -q --size -s --since" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--all -a --before --filter -f --help --latest -l -n --no-trunc --quiet -q --size -s --since" -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -1095,7 +924,7 @@ _docker_rmi() {
|
||||
COMPREPLY=( $( compgen -W "--force -f --help --no-prune" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_images
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -1103,16 +932,15 @@ _docker_rmi() {
|
||||
_docker_run() {
|
||||
local options_with_args="
|
||||
--add-host
|
||||
--attach -a
|
||||
--blkio-weight
|
||||
--attach -a
|
||||
--cap-add
|
||||
--cap-drop
|
||||
--cgroup-parent
|
||||
--cidfile
|
||||
--cpuset
|
||||
--cpu-period
|
||||
--cpu-quota
|
||||
--cpuset-cpus
|
||||
--cpuset-mems
|
||||
--cpu-shares -c
|
||||
--device
|
||||
--dns
|
||||
@@ -1124,9 +952,8 @@ _docker_run() {
|
||||
--group-add
|
||||
--hostname -h
|
||||
--ipc
|
||||
--kernel-memory
|
||||
--label-file
|
||||
--label -l
|
||||
--label-file
|
||||
--link
|
||||
--log-driver
|
||||
--log-opt
|
||||
@@ -1134,15 +961,14 @@ _docker_run() {
|
||||
--mac-address
|
||||
--memory -m
|
||||
--memory-swap
|
||||
--memory-swappiness
|
||||
--name
|
||||
--net
|
||||
--pid
|
||||
--publish -p
|
||||
--restart
|
||||
--security-opt
|
||||
--ulimit
|
||||
--user -u
|
||||
--ulimit
|
||||
--uts
|
||||
--volumes-from
|
||||
--volume -v
|
||||
@@ -1150,10 +976,8 @@ _docker_run() {
|
||||
"
|
||||
|
||||
local all_options="$options_with_args
|
||||
--disable-content-trust=false
|
||||
--help
|
||||
--interactive -i
|
||||
--oom-kill-disable
|
||||
--privileged
|
||||
--publish-all -P
|
||||
--read-only
|
||||
@@ -1163,7 +987,7 @@ _docker_run() {
|
||||
[ "$command" = "run" ] && all_options="$all_options
|
||||
--detach -d
|
||||
--rm
|
||||
--sig-proxy=false
|
||||
--sig-proxy
|
||||
"
|
||||
|
||||
local options_with_args_glob=$(__docker_to_extglob "$options_with_args")
|
||||
@@ -1305,8 +1129,9 @@ _docker_run() {
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_images
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
@@ -1325,7 +1150,7 @@ _docker_save() {
|
||||
COMPREPLY=( $( compgen -W "--help --output -o" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_images
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -1462,7 +1287,6 @@ _docker() {
|
||||
commit
|
||||
cp
|
||||
create
|
||||
daemon
|
||||
diff
|
||||
events
|
||||
exec
|
||||
@@ -1499,23 +1323,41 @@ _docker() {
|
||||
wait
|
||||
)
|
||||
|
||||
# These options are valid as global options for all client commands
|
||||
# and valid as command options for `docker daemon`
|
||||
local global_boolean_options="
|
||||
--debug -D
|
||||
--tls
|
||||
--tlsverify
|
||||
"
|
||||
local global_options_with_args="
|
||||
--config
|
||||
local main_options_with_args="
|
||||
--api-cors-header
|
||||
--bip
|
||||
--bridge -b
|
||||
--default-gateway
|
||||
--default-gateway-v6
|
||||
--default-ulimit
|
||||
--dns
|
||||
--dns-search
|
||||
--exec-driver -e
|
||||
--exec-opt
|
||||
--exec-root
|
||||
--fixed-cidr
|
||||
--fixed-cidr-v6
|
||||
--graph -g
|
||||
--group -G
|
||||
--host -H
|
||||
--insecure-registry
|
||||
--ip
|
||||
--label
|
||||
--log-driver
|
||||
--log-level -l
|
||||
--log-opt
|
||||
--mtu
|
||||
--pidfile -p
|
||||
--registry-mirror
|
||||
--storage-driver -s
|
||||
--storage-opt
|
||||
--tlscacert
|
||||
--tlscert
|
||||
--tlskey
|
||||
"
|
||||
|
||||
local host config
|
||||
local main_options_with_args_glob=$(__docker_to_extglob "$main_options_with_args")
|
||||
local host
|
||||
|
||||
COMPREPLY=()
|
||||
local cur prev words cword
|
||||
@@ -1530,12 +1372,7 @@ _docker() {
|
||||
(( counter++ ))
|
||||
host="${words[$counter]}"
|
||||
;;
|
||||
# save config so that completion can use custom configuration directories
|
||||
--config)
|
||||
(( counter++ ))
|
||||
config="${words[$counter]}"
|
||||
;;
|
||||
$(__docker_to_extglob "$global_options_with_args") )
|
||||
$main_options_with_args_glob )
|
||||
(( counter++ ))
|
||||
;;
|
||||
-*)
|
||||
|
||||
@@ -134,9 +134,9 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s i -l interac
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l ipc -d 'Default is to create a private IPC namespace (POSIX SysV IPC) for the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l link -d 'Add link to another container in the form of <name|id>:alias'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s m -l memory -d 'Memory limit (format: <number>[<unit>], where unit = b, k, m or g)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l mac-address -d 'Container MAC address (e.g. 92:d0:c6:0a:29:33)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number>[<unit>], where unit = b, k, m or g)"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l name -d 'Assign a name to the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l net -d 'Set the Network mode for the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
||||
@@ -321,9 +321,9 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s i -l interactiv
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l ipc -d 'Default is to create a private IPC namespace (POSIX SysV IPC) for the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add link to another container in the form of <name|id>:alias'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number>[<unit>], where unit = b, k, m or g)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l mac-address -d 'Container MAC address (e.g. 92:d0:c6:0a:29:33)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number>[<unit>], where unit = b, k, m or g)"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l net -d 'Set the Network mode for the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
||||
|
||||
@@ -1,502 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
@@ -1,16 +0,0 @@
|
||||
TARGETS?=docker
|
||||
MODULES?=${TARGETS:=.pp.bz2}
|
||||
SHAREDIR?=/usr/share
|
||||
|
||||
all: ${TARGETS:=.pp.bz2}
|
||||
|
||||
%.pp.bz2: %.pp
|
||||
@echo Compressing $^ -\> $@
|
||||
bzip2 -9 $^
|
||||
|
||||
%.pp: %.te
|
||||
make -f ${SHAREDIR}/selinux/devel/Makefile $@
|
||||
|
||||
clean:
|
||||
rm -f *~ *.tc *.pp *.pp.bz2
|
||||
rm -rf tmp *.tar.gz
|
||||
@@ -1,24 +0,0 @@
|
||||
/root/\.docker gen_context(system_u:object_r:docker_home_t,s0)
|
||||
|
||||
/usr/bin/docker -- gen_context(system_u:object_r:docker_exec_t,s0)
|
||||
|
||||
/usr/lib/systemd/system/docker.service -- gen_context(system_u:object_r:docker_unit_file_t,s0)
|
||||
|
||||
/etc/docker(/.*)? gen_context(system_u:object_r:docker_config_t,s0)
|
||||
|
||||
/var/lib/docker(/.*)? gen_context(system_u:object_r:docker_var_lib_t,s0)
|
||||
/var/lib/kublet(/.*)? gen_context(system_u:object_r:docker_var_lib_t,s0)
|
||||
/var/lib/docker/vfs(/.*)? gen_context(system_u:object_r:svirt_sandbox_file_t,s0)
|
||||
|
||||
/var/run/docker\.pid -- gen_context(system_u:object_r:docker_var_run_t,s0)
|
||||
/var/run/docker\.sock -s gen_context(system_u:object_r:docker_var_run_t,s0)
|
||||
/var/run/docker-client(/.*)? gen_context(system_u:object_r:docker_var_run_t,s0)
|
||||
|
||||
/var/lock/lxc(/.*)? gen_context(system_u:object_r:docker_lock_t,s0)
|
||||
|
||||
/var/log/lxc(/.*)? gen_context(system_u:object_r:docker_log_t,s0)
|
||||
|
||||
/var/lib/docker/init(/.*)? gen_context(system_u:object_r:docker_share_t,s0)
|
||||
/var/lib/docker/containers/.*/hosts gen_context(system_u:object_r:docker_share_t,s0)
|
||||
/var/lib/docker/containers/.*/hostname gen_context(system_u:object_r:docker_share_t,s0)
|
||||
/var/lib/docker/.*/config\.env gen_context(system_u:object_r:docker_share_t,s0)
|
||||
@@ -1,467 +0,0 @@
|
||||
|
||||
## <summary>The open-source application container engine.</summary>
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Execute docker in the docker domain.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed to transition.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_domtrans',`
|
||||
gen_require(`
|
||||
type docker_t, docker_exec_t;
|
||||
')
|
||||
|
||||
corecmd_search_bin($1)
|
||||
domtrans_pattern($1, docker_exec_t, docker_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Execute docker in the caller domain.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed to transition.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_exec',`
|
||||
gen_require(`
|
||||
type docker_exec_t;
|
||||
')
|
||||
|
||||
corecmd_search_bin($1)
|
||||
can_exec($1, docker_exec_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Search docker lib directories.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_search_lib',`
|
||||
gen_require(`
|
||||
type docker_var_lib_t;
|
||||
')
|
||||
|
||||
allow $1 docker_var_lib_t:dir search_dir_perms;
|
||||
files_search_var_lib($1)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Execute docker lib directories.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_exec_lib',`
|
||||
gen_require(`
|
||||
type docker_var_lib_t;
|
||||
')
|
||||
|
||||
allow $1 docker_var_lib_t:dir search_dir_perms;
|
||||
can_exec($1, docker_var_lib_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Read docker lib files.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_read_lib_files',`
|
||||
gen_require(`
|
||||
type docker_var_lib_t;
|
||||
')
|
||||
|
||||
files_search_var_lib($1)
|
||||
read_files_pattern($1, docker_var_lib_t, docker_var_lib_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Read docker share files.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_read_share_files',`
|
||||
gen_require(`
|
||||
type docker_share_t;
|
||||
')
|
||||
|
||||
files_search_var_lib($1)
|
||||
read_files_pattern($1, docker_share_t, docker_share_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Manage docker lib files.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_manage_lib_files',`
|
||||
gen_require(`
|
||||
type docker_var_lib_t;
|
||||
')
|
||||
|
||||
files_search_var_lib($1)
|
||||
manage_files_pattern($1, docker_var_lib_t, docker_var_lib_t)
|
||||
manage_lnk_files_pattern($1, docker_var_lib_t, docker_var_lib_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Manage docker lib directories.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_manage_lib_dirs',`
|
||||
gen_require(`
|
||||
type docker_var_lib_t;
|
||||
')
|
||||
|
||||
files_search_var_lib($1)
|
||||
manage_dirs_pattern($1, docker_var_lib_t, docker_var_lib_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Create objects in a docker var lib directory
|
||||
## with an automatic type transition to
|
||||
## a specified private type.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
## <param name="private_type">
|
||||
## <summary>
|
||||
## The type of the object to create.
|
||||
## </summary>
|
||||
## </param>
|
||||
## <param name="object_class">
|
||||
## <summary>
|
||||
## The class of the object to be created.
|
||||
## </summary>
|
||||
## </param>
|
||||
## <param name="name" optional="true">
|
||||
## <summary>
|
||||
## The name of the object being created.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_lib_filetrans',`
|
||||
gen_require(`
|
||||
type docker_var_lib_t;
|
||||
')
|
||||
|
||||
filetrans_pattern($1, docker_var_lib_t, $2, $3, $4)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Read docker PID files.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_read_pid_files',`
|
||||
gen_require(`
|
||||
type docker_var_run_t;
|
||||
')
|
||||
|
||||
files_search_pids($1)
|
||||
read_files_pattern($1, docker_var_run_t, docker_var_run_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Execute docker server in the docker domain.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed to transition.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_systemctl',`
|
||||
gen_require(`
|
||||
type docker_t;
|
||||
type docker_unit_file_t;
|
||||
')
|
||||
|
||||
systemd_exec_systemctl($1)
|
||||
init_reload_services($1)
|
||||
systemd_read_fifo_file_passwd_run($1)
|
||||
allow $1 docker_unit_file_t:file read_file_perms;
|
||||
allow $1 docker_unit_file_t:service manage_service_perms;
|
||||
|
||||
ps_process_pattern($1, docker_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Read and write docker shared memory.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_rw_sem',`
|
||||
gen_require(`
|
||||
type docker_t;
|
||||
')
|
||||
|
||||
allow $1 docker_t:sem rw_sem_perms;
|
||||
')
|
||||
|
||||
#######################################
|
||||
## <summary>
|
||||
## Read and write the docker pty type.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_use_ptys',`
|
||||
gen_require(`
|
||||
type docker_devpts_t;
|
||||
')
|
||||
|
||||
allow $1 docker_devpts_t:chr_file rw_term_perms;
|
||||
')
|
||||
|
||||
#######################################
|
||||
## <summary>
|
||||
## Allow domain to create docker content
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_filetrans_named_content',`
|
||||
|
||||
gen_require(`
|
||||
type docker_var_lib_t;
|
||||
type docker_share_t;
|
||||
type docker_log_t;
|
||||
type docker_var_run_t;
|
||||
type docker_home_t;
|
||||
')
|
||||
|
||||
files_pid_filetrans($1, docker_var_run_t, file, "docker.pid")
|
||||
files_pid_filetrans($1, docker_var_run_t, sock_file, "docker.sock")
|
||||
files_pid_filetrans($1, docker_var_run_t, dir, "docker-client")
|
||||
logging_log_filetrans($1, docker_log_t, dir, "lxc")
|
||||
files_var_lib_filetrans($1, docker_var_lib_t, dir, "docker")
|
||||
filetrans_pattern($1, docker_var_lib_t, docker_share_t, file, "config.env")
|
||||
filetrans_pattern($1, docker_var_lib_t, docker_share_t, file, "hosts")
|
||||
filetrans_pattern($1, docker_var_lib_t, docker_share_t, file, "hostname")
|
||||
filetrans_pattern($1, docker_var_lib_t, docker_share_t, file, "resolv.conf")
|
||||
filetrans_pattern($1, docker_var_lib_t, docker_share_t, dir, "init")
|
||||
userdom_admin_home_dir_filetrans($1, docker_home_t, dir, ".docker")
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Connect to docker over a unix stream socket.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_stream_connect',`
|
||||
gen_require(`
|
||||
type docker_t, docker_var_run_t;
|
||||
')
|
||||
|
||||
files_search_pids($1)
|
||||
stream_connect_pattern($1, docker_var_run_t, docker_var_run_t, docker_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Connect to SPC containers over a unix stream socket.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_spc_stream_connect',`
|
||||
gen_require(`
|
||||
type spc_t, spc_var_run_t;
|
||||
')
|
||||
|
||||
files_search_pids($1)
|
||||
files_write_all_pid_sockets($1)
|
||||
allow $1 spc_t:unix_stream_socket connectto;
|
||||
')
|
||||
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## All of the rules required to administrate
|
||||
## an docker environment
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`docker_admin',`
|
||||
gen_require(`
|
||||
type docker_t;
|
||||
type docker_var_lib_t, docker_var_run_t;
|
||||
type docker_unit_file_t;
|
||||
type docker_lock_t;
|
||||
type docker_log_t;
|
||||
type docker_config_t;
|
||||
')
|
||||
|
||||
allow $1 docker_t:process { ptrace signal_perms };
|
||||
ps_process_pattern($1, docker_t)
|
||||
|
||||
admin_pattern($1, docker_config_t)
|
||||
|
||||
files_search_var_lib($1)
|
||||
admin_pattern($1, docker_var_lib_t)
|
||||
|
||||
files_search_pids($1)
|
||||
admin_pattern($1, docker_var_run_t)
|
||||
|
||||
files_search_locks($1)
|
||||
admin_pattern($1, docker_lock_t)
|
||||
|
||||
logging_search_logs($1)
|
||||
admin_pattern($1, docker_log_t)
|
||||
|
||||
docker_systemctl($1)
|
||||
admin_pattern($1, docker_unit_file_t)
|
||||
allow $1 docker_unit_file_t:service all_service_perms;
|
||||
|
||||
optional_policy(`
|
||||
systemd_passwd_agent_exec($1)
|
||||
systemd_read_fifo_file_passwd_run($1)
|
||||
')
|
||||
')
|
||||
|
||||
interface(`domain_stub_named_filetrans_domain',`
|
||||
gen_require(`
|
||||
attribute named_filetrans_domain;
|
||||
')
|
||||
')
|
||||
|
||||
interface(`lvm_stub',`
|
||||
gen_require(`
|
||||
type lvm_t;
|
||||
')
|
||||
')
|
||||
interface(`staff_stub',`
|
||||
gen_require(`
|
||||
type staff_t;
|
||||
')
|
||||
')
|
||||
interface(`virt_stub_lxc',`
|
||||
gen_require(`
|
||||
type virtd_lxc_t;
|
||||
')
|
||||
')
|
||||
interface(`virt_stub_svirt_sandbox_domain',`
|
||||
gen_require(`
|
||||
attribute svirt_sandbox_domain;
|
||||
')
|
||||
')
|
||||
interface(`virt_stub_svirt_sandbox_file',`
|
||||
gen_require(`
|
||||
type svirt_sandbox_file_t;
|
||||
')
|
||||
')
|
||||
interface(`fs_dontaudit_remount_tmpfs',`
|
||||
gen_require(`
|
||||
type tmpfs_t;
|
||||
')
|
||||
|
||||
dontaudit $1 tmpfs_t:filesystem remount;
|
||||
')
|
||||
interface(`dev_dontaudit_list_all_dev_nodes',`
|
||||
gen_require(`
|
||||
type device_t;
|
||||
')
|
||||
|
||||
dontaudit $1 device_t:dir list_dir_perms;
|
||||
')
|
||||
interface(`kernel_unlabeled_entry_type',`
|
||||
gen_require(`
|
||||
type unlabeled_t;
|
||||
')
|
||||
|
||||
domain_entry_file($1, unlabeled_t)
|
||||
')
|
||||
interface(`kernel_unlabeled_domtrans',`
|
||||
gen_require(`
|
||||
type unlabeled_t;
|
||||
')
|
||||
|
||||
read_lnk_files_pattern($1, unlabeled_t, unlabeled_t)
|
||||
domain_transition_pattern($1, unlabeled_t, $2)
|
||||
type_transition $1 unlabeled_t:process $2;
|
||||
')
|
||||
interface(`files_write_all_pid_sockets',`
|
||||
gen_require(`
|
||||
attribute pidfile;
|
||||
')
|
||||
|
||||
allow $1 pidfile:sock_file write_sock_file_perms;
|
||||
')
|
||||
interface(`dev_dontaudit_mounton_sysfs',`
|
||||
gen_require(`
|
||||
type sysfs_t;
|
||||
')
|
||||
|
||||
dontaudit $1 sysfs_t:dir mounton;
|
||||
')
|
||||
@@ -1,418 +0,0 @@
|
||||
policy_module(docker, 1.0.0)
|
||||
|
||||
########################################
|
||||
#
|
||||
# Declarations
|
||||
#
|
||||
|
||||
## <desc>
|
||||
## <p>
|
||||
## Allow sandbox containers manage fuse files
|
||||
## </p>
|
||||
## </desc>
|
||||
gen_tunable(virt_sandbox_use_fusefs, false)
|
||||
|
||||
## <desc>
|
||||
## <p>
|
||||
## Determine whether docker can
|
||||
## connect to all TCP ports.
|
||||
## </p>
|
||||
## </desc>
|
||||
gen_tunable(docker_connect_any, false)
|
||||
|
||||
type docker_t;
|
||||
type docker_exec_t;
|
||||
init_daemon_domain(docker_t, docker_exec_t)
|
||||
domain_subj_id_change_exemption(docker_t)
|
||||
domain_role_change_exemption(docker_t)
|
||||
|
||||
type spc_t;
|
||||
domain_type(spc_t)
|
||||
role system_r types spc_t;
|
||||
|
||||
type spc_var_run_t;
|
||||
files_pid_file(spc_var_run_t)
|
||||
|
||||
type docker_var_lib_t;
|
||||
files_type(docker_var_lib_t)
|
||||
|
||||
type docker_home_t;
|
||||
userdom_user_home_content(docker_home_t)
|
||||
|
||||
type docker_config_t;
|
||||
files_config_file(docker_config_t)
|
||||
|
||||
type docker_lock_t;
|
||||
files_lock_file(docker_lock_t)
|
||||
|
||||
type docker_log_t;
|
||||
logging_log_file(docker_log_t)
|
||||
|
||||
type docker_tmp_t;
|
||||
files_tmp_file(docker_tmp_t)
|
||||
|
||||
type docker_tmpfs_t;
|
||||
files_tmpfs_file(docker_tmpfs_t)
|
||||
|
||||
type docker_var_run_t;
|
||||
files_pid_file(docker_var_run_t)
|
||||
|
||||
type docker_unit_file_t;
|
||||
systemd_unit_file(docker_unit_file_t)
|
||||
|
||||
type docker_devpts_t;
|
||||
term_pty(docker_devpts_t)
|
||||
|
||||
type docker_share_t;
|
||||
files_type(docker_share_t)
|
||||
|
||||
########################################
|
||||
#
|
||||
# docker local policy
|
||||
#
|
||||
allow docker_t self:capability { chown kill fowner fsetid mknod net_admin net_bind_service net_raw setfcap };
|
||||
allow docker_t self:tun_socket relabelto;
|
||||
allow docker_t self:process { getattr signal_perms setrlimit setfscreate };
|
||||
allow docker_t self:fifo_file rw_fifo_file_perms;
|
||||
allow docker_t self:unix_stream_socket create_stream_socket_perms;
|
||||
allow docker_t self:tcp_socket create_stream_socket_perms;
|
||||
allow docker_t self:udp_socket create_socket_perms;
|
||||
allow docker_t self:capability2 block_suspend;
|
||||
|
||||
manage_files_pattern(docker_t, docker_home_t, docker_home_t)
|
||||
manage_dirs_pattern(docker_t, docker_home_t, docker_home_t)
|
||||
manage_lnk_files_pattern(docker_t, docker_home_t, docker_home_t)
|
||||
userdom_admin_home_dir_filetrans(docker_t, docker_home_t, dir, ".docker")
|
||||
|
||||
manage_dirs_pattern(docker_t, docker_config_t, docker_config_t)
|
||||
manage_files_pattern(docker_t, docker_config_t, docker_config_t)
|
||||
files_etc_filetrans(docker_t, docker_config_t, dir, "docker")
|
||||
|
||||
manage_dirs_pattern(docker_t, docker_lock_t, docker_lock_t)
|
||||
manage_files_pattern(docker_t, docker_lock_t, docker_lock_t)
|
||||
files_lock_filetrans(docker_t, docker_lock_t, { dir file }, "lxc")
|
||||
|
||||
manage_dirs_pattern(docker_t, docker_log_t, docker_log_t)
|
||||
manage_files_pattern(docker_t, docker_log_t, docker_log_t)
|
||||
manage_lnk_files_pattern(docker_t, docker_log_t, docker_log_t)
|
||||
logging_log_filetrans(docker_t, docker_log_t, { dir file lnk_file })
|
||||
allow docker_t docker_log_t:dir_file_class_set { relabelfrom relabelto };
|
||||
|
||||
manage_dirs_pattern(docker_t, docker_tmp_t, docker_tmp_t)
|
||||
manage_files_pattern(docker_t, docker_tmp_t, docker_tmp_t)
|
||||
manage_lnk_files_pattern(docker_t, docker_tmp_t, docker_tmp_t)
|
||||
files_tmp_filetrans(docker_t, docker_tmp_t, { dir file lnk_file })
|
||||
|
||||
manage_dirs_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||
manage_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||
manage_lnk_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||
manage_fifo_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||
manage_chr_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||
manage_blk_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||
allow docker_t docker_tmpfs_t:dir relabelfrom;
|
||||
can_exec(docker_t, docker_tmpfs_t)
|
||||
fs_tmpfs_filetrans(docker_t, docker_tmpfs_t, { dir file })
|
||||
allow docker_t docker_tmpfs_t:chr_file mounton;
|
||||
|
||||
manage_dirs_pattern(docker_t, docker_share_t, docker_share_t)
|
||||
manage_files_pattern(docker_t, docker_share_t, docker_share_t)
|
||||
manage_lnk_files_pattern(docker_t, docker_share_t, docker_share_t)
|
||||
allow docker_t docker_share_t:dir_file_class_set { relabelfrom relabelto };
|
||||
|
||||
can_exec(docker_t, docker_share_t)
|
||||
#docker_filetrans_named_content(docker_t)
|
||||
|
||||
manage_dirs_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||
manage_chr_files_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||
manage_blk_files_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||
manage_files_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||
manage_lnk_files_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||
allow docker_t docker_var_lib_t:dir_file_class_set { relabelfrom relabelto };
|
||||
files_var_lib_filetrans(docker_t, docker_var_lib_t, { dir file lnk_file })
|
||||
|
||||
manage_dirs_pattern(docker_t, docker_var_run_t, docker_var_run_t)
|
||||
manage_files_pattern(docker_t, docker_var_run_t, docker_var_run_t)
|
||||
manage_sock_files_pattern(docker_t, docker_var_run_t, docker_var_run_t)
|
||||
manage_lnk_files_pattern(docker_t, docker_var_run_t, docker_var_run_t)
|
||||
files_pid_filetrans(docker_t, docker_var_run_t, { dir file lnk_file sock_file })
|
||||
|
||||
allow docker_t docker_devpts_t:chr_file { relabelfrom rw_chr_file_perms setattr_chr_file_perms };
|
||||
term_create_pty(docker_t, docker_devpts_t)
|
||||
|
||||
kernel_read_system_state(docker_t)
|
||||
kernel_read_network_state(docker_t)
|
||||
kernel_read_all_sysctls(docker_t)
|
||||
kernel_rw_net_sysctls(docker_t)
|
||||
kernel_setsched(docker_t)
|
||||
kernel_read_all_proc(docker_t)
|
||||
|
||||
domain_use_interactive_fds(docker_t)
|
||||
domain_dontaudit_read_all_domains_state(docker_t)
|
||||
|
||||
corecmd_exec_bin(docker_t)
|
||||
corecmd_exec_shell(docker_t)
|
||||
|
||||
corenet_tcp_bind_generic_node(docker_t)
|
||||
corenet_tcp_sendrecv_generic_if(docker_t)
|
||||
corenet_tcp_sendrecv_generic_node(docker_t)
|
||||
corenet_tcp_sendrecv_generic_port(docker_t)
|
||||
corenet_tcp_bind_all_ports(docker_t)
|
||||
corenet_tcp_connect_http_port(docker_t)
|
||||
corenet_tcp_connect_commplex_main_port(docker_t)
|
||||
corenet_udp_sendrecv_generic_if(docker_t)
|
||||
corenet_udp_sendrecv_generic_node(docker_t)
|
||||
corenet_udp_sendrecv_all_ports(docker_t)
|
||||
corenet_udp_bind_generic_node(docker_t)
|
||||
corenet_udp_bind_all_ports(docker_t)
|
||||
|
||||
files_read_config_files(docker_t)
|
||||
files_dontaudit_getattr_all_dirs(docker_t)
|
||||
files_dontaudit_getattr_all_files(docker_t)
|
||||
|
||||
fs_read_cgroup_files(docker_t)
|
||||
fs_read_tmpfs_symlinks(docker_t)
|
||||
fs_search_all(docker_t)
|
||||
fs_getattr_all_fs(docker_t)
|
||||
|
||||
storage_raw_rw_fixed_disk(docker_t)
|
||||
|
||||
auth_use_nsswitch(docker_t)
|
||||
auth_dontaudit_getattr_shadow(docker_t)
|
||||
|
||||
init_read_state(docker_t)
|
||||
init_status(docker_t)
|
||||
|
||||
logging_send_audit_msgs(docker_t)
|
||||
logging_send_syslog_msg(docker_t)
|
||||
|
||||
miscfiles_read_localization(docker_t)
|
||||
|
||||
mount_domtrans(docker_t)
|
||||
|
||||
seutil_read_default_contexts(docker_t)
|
||||
seutil_read_config(docker_t)
|
||||
|
||||
sysnet_dns_name_resolve(docker_t)
|
||||
sysnet_exec_ifconfig(docker_t)
|
||||
|
||||
optional_policy(`
|
||||
rpm_exec(docker_t)
|
||||
rpm_read_db(docker_t)
|
||||
rpm_exec(docker_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
fstools_domtrans(docker_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
iptables_domtrans(docker_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
openvswitch_stream_connect(docker_t)
|
||||
')
|
||||
|
||||
#
|
||||
# lxc rules
|
||||
#
|
||||
|
||||
allow docker_t self:capability { dac_override setgid setpcap setuid sys_admin sys_boot sys_chroot sys_ptrace };
|
||||
|
||||
allow docker_t self:process { getcap setcap setexec setpgid setsched signal_perms };
|
||||
|
||||
allow docker_t self:netlink_route_socket rw_netlink_socket_perms;;
|
||||
allow docker_t self:netlink_audit_socket create_netlink_socket_perms;
|
||||
allow docker_t self:unix_dgram_socket { create_socket_perms sendto };
|
||||
allow docker_t self:unix_stream_socket { create_stream_socket_perms connectto };
|
||||
|
||||
allow docker_t docker_var_lib_t:dir mounton;
|
||||
allow docker_t docker_var_lib_t:chr_file mounton;
|
||||
can_exec(docker_t, docker_var_lib_t)
|
||||
|
||||
kernel_dontaudit_setsched(docker_t)
|
||||
kernel_get_sysvipc_info(docker_t)
|
||||
kernel_request_load_module(docker_t)
|
||||
kernel_mounton_messages(docker_t)
|
||||
kernel_mounton_all_proc(docker_t)
|
||||
kernel_mounton_all_sysctls(docker_t)
|
||||
kernel_unlabeled_entry_type(spc_t)
|
||||
kernel_unlabeled_domtrans(docker_t, spc_t)
|
||||
|
||||
dev_getattr_all(docker_t)
|
||||
dev_getattr_sysfs_fs(docker_t)
|
||||
dev_read_urand(docker_t)
|
||||
dev_read_lvm_control(docker_t)
|
||||
dev_rw_sysfs(docker_t)
|
||||
dev_rw_loop_control(docker_t)
|
||||
dev_rw_lvm_control(docker_t)
|
||||
|
||||
files_getattr_isid_type_dirs(docker_t)
|
||||
files_manage_isid_type_dirs(docker_t)
|
||||
files_manage_isid_type_files(docker_t)
|
||||
files_manage_isid_type_symlinks(docker_t)
|
||||
files_manage_isid_type_chr_files(docker_t)
|
||||
files_manage_isid_type_blk_files(docker_t)
|
||||
files_exec_isid_files(docker_t)
|
||||
files_mounton_isid(docker_t)
|
||||
files_mounton_non_security(docker_t)
|
||||
files_mounton_isid_type_chr_file(docker_t)
|
||||
|
||||
fs_mount_all_fs(docker_t)
|
||||
fs_unmount_all_fs(docker_t)
|
||||
fs_remount_all_fs(docker_t)
|
||||
files_mounton_isid(docker_t)
|
||||
fs_manage_cgroup_dirs(docker_t)
|
||||
fs_manage_cgroup_files(docker_t)
|
||||
fs_relabelfrom_xattr_fs(docker_t)
|
||||
fs_relabelfrom_tmpfs(docker_t)
|
||||
fs_read_tmpfs_symlinks(docker_t)
|
||||
fs_list_hugetlbfs(docker_t)
|
||||
|
||||
term_use_generic_ptys(docker_t)
|
||||
term_use_ptmx(docker_t)
|
||||
term_getattr_pty_fs(docker_t)
|
||||
term_relabel_pty_fs(docker_t)
|
||||
term_mounton_unallocated_ttys(docker_t)
|
||||
|
||||
modutils_domtrans_insmod(docker_t)
|
||||
|
||||
systemd_status_all_unit_files(docker_t)
|
||||
systemd_start_systemd_services(docker_t)
|
||||
|
||||
userdom_stream_connect(docker_t)
|
||||
userdom_search_user_home_content(docker_t)
|
||||
userdom_read_all_users_state(docker_t)
|
||||
userdom_relabel_user_home_files(docker_t)
|
||||
userdom_relabel_user_tmp_files(docker_t)
|
||||
userdom_relabel_user_tmp_dirs(docker_t)
|
||||
|
||||
optional_policy(`
|
||||
gpm_getattr_gpmctl(docker_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
dbus_system_bus_client(docker_t)
|
||||
init_dbus_chat(docker_t)
|
||||
init_start_transient_unit(docker_t)
|
||||
|
||||
optional_policy(`
|
||||
systemd_dbus_chat_logind(docker_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
firewalld_dbus_chat(docker_t)
|
||||
')
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
udev_read_db(docker_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
virt_read_config(docker_t)
|
||||
virt_exec(docker_t)
|
||||
virt_stream_connect(docker_t)
|
||||
virt_stream_connect_sandbox(docker_t)
|
||||
virt_exec_sandbox_files(docker_t)
|
||||
virt_manage_sandbox_files(docker_t)
|
||||
virt_relabel_sandbox_filesystem(docker_t)
|
||||
# for lxc
|
||||
virt_transition_svirt_sandbox(docker_t, system_r)
|
||||
virt_mounton_sandbox_file(docker_t)
|
||||
# virt_attach_sandbox_tun_iface(docker_t)
|
||||
allow docker_t svirt_sandbox_domain:tun_socket relabelfrom;
|
||||
')
|
||||
|
||||
tunable_policy(`docker_connect_any',`
|
||||
corenet_tcp_connect_all_ports(docker_t)
|
||||
corenet_sendrecv_all_packets(docker_t)
|
||||
corenet_tcp_sendrecv_all_ports(docker_t)
|
||||
')
|
||||
|
||||
########################################
|
||||
#
|
||||
# spc local policy
|
||||
#
|
||||
domain_entry_file(spc_t, docker_share_t)
|
||||
domain_entry_file(spc_t, docker_var_lib_t)
|
||||
role system_r types spc_t;
|
||||
|
||||
domain_entry_file(spc_t, docker_share_t)
|
||||
domain_entry_file(spc_t, docker_var_lib_t)
|
||||
domtrans_pattern(docker_t, docker_share_t, spc_t)
|
||||
domtrans_pattern(docker_t, docker_var_lib_t, spc_t)
|
||||
allow docker_t spc_t:process { setsched signal_perms };
|
||||
ps_process_pattern(docker_t, spc_t)
|
||||
allow docker_t spc_t:socket_class_set { relabelto relabelfrom };
|
||||
|
||||
optional_policy(`
|
||||
dbus_chat_system_bus(spc_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
unconfined_domain_noaudit(spc_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
unconfined_domain(docker_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
virt_transition_svirt_sandbox(spc_t, system_r)
|
||||
')
|
||||
|
||||
########################################
|
||||
#
|
||||
# docker upstream policy
|
||||
#
|
||||
|
||||
optional_policy(`
|
||||
# domain_stub_named_filetrans_domain()
|
||||
gen_require(`
|
||||
attribute named_filetrans_domain;
|
||||
')
|
||||
|
||||
docker_filetrans_named_content(named_filetrans_domain)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
lvm_stub()
|
||||
docker_rw_sem(lvm_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
staff_stub()
|
||||
docker_stream_connect(staff_t)
|
||||
docker_exec(staff_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
virt_stub_lxc()
|
||||
docker_exec_lib(virtd_lxc_t)
|
||||
')
|
||||
|
||||
optional_policy(`
|
||||
virt_stub_svirt_sandbox_domain()
|
||||
virt_stub_svirt_sandbox_file()
|
||||
allow svirt_sandbox_domain self:netlink_kobject_uevent_socket create_socket_perms;
|
||||
docker_read_share_files(svirt_sandbox_domain)
|
||||
docker_lib_filetrans(svirt_sandbox_domain,svirt_sandbox_file_t, sock_file)
|
||||
docker_use_ptys(svirt_sandbox_domain)
|
||||
docker_spc_stream_connect(svirt_sandbox_domain)
|
||||
fs_list_tmpfs(svirt_sandbox_domain)
|
||||
fs_rw_hugetlbfs_files(svirt_sandbox_domain)
|
||||
fs_dontaudit_remount_tmpfs(svirt_sandbox_domain)
|
||||
dev_dontaudit_mounton_sysfs(svirt_sandbox_domain)
|
||||
|
||||
tunable_policy(`virt_sandbox_use_fusefs',`
|
||||
fs_manage_fusefs_dirs(svirt_sandbox_domain)
|
||||
fs_manage_fusefs_files(svirt_sandbox_domain)
|
||||
fs_manage_fusefs_symlinks(svirt_sandbox_domain)
|
||||
')
|
||||
gen_require(`
|
||||
attribute domain;
|
||||
')
|
||||
|
||||
dontaudit svirt_sandbox_domain domain:key {search link};
|
||||
')
|
||||
Binary file not shown.
Regular → Executable
+24
-9
@@ -1,19 +1,34 @@
|
||||
#!/sbin/openrc-run
|
||||
#!/sbin/runscript
|
||||
# Copyright 1999-2013 Gentoo Foundation
|
||||
# Distributed under the terms of the GNU General Public License v2
|
||||
# $Header: $
|
||||
|
||||
command="${DOCKER_BINARY:-/usr/bin/docker}"
|
||||
pidfile="${DOCKER_PIDFILE:-/run/${RC_SVCNAME}.pid}"
|
||||
command_args="daemon -p \"${pidfile}\" ${DOCKER_OPTS}"
|
||||
DOCKER_LOGFILE="${DOCKER_LOGFILE:-/var/log/${RC_SVCNAME}.log}"
|
||||
start_stop_daemon_args="--background \
|
||||
--stderr \"${DOCKER_LOGFILE}\" --stdout \"${DOCKER_LOGFILE}\""
|
||||
DOCKER_LOGFILE=${DOCKER_LOGFILE:-/var/log/${SVCNAME}.log}
|
||||
DOCKER_PIDFILE=${DOCKER_PIDFILE:-/run/${SVCNAME}.pid}
|
||||
DOCKER_BINARY=${DOCKER_BINARY:-/usr/bin/docker}
|
||||
DOCKER_OPTS=${DOCKER_OPTS:-}
|
||||
|
||||
start_pre() {
|
||||
start() {
|
||||
checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE"
|
||||
|
||||
ulimit -n 1048576
|
||||
ulimit -u 1048576
|
||||
|
||||
return 0
|
||||
ebegin "Starting docker daemon"
|
||||
start-stop-daemon --start --background \
|
||||
--exec "$DOCKER_BINARY" \
|
||||
--pidfile "$DOCKER_PIDFILE" \
|
||||
--stdout "$DOCKER_LOGFILE" \
|
||||
--stderr "$DOCKER_LOGFILE" \
|
||||
-- daemon -p "$DOCKER_PIDFILE" \
|
||||
$DOCKER_OPTS
|
||||
eend $?
|
||||
}
|
||||
|
||||
stop() {
|
||||
ebegin "Stopping docker daemon"
|
||||
start-stop-daemon --stop \
|
||||
--exec "$DOCKER_BINARY" \
|
||||
--pidfile "$DOCKER_PIDFILE"
|
||||
eend $?
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ After=network.target docker.socket
|
||||
Requires=docker.socket
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/usr/bin/docker daemon -H fd://
|
||||
MountFlags=slave
|
||||
LimitNOFILE=1048576
|
||||
|
||||
@@ -41,14 +41,7 @@ prestart() {
|
||||
}
|
||||
|
||||
start() {
|
||||
if [ ! -x $exec ]; then
|
||||
if [ ! -e $exec ]; then
|
||||
echo "Docker executable $exec not found"
|
||||
else
|
||||
echo "You do not have permission to execute the Docker executable $exec"
|
||||
fi
|
||||
exit 5
|
||||
fi
|
||||
[ -x $exec ] || exit 5
|
||||
|
||||
check_for_cleanup
|
||||
|
||||
@@ -65,13 +58,7 @@ start() {
|
||||
while [ ! -f $pidfile -a $tries -lt 10 ]; do
|
||||
sleep 1
|
||||
tries=$((tries + 1))
|
||||
echo -n '.'
|
||||
done
|
||||
if [ ! -f $pidfile ]; then
|
||||
failure
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
success
|
||||
echo
|
||||
else
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
*
|
||||
* This sample list was compiled as a combination of all the syscalls
|
||||
* available on i386 and amd64 on Ubuntu Precise, as such it may not contain
|
||||
* everything and not everything may be relevant for your system. This
|
||||
* everything and not everything may be relevent for your system. This
|
||||
* shouldn't be a problem.
|
||||
*/
|
||||
|
||||
|
||||
@@ -8,5 +8,4 @@ targets_from() {
|
||||
git ls-tree -r --name-only origin/master contrib/builder/deb | grep '/Dockerfile$' | sed -r 's!^contrib/builder/deb/|-debootstrap|/Dockerfile$!!g'
|
||||
}
|
||||
|
||||
release_branch=$(git ls-remote --heads https://github.com/docker/docker.git | awk -F 'refs/heads/' '$2 ~ /^release/ { print $2 }' | sort -V | tail -1)
|
||||
{ targets_from master; targets_from "$release_branch"; } | sort -u
|
||||
{ targets_from master; targets_from release; } | sort -u
|
||||
|
||||
@@ -31,7 +31,7 @@ stop on runlevel [!2345]
|
||||
respawn
|
||||
|
||||
script
|
||||
/usr/bin/docker daemon -H=tcp://0.0.0.0:2375
|
||||
/usr/bin/docker -d -H=tcp://0.0.0.0:2375
|
||||
end script
|
||||
```
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user