new vendoring
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
dee7479483
commit
e269cb8b7e
350 changed files with 128937 additions and 8 deletions
115
hack/.vendor-helpers.sh
Normal file
115
hack/.vendor-helpers.sh
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
PROJECT=github.com/kubernetes-incubator/ocid
|
||||||
|
|
||||||
|
# Downloads dependencies into vendor/ directory
|
||||||
|
mkdir -p vendor
|
||||||
|
|
||||||
|
original_GOPATH=$GOPATH
|
||||||
|
export GOPATH="${PWD}/vendor:$GOPATH"
|
||||||
|
|
||||||
|
find="/usr/bin/find"
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
local delete_vendor=true
|
||||||
|
if [ "x$1" = x--keep-vendor ]; then
|
||||||
|
delete_vendor=false
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
local vcs="$1"
|
||||||
|
local pkg="$2"
|
||||||
|
local rev="$3"
|
||||||
|
local url="$4"
|
||||||
|
|
||||||
|
: ${url:=https://$pkg}
|
||||||
|
local target="vendor/src/$pkg"
|
||||||
|
|
||||||
|
echo -n "$pkg @ $rev: "
|
||||||
|
|
||||||
|
if [ -d "$target" ]; then
|
||||||
|
echo -n 'rm old, '
|
||||||
|
rm -rf "$target"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n 'clone, '
|
||||||
|
case "$vcs" in
|
||||||
|
git)
|
||||||
|
git clone --quiet --no-checkout "$url" "$target"
|
||||||
|
( cd "$target" && git checkout --quiet "$rev" && git reset --quiet --hard "$rev" -- )
|
||||||
|
;;
|
||||||
|
hg)
|
||||||
|
hg clone --quiet --updaterev "$rev" "$url" "$target"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo -n 'rm VCS, '
|
||||||
|
( cd "$target" && rm -rf .{git,hg} )
|
||||||
|
|
||||||
|
if $delete_vendor; then
|
||||||
|
echo -n 'rm vendor, '
|
||||||
|
( cd "$target" && rm -rf vendor Godeps/_workspace )
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo done
|
||||||
|
}
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
# If $GOPATH starts with ./vendor, (go list) shows the short-form import paths for packages inside ./vendor.
|
||||||
|
# So, reset GOPATH to the external value (without ./vendor), so that the grep -v works.
|
||||||
|
local packages=($(GOPATH=$original_GOPATH go list -e ./... | grep -v "^${PROJECT}/vendor"))
|
||||||
|
local platforms=( linux/amd64 linux/386 )
|
||||||
|
|
||||||
|
local buildTags=( )
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -n 'collecting import graph, '
|
||||||
|
local IFS=$'\n'
|
||||||
|
local imports=( $(
|
||||||
|
for platform in "${platforms[@]}"; do
|
||||||
|
export GOOS="${platform%/*}";
|
||||||
|
export GOARCH="${platform##*/}";
|
||||||
|
go list -e -tags "$buildTags" -f '{{join .Deps "\n"}}' "${packages[@]}"
|
||||||
|
go list -e -tags "$buildTags" -f '{{join .TestImports "\n"}}' "${packages[@]}"
|
||||||
|
done | grep -vE "^${PROJECT}" | sort -u
|
||||||
|
) )
|
||||||
|
# .TestImports does not include indirect dependencies, so do one more iteration.
|
||||||
|
imports+=( $(
|
||||||
|
go list -e -f '{{join .Deps "\n"}}' "${imports[@]}" | grep -vE "^${PROJECT}" | sort -u
|
||||||
|
) )
|
||||||
|
imports=( $(go list -e -f '{{if not .Standard}}{{.ImportPath}}{{end}}' "${imports[@]}") )
|
||||||
|
unset IFS
|
||||||
|
|
||||||
|
echo -n 'pruning unused packages, '
|
||||||
|
findArgs=(
|
||||||
|
# This directory contains only .c and .h files which are necessary
|
||||||
|
# -path vendor/src/github.com/mattn/go-sqlite3/code
|
||||||
|
)
|
||||||
|
for import in "${imports[@]}"; do
|
||||||
|
[ "${#findArgs[@]}" -eq 0 ] || findArgs+=( -or )
|
||||||
|
findArgs+=( -path "vendor/src/$import" )
|
||||||
|
done
|
||||||
|
local IFS=$'\n'
|
||||||
|
local prune=( $($find vendor -depth -type d -not '(' "${findArgs[@]}" ')') )
|
||||||
|
unset IFS
|
||||||
|
for dir in "${prune[@]}"; do
|
||||||
|
$find "$dir" -maxdepth 1 -not -type d -not -name 'LICENSE*' -not -name 'COPYING*' -exec rm -v -f '{}' ';'
|
||||||
|
rmdir "$dir" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -n 'pruning unused files, '
|
||||||
|
$find vendor -type f -name '*_test.go' -exec rm -v '{}' ';'
|
||||||
|
|
||||||
|
echo done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fix up hard-coded imports that refer to Godeps paths so they'll work with our vendoring
|
||||||
|
fix_rewritten_imports () {
|
||||||
|
local pkg="$1"
|
||||||
|
local remove="${pkg}/Godeps/_workspace/src/"
|
||||||
|
local target="vendor/src/$pkg"
|
||||||
|
|
||||||
|
echo "$pkg: fixing rewritten imports"
|
||||||
|
$find "$target" -name \*.go -exec sed -i -e "s|\"${remove}|\"|g" {} \;
|
||||||
|
}
|
64
hack/vendor.sh
Executable file
64
hack/vendor.sh
Executable file
|
@ -0,0 +1,64 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# this script is used to update vendored dependencies
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# vendor.sh revendor all dependencies
|
||||||
|
# vendor.sh github.com/docker/engine-api revendor only the engine-api dependency.
|
||||||
|
# vendor.sh github.com/docker/engine-api v0.3.3 vendor only engine-api at the specified tag/commit.
|
||||||
|
# vendor.sh git github.com/docker/engine-api v0.3.3 is the same but specifies the VCS for cases where the VCS is something else than git
|
||||||
|
# vendor.sh git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git vendor only golang.org/x/sys downloading from the specified URL
|
||||||
|
|
||||||
|
cd "$(dirname "$BASH_SOURCE")/.."
|
||||||
|
source 'hack/.vendor-helpers.sh'
|
||||||
|
|
||||||
|
case $# in
|
||||||
|
0)
|
||||||
|
rm -rf vendor/
|
||||||
|
;;
|
||||||
|
# If user passed arguments to the script
|
||||||
|
1)
|
||||||
|
path="$PWD/hack/vendor.sh"
|
||||||
|
if ! cloneGrep="$(grep -E "^clone [^ ]+ $1" "$path")"; then
|
||||||
|
echo >&2 "error: failed to find 'clone ... $1' in $path"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
eval "$cloneGrep"
|
||||||
|
clean
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
rm -rf "vendor/src/$1"
|
||||||
|
clone git "$1" "$2"
|
||||||
|
clean
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
[34])
|
||||||
|
rm -rf "vendor/src/$2"
|
||||||
|
clone "$@"
|
||||||
|
clean
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
>&2 echo "error: unexpected parameters"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
clone git github.com/Sirupsen/logrus v0.10.0
|
||||||
|
clone git github.com/containers/image f6f11ab5cf8b1e70ef4aa3f8b6fdb4b671d16abd
|
||||||
|
clone git golang.org/x/net 991d3e32f76f19ee6d9caadb3a22eae8d23315f7 https://github.com/golang/net.git
|
||||||
|
clone git github.com/docker/docker master
|
||||||
|
clone git github.com/urfave/cli v1.18.1
|
||||||
|
clone git github.com/opencontainers/runtime-tools master
|
||||||
|
clone git github.com/tchap/go-patricia v2.2.6
|
||||||
|
clone git github.com/rajatchopra/ocicni master
|
||||||
|
clone git github.com/containernetworking/cni master
|
||||||
|
clone git github.com/kubernetes/kubernetes ff3ca3d616518087dc20180f69bb4038379f1028
|
||||||
|
clone git google.golang.org/grpc v1.0.1-GA https://github.com/grpc/grpc-go.git
|
||||||
|
clone git github.com/opencontainers/runtime-spec bb6925ea99f0e366a3f7d1c975f6577475ca25f0
|
||||||
|
|
||||||
|
clean
|
||||||
|
|
||||||
|
mv vendor/src/* vendor/
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/kubernetes-incubator/ocid/oci"
|
"github.com/kubernetes-incubator/ocid/oci"
|
||||||
"github.com/kubernetes-incubator/ocid/utils"
|
"github.com/kubernetes-incubator/ocid/utils"
|
||||||
pb "github.com/kubernetes/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
pb "github.com/kubernetes/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||||
"github.com/opencontainers/ocitools/generate"
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -250,12 +250,12 @@ func (s *Server) createSandboxContainer(name string, sb *sandbox, SandboxConfig
|
||||||
"network": "net",
|
"network": "net",
|
||||||
} {
|
} {
|
||||||
nsPath := fmt.Sprintf("/proc/%d/ns/%s", podInfraState.Pid, nsFile)
|
nsPath := fmt.Sprintf("/proc/%d/ns/%s", podInfraState.Pid, nsFile)
|
||||||
if err := specgen.AddOrReplaceLinuxNamespace(nsType, nsPath); err != nil {
|
if err = specgen.AddOrReplaceLinuxNamespace(nsType, nsPath); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := specgen.SaveToFile(filepath.Join(containerDir, "config.json")); err != nil {
|
if err = specgen.SaveToFile(filepath.Join(containerDir, "config.json")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ func (s *Server) createSandboxContainer(name string, sb *sandbox, SandboxConfig
|
||||||
|
|
||||||
// TODO: copy the rootfs into the bundle.
|
// TODO: copy the rootfs into the bundle.
|
||||||
// Currently, utils.CreateFakeRootfs is used to populate the rootfs.
|
// Currently, utils.CreateFakeRootfs is used to populate the rootfs.
|
||||||
if err := utils.CreateFakeRootfs(containerDir, image); err != nil {
|
if err = utils.CreateFakeRootfs(containerDir, image); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/kubernetes-incubator/ocid/oci"
|
"github.com/kubernetes-incubator/ocid/oci"
|
||||||
"github.com/kubernetes-incubator/ocid/utils"
|
"github.com/kubernetes-incubator/ocid/utils"
|
||||||
pb "github.com/kubernetes/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
pb "github.com/kubernetes/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||||
"github.com/opencontainers/ocitools/generate"
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -211,7 +211,9 @@ func (s *Server) CreatePodSandbox(ctx context.Context, req *pb.CreatePodSandboxR
|
||||||
|
|
||||||
s.addContainer(container)
|
s.addContainer(container)
|
||||||
|
|
||||||
s.podIDIndex.Add(id)
|
if err = s.podIDIndex.Add(id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err = s.runtime.UpdateStatus(container); err != nil {
|
if err = s.runtime.UpdateStatus(container); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -47,6 +47,10 @@ func (s *Server) loadSandbox(id string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
name := m.Annotations["ocid/name"]
|
name := m.Annotations["ocid/name"]
|
||||||
|
name, err = s.reservePodName(id, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
s.addSandbox(&sandbox{
|
s.addSandbox(&sandbox{
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -63,8 +67,9 @@ func (s *Server) loadSandbox(id string) error {
|
||||||
if err = s.runtime.UpdateStatus(scontainer); err != nil {
|
if err = s.runtime.UpdateStatus(scontainer); err != nil {
|
||||||
logrus.Warnf("error updating status for container %s: %v", scontainer, err)
|
logrus.Warnf("error updating status for container %s: %v", scontainer, err)
|
||||||
}
|
}
|
||||||
s.podIDIndex.Add(id)
|
if err = s.podIDIndex.Add(id); err != nil {
|
||||||
s.reservePodName(id, name)
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
Normal file
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
logrus
|
9
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- tip
|
||||||
|
install:
|
||||||
|
- go get -t ./...
|
||||||
|
script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./...
|
66
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
66
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# 0.10.0
|
||||||
|
|
||||||
|
* feature: Add a test hook (#180)
|
||||||
|
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||||
|
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||||
|
* performance: avoid re-allocations on `WithFields` (#335)
|
||||||
|
|
||||||
|
# 0.9.0
|
||||||
|
|
||||||
|
* logrus/text_formatter: don't emit empty msg
|
||||||
|
* logrus/hooks/airbrake: move out of main repository
|
||||||
|
* logrus/hooks/sentry: move out of main repository
|
||||||
|
* logrus/hooks/papertrail: move out of main repository
|
||||||
|
* logrus/hooks/bugsnag: move out of main repository
|
||||||
|
* logrus/core: run tests with `-race`
|
||||||
|
* logrus/core: detect TTY based on `stderr`
|
||||||
|
* logrus/core: support `WithError` on logger
|
||||||
|
* logrus/core: Solaris support
|
||||||
|
|
||||||
|
# 0.8.7
|
||||||
|
|
||||||
|
* logrus/core: fix possible race (#216)
|
||||||
|
* logrus/doc: small typo fixes and doc improvements
|
||||||
|
|
||||||
|
|
||||||
|
# 0.8.6
|
||||||
|
|
||||||
|
* hooks/raven: allow passing an initialized client
|
||||||
|
|
||||||
|
# 0.8.5
|
||||||
|
|
||||||
|
* logrus/core: revert #208
|
||||||
|
|
||||||
|
# 0.8.4
|
||||||
|
|
||||||
|
* formatter/text: fix data race (#218)
|
||||||
|
|
||||||
|
# 0.8.3
|
||||||
|
|
||||||
|
* logrus/core: fix entry log level (#208)
|
||||||
|
* logrus/core: improve performance of text formatter by 40%
|
||||||
|
* logrus/core: expose `LevelHooks` type
|
||||||
|
* logrus/core: add support for DragonflyBSD and NetBSD
|
||||||
|
* formatter/text: print structs more verbosely
|
||||||
|
|
||||||
|
# 0.8.2
|
||||||
|
|
||||||
|
* logrus: fix more Fatal family functions
|
||||||
|
|
||||||
|
# 0.8.1
|
||||||
|
|
||||||
|
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
||||||
|
|
||||||
|
# 0.8.0
|
||||||
|
|
||||||
|
* logrus: defaults to stderr instead of stdout
|
||||||
|
* hooks/sentry: add special field for `*http.Request`
|
||||||
|
* formatter/text: ignore Windows for colors
|
||||||
|
|
||||||
|
# 0.7.3
|
||||||
|
|
||||||
|
* formatter/\*: allow configuration of timestamp layout
|
||||||
|
|
||||||
|
# 0.7.2
|
||||||
|
|
||||||
|
* formatter/text: Add configuration option for time format (#158)
|
21
vendor/github.com/Sirupsen/logrus/LICENSE
generated
vendored
Normal file
21
vendor/github.com/Sirupsen/logrus/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Simon Eskildsen
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
388
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
Normal file
388
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
Normal file
|
@ -0,0 +1,388 @@
|
||||||
|
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus)
|
||||||
|
|
||||||
|
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||||
|
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
|
||||||
|
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
|
||||||
|
many large deployments. The core API is unlikely to change much but please
|
||||||
|
version control your Logrus to make sure you aren't fetching latest `master` on
|
||||||
|
every build.**
|
||||||
|
|
||||||
|
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||||
|
plain text):
|
||||||
|
|
||||||
|
![Colored](http://i.imgur.com/PY7qMwd.png)
|
||||||
|
|
||||||
|
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||||
|
or Splunk:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
||||||
|
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||||
|
|
||||||
|
{"level":"warning","msg":"The group's number increased tremendously!",
|
||||||
|
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
||||||
|
|
||||||
|
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
||||||
|
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
||||||
|
|
||||||
|
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
||||||
|
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
||||||
|
|
||||||
|
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
||||||
|
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||||
|
```
|
||||||
|
|
||||||
|
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
||||||
|
attached, the output is compatible with the
|
||||||
|
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
||||||
|
|
||||||
|
```text
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
||||||
|
exit status 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
The simplest way to use Logrus is simply the package-level exported logger:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
}).Info("A walrus appears")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that it's completely api-compatible with the stdlib logger, so you can
|
||||||
|
replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
|
||||||
|
and you'll now have the flexibility of Logrus. You can customize it all you
|
||||||
|
want:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Log as JSON instead of the default ASCII formatter.
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
|
||||||
|
// Output to stderr instead of stdout, could also be a file.
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
// Only log the warning severity or above.
|
||||||
|
log.SetLevel(log.WarnLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 122,
|
||||||
|
}).Warn("The group's number increased tremendously!")
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 100,
|
||||||
|
}).Fatal("The ice breaks!")
|
||||||
|
|
||||||
|
// A common pattern is to re-use fields between logging statements by re-using
|
||||||
|
// the logrus.Entry returned from WithFields()
|
||||||
|
contextLogger := log.WithFields(log.Fields{
|
||||||
|
"common": "this is a common field",
|
||||||
|
"other": "I also should be logged always",
|
||||||
|
})
|
||||||
|
|
||||||
|
contextLogger.Info("I'll be logged with common and other field")
|
||||||
|
contextLogger.Info("Me too")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more advanced usage such as logging to multiple locations from the same
|
||||||
|
application, you can also create an instance of the `logrus` Logger:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new instance of the logger. You can have any number of instances.
|
||||||
|
var log = logrus.New()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// The API for setting attributes is a little different than the package level
|
||||||
|
// exported logger. See Godoc.
|
||||||
|
log.Out = os.Stderr
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
Logrus encourages careful, structured logging though logging fields instead of
|
||||||
|
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
||||||
|
to send event %s to topic %s with key %d")`, you should log the much more
|
||||||
|
discoverable:
|
||||||
|
|
||||||
|
```go
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"event": event,
|
||||||
|
"topic": topic,
|
||||||
|
"key": key,
|
||||||
|
}).Fatal("Failed to send event")
|
||||||
|
```
|
||||||
|
|
||||||
|
We've found this API forces you to think about logging in a way that produces
|
||||||
|
much more useful logging messages. We've been in countless situations where just
|
||||||
|
a single added field to a log statement that was already there would've saved us
|
||||||
|
hours. The `WithFields` call is optional.
|
||||||
|
|
||||||
|
In general, with Logrus using any of the `printf`-family functions should be
|
||||||
|
seen as a hint you should add a field, however, you can still use the
|
||||||
|
`printf`-family functions with Logrus.
|
||||||
|
|
||||||
|
#### Hooks
|
||||||
|
|
||||||
|
You can add hooks for logging levels. For example to send errors to an exception
|
||||||
|
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
||||||
|
multiple places simultaneously, e.g. syslog.
|
||||||
|
|
||||||
|
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
||||||
|
`init`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
|
||||||
|
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
||||||
|
"log/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||||
|
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||||
|
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||||
|
|
||||||
|
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to connect to local syslog daemon")
|
||||||
|
} else {
|
||||||
|
log.AddHook(hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
||||||
|
|
||||||
|
| Hook | Description |
|
||||||
|
| ----- | ----------- |
|
||||||
|
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
|
||||||
|
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
|
||||||
|
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
|
||||||
|
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
|
||||||
|
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
||||||
|
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
|
||||||
|
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
|
||||||
|
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
|
||||||
|
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
|
||||||
|
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
|
||||||
|
| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
|
||||||
|
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
|
||||||
|
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
|
||||||
|
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
|
||||||
|
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
|
||||||
|
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
|
||||||
|
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
|
||||||
|
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
|
||||||
|
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
|
||||||
|
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
|
||||||
|
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
|
||||||
|
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
|
||||||
|
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
|
||||||
|
| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
|
||||||
|
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
|
||||||
|
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
|
||||||
|
|
||||||
|
|
||||||
|
#### Level logging
|
||||||
|
|
||||||
|
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
||||||
|
|
||||||
|
```go
|
||||||
|
log.Debug("Useful debugging information.")
|
||||||
|
log.Info("Something noteworthy happened!")
|
||||||
|
log.Warn("You should probably take a look at this.")
|
||||||
|
log.Error("Something failed but I'm not quitting.")
|
||||||
|
// Calls os.Exit(1) after logging
|
||||||
|
log.Fatal("Bye.")
|
||||||
|
// Calls panic() after logging
|
||||||
|
log.Panic("I'm bailing.")
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the logging level on a `Logger`, then it will only log entries with
|
||||||
|
that severity or anything above it:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
||||||
|
log.SetLevel(log.InfoLevel)
|
||||||
|
```
|
||||||
|
|
||||||
|
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
||||||
|
environment if your application has that.
|
||||||
|
|
||||||
|
#### Entries
|
||||||
|
|
||||||
|
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||||
|
automatically added to all logging events:
|
||||||
|
|
||||||
|
1. `time`. The timestamp when the entry was created.
|
||||||
|
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
||||||
|
the `AddFields` call. E.g. `Failed to send event.`
|
||||||
|
3. `level`. The logging level. E.g. `info`.
|
||||||
|
|
||||||
|
#### Environments
|
||||||
|
|
||||||
|
Logrus has no notion of environment.
|
||||||
|
|
||||||
|
If you wish for hooks and formatters to only be used in specific environments,
|
||||||
|
you should handle that yourself. For example, if your application has a global
|
||||||
|
variable `Environment`, which is a string representation of the environment you
|
||||||
|
could do:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// do something here to set environment depending on an environment variable
|
||||||
|
// or command-line flag
|
||||||
|
if Environment == "production" {
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
} else {
|
||||||
|
// The TextFormatter is default, you don't actually have to do this.
|
||||||
|
log.SetFormatter(&log.TextFormatter{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration is how `logrus` was intended to be used, but JSON in
|
||||||
|
production is mostly only useful if you do log aggregation with tools like
|
||||||
|
Splunk or Logstash.
|
||||||
|
|
||||||
|
#### Formatters
|
||||||
|
|
||||||
|
The built-in logging formatters are:
|
||||||
|
|
||||||
|
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
||||||
|
without colors.
|
||||||
|
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
||||||
|
field to `true`. To force no colored output even if there is a TTY set the
|
||||||
|
`DisableColors` field to `true`
|
||||||
|
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||||
|
* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events.
|
||||||
|
|
||||||
|
```go
|
||||||
|
logrus.SetFormatter(&logstash.LogstashFormatter{Type: "application_name"})
|
||||||
|
```
|
||||||
|
|
||||||
|
Third party logging formatters:
|
||||||
|
|
||||||
|
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||||
|
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||||
|
|
||||||
|
You can define your formatter by implementing the `Formatter` interface,
|
||||||
|
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||||
|
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
||||||
|
default ones (see Entries section above):
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyJSONFormatter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetFormatter(new(MyJSONFormatter))
|
||||||
|
|
||||||
|
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
// Note this doesn't include Time, Level and Message which are available on
|
||||||
|
// the Entry. Consult `godoc` on information about those fields or read the
|
||||||
|
// source of the official loggers.
|
||||||
|
serialized, err := json.Marshal(entry.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||||
|
}
|
||||||
|
return append(serialized, '\n'), nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Logger as an `io.Writer`
|
||||||
|
|
||||||
|
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
||||||
|
|
||||||
|
```go
|
||||||
|
w := logger.Writer()
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
srv := http.Server{
|
||||||
|
// create a stdlib log.Logger that writes to
|
||||||
|
// logrus.Logger.
|
||||||
|
ErrorLog: log.New(w, "", 0),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each line written to that writer will be printed the usual way, using formatters
|
||||||
|
and hooks. The level for those entries is `info`.
|
||||||
|
|
||||||
|
#### Rotation
|
||||||
|
|
||||||
|
Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||||
|
external program (like `logrotate(8)`) that can compress and delete old log
|
||||||
|
entries. It should not be a feature of the application-level logger.
|
||||||
|
|
||||||
|
#### Tools
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
| ---- | ----------- |
|
||||||
|
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|
||||||
|
|
||||||
|
#### Testing
|
||||||
|
|
||||||
|
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||||
|
|
||||||
|
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
|
||||||
|
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger, hook := NewNullLogger()
|
||||||
|
logger.Error("Hello error")
|
||||||
|
|
||||||
|
assert.Equal(1, len(hook.Entries))
|
||||||
|
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
||||||
|
assert.Equal("Hello error", hook.LastEntry().Message)
|
||||||
|
|
||||||
|
hook.Reset()
|
||||||
|
assert.Nil(hook.LastEntry())
|
||||||
|
```
|
26
vendor/github.com/Sirupsen/logrus/doc.go
generated
vendored
Normal file
26
vendor/github.com/Sirupsen/logrus/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
|
||||||
|
|
||||||
|
|
||||||
|
The simplest way to use Logrus is simply the package-level exported logger:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"number": 1,
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A walrus appears")
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
|
||||||
|
|
||||||
|
For a full guide visit https://github.com/Sirupsen/logrus
|
||||||
|
*/
|
||||||
|
package logrus
|
264
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
Normal file
264
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines the key when adding errors using WithError.
|
||||||
|
var ErrorKey = "error"
|
||||||
|
|
||||||
|
// An entry is the final or intermediate Logrus logging entry. It contains all
|
||||||
|
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
||||||
|
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
|
||||||
|
// passed around as much as you wish to avoid field duplication.
|
||||||
|
type Entry struct {
|
||||||
|
Logger *Logger
|
||||||
|
|
||||||
|
// Contains all the fields set by the user.
|
||||||
|
Data Fields
|
||||||
|
|
||||||
|
// Time at which the log entry was created
|
||||||
|
Time time.Time
|
||||||
|
|
||||||
|
// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
Level Level
|
||||||
|
|
||||||
|
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEntry(logger *Logger) *Entry {
|
||||||
|
return &Entry{
|
||||||
|
Logger: logger,
|
||||||
|
// Default is three fields, give a little extra room
|
||||||
|
Data: make(Fields, 5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a reader for the entry, which is a proxy to the formatter.
|
||||||
|
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
|
return bytes.NewBuffer(serialized), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the string representation from the reader and ultimately the
|
||||||
|
// formatter.
|
||||||
|
func (entry *Entry) String() (string, error) {
|
||||||
|
reader, err := entry.Reader()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||||
|
func (entry *Entry) WithError(err error) *Entry {
|
||||||
|
return entry.WithField(ErrorKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a single field to the Entry.
|
||||||
|
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||||
|
return entry.WithFields(Fields{key: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a map of fields to the Entry.
|
||||||
|
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||||
|
data := make(Fields, len(entry.Data)+len(fields))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range fields {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: data}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is not declared with a pointer value because otherwise
|
||||||
|
// race conditions will occur when using multiple goroutines
|
||||||
|
func (entry Entry) log(level Level, msg string) {
|
||||||
|
entry.Time = time.Now()
|
||||||
|
entry.Level = level
|
||||||
|
entry.Message = msg
|
||||||
|
|
||||||
|
if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
reader, err := entry.Reader()
|
||||||
|
if err != nil {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
defer entry.Logger.mu.Unlock()
|
||||||
|
|
||||||
|
_, err = io.Copy(entry.Logger.Out, reader)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// To avoid Entry#log() returning a value that only would make sense for
|
||||||
|
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||||
|
// directly here.
|
||||||
|
if level <= PanicLevel {
|
||||||
|
panic(&entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Debug(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= DebugLevel {
|
||||||
|
entry.log(DebugLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Print(args ...interface{}) {
|
||||||
|
entry.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Info(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= InfoLevel {
|
||||||
|
entry.log(InfoLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warn(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= WarnLevel {
|
||||||
|
entry.log(WarnLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warning(args ...interface{}) {
|
||||||
|
entry.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Error(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= ErrorLevel {
|
||||||
|
entry.log(ErrorLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatal(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= FatalLevel {
|
||||||
|
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panic(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= PanicLevel {
|
||||||
|
entry.log(PanicLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
panic(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry Printf family functions
|
||||||
|
|
||||||
|
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= DebugLevel {
|
||||||
|
entry.Debug(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Infof(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= InfoLevel {
|
||||||
|
entry.Info(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Printf(format string, args ...interface{}) {
|
||||||
|
entry.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= WarnLevel {
|
||||||
|
entry.Warn(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= ErrorLevel {
|
||||||
|
entry.Error(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= FatalLevel {
|
||||||
|
entry.Fatal(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= PanicLevel {
|
||||||
|
entry.Panic(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry Println family functions
|
||||||
|
|
||||||
|
func (entry *Entry) Debugln(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= DebugLevel {
|
||||||
|
entry.Debug(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Infoln(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= InfoLevel {
|
||||||
|
entry.Info(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Println(args ...interface{}) {
|
||||||
|
entry.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warnln(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= WarnLevel {
|
||||||
|
entry.Warn(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warningln(args ...interface{}) {
|
||||||
|
entry.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Errorln(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= ErrorLevel {
|
||||||
|
entry.Error(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatalln(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= FatalLevel {
|
||||||
|
entry.Fatal(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panicln(args ...interface{}) {
|
||||||
|
if entry.Logger.Level >= PanicLevel {
|
||||||
|
entry.Panic(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
||||||
|
// fmt.Sprintln where spaces are always added between operands, regardless of
|
||||||
|
// their type. Instead of vendoring the Sprintln implementation to spare a
|
||||||
|
// string allocation, we do the simplest thing.
|
||||||
|
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
||||||
|
msg := fmt.Sprintln(args...)
|
||||||
|
return msg[:len(msg)-1]
|
||||||
|
}
|
193
vendor/github.com/Sirupsen/logrus/exported.go
generated
vendored
Normal file
193
vendor/github.com/Sirupsen/logrus/exported.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// std is the name of the standard logger in stdlib `log`
|
||||||
|
std = New()
|
||||||
|
)
|
||||||
|
|
||||||
|
func StandardLogger() *Logger {
|
||||||
|
return std
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets the standard logger output.
|
||||||
|
func SetOutput(out io.Writer) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Out = out
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFormatter sets the standard logger formatter.
|
||||||
|
func SetFormatter(formatter Formatter) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Formatter = formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the standard logger level.
|
||||||
|
func SetLevel(level Level) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Level = level
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel returns the standard logger level.
|
||||||
|
func GetLevel() Level {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
return std.Level
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHook adds a hook to the standard logger hooks.
|
||||||
|
func AddHook(hook Hook) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Hooks.Add(hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
||||||
|
func WithError(err error) *Entry {
|
||||||
|
return std.WithField(ErrorKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithField creates an entry from the standard logger and adds a field to
|
||||||
|
// it. If you want multiple fields, use `WithFields`.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithField(key string, value interface{}) *Entry {
|
||||||
|
return std.WithField(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFields creates an entry from the standard logger and adds multiple
|
||||||
|
// fields to it. This is simply a helper for `WithField`, invoking it
|
||||||
|
// once for each field.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithFields(fields Fields) *Entry {
|
||||||
|
return std.WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug logs a message at level Debug on the standard logger.
|
||||||
|
func Debug(args ...interface{}) {
|
||||||
|
std.Debug(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print logs a message at level Info on the standard logger.
|
||||||
|
func Print(args ...interface{}) {
|
||||||
|
std.Print(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs a message at level Info on the standard logger.
|
||||||
|
func Info(args ...interface{}) {
|
||||||
|
std.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn logs a message at level Warn on the standard logger.
|
||||||
|
func Warn(args ...interface{}) {
|
||||||
|
std.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning logs a message at level Warn on the standard logger.
|
||||||
|
func Warning(args ...interface{}) {
|
||||||
|
std.Warning(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs a message at level Error on the standard logger.
|
||||||
|
func Error(args ...interface{}) {
|
||||||
|
std.Error(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic logs a message at level Panic on the standard logger.
|
||||||
|
func Panic(args ...interface{}) {
|
||||||
|
std.Panic(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal logs a message at level Fatal on the standard logger.
|
||||||
|
func Fatal(args ...interface{}) {
|
||||||
|
std.Fatal(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a message at level Debug on the standard logger.
|
||||||
|
func Debugf(format string, args ...interface{}) {
|
||||||
|
std.Debugf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf logs a message at level Info on the standard logger.
|
||||||
|
func Printf(format string, args ...interface{}) {
|
||||||
|
std.Printf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs a message at level Info on the standard logger.
|
||||||
|
func Infof(format string, args ...interface{}) {
|
||||||
|
std.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs a message at level Warn on the standard logger.
|
||||||
|
func Warnf(format string, args ...interface{}) {
|
||||||
|
std.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningf logs a message at level Warn on the standard logger.
|
||||||
|
func Warningf(format string, args ...interface{}) {
|
||||||
|
std.Warningf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs a message at level Error on the standard logger.
|
||||||
|
func Errorf(format string, args ...interface{}) {
|
||||||
|
std.Errorf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicf logs a message at level Panic on the standard logger.
|
||||||
|
func Panicf(format string, args ...interface{}) {
|
||||||
|
std.Panicf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf logs a message at level Fatal on the standard logger.
|
||||||
|
func Fatalf(format string, args ...interface{}) {
|
||||||
|
std.Fatalf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugln logs a message at level Debug on the standard logger.
|
||||||
|
func Debugln(args ...interface{}) {
|
||||||
|
std.Debugln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println logs a message at level Info on the standard logger.
|
||||||
|
func Println(args ...interface{}) {
|
||||||
|
std.Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infoln logs a message at level Info on the standard logger.
|
||||||
|
func Infoln(args ...interface{}) {
|
||||||
|
std.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnln logs a message at level Warn on the standard logger.
|
||||||
|
func Warnln(args ...interface{}) {
|
||||||
|
std.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningln logs a message at level Warn on the standard logger.
|
||||||
|
func Warningln(args ...interface{}) {
|
||||||
|
std.Warningln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorln logs a message at level Error on the standard logger.
|
||||||
|
func Errorln(args ...interface{}) {
|
||||||
|
std.Errorln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicln logs a message at level Panic on the standard logger.
|
||||||
|
func Panicln(args ...interface{}) {
|
||||||
|
std.Panicln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalln logs a message at level Fatal on the standard logger.
|
||||||
|
func Fatalln(args ...interface{}) {
|
||||||
|
std.Fatalln(args...)
|
||||||
|
}
|
48
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
Normal file
48
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const DefaultTimestampFormat = time.RFC3339
|
||||||
|
|
||||||
|
// The Formatter interface is used to implement a custom Formatter. It takes an
|
||||||
|
// `Entry`. It exposes all the fields, including the default ones:
|
||||||
|
//
|
||||||
|
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
||||||
|
// * `entry.Data["time"]`. The timestamp.
|
||||||
|
// * `entry.Data["level"]. The level the entry was logged at.
|
||||||
|
//
|
||||||
|
// Any additional fields added with `WithField` or `WithFields` are also in
|
||||||
|
// `entry.Data`. Format is expected to return an array of bytes which are then
|
||||||
|
// logged to `logger.Out`.
|
||||||
|
type Formatter interface {
|
||||||
|
Format(*Entry) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is to not silently overwrite `time`, `msg` and `level` fields when
|
||||||
|
// dumping it. If this code wasn't there doing:
|
||||||
|
//
|
||||||
|
// logrus.WithField("level", 1).Info("hello")
|
||||||
|
//
|
||||||
|
// Would just silently drop the user provided level. Instead with this code
|
||||||
|
// it'll logged as:
|
||||||
|
//
|
||||||
|
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
||||||
|
//
|
||||||
|
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||||
|
// avoid code duplication between the two default formatters.
|
||||||
|
func prefixFieldClashes(data Fields) {
|
||||||
|
_, ok := data["time"]
|
||||||
|
if ok {
|
||||||
|
data["fields.time"] = data["time"]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok = data["msg"]
|
||||||
|
if ok {
|
||||||
|
data["fields.msg"] = data["msg"]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok = data["level"]
|
||||||
|
if ok {
|
||||||
|
data["fields.level"] = data["level"]
|
||||||
|
}
|
||||||
|
}
|
34
vendor/github.com/Sirupsen/logrus/hooks.go
generated
vendored
Normal file
34
vendor/github.com/Sirupsen/logrus/hooks.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
// A hook to be fired when logging on the logging levels returned from
|
||||||
|
// `Levels()` on your implementation of the interface. Note that this is not
|
||||||
|
// fired in a goroutine or a channel with workers, you should handle such
|
||||||
|
// functionality yourself if your call is non-blocking and you don't wish for
|
||||||
|
// the logging calls for levels returned from `Levels()` to block.
|
||||||
|
type Hook interface {
|
||||||
|
Levels() []Level
|
||||||
|
Fire(*Entry) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal type for storing the hooks on a logger instance.
|
||||||
|
type LevelHooks map[Level][]Hook
|
||||||
|
|
||||||
|
// Add a hook to an instance of logger. This is called with
|
||||||
|
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
||||||
|
func (hooks LevelHooks) Add(hook Hook) {
|
||||||
|
for _, level := range hook.Levels() {
|
||||||
|
hooks[level] = append(hooks[level], hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
||||||
|
// appropriate hooks for a log entry.
|
||||||
|
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
||||||
|
for _, hook := range hooks[level] {
|
||||||
|
if err := hook.Fire(entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
41
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
41
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSONFormatter struct {
|
||||||
|
// TimestampFormat sets the format used for marshaling timestamps.
|
||||||
|
TimestampFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
data := make(Fields, len(entry.Data)+3)
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case error:
|
||||||
|
// Otherwise errors are ignored by `encoding/json`
|
||||||
|
// https://github.com/Sirupsen/logrus/issues/137
|
||||||
|
data[k] = v.Error()
|
||||||
|
default:
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefixFieldClashes(data)
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = DefaultTimestampFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
data["time"] = entry.Time.Format(timestampFormat)
|
||||||
|
data["msg"] = entry.Message
|
||||||
|
data["level"] = entry.Level.String()
|
||||||
|
|
||||||
|
serialized, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||||
|
}
|
||||||
|
return append(serialized, '\n'), nil
|
||||||
|
}
|
212
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
Normal file
212
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||||
|
// file, or leave it default which is `os.Stderr`. You can also set this to
|
||||||
|
// something more adventorous, such as logging to Kafka.
|
||||||
|
Out io.Writer
|
||||||
|
// Hooks for the logger instance. These allow firing events based on logging
|
||||||
|
// levels and log entries. For example, to send errors to an error tracking
|
||||||
|
// service, log to StatsD or dump the core on fatal errors.
|
||||||
|
Hooks LevelHooks
|
||||||
|
// All log entries pass through the formatter before logged to Out. The
|
||||||
|
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
||||||
|
// TextFormatter is the default. In development (when a TTY is attached) it
|
||||||
|
// logs with colors, but to a file it wouldn't. You can easily implement your
|
||||||
|
// own that implements the `Formatter` interface, see the `README` or included
|
||||||
|
// formatters for examples.
|
||||||
|
Formatter Formatter
|
||||||
|
// The logging level the logger should log at. This is typically (and defaults
|
||||||
|
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||||
|
// logged. `logrus.Debug` is useful in
|
||||||
|
Level Level
|
||||||
|
// Used to sync writing to the log.
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||||
|
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
||||||
|
// instantiate your own:
|
||||||
|
//
|
||||||
|
// var log = &Logger{
|
||||||
|
// Out: os.Stderr,
|
||||||
|
// Formatter: new(JSONFormatter),
|
||||||
|
// Hooks: make(LevelHooks),
|
||||||
|
// Level: logrus.DebugLevel,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// It's recommended to make this a global instance called `log`.
|
||||||
|
func New() *Logger {
|
||||||
|
return &Logger{
|
||||||
|
Out: os.Stderr,
|
||||||
|
Formatter: new(TextFormatter),
|
||||||
|
Hooks: make(LevelHooks),
|
||||||
|
Level: InfoLevel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||||
|
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||||
|
// If you want multiple fields, use `WithFields`.
|
||||||
|
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||||
|
return NewEntry(logger).WithField(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||||
|
// each `Field`.
|
||||||
|
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||||
|
return NewEntry(logger).WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an error as single field to the log entry. All it does is call
|
||||||
|
// `WithError` for the given `error`.
|
||||||
|
func (logger *Logger) WithError(err error) *Entry {
|
||||||
|
return NewEntry(logger).WithError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= DebugLevel {
|
||||||
|
NewEntry(logger).Debugf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= InfoLevel {
|
||||||
|
NewEntry(logger).Infof(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||||
|
NewEntry(logger).Printf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warnf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warnf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= ErrorLevel {
|
||||||
|
NewEntry(logger).Errorf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= FatalLevel {
|
||||||
|
NewEntry(logger).Fatalf(format, args...)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= PanicLevel {
|
||||||
|
NewEntry(logger).Panicf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debug(args ...interface{}) {
|
||||||
|
if logger.Level >= DebugLevel {
|
||||||
|
NewEntry(logger).Debug(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Info(args ...interface{}) {
|
||||||
|
if logger.Level >= InfoLevel {
|
||||||
|
NewEntry(logger).Info(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Print(args ...interface{}) {
|
||||||
|
NewEntry(logger).Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warn(args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warn(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warning(args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warn(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Error(args ...interface{}) {
|
||||||
|
if logger.Level >= ErrorLevel {
|
||||||
|
NewEntry(logger).Error(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatal(args ...interface{}) {
|
||||||
|
if logger.Level >= FatalLevel {
|
||||||
|
NewEntry(logger).Fatal(args...)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panic(args ...interface{}) {
|
||||||
|
if logger.Level >= PanicLevel {
|
||||||
|
NewEntry(logger).Panic(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugln(args ...interface{}) {
|
||||||
|
if logger.Level >= DebugLevel {
|
||||||
|
NewEntry(logger).Debugln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infoln(args ...interface{}) {
|
||||||
|
if logger.Level >= InfoLevel {
|
||||||
|
NewEntry(logger).Infoln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Println(args ...interface{}) {
|
||||||
|
NewEntry(logger).Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnln(args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warnln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningln(args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warnln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorln(args ...interface{}) {
|
||||||
|
if logger.Level >= ErrorLevel {
|
||||||
|
NewEntry(logger).Errorln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||||
|
if logger.Level >= FatalLevel {
|
||||||
|
NewEntry(logger).Fatalln(args...)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicln(args ...interface{}) {
|
||||||
|
if logger.Level >= PanicLevel {
|
||||||
|
NewEntry(logger).Panicln(args...)
|
||||||
|
}
|
||||||
|
}
|
143
vendor/github.com/Sirupsen/logrus/logrus.go
generated
vendored
Normal file
143
vendor/github.com/Sirupsen/logrus/logrus.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fields type, used to pass to `WithFields`.
|
||||||
|
type Fields map[string]interface{}
|
||||||
|
|
||||||
|
// Level type
|
||||||
|
type Level uint8
|
||||||
|
|
||||||
|
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
||||||
|
func (level Level) String() string {
|
||||||
|
switch level {
|
||||||
|
case DebugLevel:
|
||||||
|
return "debug"
|
||||||
|
case InfoLevel:
|
||||||
|
return "info"
|
||||||
|
case WarnLevel:
|
||||||
|
return "warning"
|
||||||
|
case ErrorLevel:
|
||||||
|
return "error"
|
||||||
|
case FatalLevel:
|
||||||
|
return "fatal"
|
||||||
|
case PanicLevel:
|
||||||
|
return "panic"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||||
|
func ParseLevel(lvl string) (Level, error) {
|
||||||
|
switch strings.ToLower(lvl) {
|
||||||
|
case "panic":
|
||||||
|
return PanicLevel, nil
|
||||||
|
case "fatal":
|
||||||
|
return FatalLevel, nil
|
||||||
|
case "error":
|
||||||
|
return ErrorLevel, nil
|
||||||
|
case "warn", "warning":
|
||||||
|
return WarnLevel, nil
|
||||||
|
case "info":
|
||||||
|
return InfoLevel, nil
|
||||||
|
case "debug":
|
||||||
|
return DebugLevel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var l Level
|
||||||
|
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A constant exposing all logging levels
|
||||||
|
var AllLevels = []Level{
|
||||||
|
PanicLevel,
|
||||||
|
FatalLevel,
|
||||||
|
ErrorLevel,
|
||||||
|
WarnLevel,
|
||||||
|
InfoLevel,
|
||||||
|
DebugLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the different logging levels. You can set the logging level to log
|
||||||
|
// on your instance of logger, obtained with `logrus.New()`.
|
||||||
|
const (
|
||||||
|
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
||||||
|
// message passed to Debug, Info, ...
|
||||||
|
PanicLevel Level = iota
|
||||||
|
// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
|
||||||
|
// logging level is set to Panic.
|
||||||
|
FatalLevel
|
||||||
|
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
||||||
|
// Commonly used for hooks to send errors to an error tracking service.
|
||||||
|
ErrorLevel
|
||||||
|
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||||
|
WarnLevel
|
||||||
|
// InfoLevel level. General operational entries about what's going on inside the
|
||||||
|
// application.
|
||||||
|
InfoLevel
|
||||||
|
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
||||||
|
DebugLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
// Won't compile if StdLogger can't be realized by a log.Logger
|
||||||
|
var (
|
||||||
|
_ StdLogger = &log.Logger{}
|
||||||
|
_ StdLogger = &Entry{}
|
||||||
|
_ StdLogger = &Logger{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// StdLogger is what your logrus-enabled library should take, that way
|
||||||
|
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
||||||
|
// interface, this is the closest we get, unfortunately.
|
||||||
|
type StdLogger interface {
|
||||||
|
Print(...interface{})
|
||||||
|
Printf(string, ...interface{})
|
||||||
|
Println(...interface{})
|
||||||
|
|
||||||
|
Fatal(...interface{})
|
||||||
|
Fatalf(string, ...interface{})
|
||||||
|
Fatalln(...interface{})
|
||||||
|
|
||||||
|
Panic(...interface{})
|
||||||
|
Panicf(string, ...interface{})
|
||||||
|
Panicln(...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// The FieldLogger interface generalizes the Entry and Logger types
|
||||||
|
type FieldLogger interface {
|
||||||
|
WithField(key string, value interface{}) *Entry
|
||||||
|
WithFields(fields Fields) *Entry
|
||||||
|
WithError(err error) *Entry
|
||||||
|
|
||||||
|
Debugf(format string, args ...interface{})
|
||||||
|
Infof(format string, args ...interface{})
|
||||||
|
Printf(format string, args ...interface{})
|
||||||
|
Warnf(format string, args ...interface{})
|
||||||
|
Warningf(format string, args ...interface{})
|
||||||
|
Errorf(format string, args ...interface{})
|
||||||
|
Fatalf(format string, args ...interface{})
|
||||||
|
Panicf(format string, args ...interface{})
|
||||||
|
|
||||||
|
Debug(args ...interface{})
|
||||||
|
Info(args ...interface{})
|
||||||
|
Print(args ...interface{})
|
||||||
|
Warn(args ...interface{})
|
||||||
|
Warning(args ...interface{})
|
||||||
|
Error(args ...interface{})
|
||||||
|
Fatal(args ...interface{})
|
||||||
|
Panic(args ...interface{})
|
||||||
|
|
||||||
|
Debugln(args ...interface{})
|
||||||
|
Infoln(args ...interface{})
|
||||||
|
Println(args ...interface{})
|
||||||
|
Warnln(args ...interface{})
|
||||||
|
Warningln(args ...interface{})
|
||||||
|
Errorln(args ...interface{})
|
||||||
|
Fatalln(args ...interface{})
|
||||||
|
Panicln(args ...interface{})
|
||||||
|
}
|
9
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
Normal file
9
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// +build darwin freebsd openbsd netbsd dragonfly
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TIOCGETA
|
||||||
|
|
||||||
|
type Termios syscall.Termios
|
12
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
Normal file
12
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// Based on ssh/terminal:
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TCGETS
|
||||||
|
|
||||||
|
type Termios syscall.Termios
|
21
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
Normal file
21
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Based on ssh/terminal:
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||||
|
func IsTerminal() bool {
|
||||||
|
fd := syscall.Stderr
|
||||||
|
var termios Termios
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
}
|
15
vendor/github.com/Sirupsen/logrus/terminal_solaris.go
generated
vendored
Normal file
15
vendor/github.com/Sirupsen/logrus/terminal_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// +build solaris
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal() bool {
|
||||||
|
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
|
||||||
|
return err == nil
|
||||||
|
}
|
27
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
Normal file
27
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Based on ssh/terminal:
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
|
||||||
|
var (
|
||||||
|
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||||
|
func IsTerminal() bool {
|
||||||
|
fd := syscall.Stderr
|
||||||
|
var st uint32
|
||||||
|
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||||
|
return r != 0 && e == 0
|
||||||
|
}
|
161
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
161
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nocolor = 0
|
||||||
|
red = 31
|
||||||
|
green = 32
|
||||||
|
yellow = 33
|
||||||
|
blue = 34
|
||||||
|
gray = 37
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseTimestamp time.Time
|
||||||
|
isTerminal bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
baseTimestamp = time.Now()
|
||||||
|
isTerminal = IsTerminal()
|
||||||
|
}
|
||||||
|
|
||||||
|
func miniTS() int {
|
||||||
|
return int(time.Since(baseTimestamp) / time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TextFormatter struct {
|
||||||
|
// Set to true to bypass checking for a TTY before outputting colors.
|
||||||
|
ForceColors bool
|
||||||
|
|
||||||
|
// Force disabling colors.
|
||||||
|
DisableColors bool
|
||||||
|
|
||||||
|
// Disable timestamp logging. useful when output is redirected to logging
|
||||||
|
// system that already adds timestamps.
|
||||||
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// Enable logging the full timestamp when a TTY is attached instead of just
|
||||||
|
// the time passed since beginning of execution.
|
||||||
|
FullTimestamp bool
|
||||||
|
|
||||||
|
// TimestampFormat to use for display when a full timestamp is printed
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// The fields are sorted by default for a consistent output. For applications
|
||||||
|
// that log extremely frequently and don't use the JSON formatter this may not
|
||||||
|
// be desired.
|
||||||
|
DisableSorting bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
var keys []string = make([]string, 0, len(entry.Data))
|
||||||
|
for k := range entry.Data {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.DisableSorting {
|
||||||
|
sort.Strings(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
|
||||||
|
prefixFieldClashes(entry.Data)
|
||||||
|
|
||||||
|
isColorTerminal := isTerminal && (runtime.GOOS != "windows")
|
||||||
|
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = DefaultTimestampFormat
|
||||||
|
}
|
||||||
|
if isColored {
|
||||||
|
f.printColored(b, entry, keys, timestampFormat)
|
||||||
|
} else {
|
||||||
|
if !f.DisableTimestamp {
|
||||||
|
f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
|
||||||
|
}
|
||||||
|
f.appendKeyValue(b, "level", entry.Level.String())
|
||||||
|
if entry.Message != "" {
|
||||||
|
f.appendKeyValue(b, "msg", entry.Message)
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
f.appendKeyValue(b, key, entry.Data[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte('\n')
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
|
||||||
|
var levelColor int
|
||||||
|
switch entry.Level {
|
||||||
|
case DebugLevel:
|
||||||
|
levelColor = gray
|
||||||
|
case WarnLevel:
|
||||||
|
levelColor = yellow
|
||||||
|
case ErrorLevel, FatalLevel, PanicLevel:
|
||||||
|
levelColor = red
|
||||||
|
default:
|
||||||
|
levelColor = blue
|
||||||
|
}
|
||||||
|
|
||||||
|
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||||
|
|
||||||
|
if !f.FullTimestamp {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
v := entry.Data[k]
|
||||||
|
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func needsQuoting(text string) bool {
|
||||||
|
for _, ch := range text {
|
||||||
|
if !((ch >= 'a' && ch <= 'z') ||
|
||||||
|
(ch >= 'A' && ch <= 'Z') ||
|
||||||
|
(ch >= '0' && ch <= '9') ||
|
||||||
|
ch == '-' || ch == '.') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||||
|
|
||||||
|
b.WriteString(key)
|
||||||
|
b.WriteByte('=')
|
||||||
|
|
||||||
|
switch value := value.(type) {
|
||||||
|
case string:
|
||||||
|
if needsQuoting(value) {
|
||||||
|
b.WriteString(value)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "%q", value)
|
||||||
|
}
|
||||||
|
case error:
|
||||||
|
errmsg := value.Error()
|
||||||
|
if needsQuoting(errmsg) {
|
||||||
|
b.WriteString(errmsg)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "%q", value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Fprint(b, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte(' ')
|
||||||
|
}
|
31
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
Normal file
31
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (logger *Logger) Writer() *io.PipeWriter {
|
||||||
|
reader, writer := io.Pipe()
|
||||||
|
|
||||||
|
go logger.writerScanner(reader)
|
||||||
|
runtime.SetFinalizer(writer, writerFinalizer)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
for scanner.Scan() {
|
||||||
|
logger.Print(scanner.Text())
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.Errorf("Error while reading from Writer: %s", err)
|
||||||
|
}
|
||||||
|
reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writerFinalizer(writer *io.PipeWriter) {
|
||||||
|
writer.Close()
|
||||||
|
}
|
201
vendor/github.com/containernetworking/cni/LICENSE
generated
vendored
Normal file
201
vendor/github.com/containernetworking/cni/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
87
vendor/github.com/containernetworking/cni/libcni/api.go
generated
vendored
Normal file
87
vendor/github.com/containernetworking/cni/libcni/api.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright 2015 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package libcni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/invoke"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RuntimeConf struct {
|
||||||
|
ContainerID string
|
||||||
|
NetNS string
|
||||||
|
IfName string
|
||||||
|
Args [][2]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkConfig struct {
|
||||||
|
Network *types.NetConf
|
||||||
|
Bytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type CNI interface {
|
||||||
|
AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error)
|
||||||
|
DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CNIConfig struct {
|
||||||
|
Path []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNetwork executes the plugin with the ADD command
|
||||||
|
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) {
|
||||||
|
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelNetwork executes the plugin with the DEL command
|
||||||
|
func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
|
||||||
|
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionInfo reports which versions of the CNI spec are supported by
|
||||||
|
// the given plugin.
|
||||||
|
func (c *CNIConfig) GetVersionInfo(pluginType string) (version.PluginInfo, error) {
|
||||||
|
pluginPath, err := invoke.FindInPath(pluginType, c.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke.GetVersionInfo(pluginPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =====
|
||||||
|
func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
|
||||||
|
return &invoke.Args{
|
||||||
|
Command: action,
|
||||||
|
ContainerID: rt.ContainerID,
|
||||||
|
NetNS: rt.NetNS,
|
||||||
|
PluginArgs: rt.Args,
|
||||||
|
IfName: rt.IfName,
|
||||||
|
Path: strings.Join(c.Path, ":"),
|
||||||
|
}
|
||||||
|
}
|
110
vendor/github.com/containernetworking/cni/libcni/conf.go
generated
vendored
Normal file
110
vendor/github.com/containernetworking/cni/libcni/conf.go
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2015 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package libcni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
|
||||||
|
conf := &NetworkConfig{Bytes: bytes}
|
||||||
|
if err := json.Unmarshal(bytes, &conf.Network); err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration: %s", err)
|
||||||
|
}
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfFromFile(filename string) (*NetworkConfig, error) {
|
||||||
|
bytes, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading %s: %s", filename, err)
|
||||||
|
}
|
||||||
|
return ConfFromBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfFiles(dir string) ([]string, error) {
|
||||||
|
// In part, adapted from rkt/networking/podenv.go#listFiles
|
||||||
|
files, err := ioutil.ReadDir(dir)
|
||||||
|
switch {
|
||||||
|
case err == nil: // break
|
||||||
|
case os.IsNotExist(err):
|
||||||
|
return nil, nil
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
confFiles := []string{}
|
||||||
|
for _, f := range files {
|
||||||
|
if f.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if filepath.Ext(f.Name()) == ".conf" {
|
||||||
|
confFiles = append(confFiles, filepath.Join(dir, f.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return confFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConf(dir, name string) (*NetworkConfig, error) {
|
||||||
|
files, err := ConfFiles(dir)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return nil, err
|
||||||
|
case len(files) == 0:
|
||||||
|
return nil, fmt.Errorf("no net configurations found")
|
||||||
|
}
|
||||||
|
sort.Strings(files)
|
||||||
|
|
||||||
|
for _, confFile := range files {
|
||||||
|
conf, err := ConfFromFile(confFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if conf.Network.Name == name {
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(`no net configuration with name "%s" in %s`, name, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InjectConf(original *NetworkConfig, key string, newValue interface{}) (*NetworkConfig, error) {
|
||||||
|
config := make(map[string]interface{})
|
||||||
|
err := json.Unmarshal(original.Bytes, &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal existing network bytes: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == "" {
|
||||||
|
return nil, fmt.Errorf("key value can not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if newValue == nil {
|
||||||
|
return nil, fmt.Errorf("newValue must be specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
config[key] = newValue
|
||||||
|
|
||||||
|
newBytes, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConfFromBytes(newBytes)
|
||||||
|
}
|
76
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
Normal file
76
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright 2015 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CNIArgs interface {
|
||||||
|
// For use with os/exec; i.e., return nil to inherit the
|
||||||
|
// environment from this process
|
||||||
|
AsEnv() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type inherited struct{}
|
||||||
|
|
||||||
|
var inheritArgsFromEnv inherited
|
||||||
|
|
||||||
|
func (_ *inherited) AsEnv() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ArgsFromEnv() CNIArgs {
|
||||||
|
return &inheritArgsFromEnv
|
||||||
|
}
|
||||||
|
|
||||||
|
type Args struct {
|
||||||
|
Command string
|
||||||
|
ContainerID string
|
||||||
|
NetNS string
|
||||||
|
PluginArgs [][2]string
|
||||||
|
PluginArgsStr string
|
||||||
|
IfName string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args *Args) AsEnv() []string {
|
||||||
|
env := os.Environ()
|
||||||
|
pluginArgsStr := args.PluginArgsStr
|
||||||
|
if pluginArgsStr == "" {
|
||||||
|
pluginArgsStr = stringify(args.PluginArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
env = append(env,
|
||||||
|
"CNI_COMMAND="+args.Command,
|
||||||
|
"CNI_CONTAINERID="+args.ContainerID,
|
||||||
|
"CNI_NETNS="+args.NetNS,
|
||||||
|
"CNI_ARGS="+pluginArgsStr,
|
||||||
|
"CNI_IFNAME="+args.IfName,
|
||||||
|
"CNI_PATH="+args.Path)
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
// taken from rkt/networking/net_plugin.go
|
||||||
|
func stringify(pluginArgs [][2]string) string {
|
||||||
|
entries := make([]string, len(pluginArgs))
|
||||||
|
|
||||||
|
for i, kv := range pluginArgs {
|
||||||
|
entries[i] = strings.Join(kv[:], "=")
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(entries, ";")
|
||||||
|
}
|
53
vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
generated
vendored
Normal file
53
vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2016 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DelegateAdd(delegatePlugin string, netconf []byte) (*types.Result, error) {
|
||||||
|
if os.Getenv("CNI_COMMAND") != "ADD" {
|
||||||
|
return nil, fmt.Errorf("CNI_COMMAND is not ADD")
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := strings.Split(os.Getenv("CNI_PATH"), ":")
|
||||||
|
|
||||||
|
pluginPath, err := FindInPath(delegatePlugin, paths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv())
|
||||||
|
}
|
||||||
|
|
||||||
|
func DelegateDel(delegatePlugin string, netconf []byte) error {
|
||||||
|
if os.Getenv("CNI_COMMAND") != "DEL" {
|
||||||
|
return fmt.Errorf("CNI_COMMAND is not DEL")
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := strings.Split(os.Getenv("CNI_PATH"), ":")
|
||||||
|
|
||||||
|
pluginPath, err := FindInPath(delegatePlugin, paths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv())
|
||||||
|
}
|
89
vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
generated
vendored
Normal file
89
vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2015 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
|
||||||
|
return defaultPluginExec.WithResult(pluginPath, netconf, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
|
||||||
|
return defaultPluginExec.WithoutResult(pluginPath, netconf, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
|
||||||
|
return defaultPluginExec.GetVersionInfo(pluginPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultPluginExec = &PluginExec{
|
||||||
|
RawExec: &RawExec{Stderr: os.Stderr},
|
||||||
|
VersionDecoder: &version.PluginDecoder{},
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginExec struct {
|
||||||
|
RawExec interface {
|
||||||
|
ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error)
|
||||||
|
}
|
||||||
|
VersionDecoder interface {
|
||||||
|
Decode(jsonBytes []byte) (version.PluginInfo, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
|
||||||
|
stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := &types.Result{}
|
||||||
|
err = json.Unmarshal(stdoutBytes, res)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
|
||||||
|
_, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionInfo returns the version information available about the plugin.
|
||||||
|
// For recent-enough plugins, it uses the information returned by the VERSION
|
||||||
|
// command. For older plugins which do not recognize that command, it reports
|
||||||
|
// version 0.1.0
|
||||||
|
func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
|
||||||
|
args := &Args{
|
||||||
|
Command: "VERSION",
|
||||||
|
|
||||||
|
// set fake values required by plugins built against an older version of skel
|
||||||
|
NetNS: "dummy",
|
||||||
|
IfName: "dummy",
|
||||||
|
Path: "dummy",
|
||||||
|
}
|
||||||
|
stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, nil, args.AsEnv())
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "unknown CNI_COMMAND: VERSION" {
|
||||||
|
return version.PluginSupports("0.1.0"), nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.VersionDecoder.Decode(stdoutBytes)
|
||||||
|
}
|
47
vendor/github.com/containernetworking/cni/pkg/invoke/find.go
generated
vendored
Normal file
47
vendor/github.com/containernetworking/cni/pkg/invoke/find.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2015 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindInPath returns the full path of the plugin by searching in the provided path
|
||||||
|
func FindInPath(plugin string, paths []string) (string, error) {
|
||||||
|
if plugin == "" {
|
||||||
|
return "", fmt.Errorf("no plugin name provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return "", fmt.Errorf("no paths provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
var fullpath string
|
||||||
|
for _, path := range paths {
|
||||||
|
full := filepath.Join(path, plugin)
|
||||||
|
if fi, err := os.Stat(full); err == nil && fi.Mode().IsRegular() {
|
||||||
|
fullpath = full
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fullpath == "" {
|
||||||
|
return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullpath, nil
|
||||||
|
}
|
63
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
generated
vendored
Normal file
63
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2016 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RawExec struct {
|
||||||
|
Stderr io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
|
||||||
|
stdout := &bytes.Buffer{}
|
||||||
|
|
||||||
|
c := exec.Cmd{
|
||||||
|
Env: environ,
|
||||||
|
Path: pluginPath,
|
||||||
|
Args: []string{pluginPath},
|
||||||
|
Stdin: bytes.NewBuffer(stdinData),
|
||||||
|
Stdout: stdout,
|
||||||
|
Stderr: e.Stderr,
|
||||||
|
}
|
||||||
|
if err := c.Run(); err != nil {
|
||||||
|
return nil, pluginErr(err, stdout.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdout.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pluginErr(err error, output []byte) error {
|
||||||
|
if _, ok := err.(*exec.ExitError); ok {
|
||||||
|
emsg := types.Error{}
|
||||||
|
if perr := json.Unmarshal(output, &emsg); perr != nil {
|
||||||
|
return fmt.Errorf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
|
||||||
|
}
|
||||||
|
details := ""
|
||||||
|
if emsg.Details != "" {
|
||||||
|
details = fmt.Sprintf("; %v", emsg.Details)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%v%v", emsg.Msg, details)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
101
vendor/github.com/containernetworking/cni/pkg/types/args.go
generated
vendored
Normal file
101
vendor/github.com/containernetworking/cni/pkg/types/args.go
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright 2015 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshallableBool typedef for builtin bool
|
||||||
|
// because builtin type's methods can't be declared
|
||||||
|
type UnmarshallableBool bool
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||||
|
// Returns boolean true if the string is "1" or "[Tt]rue"
|
||||||
|
// Returns boolean false if the string is "0" or "[Ff]alse"
|
||||||
|
func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
|
||||||
|
s := strings.ToLower(string(data))
|
||||||
|
switch s {
|
||||||
|
case "1", "true":
|
||||||
|
*b = true
|
||||||
|
case "0", "false":
|
||||||
|
*b = false
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Boolean unmarshal error: invalid input %s", s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshallableString typedef for builtin string
|
||||||
|
type UnmarshallableString string
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||||
|
// Returns the string
|
||||||
|
func (s *UnmarshallableString) UnmarshalText(data []byte) error {
|
||||||
|
*s = UnmarshallableString(data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonArgs contains the IgnoreUnknown argument
|
||||||
|
// and must be embedded by all Arg structs
|
||||||
|
type CommonArgs struct {
|
||||||
|
IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKeyField is a helper function to receive Values
|
||||||
|
// Values that represent a pointer to a struct
|
||||||
|
func GetKeyField(keyString string, v reflect.Value) reflect.Value {
|
||||||
|
return v.Elem().FieldByName(keyString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadArgs parses args from a string in the form "K=V;K2=V2;..."
|
||||||
|
func LoadArgs(args string, container interface{}) error {
|
||||||
|
if args == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
containerValue := reflect.ValueOf(container)
|
||||||
|
|
||||||
|
pairs := strings.Split(args, ";")
|
||||||
|
unknownArgs := []string{}
|
||||||
|
for _, pair := range pairs {
|
||||||
|
kv := strings.Split(pair, "=")
|
||||||
|
if len(kv) != 2 {
|
||||||
|
return fmt.Errorf("ARGS: invalid pair %q", pair)
|
||||||
|
}
|
||||||
|
keyString := kv[0]
|
||||||
|
valueString := kv[1]
|
||||||
|
keyField := GetKeyField(keyString, containerValue)
|
||||||
|
if !keyField.IsValid() {
|
||||||
|
unknownArgs = append(unknownArgs, pair)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
u := keyField.Addr().Interface().(encoding.TextUnmarshaler)
|
||||||
|
err := u.UnmarshalText([]byte(valueString))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool()
|
||||||
|
if len(unknownArgs) > 0 && !isIgnoreUnknown {
|
||||||
|
return fmt.Errorf("ARGS: unknown args %q", unknownArgs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
193
vendor/github.com/containernetworking/cni/pkg/types/types.go
generated
vendored
Normal file
193
vendor/github.com/containernetworking/cni/pkg/types/types.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
// Copyright 2015 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// like net.IPNet but adds JSON marshalling and unmarshalling
|
||||||
|
type IPNet net.IPNet
|
||||||
|
|
||||||
|
// ParseCIDR takes a string like "10.2.3.1/24" and
|
||||||
|
// return IPNet with "10.2.3.1" and /24 mask
|
||||||
|
func ParseCIDR(s string) (*net.IPNet, error) {
|
||||||
|
ip, ipn, err := net.ParseCIDR(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ipn.IP = ip
|
||||||
|
return ipn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n IPNet) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal((*net.IPNet)(&n).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *IPNet) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp, err := ParseCIDR(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*n = IPNet(*tmp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetConf describes a network.
|
||||||
|
type NetConf struct {
|
||||||
|
CNIVersion string `json:"cniVersion,omitempty"`
|
||||||
|
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
IPAM struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
} `json:"ipam,omitempty"`
|
||||||
|
DNS DNS `json:"dns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result is what gets returned from the plugin (via stdout) to the caller
|
||||||
|
type Result struct {
|
||||||
|
IP4 *IPConfig `json:"ip4,omitempty"`
|
||||||
|
IP6 *IPConfig `json:"ip6,omitempty"`
|
||||||
|
DNS DNS `json:"dns,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) Print() error {
|
||||||
|
return prettyPrint(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
|
||||||
|
// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
|
||||||
|
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||||
|
func (r *Result) String() string {
|
||||||
|
var str string
|
||||||
|
if r.IP4 != nil {
|
||||||
|
str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
|
||||||
|
}
|
||||||
|
if r.IP6 != nil {
|
||||||
|
str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPConfig contains values necessary to configure an interface
|
||||||
|
type IPConfig struct {
|
||||||
|
IP net.IPNet
|
||||||
|
Gateway net.IP
|
||||||
|
Routes []Route
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS contains values interesting for DNS resolvers
|
||||||
|
type DNS struct {
|
||||||
|
Nameservers []string `json:"nameservers,omitempty"`
|
||||||
|
Domain string `json:"domain,omitempty"`
|
||||||
|
Search []string `json:"search,omitempty"`
|
||||||
|
Options []string `json:"options,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
Dst net.IPNet
|
||||||
|
GW net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Code uint `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Details string `json:"details,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return e.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Print() error {
|
||||||
|
return prettyPrint(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// net.IPNet is not JSON (un)marshallable so this duality is needed
|
||||||
|
// for our custom IPNet type
|
||||||
|
|
||||||
|
// JSON (un)marshallable types
|
||||||
|
type ipConfig struct {
|
||||||
|
IP IPNet `json:"ip"`
|
||||||
|
Gateway net.IP `json:"gateway,omitempty"`
|
||||||
|
Routes []Route `json:"routes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type route struct {
|
||||||
|
Dst IPNet `json:"dst"`
|
||||||
|
GW net.IP `json:"gw,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *IPConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
ipc := ipConfig{
|
||||||
|
IP: IPNet(c.IP),
|
||||||
|
Gateway: c.Gateway,
|
||||||
|
Routes: c.Routes,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(ipc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *IPConfig) UnmarshalJSON(data []byte) error {
|
||||||
|
ipc := ipConfig{}
|
||||||
|
if err := json.Unmarshal(data, &ipc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.IP = net.IPNet(ipc.IP)
|
||||||
|
c.Gateway = ipc.Gateway
|
||||||
|
c.Routes = ipc.Routes
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) UnmarshalJSON(data []byte) error {
|
||||||
|
rt := route{}
|
||||||
|
if err := json.Unmarshal(data, &rt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Dst = net.IPNet(rt.Dst)
|
||||||
|
r.GW = rt.GW
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) MarshalJSON() ([]byte, error) {
|
||||||
|
rt := route{
|
||||||
|
Dst: IPNet(r.Dst),
|
||||||
|
GW: r.GW,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prettyPrint(obj interface{}) error {
|
||||||
|
data, err := json.MarshalIndent(obj, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = os.Stdout.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
77
vendor/github.com/containernetworking/cni/pkg/version/plugin.go
generated
vendored
Normal file
77
vendor/github.com/containernetworking/cni/pkg/version/plugin.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2016 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PluginInfo reports information about CNI versioning
|
||||||
|
type PluginInfo interface {
|
||||||
|
// SupportedVersions returns one or more CNI spec versions that the plugin
|
||||||
|
// supports. If input is provided in one of these versions, then the plugin
|
||||||
|
// promises to use the same CNI version in its response
|
||||||
|
SupportedVersions() []string
|
||||||
|
|
||||||
|
// Encode writes this CNI version information as JSON to the given Writer
|
||||||
|
Encode(io.Writer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type pluginInfo struct {
|
||||||
|
CNIVersion_ string `json:"cniVersion"`
|
||||||
|
SupportedVersions_ []string `json:"supportedVersions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pluginInfo) Encode(w io.Writer) error {
|
||||||
|
return json.NewEncoder(w).Encode(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pluginInfo) SupportedVersions() []string {
|
||||||
|
return p.SupportedVersions_
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginSupports returns a new PluginInfo that will report the given versions
|
||||||
|
// as supported
|
||||||
|
func PluginSupports(supportedVersions ...string) PluginInfo {
|
||||||
|
if len(supportedVersions) < 1 {
|
||||||
|
panic("programmer error: you must support at least one version")
|
||||||
|
}
|
||||||
|
return &pluginInfo{
|
||||||
|
CNIVersion_: Current(),
|
||||||
|
SupportedVersions_: supportedVersions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginDecoder struct{}
|
||||||
|
|
||||||
|
func (_ *PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) {
|
||||||
|
var info pluginInfo
|
||||||
|
err := json.Unmarshal(jsonBytes, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding version info: %s", err)
|
||||||
|
}
|
||||||
|
if info.CNIVersion_ == "" {
|
||||||
|
return nil, fmt.Errorf("decoding version info: missing field cniVersion")
|
||||||
|
}
|
||||||
|
if len(info.SupportedVersions_) == 0 {
|
||||||
|
if info.CNIVersion_ == "0.2.0" {
|
||||||
|
return PluginSupports("0.1.0", "0.2.0"), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("decoding version info: missing field supportedVersions")
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
29
vendor/github.com/containernetworking/cni/pkg/version/version.go
generated
vendored
Normal file
29
vendor/github.com/containernetworking/cni/pkg/version/version.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2016 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package version
|
||||||
|
|
||||||
|
// Current reports the version of the CNI spec implemented by this library
|
||||||
|
func Current() string {
|
||||||
|
return "0.2.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy PluginInfo describes a plugin that is backwards compatible with the
|
||||||
|
// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0
|
||||||
|
// library ought to work correctly with a plugin that reports support for
|
||||||
|
// Legacy versions.
|
||||||
|
//
|
||||||
|
// Any future CNI spec versions which meet this definition should be added to
|
||||||
|
// this list.
|
||||||
|
var Legacy = PluginSupports("0.1.0", "0.2.0")
|
189
vendor/github.com/containers/image/LICENSE
generated
vendored
Normal file
189
vendor/github.com/containers/image/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
106
vendor/github.com/containers/image/directory/directory_dest.go
generated
vendored
Normal file
106
vendor/github.com/containers/image/directory/directory_dest.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package directory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dirImageDestination struct {
|
||||||
|
ref dirReference
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageDestination returns an ImageDestination for writing to an existing directory.
|
||||||
|
func newImageDestination(ref dirReference) types.ImageDestination {
|
||||||
|
return &dirImageDestination{ref}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
||||||
|
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
|
||||||
|
func (d *dirImageDestination) Reference() types.ImageReference {
|
||||||
|
return d.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized ImageDestination, if any.
|
||||||
|
func (d *dirImageDestination) Close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dirImageDestination) SupportedManifestMIMETypes() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
|
||||||
|
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
|
||||||
|
func (d *dirImageDestination) SupportsSignatures() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutBlob writes contents of stream and returns its computed digest and size.
|
||||||
|
// A digest can be optionally provided if known, the specific image destination can decide to play with it or not.
|
||||||
|
// The length of stream is expected to be expectedSize; if expectedSize == -1, it is not known.
|
||||||
|
// WARNING: The contents of stream are being verified on the fly. Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
|
||||||
|
// to any other readers for download using the supplied digest.
|
||||||
|
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
||||||
|
func (d *dirImageDestination) PutBlob(stream io.Reader, digest string, expectedSize int64) (string, int64, error) {
|
||||||
|
blobFile, err := ioutil.TempFile(d.ref.path, "dir-put-blob")
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
succeeded := false
|
||||||
|
defer func() {
|
||||||
|
blobFile.Close()
|
||||||
|
if !succeeded {
|
||||||
|
os.Remove(blobFile.Name())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
tee := io.TeeReader(stream, h)
|
||||||
|
|
||||||
|
size, err := io.Copy(blobFile, tee)
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
computedDigest := hex.EncodeToString(h.Sum(nil))
|
||||||
|
if expectedSize != -1 && size != expectedSize {
|
||||||
|
return "", -1, fmt.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, expectedSize, size)
|
||||||
|
}
|
||||||
|
if err := blobFile.Sync(); err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
if err := blobFile.Chmod(0644); err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
blobPath := d.ref.layerPath(computedDigest)
|
||||||
|
if err := os.Rename(blobFile.Name(), blobPath); err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
succeeded = true
|
||||||
|
return "sha256:" + computedDigest, size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dirImageDestination) PutManifest(manifest []byte) error {
|
||||||
|
return ioutil.WriteFile(d.ref.manifestPath(), manifest, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dirImageDestination) PutSignatures(signatures [][]byte) error {
|
||||||
|
for i, sig := range signatures {
|
||||||
|
if err := ioutil.WriteFile(d.ref.signaturePath(i), sig, 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit marks the process of storing the image as successful and asks for the image to be persisted.
|
||||||
|
// WARNING: This does not have any transactional semantics:
|
||||||
|
// - Uploaded data MAY be visible to others before Commit() is called
|
||||||
|
// - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed)
|
||||||
|
func (d *dirImageDestination) Commit() error {
|
||||||
|
return nil
|
||||||
|
}
|
66
vendor/github.com/containers/image/directory/directory_src.go
generated
vendored
Normal file
66
vendor/github.com/containers/image/directory/directory_src.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package directory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dirImageSource struct {
|
||||||
|
ref dirReference
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageSource returns an ImageSource reading from an existing directory.
|
||||||
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
|
func newImageSource(ref dirReference) types.ImageSource {
|
||||||
|
return &dirImageSource{ref}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this source, _as specified by the user_
|
||||||
|
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
|
||||||
|
func (s *dirImageSource) Reference() types.ImageReference {
|
||||||
|
return s.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized ImageSource, if any.
|
||||||
|
func (s *dirImageSource) Close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's up to the caller to determine the MIME type of the returned manifest's bytes
|
||||||
|
func (s *dirImageSource) GetManifest() ([]byte, string, error) {
|
||||||
|
m, err := ioutil.ReadFile(s.ref.manifestPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return m, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
||||||
|
func (s *dirImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
|
||||||
|
r, err := os.Open(s.ref.layerPath(digest))
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, nil
|
||||||
|
}
|
||||||
|
fi, err := r.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, nil
|
||||||
|
}
|
||||||
|
return r, fi.Size(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dirImageSource) GetSignatures() ([][]byte, error) {
|
||||||
|
signatures := [][]byte{}
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
signature, err := ioutil.ReadFile(s.ref.signaturePath(i))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signatures = append(signatures, signature)
|
||||||
|
}
|
||||||
|
return signatures, nil
|
||||||
|
}
|
170
vendor/github.com/containers/image/directory/directory_transport.go
generated
vendored
Normal file
170
vendor/github.com/containers/image/directory/directory_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
package directory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/directory/explicitfilepath"
|
||||||
|
"github.com/containers/image/image"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/docker/docker/reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transport is an ImageTransport for directory paths.
|
||||||
|
var Transport = dirTransport{}
|
||||||
|
|
||||||
|
type dirTransport struct{}
|
||||||
|
|
||||||
|
func (t dirTransport) Name() string {
|
||||||
|
return "dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
|
||||||
|
func (t dirTransport) ParseReference(reference string) (types.ImageReference, error) {
|
||||||
|
return NewReference(reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
||||||
|
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
|
||||||
|
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
|
||||||
|
// scope passed to this function will not be "", that value is always allowed.
|
||||||
|
func (t dirTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
|
if !strings.HasPrefix(scope, "/") {
|
||||||
|
return fmt.Errorf("Invalid scope %s: Must be an absolute path", scope)
|
||||||
|
}
|
||||||
|
// Refuse also "/", otherwise "/" and "" would have the same semantics,
|
||||||
|
// and "" could be unexpectedly shadowed by the "/" entry.
|
||||||
|
if scope == "/" {
|
||||||
|
return errors.New(`Invalid scope "/": Use the generic default scope ""`)
|
||||||
|
}
|
||||||
|
cleaned := filepath.Clean(scope)
|
||||||
|
if cleaned != scope {
|
||||||
|
return fmt.Errorf(`Invalid scope %s: Uses non-canonical format, perhaps try %s`, scope, cleaned)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dirReference is an ImageReference for directory paths.
|
||||||
|
type dirReference struct {
|
||||||
|
// Note that the interpretation of paths below depends on the underlying filesystem state, which may change under us at any time!
|
||||||
|
// Either of the paths may point to a different, or no, inode over time. resolvedPath may contain symbolic links, and so on.
|
||||||
|
|
||||||
|
// Generally we follow the intent of the user, and use the "path" member for filesystem operations (e.g. the user can use a relative path to avoid
|
||||||
|
// being exposed to symlinks and renames in the parent directories to the working directory).
|
||||||
|
// (But in general, we make no attempt to be completely safe against concurrent hostile filesystem modifications.)
|
||||||
|
path string // As specified by the user. May be relative, contain symlinks, etc.
|
||||||
|
resolvedPath string // Absolute path with no symlinks, at least at the time of its creation. Primarily used for policy namespaces.
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no directory.ParseReference because it is rather pointless.
|
||||||
|
// Callers who need a transport-independent interface will go through
|
||||||
|
// dirTransport.ParseReference; callers who intentionally deal with directories
|
||||||
|
// can use directory.NewReference.
|
||||||
|
|
||||||
|
// NewReference returns a directory reference for a specified path.
|
||||||
|
//
|
||||||
|
// We do not expose an API supplying the resolvedPath; we could, but recomputing it
|
||||||
|
// is generally cheap enough that we prefer being confident about the properties of resolvedPath.
|
||||||
|
func NewReference(path string) (types.ImageReference, error) {
|
||||||
|
resolved, err := explicitfilepath.ResolvePathToFullyExplicit(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dirReference{path: path, resolvedPath: resolved}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ref dirReference) Transport() types.ImageTransport {
|
||||||
|
return Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringWithinTransport returns a string representation of the reference, which MUST be such that
|
||||||
|
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
|
||||||
|
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
|
||||||
|
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
|
||||||
|
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix.
|
||||||
|
func (ref dirReference) StringWithinTransport() string {
|
||||||
|
return ref.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerReference returns a Docker reference associated with this reference
|
||||||
|
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
|
||||||
|
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
|
||||||
|
func (ref dirReference) DockerReference() reference.Named {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
|
||||||
|
// This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases;
|
||||||
|
// The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical
|
||||||
|
// (i.e. various references with exactly the same semantics should return the same configuration identity)
|
||||||
|
// It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but
|
||||||
|
// not required/guaranteed that it will be a valid input to Transport().ParseReference().
|
||||||
|
// Returns "" if configuration identities for these references are not supported.
|
||||||
|
func (ref dirReference) PolicyConfigurationIdentity() string {
|
||||||
|
return ref.resolvedPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
|
||||||
|
// for if explicit configuration for PolicyConfigurationIdentity() is not set. The list will be processed
|
||||||
|
// in order, terminating on first match, and an implicit "" is always checked at the end.
|
||||||
|
// It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(),
|
||||||
|
// and each following element to be a prefix of the element preceding it.
|
||||||
|
func (ref dirReference) PolicyConfigurationNamespaces() []string {
|
||||||
|
res := []string{}
|
||||||
|
path := ref.resolvedPath
|
||||||
|
for {
|
||||||
|
lastSlash := strings.LastIndex(path, "/")
|
||||||
|
if lastSlash == -1 || lastSlash == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
path = path[:lastSlash]
|
||||||
|
res = append(res, path)
|
||||||
|
}
|
||||||
|
// Note that we do not include "/"; it is redundant with the default "" global default,
|
||||||
|
// and rejected by dirTransport.ValidatePolicyConfigurationScope above.
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImage returns a types.Image for this reference.
|
||||||
|
// The caller must call .Close() on the returned Image.
|
||||||
|
func (ref dirReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
||||||
|
src := newImageSource(ref)
|
||||||
|
return image.FromSource(src), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageSource returns a types.ImageSource for this reference,
|
||||||
|
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
|
||||||
|
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||||
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
|
func (ref dirReference) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||||
|
return newImageSource(ref), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||||
|
// The caller must call .Close() on the returned ImageDestination.
|
||||||
|
func (ref dirReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
|
return newImageDestination(ref), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteImage deletes the named image from the registry, if supported.
|
||||||
|
func (ref dirReference) DeleteImage(ctx *types.SystemContext) error {
|
||||||
|
return fmt.Errorf("Deleting images not implemented for dir: images")
|
||||||
|
}
|
||||||
|
|
||||||
|
// manifestPath returns a path for the manifest within a directory using our conventions.
|
||||||
|
func (ref dirReference) manifestPath() string {
|
||||||
|
return filepath.Join(ref.path, "manifest.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
// layerPath returns a path for a layer tarball within a directory using our conventions.
|
||||||
|
func (ref dirReference) layerPath(digest string) string {
|
||||||
|
// FIXME: Should we keep the digest identification?
|
||||||
|
return filepath.Join(ref.path, strings.TrimPrefix(digest, "sha256:")+".tar")
|
||||||
|
}
|
||||||
|
|
||||||
|
// signaturePath returns a path for a signature within a directory using our conventions.
|
||||||
|
func (ref dirReference) signaturePath(index int) string {
|
||||||
|
return filepath.Join(ref.path, fmt.Sprintf("signature-%d", index+1))
|
||||||
|
}
|
55
vendor/github.com/containers/image/directory/explicitfilepath/path.go
generated
vendored
Normal file
55
vendor/github.com/containers/image/directory/explicitfilepath/path.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package explicitfilepath
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResolvePathToFullyExplicit returns the input path converted to an absolute, no-symlinks, cleaned up path.
|
||||||
|
// To do so, all elements of the input path must exist; as a special case, the final component may be
|
||||||
|
// a non-existent name (but not a symlink pointing to a non-existent name)
|
||||||
|
// This is intended as a a helper for implementations of types.ImageReference.PolicyConfigurationIdentity etc.
|
||||||
|
func ResolvePathToFullyExplicit(path string) (string, error) {
|
||||||
|
switch _, err := os.Lstat(path); {
|
||||||
|
case err == nil:
|
||||||
|
return resolveExistingPathToFullyExplicit(path)
|
||||||
|
case os.IsNotExist(err):
|
||||||
|
parent, file := filepath.Split(path)
|
||||||
|
resolvedParent, err := resolveExistingPathToFullyExplicit(parent)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if file == "." || file == ".." {
|
||||||
|
// Coverage: This can happen, but very rarely: if we have successfully resolved the parent, both "." and ".." in it should have been resolved as well.
|
||||||
|
// This can still happen if there is a filesystem race condition, causing the Lstat() above to fail but the later resolution to succeed.
|
||||||
|
// We do not care to promise anything if such filesystem race conditions can happen, but we definitely don't want to return "."/".." components
|
||||||
|
// in the resulting path, and especially not at the end.
|
||||||
|
return "", fmt.Errorf("Unexpectedly missing special filename component in %s", path)
|
||||||
|
}
|
||||||
|
resolvedPath := filepath.Join(resolvedParent, file)
|
||||||
|
// As a sanity check, ensure that there are no "." or ".." components.
|
||||||
|
cleanedResolvedPath := filepath.Clean(resolvedPath)
|
||||||
|
if cleanedResolvedPath != resolvedPath {
|
||||||
|
// Coverage: This should never happen.
|
||||||
|
return "", fmt.Errorf("Internal inconsistency: Path %s resolved to %s still cleaned up to %s", path, resolvedPath, cleanedResolvedPath)
|
||||||
|
}
|
||||||
|
return resolvedPath, nil
|
||||||
|
default: // err != nil, unrecognized
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveExistingPathToFullyExplicit is the same as ResolvePathToFullyExplicit,
|
||||||
|
// but without the special case for missing final component.
|
||||||
|
func resolveExistingPathToFullyExplicit(path string) (string, error) {
|
||||||
|
resolved, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err // Coverage: This can fail only if os.Getwd() fails.
|
||||||
|
}
|
||||||
|
resolved, err = filepath.EvalSymlinks(resolved)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Clean(resolved), nil
|
||||||
|
}
|
373
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
Normal file
373
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
Normal file
|
@ -0,0 +1,373 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/docker/docker/pkg/homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dockerHostname = "docker.io"
|
||||||
|
dockerRegistry = "registry-1.docker.io"
|
||||||
|
dockerAuthRegistry = "https://index.docker.io/v1/"
|
||||||
|
|
||||||
|
dockerCfg = ".docker"
|
||||||
|
dockerCfgFileName = "config.json"
|
||||||
|
dockerCfgObsolete = ".dockercfg"
|
||||||
|
|
||||||
|
baseURL = "%s://%s/v2/"
|
||||||
|
tagsURL = "%s/tags/list"
|
||||||
|
manifestURL = "%s/manifests/%s"
|
||||||
|
blobsURL = "%s/blobs/%s"
|
||||||
|
blobUploadURL = "%s/blobs/uploads/"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dockerClient is configuration for dealing with a single Docker registry.
|
||||||
|
type dockerClient struct {
|
||||||
|
ctx *types.SystemContext
|
||||||
|
registry string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
wwwAuthenticate string // Cache of a value set by ping() if scheme is not empty
|
||||||
|
scheme string // Cache of a value returned by a successful ping() if not empty
|
||||||
|
client *http.Client
|
||||||
|
signatureBase signatureStorageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// newDockerClient returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
|
||||||
|
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
|
||||||
|
func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool) (*dockerClient, error) {
|
||||||
|
registry := ref.ref.Hostname()
|
||||||
|
if registry == dockerHostname {
|
||||||
|
registry = dockerRegistry
|
||||||
|
}
|
||||||
|
username, password, err := getAuth(ref.ref.Hostname())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var tr *http.Transport
|
||||||
|
if ctx != nil && (ctx.DockerCertPath != "" || ctx.DockerInsecureSkipTLSVerify) {
|
||||||
|
tlsc := &tls.Config{}
|
||||||
|
|
||||||
|
if ctx.DockerCertPath != "" {
|
||||||
|
cert, err := tls.LoadX509KeyPair(filepath.Join(ctx.DockerCertPath, "cert.pem"), filepath.Join(ctx.DockerCertPath, "key.pem"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
|
||||||
|
}
|
||||||
|
tlsc.Certificates = append(tlsc.Certificates, cert)
|
||||||
|
}
|
||||||
|
tlsc.InsecureSkipVerify = ctx.DockerInsecureSkipTLSVerify
|
||||||
|
tr = &http.Transport{
|
||||||
|
TLSClientConfig: tlsc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 1 * time.Minute,
|
||||||
|
}
|
||||||
|
if tr != nil {
|
||||||
|
client.Transport = tr
|
||||||
|
}
|
||||||
|
|
||||||
|
sigBase, err := configuredSignatureStorageBase(ctx, ref, write)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dockerClient{
|
||||||
|
ctx: ctx,
|
||||||
|
registry: registry,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
client: client,
|
||||||
|
signatureBase: sigBase,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
|
||||||
|
// url is NOT an absolute URL, but a path relative to the /v2/ top-level API path. The host name and schema is taken from the client or autodetected.
|
||||||
|
func (c *dockerClient) makeRequest(method, url string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
|
||||||
|
if c.scheme == "" {
|
||||||
|
pr, err := c.ping()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.wwwAuthenticate = pr.WWWAuthenticate
|
||||||
|
c.scheme = pr.scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
url = fmt.Sprintf(baseURL, c.scheme, c.registry) + url
|
||||||
|
return c.makeRequestToResolvedURL(method, url, headers, stream, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
|
||||||
|
// streamLen, if not -1, specifies the length of the data expected on stream.
|
||||||
|
// makeRequest should generally be preferred.
|
||||||
|
func (c *dockerClient) makeRequestToResolvedURL(method, url string, headers map[string][]string, stream io.Reader, streamLen int64) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest(method, url, stream)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if streamLen != -1 { // Do not blindly overwrite if streamLen == -1, http.NewRequest above can figure out the length of bytes.Reader and similar objects without us having to compute it.
|
||||||
|
req.ContentLength = streamLen
|
||||||
|
}
|
||||||
|
req.Header.Set("Docker-Distribution-API-Version", "registry/2.0")
|
||||||
|
for n, h := range headers {
|
||||||
|
for _, hh := range h {
|
||||||
|
req.Header.Add(n, hh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.wwwAuthenticate != "" {
|
||||||
|
if err := c.setupRequestAuth(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logrus.Debugf("%s %s", method, url)
|
||||||
|
res, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dockerClient) setupRequestAuth(req *http.Request) error {
|
||||||
|
tokens := strings.SplitN(strings.TrimSpace(c.wwwAuthenticate), " ", 2)
|
||||||
|
if len(tokens) != 2 {
|
||||||
|
return fmt.Errorf("expected 2 tokens in WWW-Authenticate: %d, %s", len(tokens), c.wwwAuthenticate)
|
||||||
|
}
|
||||||
|
switch tokens[0] {
|
||||||
|
case "Basic":
|
||||||
|
req.SetBasicAuth(c.username, c.password)
|
||||||
|
return nil
|
||||||
|
case "Bearer":
|
||||||
|
// FIXME? This gets a new token for every API request;
|
||||||
|
// we may be easily able to reuse a previous token, e.g.
|
||||||
|
// for OpenShift the token only identifies the user and does not vary
|
||||||
|
// across operations. Should we just try the request first, and
|
||||||
|
// only get a new token on failure?
|
||||||
|
// OTOH what to do with the single-use body stream in that case?
|
||||||
|
|
||||||
|
// Try performing the request, expecting it to fail.
|
||||||
|
testReq := *req
|
||||||
|
// Do not use the body stream, or we couldn't reuse it for the "real" call later.
|
||||||
|
testReq.Body = nil
|
||||||
|
testReq.ContentLength = 0
|
||||||
|
res, err := c.client.Do(&testReq)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
chs := parseAuthHeader(res.Header)
|
||||||
|
if res.StatusCode != http.StatusUnauthorized || chs == nil || len(chs) == 0 {
|
||||||
|
// no need for bearer? wtf?
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Arbitrarily use the first challenge, there is no reason to expect more than one.
|
||||||
|
challenge := chs[0]
|
||||||
|
if challenge.Scheme != "bearer" { // Another artifact of trying to handle WWW-Authenticate before it actually happens.
|
||||||
|
return fmt.Errorf("Unimplemented: WWW-Authenticate Bearer replaced by %#v", challenge.Scheme)
|
||||||
|
}
|
||||||
|
realm, ok := challenge.Parameters["realm"]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("missing realm in bearer auth challenge")
|
||||||
|
}
|
||||||
|
service, _ := challenge.Parameters["service"] // Will be "" if not present
|
||||||
|
scope, _ := challenge.Parameters["scope"] // Will be "" if not present
|
||||||
|
token, err := c.getBearerToken(realm, service, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("no handler for %s authentication", tokens[0])
|
||||||
|
// support docker bearer with authconfig's Auth string? see docker2aci
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dockerClient) getBearerToken(realm, service, scope string) (string, error) {
|
||||||
|
authReq, err := http.NewRequest("GET", realm, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
getParams := authReq.URL.Query()
|
||||||
|
if service != "" {
|
||||||
|
getParams.Add("service", service)
|
||||||
|
}
|
||||||
|
if scope != "" {
|
||||||
|
getParams.Add("scope", scope)
|
||||||
|
}
|
||||||
|
authReq.URL.RawQuery = getParams.Encode()
|
||||||
|
if c.username != "" && c.password != "" {
|
||||||
|
authReq.SetBasicAuth(c.username, c.password)
|
||||||
|
}
|
||||||
|
// insecure for now to contact the external token service
|
||||||
|
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
|
||||||
|
client := &http.Client{Transport: tr}
|
||||||
|
res, err := client.Do(authReq)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
switch res.StatusCode {
|
||||||
|
case http.StatusUnauthorized:
|
||||||
|
return "", fmt.Errorf("unable to retrieve auth token: 401 unauthorized")
|
||||||
|
case http.StatusOK:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unexpected http code: %d, URL: %s", res.StatusCode, authReq.URL)
|
||||||
|
}
|
||||||
|
tokenBlob, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
tokenStruct := struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(tokenBlob, &tokenStruct); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// TODO(runcom): reuse tokens?
|
||||||
|
//hostAuthTokens, ok = rb.hostsV2AuthTokens[req.URL.Host]
|
||||||
|
//if !ok {
|
||||||
|
//hostAuthTokens = make(map[string]string)
|
||||||
|
//rb.hostsV2AuthTokens[req.URL.Host] = hostAuthTokens
|
||||||
|
//}
|
||||||
|
//hostAuthTokens[repo] = tokenStruct.Token
|
||||||
|
return tokenStruct.Token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAuth(hostname string) (string, string, error) {
|
||||||
|
// TODO(runcom): get this from *cli.Context somehow
|
||||||
|
//if username != "" && password != "" {
|
||||||
|
//return username, password, nil
|
||||||
|
//}
|
||||||
|
if hostname == dockerHostname {
|
||||||
|
hostname = dockerAuthRegistry
|
||||||
|
}
|
||||||
|
dockerCfgPath := filepath.Join(getDefaultConfigDir(".docker"), dockerCfgFileName)
|
||||||
|
if _, err := os.Stat(dockerCfgPath); err == nil {
|
||||||
|
j, err := ioutil.ReadFile(dockerCfgPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
var dockerAuth dockerConfigFile
|
||||||
|
if err := json.Unmarshal(j, &dockerAuth); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
// try the normal case
|
||||||
|
if c, ok := dockerAuth.AuthConfigs[hostname]; ok {
|
||||||
|
return decodeDockerAuth(c.Auth)
|
||||||
|
}
|
||||||
|
} else if os.IsNotExist(err) {
|
||||||
|
oldDockerCfgPath := filepath.Join(getDefaultConfigDir(dockerCfgObsolete))
|
||||||
|
if _, err := os.Stat(oldDockerCfgPath); err != nil {
|
||||||
|
return "", "", nil //missing file is not an error
|
||||||
|
}
|
||||||
|
j, err := ioutil.ReadFile(oldDockerCfgPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
var dockerAuthOld map[string]dockerAuthConfigObsolete
|
||||||
|
if err := json.Unmarshal(j, &dockerAuthOld); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if c, ok := dockerAuthOld[hostname]; ok {
|
||||||
|
return decodeDockerAuth(c.Auth)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if file is there but we can't stat it for any reason other
|
||||||
|
// than it doesn't exist then stop
|
||||||
|
return "", "", fmt.Errorf("%s - %v", dockerCfgPath, err)
|
||||||
|
}
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiErr struct {
|
||||||
|
Code string
|
||||||
|
Message string
|
||||||
|
Detail interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type pingResponse struct {
|
||||||
|
WWWAuthenticate string
|
||||||
|
APIVersion string
|
||||||
|
scheme string
|
||||||
|
errors []apiErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dockerClient) ping() (*pingResponse, error) {
|
||||||
|
ping := func(scheme string) (*pingResponse, error) {
|
||||||
|
url := fmt.Sprintf(baseURL, scheme, c.registry)
|
||||||
|
resp, err := c.client.Get(url)
|
||||||
|
logrus.Debugf("Ping %s err %#v", url, err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
logrus.Debugf("Ping %s status %d", scheme+"://"+c.registry+"/v2/", resp.StatusCode)
|
||||||
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
|
||||||
|
return nil, fmt.Errorf("error pinging repository, response code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
pr := &pingResponse{}
|
||||||
|
pr.WWWAuthenticate = resp.Header.Get("WWW-Authenticate")
|
||||||
|
pr.APIVersion = resp.Header.Get("Docker-Distribution-Api-Version")
|
||||||
|
pr.scheme = scheme
|
||||||
|
if resp.StatusCode == http.StatusUnauthorized {
|
||||||
|
type APIErrors struct {
|
||||||
|
Errors []apiErr
|
||||||
|
}
|
||||||
|
errs := &APIErrors{}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(errs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pr.errors = errs.Errors
|
||||||
|
}
|
||||||
|
return pr, nil
|
||||||
|
}
|
||||||
|
pr, err := ping("https")
|
||||||
|
if err != nil && c.ctx.DockerInsecureSkipTLSVerify {
|
||||||
|
pr, err = ping("http")
|
||||||
|
}
|
||||||
|
return pr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultConfigDir(confPath string) string {
|
||||||
|
return filepath.Join(homedir.Get(), confPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dockerAuthConfigObsolete struct {
|
||||||
|
Auth string `json:"auth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type dockerAuthConfig struct {
|
||||||
|
Auth string `json:"auth,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type dockerConfigFile struct {
|
||||||
|
AuthConfigs map[string]dockerAuthConfig `json:"auths"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeDockerAuth(s string) (string, string, error) {
|
||||||
|
decoded, err := base64.StdEncoding.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(string(decoded), ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
// if it's invalid just skip, as docker does
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
user := parts[0]
|
||||||
|
password := strings.Trim(parts[1], "\x00")
|
||||||
|
return user, password, nil
|
||||||
|
}
|
55
vendor/github.com/containers/image/docker/docker_image.go
generated
vendored
Normal file
55
vendor/github.com/containers/image/docker/docker_image.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/containers/image/image"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Image is a Docker-specific implementation of types.Image with a few extra methods
|
||||||
|
// which are specific to Docker.
|
||||||
|
type Image struct {
|
||||||
|
types.Image
|
||||||
|
src *dockerImageSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImage returns a new Image interface type after setting up
|
||||||
|
// a client to the registry hosting the given image.
|
||||||
|
// The caller must call .Close() on the returned Image.
|
||||||
|
func newImage(ctx *types.SystemContext, ref dockerReference) (types.Image, error) {
|
||||||
|
s, err := newImageSource(ctx, ref, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Image{Image: image.FromSource(s), src: s}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SourceRefFullName returns a fully expanded name for the repository this image is in.
|
||||||
|
func (i *Image) SourceRefFullName() string {
|
||||||
|
return i.src.ref.ref.FullName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepositoryTags list all tags available in the repository. Note that this has no connection with the tag(s) used for this specific image, if any.
|
||||||
|
func (i *Image) GetRepositoryTags() ([]string, error) {
|
||||||
|
url := fmt.Sprintf(tagsURL, i.src.ref.ref.RemoteName())
|
||||||
|
res, err := i.src.c.makeRequest("GET", url, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
// print url also
|
||||||
|
return nil, fmt.Errorf("Invalid status code returned when fetching tags list %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
type tagsRes struct {
|
||||||
|
Tags []string
|
||||||
|
}
|
||||||
|
tags := &tagsRes{}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(tags); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tags.Tags, nil
|
||||||
|
}
|
268
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
Normal file
268
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dockerImageDestination struct {
|
||||||
|
ref dockerReference
|
||||||
|
c *dockerClient
|
||||||
|
// State
|
||||||
|
manifestDigest string // or "" if not yet known.
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageDestination creates a new ImageDestination for the specified image reference.
|
||||||
|
func newImageDestination(ctx *types.SystemContext, ref dockerReference) (types.ImageDestination, error) {
|
||||||
|
c, err := newDockerClient(ctx, ref, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dockerImageDestination{
|
||||||
|
ref: ref,
|
||||||
|
c: c,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
||||||
|
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
|
||||||
|
func (d *dockerImageDestination) Reference() types.ImageReference {
|
||||||
|
return d.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized ImageDestination, if any.
|
||||||
|
func (d *dockerImageDestination) Close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerImageDestination) SupportedManifestMIMETypes() []string {
|
||||||
|
return []string{
|
||||||
|
// TODO(runcom): we'll add OCI as part of another PR here
|
||||||
|
manifest.DockerV2Schema2MediaType,
|
||||||
|
manifest.DockerV2Schema1SignedMediaType,
|
||||||
|
manifest.DockerV2Schema1MediaType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
|
||||||
|
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
|
||||||
|
func (d *dockerImageDestination) SupportsSignatures() error {
|
||||||
|
return fmt.Errorf("Pushing signatures to a Docker Registry is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutBlob writes contents of stream and returns its computed digest and size.
|
||||||
|
// A digest can be optionally provided if known, the specific image destination can decide to play with it or not.
|
||||||
|
// The length of stream is expected to be expectedSize; if expectedSize == -1, it is not known.
|
||||||
|
// WARNING: The contents of stream are being verified on the fly. Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
|
||||||
|
// to any other readers for download using the supplied digest.
|
||||||
|
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
||||||
|
func (d *dockerImageDestination) PutBlob(stream io.Reader, digest string, expectedSize int64) (string, int64, error) {
|
||||||
|
if digest != "" {
|
||||||
|
checkURL := fmt.Sprintf(blobsURL, d.ref.ref.RemoteName(), digest)
|
||||||
|
|
||||||
|
logrus.Debugf("Checking %s", checkURL)
|
||||||
|
res, err := d.c.makeRequest("HEAD", checkURL, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode == http.StatusOK {
|
||||||
|
logrus.Debugf("... already exists, not uploading")
|
||||||
|
blobLength, err := strconv.ParseInt(res.Header.Get("Content-Length"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
return digest, blobLength, nil
|
||||||
|
}
|
||||||
|
logrus.Debugf("... failed, status %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME? Chunked upload, progress reporting, etc.
|
||||||
|
uploadURL := fmt.Sprintf(blobUploadURL, d.ref.ref.RemoteName())
|
||||||
|
logrus.Debugf("Uploading %s", uploadURL)
|
||||||
|
res, err := d.c.makeRequest("POST", uploadURL, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode != http.StatusAccepted {
|
||||||
|
logrus.Debugf("Error initiating layer upload, response %#v", *res)
|
||||||
|
return "", -1, fmt.Errorf("Error initiating layer upload to %s, status %d", uploadURL, res.StatusCode)
|
||||||
|
}
|
||||||
|
uploadLocation, err := res.Location()
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, fmt.Errorf("Error determining upload URL: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
tee := io.TeeReader(stream, h)
|
||||||
|
res, err = d.c.makeRequestToResolvedURL("PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, expectedSize)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Error uploading layer chunked, response %#v", *res)
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
hash := h.Sum(nil)
|
||||||
|
computedDigest := "sha256:" + hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
|
uploadLocation, err = res.Location()
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, fmt.Errorf("Error determining upload URL: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: DELETE uploadLocation on failure
|
||||||
|
|
||||||
|
locationQuery := uploadLocation.Query()
|
||||||
|
// TODO: check digest == computedDigest https://github.com/containers/image/pull/70#discussion_r77646717
|
||||||
|
locationQuery.Set("digest", computedDigest)
|
||||||
|
uploadLocation.RawQuery = locationQuery.Encode()
|
||||||
|
res, err = d.c.makeRequestToResolvedURL("PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1)
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode != http.StatusCreated {
|
||||||
|
logrus.Debugf("Error uploading layer, response %#v", *res)
|
||||||
|
return "", -1, fmt.Errorf("Error uploading layer to %s, status %d", uploadLocation, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("Upload of layer %s complete", digest)
|
||||||
|
return computedDigest, res.Request.ContentLength, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerImageDestination) PutManifest(m []byte) error {
|
||||||
|
// FIXME: This only allows upload by digest, not creating a tag. See the
|
||||||
|
// corresponding comment in openshift.NewImageDestination.
|
||||||
|
digest, err := manifest.Digest(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.manifestDigest = digest
|
||||||
|
url := fmt.Sprintf(manifestURL, d.ref.ref.RemoteName(), digest)
|
||||||
|
|
||||||
|
headers := map[string][]string{}
|
||||||
|
mimeType := manifest.GuessMIMEType(m)
|
||||||
|
if mimeType != "" {
|
||||||
|
headers["Content-Type"] = []string{mimeType}
|
||||||
|
}
|
||||||
|
res, err := d.c.makeRequest("PUT", url, headers, bytes.NewReader(m))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode != http.StatusCreated {
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err == nil {
|
||||||
|
logrus.Debugf("Error body %s", string(body))
|
||||||
|
}
|
||||||
|
logrus.Debugf("Error uploading manifest, status %d, %#v", res.StatusCode, res)
|
||||||
|
return fmt.Errorf("Error uploading manifest to %s, status %d", url, res.StatusCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerImageDestination) PutSignatures(signatures [][]byte) error {
|
||||||
|
// FIXME? This overwrites files one at a time, definitely not atomic.
|
||||||
|
// A failure when updating signatures with a reordered copy could lose some of them.
|
||||||
|
|
||||||
|
// Skip dealing with the manifest digest if not necessary.
|
||||||
|
if len(signatures) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if d.c.signatureBase == nil {
|
||||||
|
return fmt.Errorf("Pushing signatures to a Docker Registry is not supported, and there is no applicable signature storage configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This assumption that signatures are stored after the manifest rather breaks the model.
|
||||||
|
if d.manifestDigest == "" {
|
||||||
|
return fmt.Errorf("Unknown manifest digest, can't add signatures")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, signature := range signatures {
|
||||||
|
url := signatureStorageURL(d.c.signatureBase, d.manifestDigest, i)
|
||||||
|
if url == nil {
|
||||||
|
return fmt.Errorf("Internal error: signatureStorageURL with non-nil base returned nil")
|
||||||
|
}
|
||||||
|
err := d.putOneSignature(url, signature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove any other signatures, if present.
|
||||||
|
// We stop at the first missing signature; if a previous deleting loop aborted
|
||||||
|
// prematurely, this may not clean up all of them, but one missing signature
|
||||||
|
// is enough for dockerImageSource to stop looking for other signatures, so that
|
||||||
|
// is sufficient.
|
||||||
|
for i := len(signatures); ; i++ {
|
||||||
|
url := signatureStorageURL(d.c.signatureBase, d.manifestDigest, i)
|
||||||
|
if url == nil {
|
||||||
|
return fmt.Errorf("Internal error: signatureStorageURL with non-nil base returned nil")
|
||||||
|
}
|
||||||
|
missing, err := d.c.deleteOneSignature(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if missing {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// putOneSignature stores one signature to url.
|
||||||
|
func (d *dockerImageDestination) putOneSignature(url *url.URL, signature []byte) error {
|
||||||
|
switch url.Scheme {
|
||||||
|
case "file":
|
||||||
|
logrus.Debugf("Writing to %s", url.Path)
|
||||||
|
err := os.MkdirAll(filepath.Dir(url.Path), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(url.Path, signature, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unsupported scheme when writing signature to %s", url.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteOneSignature deletes a signature from url, if it exists.
|
||||||
|
// If it successfully determines that the signature does not exist, returns (true, nil)
|
||||||
|
func (c *dockerClient) deleteOneSignature(url *url.URL) (missing bool, err error) {
|
||||||
|
switch url.Scheme {
|
||||||
|
case "file":
|
||||||
|
logrus.Debugf("Deleting %s", url.Path)
|
||||||
|
err := os.Remove(url.Path)
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("Unsupported scheme when deleting signature from %s", url.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit marks the process of storing the image as successful and asks for the image to be persisted.
|
||||||
|
// WARNING: This does not have any transactional semantics:
|
||||||
|
// - Uploaded data MAY be visible to others before Commit() is called
|
||||||
|
// - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed)
|
||||||
|
func (d *dockerImageDestination) Commit() error {
|
||||||
|
return nil
|
||||||
|
}
|
288
vendor/github.com/containers/image/docker/docker_image_src.go
generated
vendored
Normal file
288
vendor/github.com/containers/image/docker/docker_image_src.go
generated
vendored
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type errFetchManifest struct {
|
||||||
|
statusCode int
|
||||||
|
body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e errFetchManifest) Error() string {
|
||||||
|
return fmt.Sprintf("error fetching manifest: status code: %d, body: %s", e.statusCode, string(e.body))
|
||||||
|
}
|
||||||
|
|
||||||
|
type dockerImageSource struct {
|
||||||
|
ref dockerReference
|
||||||
|
requestedManifestMIMETypes []string
|
||||||
|
c *dockerClient
|
||||||
|
// State
|
||||||
|
cachedManifest []byte // nil if not loaded yet
|
||||||
|
cachedManifestMIMEType string // Only valid if cachedManifest != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageSource creates a new ImageSource for the specified image reference,
|
||||||
|
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
|
||||||
|
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||||
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
|
func newImageSource(ctx *types.SystemContext, ref dockerReference, requestedManifestMIMETypes []string) (*dockerImageSource, error) {
|
||||||
|
c, err := newDockerClient(ctx, ref, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if requestedManifestMIMETypes == nil {
|
||||||
|
requestedManifestMIMETypes = manifest.DefaultRequestedManifestMIMETypes
|
||||||
|
}
|
||||||
|
return &dockerImageSource{
|
||||||
|
ref: ref,
|
||||||
|
requestedManifestMIMETypes: requestedManifestMIMETypes,
|
||||||
|
c: c,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this source, _as specified by the user_
|
||||||
|
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
|
||||||
|
func (s *dockerImageSource) Reference() types.ImageReference {
|
||||||
|
return s.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized ImageSource, if any.
|
||||||
|
func (s *dockerImageSource) Close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// simplifyContentType drops parameters from a HTTP media type (see https://tools.ietf.org/html/rfc7231#section-3.1.1.1)
|
||||||
|
// Alternatively, an empty string is returned unchanged, and invalid values are "simplified" to an empty string.
|
||||||
|
func simplifyContentType(contentType string) string {
|
||||||
|
if contentType == "" {
|
||||||
|
return contentType
|
||||||
|
}
|
||||||
|
mimeType, _, err := mime.ParseMediaType(contentType)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return mimeType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dockerImageSource) GetManifest() ([]byte, string, error) {
|
||||||
|
err := s.ensureManifestIsLoaded()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return s.cachedManifest, s.cachedManifestMIMEType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureManifestIsLoaded sets s.cachedManifest and s.cachedManifestMIMEType
|
||||||
|
//
|
||||||
|
// ImageSource implementations are not required or expected to do any caching,
|
||||||
|
// but because our signatures are “attached” to the manifest digest,
|
||||||
|
// we need to ensure that the digest of the manifest returned by GetManifest
|
||||||
|
// and used by GetSignatures are consistent, otherwise we would get spurious
|
||||||
|
// signature verification failures when pulling while a tag is being updated.
|
||||||
|
func (s *dockerImageSource) ensureManifestIsLoaded() error {
|
||||||
|
if s.cachedManifest != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
reference, err := s.ref.tagOrDigest()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf(manifestURL, s.ref.ref.RemoteName(), reference)
|
||||||
|
// TODO(runcom) set manifest version header! schema1 for now - then schema2 etc etc and v1
|
||||||
|
// TODO(runcom) NO, switch on the resulter manifest like Docker is doing
|
||||||
|
headers := make(map[string][]string)
|
||||||
|
headers["Accept"] = s.requestedManifestMIMETypes
|
||||||
|
res, err := s.c.makeRequest("GET", url, headers, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
manblob, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return errFetchManifest{res.StatusCode, manblob}
|
||||||
|
}
|
||||||
|
// We might validate manblob against the Docker-Content-Digest header here to protect against transport errors.
|
||||||
|
s.cachedManifest = manblob
|
||||||
|
s.cachedManifestMIMEType = simplifyContentType(res.Header.Get("Content-Type"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
||||||
|
func (s *dockerImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
|
||||||
|
url := fmt.Sprintf(blobsURL, s.ref.ref.RemoteName(), digest)
|
||||||
|
logrus.Debugf("Downloading %s", url)
|
||||||
|
res, err := s.c.makeRequest("GET", url, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
// print url also
|
||||||
|
return nil, 0, fmt.Errorf("Invalid status code returned when fetching blob %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
size, err := strconv.ParseInt(res.Header.Get("Content-Length"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
size = -1
|
||||||
|
}
|
||||||
|
return res.Body, size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dockerImageSource) GetSignatures() ([][]byte, error) {
|
||||||
|
if s.c.signatureBase == nil { // Skip dealing with the manifest digest if not necessary.
|
||||||
|
return [][]byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.ensureManifestIsLoaded(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
manifestDigest, err := manifest.Digest(s.cachedManifest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signatures := [][]byte{}
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
url := signatureStorageURL(s.c.signatureBase, manifestDigest, i)
|
||||||
|
if url == nil {
|
||||||
|
return nil, fmt.Errorf("Internal error: signatureStorageURL with non-nil base returned nil")
|
||||||
|
}
|
||||||
|
signature, missing, err := s.getOneSignature(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if missing {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
signatures = append(signatures, signature)
|
||||||
|
}
|
||||||
|
return signatures, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOneSignature downloads one signature from url.
|
||||||
|
// If it successfully determines that the signature does not exist, returns with missing set to true and error set to nil.
|
||||||
|
func (s *dockerImageSource) getOneSignature(url *url.URL) (signature []byte, missing bool, err error) {
|
||||||
|
switch url.Scheme {
|
||||||
|
case "file":
|
||||||
|
logrus.Debugf("Reading %s", url.Path)
|
||||||
|
sig, err := ioutil.ReadFile(url.Path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, true, nil
|
||||||
|
}
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return sig, false, nil
|
||||||
|
|
||||||
|
case "http", "https":
|
||||||
|
logrus.Debugf("GET %s", url)
|
||||||
|
res, err := s.c.client.Get(url.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode == http.StatusNotFound {
|
||||||
|
return nil, true, nil
|
||||||
|
} else if res.StatusCode != http.StatusOK {
|
||||||
|
return nil, false, fmt.Errorf("Error reading signature from %s: status %d", url.String(), res.StatusCode)
|
||||||
|
}
|
||||||
|
sig, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return sig, false, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, false, fmt.Errorf("Unsupported scheme when reading signature from %s", url.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteImage deletes the named image from the registry, if supported.
|
||||||
|
func deleteImage(ctx *types.SystemContext, ref dockerReference) error {
|
||||||
|
c, err := newDockerClient(ctx, ref, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// When retrieving the digest from a registry >= 2.3 use the following header:
|
||||||
|
// "Accept": "application/vnd.docker.distribution.manifest.v2+json"
|
||||||
|
headers := make(map[string][]string)
|
||||||
|
headers["Accept"] = []string{manifest.DockerV2Schema2MediaType}
|
||||||
|
|
||||||
|
reference, err := ref.tagOrDigest()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
getURL := fmt.Sprintf(manifestURL, ref.ref.RemoteName(), reference)
|
||||||
|
get, err := c.makeRequest("GET", getURL, headers, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer get.Body.Close()
|
||||||
|
manifestBody, err := ioutil.ReadAll(get.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch get.StatusCode {
|
||||||
|
case http.StatusOK:
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return fmt.Errorf("Unable to delete %v. Image may not exist or is not stored with a v2 Schema in a v2 registry.", ref.ref)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Failed to delete %v: %s (%v)", ref.ref, manifestBody, get.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
digest := get.Header.Get("Docker-Content-Digest")
|
||||||
|
deleteURL := fmt.Sprintf(manifestURL, ref.ref.RemoteName(), digest)
|
||||||
|
|
||||||
|
// When retrieving the digest from a registry >= 2.3 use the following header:
|
||||||
|
// "Accept": "application/vnd.docker.distribution.manifest.v2+json"
|
||||||
|
delete, err := c.makeRequest("DELETE", deleteURL, headers, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer delete.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(delete.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if delete.StatusCode != http.StatusAccepted {
|
||||||
|
return fmt.Errorf("Failed to delete %v: %s (%v)", deleteURL, string(body), delete.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.signatureBase != nil {
|
||||||
|
manifestDigest, err := manifest.Digest(manifestBody)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
url := signatureStorageURL(c.signatureBase, manifestDigest, i)
|
||||||
|
if url == nil {
|
||||||
|
return fmt.Errorf("Internal error: signatureStorageURL with non-nil base returned nil")
|
||||||
|
}
|
||||||
|
missing, err := c.deleteOneSignature(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if missing {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
153
vendor/github.com/containers/image/docker/docker_transport.go
generated
vendored
Normal file
153
vendor/github.com/containers/image/docker/docker_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/docker/policyconfiguration"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/docker/docker/reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transport is an ImageTransport for Docker registry-hosted images.
|
||||||
|
var Transport = dockerTransport{}
|
||||||
|
|
||||||
|
type dockerTransport struct{}
|
||||||
|
|
||||||
|
func (t dockerTransport) Name() string {
|
||||||
|
return "docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
|
||||||
|
func (t dockerTransport) ParseReference(reference string) (types.ImageReference, error) {
|
||||||
|
return ParseReference(reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
||||||
|
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
|
||||||
|
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
|
||||||
|
// scope passed to this function will not be "", that value is always allowed.
|
||||||
|
func (t dockerTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
|
// FIXME? We could be verifying the various character set and length restrictions
|
||||||
|
// from docker/distribution/reference.regexp.go, but other than that there
|
||||||
|
// are few semantically invalid strings.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dockerReference is an ImageReference for Docker images.
|
||||||
|
type dockerReference struct {
|
||||||
|
ref reference.Named // By construction we know that !reference.IsNameOnly(ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an Docker ImageReference.
|
||||||
|
func ParseReference(refString string) (types.ImageReference, error) {
|
||||||
|
if !strings.HasPrefix(refString, "//") {
|
||||||
|
return nil, fmt.Errorf("docker: image reference %s does not start with //", refString)
|
||||||
|
}
|
||||||
|
ref, err := reference.ParseNamed(strings.TrimPrefix(refString, "//"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ref = reference.WithDefaultTag(ref)
|
||||||
|
return NewReference(ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReference returns a Docker reference for a named reference. The reference must satisfy !reference.IsNameOnly().
|
||||||
|
func NewReference(ref reference.Named) (types.ImageReference, error) {
|
||||||
|
if reference.IsNameOnly(ref) {
|
||||||
|
return nil, fmt.Errorf("Docker reference %s has neither a tag nor a digest", ref.String())
|
||||||
|
}
|
||||||
|
// A github.com/distribution/reference value can have a tag and a digest at the same time!
|
||||||
|
// docker/reference does not handle that, so fail.
|
||||||
|
// (Even if it were supported, the semantics of policy namespaces are unclear - should we drop
|
||||||
|
// the tag or the digest first?)
|
||||||
|
_, isTagged := ref.(reference.NamedTagged)
|
||||||
|
_, isDigested := ref.(reference.Canonical)
|
||||||
|
if isTagged && isDigested {
|
||||||
|
return nil, fmt.Errorf("Docker references with both a tag and digest are currently not supported")
|
||||||
|
}
|
||||||
|
return dockerReference{
|
||||||
|
ref: ref,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ref dockerReference) Transport() types.ImageTransport {
|
||||||
|
return Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringWithinTransport returns a string representation of the reference, which MUST be such that
|
||||||
|
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
|
||||||
|
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
|
||||||
|
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
|
||||||
|
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix.
|
||||||
|
func (ref dockerReference) StringWithinTransport() string {
|
||||||
|
return "//" + ref.ref.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerReference returns a Docker reference associated with this reference
|
||||||
|
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
|
||||||
|
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
|
||||||
|
func (ref dockerReference) DockerReference() reference.Named {
|
||||||
|
return ref.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
|
||||||
|
// This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases;
|
||||||
|
// The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical
|
||||||
|
// (i.e. various references with exactly the same semantics should return the same configuration identity)
|
||||||
|
// It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but
|
||||||
|
// not required/guaranteed that it will be a valid input to Transport().ParseReference().
|
||||||
|
// Returns "" if configuration identities for these references are not supported.
|
||||||
|
func (ref dockerReference) PolicyConfigurationIdentity() string {
|
||||||
|
res, err := policyconfiguration.DockerReferenceIdentity(ref.ref)
|
||||||
|
if res == "" || err != nil { // Coverage: Should never happen, NewReference above should refuse values which could cause a failure.
|
||||||
|
panic(fmt.Sprintf("Internal inconsistency: policyconfiguration.DockerReferenceIdentity returned %#v, %v", res, err))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
|
||||||
|
// for if explicit configuration for PolicyConfigurationIdentity() is not set. The list will be processed
|
||||||
|
// in order, terminating on first match, and an implicit "" is always checked at the end.
|
||||||
|
// It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(),
|
||||||
|
// and each following element to be a prefix of the element preceding it.
|
||||||
|
func (ref dockerReference) PolicyConfigurationNamespaces() []string {
|
||||||
|
return policyconfiguration.DockerReferenceNamespaces(ref.ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImage returns a types.Image for this reference.
|
||||||
|
// The caller must call .Close() on the returned Image.
|
||||||
|
func (ref dockerReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
||||||
|
return newImage(ctx, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageSource returns a types.ImageSource for this reference,
|
||||||
|
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
|
||||||
|
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||||
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
|
func (ref dockerReference) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||||
|
return newImageSource(ctx, ref, requestedManifestMIMETypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||||
|
// The caller must call .Close() on the returned ImageDestination.
|
||||||
|
func (ref dockerReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
|
return newImageDestination(ctx, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteImage deletes the named image from the registry, if supported.
|
||||||
|
func (ref dockerReference) DeleteImage(ctx *types.SystemContext) error {
|
||||||
|
return deleteImage(ctx, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagOrDigest returns a tag or digest from the reference.
|
||||||
|
func (ref dockerReference) tagOrDigest() (string, error) {
|
||||||
|
if ref, ok := ref.ref.(reference.Canonical); ok {
|
||||||
|
return ref.Digest().String(), nil
|
||||||
|
}
|
||||||
|
if ref, ok := ref.ref.(reference.NamedTagged); ok {
|
||||||
|
return ref.Tag(), nil
|
||||||
|
}
|
||||||
|
// This should not happen, NewReference above refuses reference.IsNameOnly values.
|
||||||
|
return "", fmt.Errorf("Internal inconsistency: Reference %s unexpectedly has neither a digest nor a tag", ref.ref.String())
|
||||||
|
}
|
198
vendor/github.com/containers/image/docker/lookaside.go
generated
vendored
Normal file
198
vendor/github.com/containers/image/docker/lookaside.go
generated
vendored
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// systemRegistriesDirPath is the path to registries.d, used for locating lookaside Docker signature storage.
|
||||||
|
// You can override this at build time with
|
||||||
|
// -ldflags '-X github.com/containers/image/docker.systemRegistriesDirPath=$your_path'
|
||||||
|
var systemRegistriesDirPath = builtinRegistriesDirPath
|
||||||
|
|
||||||
|
// builtinRegistriesDirPath is the path to registries.d.
|
||||||
|
// DO NOT change this, instead see systemRegistriesDirPath above.
|
||||||
|
const builtinRegistriesDirPath = "/etc/containers/registries.d"
|
||||||
|
|
||||||
|
// registryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all.
|
||||||
|
// NOTE: Keep this in sync with docs/registries.d.md!
|
||||||
|
type registryConfiguration struct {
|
||||||
|
DefaultDocker *registryNamespace `json:"default-docker"`
|
||||||
|
// The key is a namespace, using fully-expanded Docker reference format or parent namespaces (per dockerReference.PolicyConfiguration*),
|
||||||
|
Docker map[string]registryNamespace `json:"docker"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// registryNamespace defines lookaside locations for a single namespace.
|
||||||
|
type registryNamespace struct {
|
||||||
|
SigStore string `json:"sigstore"` // For reading, and if SigStoreStaging is not present, for writing.
|
||||||
|
SigStoreStaging string `json:"sigstore-staging"` // For writing only.
|
||||||
|
}
|
||||||
|
|
||||||
|
// signatureStorageBase is an "opaque" type representing a lookaside Docker signature storage.
|
||||||
|
// Users outside of this file should use configuredSignatureStorageBase and signatureStorageURL below.
|
||||||
|
type signatureStorageBase *url.URL // The only documented value is nil, meaning storage is not supported.
|
||||||
|
|
||||||
|
// configuredSignatureStorageBase reads configuration to find an appropriate signature storage URL for ref, for write access if “write”.
|
||||||
|
func configuredSignatureStorageBase(ctx *types.SystemContext, ref dockerReference, write bool) (signatureStorageBase, error) {
|
||||||
|
// FIXME? Loading and parsing the config could be cached across calls.
|
||||||
|
dirPath := registriesDirPath(ctx)
|
||||||
|
logrus.Debugf(`Using registries.d directory %s for sigstore configuration`, dirPath)
|
||||||
|
config, err := loadAndMergeConfig(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
topLevel := config.signatureTopLevel(ref, write)
|
||||||
|
if topLevel == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
url, err := url.Parse(topLevel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Invalid signature storage URL %s: %v", topLevel, err)
|
||||||
|
}
|
||||||
|
// FIXME? Restrict to explicitly supported schemes?
|
||||||
|
repo := ref.ref.FullName() // Note that this is without a tag or digest.
|
||||||
|
if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references
|
||||||
|
return nil, fmt.Errorf("Unexpected path elements in Docker reference %s for signature storage", ref.ref.String())
|
||||||
|
}
|
||||||
|
url.Path = url.Path + "/" + repo
|
||||||
|
return url, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registriesDirPath returns a path to registries.d
|
||||||
|
func registriesDirPath(ctx *types.SystemContext) string {
|
||||||
|
if ctx != nil {
|
||||||
|
if ctx.RegistriesDirPath != "" {
|
||||||
|
return ctx.RegistriesDirPath
|
||||||
|
}
|
||||||
|
if ctx.RootForImplicitAbsolutePaths != "" {
|
||||||
|
return filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesDirPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return systemRegistriesDirPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadAndMergeConfig loads configuration files in dirPath
|
||||||
|
func loadAndMergeConfig(dirPath string) (*registryConfiguration, error) {
|
||||||
|
mergedConfig := registryConfiguration{Docker: map[string]registryNamespace{}}
|
||||||
|
dockerDefaultMergedFrom := ""
|
||||||
|
nsMergedFrom := map[string]string{}
|
||||||
|
|
||||||
|
dir, err := os.Open(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return &mergedConfig, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
configNames, err := dir.Readdirnames(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, configName := range configNames {
|
||||||
|
if !strings.HasSuffix(configName, ".yaml") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
configPath := filepath.Join(dirPath, configName)
|
||||||
|
configBytes, err := ioutil.ReadFile(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var config registryConfiguration
|
||||||
|
err = yaml.Unmarshal(configBytes, &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing %s: %v", configPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DefaultDocker != nil {
|
||||||
|
if mergedConfig.DefaultDocker != nil {
|
||||||
|
return nil, fmt.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`,
|
||||||
|
dockerDefaultMergedFrom, configPath)
|
||||||
|
}
|
||||||
|
mergedConfig.DefaultDocker = config.DefaultDocker
|
||||||
|
dockerDefaultMergedFrom = configPath
|
||||||
|
}
|
||||||
|
|
||||||
|
for nsName, nsConfig := range config.Docker { // includes config.Docker == nil
|
||||||
|
if _, ok := mergedConfig.Docker[nsName]; ok {
|
||||||
|
return nil, fmt.Errorf(`Error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`,
|
||||||
|
nsName, nsMergedFrom[nsName], configPath)
|
||||||
|
}
|
||||||
|
mergedConfig.Docker[nsName] = nsConfig
|
||||||
|
nsMergedFrom[nsName] = configPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mergedConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// config.signatureTopLevel returns an URL string configured in config for ref, for write access if “write”.
|
||||||
|
// (the top level of the storage, namespaced by repo.FullName etc.), or "" if no signature storage should be used.
|
||||||
|
func (config *registryConfiguration) signatureTopLevel(ref dockerReference, write bool) string {
|
||||||
|
if config.Docker != nil {
|
||||||
|
// Look for a full match.
|
||||||
|
identity := ref.PolicyConfigurationIdentity()
|
||||||
|
if ns, ok := config.Docker[identity]; ok {
|
||||||
|
logrus.Debugf(` Using "docker" namespace %s`, identity)
|
||||||
|
if url := ns.signatureTopLevel(write); url != "" {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for a match of the possible parent namespaces.
|
||||||
|
for _, name := range ref.PolicyConfigurationNamespaces() {
|
||||||
|
if ns, ok := config.Docker[name]; ok {
|
||||||
|
logrus.Debugf(` Using "docker" namespace %s`, name)
|
||||||
|
if url := ns.signatureTopLevel(write); url != "" {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Look for a default location
|
||||||
|
if config.DefaultDocker != nil {
|
||||||
|
logrus.Debugf(` Using "default-docker" configuration`)
|
||||||
|
if url := config.DefaultDocker.signatureTopLevel(write); url != "" {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logrus.Debugf(" No signature storage configuration found for %s", ref.PolicyConfigurationIdentity())
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// ns.signatureTopLevel returns an URL string configured in ns for ref, for write access if “write”.
|
||||||
|
// or "" if nothing has been configured.
|
||||||
|
func (ns registryNamespace) signatureTopLevel(write bool) string {
|
||||||
|
if write && ns.SigStoreStaging != "" {
|
||||||
|
logrus.Debugf(` Using %s`, ns.SigStoreStaging)
|
||||||
|
return ns.SigStoreStaging
|
||||||
|
}
|
||||||
|
if ns.SigStore != "" {
|
||||||
|
logrus.Debugf(` Using %s`, ns.SigStore)
|
||||||
|
return ns.SigStore
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// signatureStorageURL returns an URL usable for acessing signature index in base with known manifestDigest, or nil if not applicable.
|
||||||
|
// Returns nil iff base == nil.
|
||||||
|
func signatureStorageURL(base signatureStorageBase, manifestDigest string, index int) *url.URL {
|
||||||
|
if base == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
url := *base
|
||||||
|
url.Path = fmt.Sprintf("%s@%s/signature-%d", url.Path, manifestDigest, index+1)
|
||||||
|
return &url
|
||||||
|
}
|
57
vendor/github.com/containers/image/docker/policyconfiguration/naming.go
generated
vendored
Normal file
57
vendor/github.com/containers/image/docker/policyconfiguration/naming.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package policyconfiguration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DockerReferenceIdentity returns a string representation of the reference, suitable for policy lookup,
|
||||||
|
// as a backend for ImageReference.PolicyConfigurationIdentity.
|
||||||
|
// The reference must satisfy !reference.IsNameOnly().
|
||||||
|
func DockerReferenceIdentity(ref reference.Named) (string, error) {
|
||||||
|
res := ref.FullName()
|
||||||
|
tagged, isTagged := ref.(reference.NamedTagged)
|
||||||
|
digested, isDigested := ref.(reference.Canonical)
|
||||||
|
switch {
|
||||||
|
case isTagged && isDigested: // This should not happen, docker/reference.ParseNamed drops the tag.
|
||||||
|
return "", fmt.Errorf("Unexpected Docker reference %s with both a name and a digest", ref.String())
|
||||||
|
case !isTagged && !isDigested: // This should not happen, the caller is expected to ensure !reference.IsNameOnly()
|
||||||
|
return "", fmt.Errorf("Internal inconsistency: Docker reference %s with neither a tag nor a digest", ref.String())
|
||||||
|
case isTagged:
|
||||||
|
res = res + ":" + tagged.Tag()
|
||||||
|
case isDigested:
|
||||||
|
res = res + "@" + digested.Digest().String()
|
||||||
|
default: // Coverage: The above was supposed to be exhaustive.
|
||||||
|
return "", errors.New("Internal inconsistency, unexpected default branch")
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerReferenceNamespaces returns a list of other policy configuration namespaces to search,
|
||||||
|
// as a backend for ImageReference.PolicyConfigurationIdentity.
|
||||||
|
// The reference must satisfy !reference.IsNameOnly().
|
||||||
|
func DockerReferenceNamespaces(ref reference.Named) []string {
|
||||||
|
// Look for a match of the repository, and then of the possible parent
|
||||||
|
// namespaces. Note that this only happens on the expanded host names
|
||||||
|
// and repository names, i.e. "busybox" is looked up as "docker.io/library/busybox",
|
||||||
|
// then in its parent "docker.io/library"; in none of "busybox",
|
||||||
|
// un-namespaced "library" nor in "" supposedly implicitly representing "library/".
|
||||||
|
//
|
||||||
|
// ref.FullName() == ref.Hostname() + "/" + ref.RemoteName(), so the last
|
||||||
|
// iteration matches the host name (for any namespace).
|
||||||
|
res := []string{}
|
||||||
|
name := ref.FullName()
|
||||||
|
for {
|
||||||
|
res = append(res, name)
|
||||||
|
|
||||||
|
lastSlash := strings.LastIndex(name, "/")
|
||||||
|
if lastSlash == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
name = name[:lastSlash]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
159
vendor/github.com/containers/image/docker/wwwauthenticate.go
generated
vendored
Normal file
159
vendor/github.com/containers/image/docker/wwwauthenticate.go
generated
vendored
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
// Based on github.com/docker/distribution/registry/client/auth/authchallenge.go, primarily stripping unnecessary dependencies.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// challenge carries information from a WWW-Authenticate response header.
|
||||||
|
// See RFC 7235.
|
||||||
|
type challenge struct {
|
||||||
|
// Scheme is the auth-scheme according to RFC 7235
|
||||||
|
Scheme string
|
||||||
|
|
||||||
|
// Parameters are the auth-params according to RFC 7235
|
||||||
|
Parameters map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Octet types from RFC 7230.
|
||||||
|
type octetType byte
|
||||||
|
|
||||||
|
var octetTypes [256]octetType
|
||||||
|
|
||||||
|
const (
|
||||||
|
isToken octetType = 1 << iota
|
||||||
|
isSpace
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// OCTET = <any 8-bit sequence of data>
|
||||||
|
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||||
|
// CR = <US-ASCII CR, carriage return (13)>
|
||||||
|
// LF = <US-ASCII LF, linefeed (10)>
|
||||||
|
// SP = <US-ASCII SP, space (32)>
|
||||||
|
// HT = <US-ASCII HT, horizontal-tab (9)>
|
||||||
|
// <"> = <US-ASCII double-quote mark (34)>
|
||||||
|
// CRLF = CR LF
|
||||||
|
// LWS = [CRLF] 1*( SP | HT )
|
||||||
|
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||||
|
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
|
||||||
|
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
|
||||||
|
// token = 1*<any CHAR except CTLs or separators>
|
||||||
|
// qdtext = <any TEXT except <">>
|
||||||
|
|
||||||
|
for c := 0; c < 256; c++ {
|
||||||
|
var t octetType
|
||||||
|
isCtl := c <= 31 || c == 127
|
||||||
|
isChar := 0 <= c && c <= 127
|
||||||
|
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
|
||||||
|
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
|
||||||
|
t |= isSpace
|
||||||
|
}
|
||||||
|
if isChar && !isCtl && !isSeparator {
|
||||||
|
t |= isToken
|
||||||
|
}
|
||||||
|
octetTypes[c] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAuthHeader(header http.Header) []challenge {
|
||||||
|
challenges := []challenge{}
|
||||||
|
for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] {
|
||||||
|
v, p := parseValueAndParams(h)
|
||||||
|
if v != "" {
|
||||||
|
challenges = append(challenges, challenge{Scheme: v, Parameters: p})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return challenges
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This is not a fully compliant parser per RFC 7235:
|
||||||
|
// Most notably it does not support more than one challenge within a single header
|
||||||
|
// Some of the whitespace parsing also seems noncompliant.
|
||||||
|
// But it is clearly better than what we used to have…
|
||||||
|
func parseValueAndParams(header string) (value string, params map[string]string) {
|
||||||
|
params = make(map[string]string)
|
||||||
|
value, s := expectToken(header)
|
||||||
|
if value == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
value = strings.ToLower(value)
|
||||||
|
s = "," + skipSpace(s)
|
||||||
|
for strings.HasPrefix(s, ",") {
|
||||||
|
var pkey string
|
||||||
|
pkey, s = expectToken(skipSpace(s[1:]))
|
||||||
|
if pkey == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(s, "=") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pvalue string
|
||||||
|
pvalue, s = expectTokenOrQuoted(s[1:])
|
||||||
|
if pvalue == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pkey = strings.ToLower(pkey)
|
||||||
|
params[pkey] = pvalue
|
||||||
|
s = skipSpace(s)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipSpace(s string) (rest string) {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
if octetTypes[s[i]]&isSpace == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectToken(s string) (token, rest string) {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
if octetTypes[s[i]]&isToken == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s[:i], s[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectTokenOrQuoted(s string) (value string, rest string) {
|
||||||
|
if !strings.HasPrefix(s, "\"") {
|
||||||
|
return expectToken(s)
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '"':
|
||||||
|
return s[:i], s[i+1:]
|
||||||
|
case '\\':
|
||||||
|
p := make([]byte, len(s)-1)
|
||||||
|
j := copy(p, s[:i])
|
||||||
|
escape := true
|
||||||
|
for i = i + 1; i < len(s); i++ {
|
||||||
|
b := s[i]
|
||||||
|
switch {
|
||||||
|
case escape:
|
||||||
|
escape = false
|
||||||
|
p[j] = b
|
||||||
|
j++
|
||||||
|
case b == '\\':
|
||||||
|
escape = true
|
||||||
|
case b == '"':
|
||||||
|
return string(p[:j]), s[i+1:]
|
||||||
|
default:
|
||||||
|
p[j] = b
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
365
vendor/github.com/containers/image/image/image.go
generated
vendored
Normal file
365
vendor/github.com/containers/image/image/image.go
generated
vendored
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
// Package image consolidates knowledge about various container image formats
|
||||||
|
// (as opposed to image storage mechanisms, which are handled by types.ImageSource)
|
||||||
|
// and exposes all of them using an unified interface.
|
||||||
|
package image
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// genericImage is a general set of utilities for working with container images,
|
||||||
|
// whatever is their underlying location (i.e. dockerImageSource-independent).
|
||||||
|
// Note the existence of skopeo/docker.Image: some instances of a `types.Image`
|
||||||
|
// may not be a `genericImage` directly. However, most users of `types.Image`
|
||||||
|
// do not care, and those who care about `skopeo/docker.Image` know they do.
|
||||||
|
type genericImage struct {
|
||||||
|
src types.ImageSource
|
||||||
|
// private cache for Manifest(); nil if not yet known.
|
||||||
|
cachedManifest []byte
|
||||||
|
// private cache for the manifest media type w/o having to guess it
|
||||||
|
// this may be the empty string in case the MIME Type wasn't guessed correctly
|
||||||
|
// this field is valid only if cachedManifest is not nil
|
||||||
|
cachedManifestMIMEType string
|
||||||
|
// private cache for Signatures(); nil if not yet known.
|
||||||
|
cachedSignatures [][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromSource returns a types.Image implementation for source.
|
||||||
|
// The caller must call .Close() on the returned Image.
|
||||||
|
//
|
||||||
|
// FromSource “takes ownership” of the input ImageSource and will call src.Close()
|
||||||
|
// when the image is closed. (This does not prevent callers from using both the
|
||||||
|
// Image and ImageSource objects simultaneously, but it means that they only need to
|
||||||
|
// the Image.)
|
||||||
|
func FromSource(src types.ImageSource) types.Image {
|
||||||
|
return &genericImage{src: src}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this source, _as specified by the user_
|
||||||
|
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
|
||||||
|
func (i *genericImage) Reference() types.ImageReference {
|
||||||
|
return i.src.Reference()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized Image, if any.
|
||||||
|
func (i *genericImage) Close() {
|
||||||
|
i.src.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
|
||||||
|
// NOTE: It is essential for signature verification that Manifest returns the manifest from which BlobDigests is computed.
|
||||||
|
func (i *genericImage) Manifest() ([]byte, string, error) {
|
||||||
|
if i.cachedManifest == nil {
|
||||||
|
m, mt, err := i.src.GetManifest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
i.cachedManifest = m
|
||||||
|
if mt == "" || mt == "text/plain" {
|
||||||
|
// Crane registries can return "text/plain".
|
||||||
|
// This makes no real sense, but it happens
|
||||||
|
// because requests for manifests are
|
||||||
|
// redirected to a content distribution
|
||||||
|
// network which is configured that way.
|
||||||
|
mt = manifest.GuessMIMEType(i.cachedManifest)
|
||||||
|
}
|
||||||
|
i.cachedManifestMIMEType = mt
|
||||||
|
}
|
||||||
|
return i.cachedManifest, i.cachedManifestMIMEType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
||||||
|
func (i *genericImage) Signatures() ([][]byte, error) {
|
||||||
|
if i.cachedSignatures == nil {
|
||||||
|
sigs, err := i.src.GetSignatures()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i.cachedSignatures = sigs
|
||||||
|
}
|
||||||
|
return i.cachedSignatures, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *genericImage) Inspect() (*types.ImageInspectInfo, error) {
|
||||||
|
// TODO(runcom): unused version param for now, default to docker v2-1
|
||||||
|
m, err := i.getParsedManifest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m.ImageInspectInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Labels map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type v1Image struct {
|
||||||
|
// Config is the configuration of the container received from the client
|
||||||
|
Config *config `json:"config,omitempty"`
|
||||||
|
// DockerVersion specifies version on which image is built
|
||||||
|
DockerVersion string `json:"docker_version,omitempty"`
|
||||||
|
// Created timestamp when image was created
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// Architecture is the hardware that the image is build and runs on
|
||||||
|
Architecture string `json:"architecture,omitempty"`
|
||||||
|
// OS is the operating system used to build and run the image
|
||||||
|
OS string `json:"os,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// will support v1 one day...
|
||||||
|
type genericManifest interface {
|
||||||
|
Config() ([]byte, error)
|
||||||
|
LayerDigests() []string
|
||||||
|
BlobDigests() []string
|
||||||
|
ImageInspectInfo() (*types.ImageInspectInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type fsLayersSchema1 struct {
|
||||||
|
BlobSum string `json:"blobSum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile-time check that manifestSchema1 implements genericManifest
|
||||||
|
var _ genericManifest = (*manifestSchema1)(nil)
|
||||||
|
|
||||||
|
type manifestSchema1 struct {
|
||||||
|
Name string
|
||||||
|
Tag string
|
||||||
|
FSLayers []fsLayersSchema1 `json:"fsLayers"`
|
||||||
|
History []struct {
|
||||||
|
V1Compatibility string `json:"v1Compatibility"`
|
||||||
|
} `json:"history"`
|
||||||
|
// TODO(runcom) verify the downloaded manifest
|
||||||
|
//Signature []byte `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema1) LayerDigests() []string {
|
||||||
|
layers := make([]string, len(m.FSLayers))
|
||||||
|
for i, layer := range m.FSLayers {
|
||||||
|
layers[i] = layer.BlobSum
|
||||||
|
}
|
||||||
|
return layers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema1) BlobDigests() []string {
|
||||||
|
return m.LayerDigests()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema1) Config() ([]byte, error) {
|
||||||
|
return []byte(m.History[0].V1Compatibility), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema1) ImageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
|
v1 := &v1Image{}
|
||||||
|
config, err := m.Config()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(config, v1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &types.ImageInspectInfo{
|
||||||
|
Tag: m.Tag,
|
||||||
|
DockerVersion: v1.DockerVersion,
|
||||||
|
Created: v1.Created,
|
||||||
|
Labels: v1.Config.Labels,
|
||||||
|
Architecture: v1.Architecture,
|
||||||
|
Os: v1.OS,
|
||||||
|
Layers: m.LayerDigests(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile-time check that manifestSchema2 implements genericManifest
|
||||||
|
var _ genericManifest = (*manifestSchema2)(nil)
|
||||||
|
|
||||||
|
type manifestSchema2 struct {
|
||||||
|
src types.ImageSource
|
||||||
|
ConfigDescriptor descriptor `json:"config"`
|
||||||
|
LayersDescriptors []descriptor `json:"layers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type descriptor struct {
|
||||||
|
MediaType string `json:"mediaType"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Digest string `json:"digest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema2) LayerDigests() []string {
|
||||||
|
blobs := []string{}
|
||||||
|
for _, layer := range m.LayersDescriptors {
|
||||||
|
blobs = append(blobs, layer.Digest)
|
||||||
|
}
|
||||||
|
return blobs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema2) BlobDigests() []string {
|
||||||
|
blobs := m.LayerDigests()
|
||||||
|
blobs = append(blobs, m.ConfigDescriptor.Digest)
|
||||||
|
return blobs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema2) Config() ([]byte, error) {
|
||||||
|
rawConfig, _, err := m.src.GetBlob(m.ConfigDescriptor.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config, err := ioutil.ReadAll(rawConfig)
|
||||||
|
rawConfig.Close()
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema2) ImageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
|
config, err := m.Config()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v1 := &v1Image{}
|
||||||
|
if err := json.Unmarshal(config, v1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &types.ImageInspectInfo{
|
||||||
|
DockerVersion: v1.DockerVersion,
|
||||||
|
Created: v1.Created,
|
||||||
|
Labels: v1.Config.Labels,
|
||||||
|
Architecture: v1.Architecture,
|
||||||
|
Os: v1.OS,
|
||||||
|
Layers: m.LayerDigests(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getParsedManifest parses the manifest into a data structure, cleans it up, and returns it.
|
||||||
|
// NOTE: The manifest may have been modified in the process; DO NOT reserialize and store the return value
|
||||||
|
// if you want to preserve the original manifest; use the blob returned by Manifest() directly.
|
||||||
|
// NOTE: It is essential for signature verification that the object is computed from the same manifest which is returned by Manifest().
|
||||||
|
func (i *genericImage) getParsedManifest() (genericManifest, error) {
|
||||||
|
manblob, mt, err := i.Manifest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch mt {
|
||||||
|
// "application/json" is a valid v2s1 value per https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md .
|
||||||
|
// This works for now, when nothing else seems to return "application/json"; if that were not true, the mapping/detection might
|
||||||
|
// need to happen within the ImageSource.
|
||||||
|
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType, "application/json":
|
||||||
|
mschema1 := &manifestSchema1{}
|
||||||
|
if err := json.Unmarshal(manblob, mschema1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := fixManifestLayers(mschema1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// TODO(runcom): verify manifest schema 1, 2 etc
|
||||||
|
//if len(m.FSLayers) != len(m.History) {
|
||||||
|
//return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
|
||||||
|
//}
|
||||||
|
//if len(m.FSLayers) == 0 {
|
||||||
|
//return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
|
||||||
|
//}
|
||||||
|
return mschema1, nil
|
||||||
|
case manifest.DockerV2Schema2MediaType:
|
||||||
|
v2s2 := manifestSchema2{src: i.src}
|
||||||
|
if err := json.Unmarshal(manblob, &v2s2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &v2s2, nil
|
||||||
|
case "":
|
||||||
|
return nil, errors.New("could not guess manifest media type")
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported manifest media type %s", mt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// uniqueBlobDigests returns a list of blob digests referenced from a manifest.
|
||||||
|
// The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image.
|
||||||
|
func uniqueBlobDigests(m genericManifest) []string {
|
||||||
|
var res []string
|
||||||
|
seen := make(map[string]struct{})
|
||||||
|
for _, digest := range m.BlobDigests() {
|
||||||
|
if _, ok := seen[digest]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[digest] = struct{}{}
|
||||||
|
res = append(res, digest)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlobDigests returns a list of blob digests referenced by this image.
|
||||||
|
// The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image.
|
||||||
|
// NOTE: It is essential for signature verification that BlobDigests is computed from the same manifest which is returned by Manifest().
|
||||||
|
func (i *genericImage) BlobDigests() ([]string, error) {
|
||||||
|
m, err := i.getParsedManifest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return uniqueBlobDigests(m), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixManifestLayers, after validating the supplied manifest
|
||||||
|
// (to use correctly-formatted IDs, and to not have non-consecutive ID collisions in manifest.History),
|
||||||
|
// modifies manifest to only have one entry for each layer ID in manifest.History (deleting the older duplicates,
|
||||||
|
// both from manifest.History and manifest.FSLayers).
|
||||||
|
// Note that even after this succeeds, manifest.FSLayers may contain duplicate entries
|
||||||
|
// (for Dockerfile operations which change the configuration but not the filesystem).
|
||||||
|
func fixManifestLayers(manifest *manifestSchema1) error {
|
||||||
|
type imageV1 struct {
|
||||||
|
ID string
|
||||||
|
Parent string
|
||||||
|
}
|
||||||
|
// Per the specification, we can assume that len(manifest.FSLayers) == len(manifest.History)
|
||||||
|
imgs := make([]*imageV1, len(manifest.FSLayers))
|
||||||
|
for i := range manifest.FSLayers {
|
||||||
|
img := &imageV1{}
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(manifest.History[i].V1Compatibility), img); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
imgs[i] = img
|
||||||
|
if err := validateV1ID(img.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if imgs[len(imgs)-1].Parent != "" {
|
||||||
|
return errors.New("Invalid parent ID in the base layer of the image.")
|
||||||
|
}
|
||||||
|
// check general duplicates to error instead of a deadlock
|
||||||
|
idmap := make(map[string]struct{})
|
||||||
|
var lastID string
|
||||||
|
for _, img := range imgs {
|
||||||
|
// skip IDs that appear after each other, we handle those later
|
||||||
|
if _, exists := idmap[img.ID]; img.ID != lastID && exists {
|
||||||
|
return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID)
|
||||||
|
}
|
||||||
|
lastID = img.ID
|
||||||
|
idmap[lastID] = struct{}{}
|
||||||
|
}
|
||||||
|
// backwards loop so that we keep the remaining indexes after removing items
|
||||||
|
for i := len(imgs) - 2; i >= 0; i-- {
|
||||||
|
if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
|
||||||
|
manifest.FSLayers = append(manifest.FSLayers[:i], manifest.FSLayers[i+1:]...)
|
||||||
|
manifest.History = append(manifest.History[:i], manifest.History[i+1:]...)
|
||||||
|
} else if imgs[i].Parent != imgs[i+1].ID {
|
||||||
|
return fmt.Errorf("Invalid parent ID. Expected %v, got %v.", imgs[i+1].ID, imgs[i].Parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateV1ID(id string) error {
|
||||||
|
if ok := validHex.MatchString(id); !ok {
|
||||||
|
return fmt.Errorf("image ID %q is invalid", id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
97
vendor/github.com/containers/image/manifest/manifest.go
generated
vendored
Normal file
97
vendor/github.com/containers/image/manifest/manifest.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FIXME: Should we just use docker/distribution and docker/docker implementations directly?
|
||||||
|
|
||||||
|
// FIXME(runcom, mitr): should we havea mediatype pkg??
|
||||||
|
const (
|
||||||
|
// DockerV2Schema1MediaType MIME type represents Docker manifest schema 1
|
||||||
|
DockerV2Schema1MediaType = "application/vnd.docker.distribution.manifest.v1+json"
|
||||||
|
// DockerV2Schema1MediaType MIME type represents Docker manifest schema 1 with a JWS signature
|
||||||
|
DockerV2Schema1SignedMediaType = "application/vnd.docker.distribution.manifest.v1+prettyjws"
|
||||||
|
// DockerV2Schema2MediaType MIME type represents Docker manifest schema 2
|
||||||
|
DockerV2Schema2MediaType = "application/vnd.docker.distribution.manifest.v2+json"
|
||||||
|
// DockerV2ListMediaType MIME type represents Docker manifest schema 2 list
|
||||||
|
DockerV2ListMediaType = "application/vnd.docker.distribution.manifest.list.v2+json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultRequestedManifestMIMETypes is a list of MIME types a types.ImageSource
|
||||||
|
// should request from the backend unless directed otherwise.
|
||||||
|
var DefaultRequestedManifestMIMETypes = []string{
|
||||||
|
imgspecv1.MediaTypeImageManifest,
|
||||||
|
DockerV2Schema2MediaType,
|
||||||
|
DockerV2Schema1SignedMediaType,
|
||||||
|
DockerV2Schema1MediaType,
|
||||||
|
}
|
||||||
|
|
||||||
|
// GuessMIMEType guesses MIME type of a manifest and returns it _if it is recognized_, or "" if unknown or unrecognized.
|
||||||
|
// FIXME? We should, in general, prefer out-of-band MIME type instead of blindly parsing the manifest,
|
||||||
|
// but we may not have such metadata available (e.g. when the manifest is a local file).
|
||||||
|
func GuessMIMEType(manifest []byte) string {
|
||||||
|
// A subset of manifest fields; the rest is silently ignored by json.Unmarshal.
|
||||||
|
// Also docker/distribution/manifest.Versioned.
|
||||||
|
meta := struct {
|
||||||
|
MediaType string `json:"mediaType"`
|
||||||
|
SchemaVersion int `json:"schemaVersion"`
|
||||||
|
Signatures interface{} `json:"signatures"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(manifest, &meta); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
switch meta.MediaType {
|
||||||
|
case DockerV2Schema2MediaType, DockerV2ListMediaType, imgspecv1.MediaTypeImageManifest, imgspecv1.MediaTypeImageManifestList: // A recognized type.
|
||||||
|
return meta.MediaType
|
||||||
|
}
|
||||||
|
// this is the only way the function can return DockerV2Schema1MediaType, and recognizing that is essential for stripping the JWS signatures = computing the correct manifest digest.
|
||||||
|
switch meta.SchemaVersion {
|
||||||
|
case 1:
|
||||||
|
if meta.Signatures != nil {
|
||||||
|
return DockerV2Schema1SignedMediaType
|
||||||
|
}
|
||||||
|
return DockerV2Schema1MediaType
|
||||||
|
case 2: // Really should not happen, meta.MediaType should have been set. But given the data, this is our best guess.
|
||||||
|
return DockerV2Schema2MediaType
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Digest returns the a digest of a docker manifest, with any necessary implied transformations like stripping v1s1 signatures.
|
||||||
|
func Digest(manifest []byte) (string, error) {
|
||||||
|
if GuessMIMEType(manifest) == DockerV2Schema1SignedMediaType {
|
||||||
|
sig, err := libtrust.ParsePrettySignature(manifest, "signatures")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
manifest, err = sig.Payload()
|
||||||
|
if err != nil {
|
||||||
|
// Coverage: This should never happen, libtrust's Payload() can fail only if joseBase64UrlDecode() fails, on a string
|
||||||
|
// that libtrust itself has josebase64UrlEncode()d
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.Sum256(manifest)
|
||||||
|
return "sha256:" + hex.EncodeToString(hash[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesDigest returns true iff the manifest matches expectedDigest.
|
||||||
|
// Error may be set if this returns false.
|
||||||
|
// Note that this is not doing ConstantTimeCompare; by the time we get here, the cryptographic signature must already have been verified,
|
||||||
|
// or we are not using a cryptographic channel and the attacker can modify the digest along with the manifest blob.
|
||||||
|
func MatchesDigest(manifest []byte, expectedDigest string) (bool, error) {
|
||||||
|
// This should eventually support various digest types.
|
||||||
|
actualDigest, err := Digest(manifest)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return expectedDigest == actualDigest, nil
|
||||||
|
}
|
206
vendor/github.com/containers/image/oci/layout/oci_dest.go
generated
vendored
Normal file
206
vendor/github.com/containers/image/oci/layout/oci_dest.go
generated
vendored
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
package layout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
imgspec "github.com/opencontainers/image-spec/specs-go"
|
||||||
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ociImageDestination struct {
|
||||||
|
ref ociReference
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageDestination returns an ImageDestination for writing to an existing directory.
|
||||||
|
func newImageDestination(ref ociReference) types.ImageDestination {
|
||||||
|
return &ociImageDestination{ref: ref}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
||||||
|
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
|
||||||
|
func (d *ociImageDestination) Reference() types.ImageReference {
|
||||||
|
return d.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized ImageDestination, if any.
|
||||||
|
func (d *ociImageDestination) Close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ociImageDestination) SupportedManifestMIMETypes() []string {
|
||||||
|
return []string{
|
||||||
|
imgspecv1.MediaTypeImageManifest,
|
||||||
|
manifest.DockerV2Schema2MediaType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
|
||||||
|
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
|
||||||
|
func (d *ociImageDestination) SupportsSignatures() error {
|
||||||
|
return fmt.Errorf("Pushing signatures for OCI images is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutBlob writes contents of stream and returns its computed digest and size.
|
||||||
|
// A digest can be optionally provided if known, the specific image destination can decide to play with it or not.
|
||||||
|
// The length of stream is expected to be expectedSize; if expectedSize == -1, it is not known.
|
||||||
|
// WARNING: The contents of stream are being verified on the fly. Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
|
||||||
|
// to any other readers for download using the supplied digest.
|
||||||
|
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
||||||
|
func (d *ociImageDestination) PutBlob(stream io.Reader, _ string, expectedSize int64) (string, int64, error) {
|
||||||
|
if err := ensureDirectoryExists(d.ref.dir); err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
blobFile, err := ioutil.TempFile(d.ref.dir, "oci-put-blob")
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
succeeded := false
|
||||||
|
defer func() {
|
||||||
|
blobFile.Close()
|
||||||
|
if !succeeded {
|
||||||
|
os.Remove(blobFile.Name())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
tee := io.TeeReader(stream, h)
|
||||||
|
|
||||||
|
size, err := io.Copy(blobFile, tee)
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
computedDigest := "sha256:" + hex.EncodeToString(h.Sum(nil))
|
||||||
|
if expectedSize != -1 && size != expectedSize {
|
||||||
|
return "", -1, fmt.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, expectedSize, size)
|
||||||
|
}
|
||||||
|
if err := blobFile.Sync(); err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
if err := blobFile.Chmod(0644); err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blobPath, err := d.ref.blobPath(computedDigest)
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
if err := ensureParentDirectoryExists(blobPath); err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
if err := os.Rename(blobFile.Name(), blobPath); err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
succeeded = true
|
||||||
|
return computedDigest, size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createManifest(m []byte) ([]byte, string, error) {
|
||||||
|
om := imgspecv1.Manifest{}
|
||||||
|
mt := manifest.GuessMIMEType(m)
|
||||||
|
switch mt {
|
||||||
|
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
|
||||||
|
// There a simple reason about not yet implementing this.
|
||||||
|
// OCI image-spec assure about backward compatibility with docker v2s2 but not v2s1
|
||||||
|
// generating a v2s2 is a migration docker does when upgrading to 1.10.3
|
||||||
|
// and I don't think we should bother about this now (I don't want to have migration code here in skopeo)
|
||||||
|
return nil, "", errors.New("can't create an OCI manifest from Docker V2 schema 1 manifest")
|
||||||
|
case manifest.DockerV2Schema2MediaType:
|
||||||
|
if err := json.Unmarshal(m, &om); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
om.MediaType = imgspecv1.MediaTypeImageManifest
|
||||||
|
for i := range om.Layers {
|
||||||
|
om.Layers[i].MediaType = imgspecv1.MediaTypeImageLayer
|
||||||
|
}
|
||||||
|
om.Config.MediaType = imgspecv1.MediaTypeImageConfig
|
||||||
|
b, err := json.Marshal(om)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return b, om.MediaType, nil
|
||||||
|
case manifest.DockerV2ListMediaType:
|
||||||
|
return nil, "", errors.New("can't create an OCI manifest from Docker V2 schema 2 manifest list")
|
||||||
|
case imgspecv1.MediaTypeImageManifestList:
|
||||||
|
return nil, "", errors.New("can't create an OCI manifest from OCI manifest list")
|
||||||
|
case imgspecv1.MediaTypeImageManifest:
|
||||||
|
return m, mt, nil
|
||||||
|
}
|
||||||
|
return nil, "", fmt.Errorf("unrecognized manifest media type %q", mt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ociImageDestination) PutManifest(m []byte) error {
|
||||||
|
// TODO(mitr, runcom): this breaks signatures entirely since at this point we're creating a new manifest
|
||||||
|
// and signatures don't apply anymore. Will fix.
|
||||||
|
ociMan, mt, err := createManifest(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
digest, err := manifest.Digest(ociMan)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
desc := imgspec.Descriptor{}
|
||||||
|
desc.Digest = digest
|
||||||
|
// TODO(runcom): beaware and add support for OCI manifest list
|
||||||
|
desc.MediaType = mt
|
||||||
|
desc.Size = int64(len(ociMan))
|
||||||
|
data, err := json.Marshal(desc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
blobPath, err := d.ref.blobPath(digest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(blobPath, ociMan, 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO(runcom): ugly here?
|
||||||
|
if err := ioutil.WriteFile(d.ref.ociLayoutPath(), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
descriptorPath := d.ref.descriptorPath(d.ref.tag)
|
||||||
|
if err := ensureParentDirectoryExists(descriptorPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(descriptorPath, data, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureDirectoryExists(path string) error {
|
||||||
|
if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(path, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureParentDirectoryExists ensures the parent of the supplied path exists.
|
||||||
|
func ensureParentDirectoryExists(path string) error {
|
||||||
|
return ensureDirectoryExists(filepath.Dir(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ociImageDestination) PutSignatures(signatures [][]byte) error {
|
||||||
|
if len(signatures) != 0 {
|
||||||
|
return fmt.Errorf("Pushing signatures for OCI images is not supported")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit marks the process of storing the image as successful and asks for the image to be persisted.
|
||||||
|
// WARNING: This does not have any transactional semantics:
|
||||||
|
// - Uploaded data MAY be visible to others before Commit() is called
|
||||||
|
// - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed)
|
||||||
|
func (d *ociImageDestination) Commit() error {
|
||||||
|
return nil
|
||||||
|
}
|
209
vendor/github.com/containers/image/oci/layout/oci_transport.go
generated
vendored
Normal file
209
vendor/github.com/containers/image/oci/layout/oci_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
package layout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/directory/explicitfilepath"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/docker/docker/reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transport is an ImageTransport for OCI directories.
|
||||||
|
var Transport = ociTransport{}
|
||||||
|
|
||||||
|
type ociTransport struct{}
|
||||||
|
|
||||||
|
func (t ociTransport) Name() string {
|
||||||
|
return "oci"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
|
||||||
|
func (t ociTransport) ParseReference(reference string) (types.ImageReference, error) {
|
||||||
|
return ParseReference(reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
var refRegexp = regexp.MustCompile(`^([A-Za-z0-9._-]+)+$`)
|
||||||
|
|
||||||
|
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
||||||
|
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
|
||||||
|
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
|
||||||
|
// scope passed to this function will not be "", that value is always allowed.
|
||||||
|
func (t ociTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
|
var dir string
|
||||||
|
sep := strings.LastIndex(scope, ":")
|
||||||
|
if sep == -1 {
|
||||||
|
dir = scope
|
||||||
|
} else {
|
||||||
|
dir = scope[:sep]
|
||||||
|
tag := scope[sep+1:]
|
||||||
|
if !refRegexp.MatchString(tag) {
|
||||||
|
return fmt.Errorf("Invalid tag %s", tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(dir, ":") {
|
||||||
|
return fmt.Errorf("Invalid OCI reference %s: path contains a colon", scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(dir, "/") {
|
||||||
|
return fmt.Errorf("Invalid scope %s: must be an absolute path", scope)
|
||||||
|
}
|
||||||
|
// Refuse also "/", otherwise "/" and "" would have the same semantics,
|
||||||
|
// and "" could be unexpectedly shadowed by the "/" entry.
|
||||||
|
// (Note: we do allow "/:sometag", a bit ridiculous but why refuse it?)
|
||||||
|
if scope == "/" {
|
||||||
|
return errors.New(`Invalid scope "/": Use the generic default scope ""`)
|
||||||
|
}
|
||||||
|
cleaned := filepath.Clean(dir)
|
||||||
|
if cleaned != dir {
|
||||||
|
return fmt.Errorf(`Invalid scope %s: Uses non-canonical path format, perhaps try with path %s`, scope, cleaned)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ociReference is an ImageReference for OCI directory paths.
|
||||||
|
type ociReference struct {
|
||||||
|
// Note that the interpretation of paths below depends on the underlying filesystem state, which may change under us at any time!
|
||||||
|
// Either of the paths may point to a different, or no, inode over time. resolvedDir may contain symbolic links, and so on.
|
||||||
|
|
||||||
|
// Generally we follow the intent of the user, and use the "dir" member for filesystem operations (e.g. the user can use a relative path to avoid
|
||||||
|
// being exposed to symlinks and renames in the parent directories to the working directory).
|
||||||
|
// (But in general, we make no attempt to be completely safe against concurrent hostile filesystem modifications.)
|
||||||
|
dir string // As specified by the user. May be relative, contain symlinks, etc.
|
||||||
|
resolvedDir string // Absolute path with no symlinks, at least at the time of its creation. Primarily used for policy namespaces.
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference.
|
||||||
|
func ParseReference(reference string) (types.ImageReference, error) {
|
||||||
|
var dir, tag string
|
||||||
|
sep := strings.LastIndex(reference, ":")
|
||||||
|
if sep == -1 {
|
||||||
|
dir = reference
|
||||||
|
tag = "latest"
|
||||||
|
} else {
|
||||||
|
dir = reference[:sep]
|
||||||
|
tag = reference[sep+1:]
|
||||||
|
}
|
||||||
|
return NewReference(dir, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReference returns an OCI reference for a directory and a tag.
|
||||||
|
//
|
||||||
|
// We do not expose an API supplying the resolvedDir; we could, but recomputing it
|
||||||
|
// is generally cheap enough that we prefer being confident about the properties of resolvedDir.
|
||||||
|
func NewReference(dir, tag string) (types.ImageReference, error) {
|
||||||
|
resolved, err := explicitfilepath.ResolvePathToFullyExplicit(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces
|
||||||
|
// from being ambiguous with values of PolicyConfigurationIdentity.
|
||||||
|
if strings.Contains(resolved, ":") {
|
||||||
|
return nil, fmt.Errorf("Invalid OCI reference %s:%s: path %s contains a colon", dir, tag, resolved)
|
||||||
|
}
|
||||||
|
if !refRegexp.MatchString(tag) {
|
||||||
|
return nil, fmt.Errorf("Invalid tag %s", tag)
|
||||||
|
}
|
||||||
|
return ociReference{dir: dir, resolvedDir: resolved, tag: tag}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ref ociReference) Transport() types.ImageTransport {
|
||||||
|
return Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringWithinTransport returns a string representation of the reference, which MUST be such that
|
||||||
|
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
|
||||||
|
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
|
||||||
|
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
|
||||||
|
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix.
|
||||||
|
func (ref ociReference) StringWithinTransport() string {
|
||||||
|
return fmt.Sprintf("%s:%s", ref.dir, ref.tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerReference returns a Docker reference associated with this reference
|
||||||
|
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
|
||||||
|
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
|
||||||
|
func (ref ociReference) DockerReference() reference.Named {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
|
||||||
|
// This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases;
|
||||||
|
// The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical
|
||||||
|
// (i.e. various references with exactly the same semantics should return the same configuration identity)
|
||||||
|
// It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but
|
||||||
|
// not required/guaranteed that it will be a valid input to Transport().ParseReference().
|
||||||
|
// Returns "" if configuration identities for these references are not supported.
|
||||||
|
func (ref ociReference) PolicyConfigurationIdentity() string {
|
||||||
|
return fmt.Sprintf("%s:%s", ref.resolvedDir, ref.tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
|
||||||
|
// for if explicit configuration for PolicyConfigurationIdentity() is not set. The list will be processed
|
||||||
|
// in order, terminating on first match, and an implicit "" is always checked at the end.
|
||||||
|
// It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(),
|
||||||
|
// and each following element to be a prefix of the element preceding it.
|
||||||
|
func (ref ociReference) PolicyConfigurationNamespaces() []string {
|
||||||
|
res := []string{}
|
||||||
|
path := ref.resolvedDir
|
||||||
|
for {
|
||||||
|
lastSlash := strings.LastIndex(path, "/")
|
||||||
|
// Note that we do not include "/"; it is redundant with the default "" global default,
|
||||||
|
// and rejected by ociTransport.ValidatePolicyConfigurationScope above.
|
||||||
|
if lastSlash == -1 || path == "/" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res = append(res, path)
|
||||||
|
path = path[:lastSlash]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImage returns a types.Image for this reference.
|
||||||
|
// The caller must call .Close() on the returned Image.
|
||||||
|
func (ref ociReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
||||||
|
return nil, errors.New("Full Image support not implemented for oci: image names")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageSource returns a types.ImageSource for this reference,
|
||||||
|
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
|
||||||
|
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||||
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
|
func (ref ociReference) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||||
|
return nil, errors.New("Reading images not implemented for oci: image names")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||||
|
// The caller must call .Close() on the returned ImageDestination.
|
||||||
|
func (ref ociReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
|
return newImageDestination(ref), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteImage deletes the named image from the registry, if supported.
|
||||||
|
func (ref ociReference) DeleteImage(ctx *types.SystemContext) error {
|
||||||
|
return fmt.Errorf("Deleting images not implemented for oci: images")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ociLayoutPathPath returns a path for the oci-layout within a directory using OCI conventions.
|
||||||
|
func (ref ociReference) ociLayoutPath() string {
|
||||||
|
return filepath.Join(ref.dir, "oci-layout")
|
||||||
|
}
|
||||||
|
|
||||||
|
// blobPath returns a path for a blob within a directory using OCI image-layout conventions.
|
||||||
|
func (ref ociReference) blobPath(digest string) (string, error) {
|
||||||
|
pts := strings.SplitN(digest, ":", 2)
|
||||||
|
if len(pts) != 2 {
|
||||||
|
return "", fmt.Errorf("unexpected digest reference %s", digest)
|
||||||
|
}
|
||||||
|
return filepath.Join(ref.dir, "blobs", pts[0], pts[1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// descriptorPath returns a path for the manifest within a directory using OCI conventions.
|
||||||
|
func (ref ociReference) descriptorPath(digest string) string {
|
||||||
|
return filepath.Join(ref.dir, "refs", digest)
|
||||||
|
}
|
1071
vendor/github.com/containers/image/openshift/openshift-copies.go
generated
vendored
Normal file
1071
vendor/github.com/containers/image/openshift/openshift-copies.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
537
vendor/github.com/containers/image/openshift/openshift.go
generated
vendored
Normal file
537
vendor/github.com/containers/image/openshift/openshift.go
generated
vendored
Normal file
|
@ -0,0 +1,537 @@
|
||||||
|
package openshift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/containers/image/docker"
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/containers/image/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
// openshiftClient is configuration for dealing with a single image stream, for reading or writing.
|
||||||
|
type openshiftClient struct {
|
||||||
|
ref openshiftReference
|
||||||
|
baseURL *url.URL
|
||||||
|
// Values from Kubernetes configuration
|
||||||
|
httpClient *http.Client
|
||||||
|
bearerToken string // "" if not used
|
||||||
|
username string // "" if not used
|
||||||
|
password string // if username != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// newOpenshiftClient creates a new openshiftClient for the specified reference.
|
||||||
|
func newOpenshiftClient(ref openshiftReference) (*openshiftClient, error) {
|
||||||
|
// We have already done this parsing in ParseReference, but thrown away
|
||||||
|
// httpClient. So, parse again.
|
||||||
|
// (We could also rework/split restClientFor to "get base URL" to be done
|
||||||
|
// in ParseReference, and "get httpClient" to be done here. But until/unless
|
||||||
|
// we support non-default clusters, this is good enough.)
|
||||||
|
|
||||||
|
// Overall, this is modelled on openshift/origin/pkg/cmd/util/clientcmd.New().ClientConfig() and openshift/origin/pkg/client.
|
||||||
|
cmdConfig := defaultClientConfig()
|
||||||
|
logrus.Debugf("cmdConfig: %#v", cmdConfig)
|
||||||
|
restConfig, err := cmdConfig.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// REMOVED: SetOpenShiftDefaults (values are not overridable in config files, so hard-coded these defaults.)
|
||||||
|
logrus.Debugf("restConfig: %#v", restConfig)
|
||||||
|
baseURL, httpClient, err := restClientFor(restConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logrus.Debugf("URL: %#v", *baseURL)
|
||||||
|
|
||||||
|
if httpClient == nil {
|
||||||
|
httpClient = http.DefaultClient
|
||||||
|
}
|
||||||
|
httpClient.Timeout = 1 * time.Minute
|
||||||
|
|
||||||
|
return &openshiftClient{
|
||||||
|
ref: ref,
|
||||||
|
baseURL: baseURL,
|
||||||
|
httpClient: httpClient,
|
||||||
|
bearerToken: restConfig.BearerToken,
|
||||||
|
username: restConfig.Username,
|
||||||
|
password: restConfig.Password,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// doRequest performs a correctly authenticated request to a specified path, and returns response body or an error object.
|
||||||
|
func (c *openshiftClient) doRequest(method, path string, requestBody []byte) ([]byte, error) {
|
||||||
|
url := *c.baseURL
|
||||||
|
url.Path = path
|
||||||
|
var requestBodyReader io.Reader
|
||||||
|
if requestBody != nil {
|
||||||
|
logrus.Debugf("Will send body: %s", requestBody)
|
||||||
|
requestBodyReader = bytes.NewReader(requestBody)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(method, url.String(), requestBodyReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.bearerToken) != 0 {
|
||||||
|
req.Header.Set("Authorization", "Bearer "+c.bearerToken)
|
||||||
|
} else if len(c.username) != 0 {
|
||||||
|
req.SetBasicAuth(c.username, c.password)
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", "application/json, */*")
|
||||||
|
req.Header.Set("User-Agent", fmt.Sprintf("skopeo/%s", version.Version))
|
||||||
|
if requestBody != nil {
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("%s %s", method, url)
|
||||||
|
res, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logrus.Debugf("Got body: %s", body)
|
||||||
|
// FIXME: Just throwing this useful information away only to try to guess later...
|
||||||
|
logrus.Debugf("Got content-type: %s", res.Header.Get("Content-Type"))
|
||||||
|
|
||||||
|
var status status
|
||||||
|
statusValid := false
|
||||||
|
if err := json.Unmarshal(body, &status); err == nil && len(status.Status) > 0 {
|
||||||
|
statusValid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case res.StatusCode == http.StatusSwitchingProtocols: // FIXME?! No idea why this weird case exists in k8s.io/kubernetes/pkg/client/restclient.
|
||||||
|
if statusValid && status.Status != "Success" {
|
||||||
|
return nil, errors.New(status.Message)
|
||||||
|
}
|
||||||
|
case res.StatusCode >= http.StatusOK && res.StatusCode <= http.StatusPartialContent:
|
||||||
|
// OK.
|
||||||
|
default:
|
||||||
|
if statusValid {
|
||||||
|
return nil, errors.New(status.Message)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("HTTP error: status code: %d, body: %s", res.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getImage loads the specified image object.
|
||||||
|
func (c *openshiftClient) getImage(imageStreamImageName string) (*image, error) {
|
||||||
|
// FIXME: validate components per validation.IsValidPathSegmentName?
|
||||||
|
path := fmt.Sprintf("/oapi/v1/namespaces/%s/imagestreamimages/%s@%s", c.ref.namespace, c.ref.stream, imageStreamImageName)
|
||||||
|
body, err := c.doRequest("GET", path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Note: This does absolutely no kind/version checking or conversions.
|
||||||
|
var isi imageStreamImage
|
||||||
|
if err := json.Unmarshal(body, &isi); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &isi.Image, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertDockerImageReference takes an image API DockerImageReference value and returns a reference we can actually use;
|
||||||
|
// currently OpenShift stores the cluster-internal service IPs here, which are unusable from the outside.
|
||||||
|
func (c *openshiftClient) convertDockerImageReference(ref string) (string, error) {
|
||||||
|
parts := strings.SplitN(ref, "/", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return "", fmt.Errorf("Invalid format of docker reference %s: missing '/'", ref)
|
||||||
|
}
|
||||||
|
return c.ref.dockerReference.Hostname() + "/" + parts[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type openshiftImageSource struct {
|
||||||
|
client *openshiftClient
|
||||||
|
// Values specific to this image
|
||||||
|
ctx *types.SystemContext
|
||||||
|
requestedManifestMIMETypes []string
|
||||||
|
// State
|
||||||
|
docker types.ImageSource // The Docker Registry endpoint, or nil if not resolved yet
|
||||||
|
imageStreamImageName string // Resolved image identifier, or "" if not known yet
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageSource creates a new ImageSource for the specified reference,
|
||||||
|
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
|
||||||
|
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||||
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
|
func newImageSource(ctx *types.SystemContext, ref openshiftReference, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||||
|
client, err := newOpenshiftClient(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &openshiftImageSource{
|
||||||
|
client: client,
|
||||||
|
ctx: ctx,
|
||||||
|
requestedManifestMIMETypes: requestedManifestMIMETypes,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this source, _as specified by the user_
|
||||||
|
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
|
||||||
|
func (s *openshiftImageSource) Reference() types.ImageReference {
|
||||||
|
return s.client.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized ImageSource, if any.
|
||||||
|
func (s *openshiftImageSource) Close() {
|
||||||
|
if s.docker != nil {
|
||||||
|
s.docker.Close()
|
||||||
|
s.docker = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openshiftImageSource) GetManifest() ([]byte, string, error) {
|
||||||
|
if err := s.ensureImageIsResolved(); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return s.docker.GetManifest()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
||||||
|
func (s *openshiftImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
|
||||||
|
if err := s.ensureImageIsResolved(); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
return s.docker.GetBlob(digest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *openshiftImageSource) GetSignatures() ([][]byte, error) {
|
||||||
|
if err := s.ensureImageIsResolved(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := s.client.getImage(s.imageStreamImageName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var sigs [][]byte
|
||||||
|
for _, sig := range image.Signatures {
|
||||||
|
if sig.Type == imageSignatureTypeAtomic {
|
||||||
|
sigs = append(sigs, sig.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sigs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureImageIsResolved sets up s.docker and s.imageStreamImageName
|
||||||
|
func (s *openshiftImageSource) ensureImageIsResolved() error {
|
||||||
|
if s.docker != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: validate components per validation.IsValidPathSegmentName?
|
||||||
|
path := fmt.Sprintf("/oapi/v1/namespaces/%s/imagestreams/%s", s.client.ref.namespace, s.client.ref.stream)
|
||||||
|
body, err := s.client.doRequest("GET", path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Note: This does absolutely no kind/version checking or conversions.
|
||||||
|
var is imageStream
|
||||||
|
if err := json.Unmarshal(body, &is); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var te *tagEvent
|
||||||
|
for _, tag := range is.Status.Tags {
|
||||||
|
if tag.Tag != s.client.ref.dockerReference.Tag() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(tag.Items) > 0 {
|
||||||
|
te = &tag.Items[0]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if te == nil {
|
||||||
|
return fmt.Errorf("No matching tag found")
|
||||||
|
}
|
||||||
|
logrus.Debugf("tag event %#v", te)
|
||||||
|
dockerRefString, err := s.client.convertDockerImageReference(te.DockerImageReference)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logrus.Debugf("Resolved reference %#v", dockerRefString)
|
||||||
|
dockerRef, err := docker.ParseReference("//" + dockerRefString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d, err := dockerRef.NewImageSource(s.ctx, s.requestedManifestMIMETypes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.docker = d
|
||||||
|
s.imageStreamImageName = te.Image
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type openshiftImageDestination struct {
|
||||||
|
client *openshiftClient
|
||||||
|
docker types.ImageDestination // The Docker Registry endpoint
|
||||||
|
// State
|
||||||
|
imageStreamImageName string // "" if not yet known
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageDestination creates a new ImageDestination for the specified reference.
|
||||||
|
func newImageDestination(ctx *types.SystemContext, ref openshiftReference) (types.ImageDestination, error) {
|
||||||
|
client, err := newOpenshiftClient(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Should this always use a digest, not a tag? Uploading to Docker by tag requires the tag _inside_ the manifest to match,
|
||||||
|
// i.e. a single signed image cannot be available under multiple tags. But with types.ImageDestination, we don't know
|
||||||
|
// the manifest digest at this point.
|
||||||
|
dockerRefString := fmt.Sprintf("//%s/%s/%s:%s", client.ref.dockerReference.Hostname(), client.ref.namespace, client.ref.stream, client.ref.dockerReference.Tag())
|
||||||
|
dockerRef, err := docker.ParseReference(dockerRefString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
docker, err := dockerRef.NewImageDestination(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &openshiftImageDestination{
|
||||||
|
client: client,
|
||||||
|
docker: docker,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
||||||
|
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
|
||||||
|
func (d *openshiftImageDestination) Reference() types.ImageReference {
|
||||||
|
return d.client.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized ImageDestination, if any.
|
||||||
|
func (d *openshiftImageDestination) Close() {
|
||||||
|
d.docker.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *openshiftImageDestination) SupportedManifestMIMETypes() []string {
|
||||||
|
return []string{
|
||||||
|
manifest.DockerV2Schema1SignedMediaType,
|
||||||
|
manifest.DockerV2Schema1MediaType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
|
||||||
|
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
|
||||||
|
func (d *openshiftImageDestination) SupportsSignatures() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutBlob writes contents of stream and returns its computed digest and size.
|
||||||
|
// A digest can be optionally provided if known, the specific image destination can decide to play with it or not.
|
||||||
|
// The length of stream is expected to be expectedSize; if expectedSize == -1, it is not known.
|
||||||
|
// WARNING: The contents of stream are being verified on the fly. Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
|
||||||
|
// to any other readers for download using the supplied digest.
|
||||||
|
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
||||||
|
func (d *openshiftImageDestination) PutBlob(stream io.Reader, digest string, expectedSize int64) (string, int64, error) {
|
||||||
|
return d.docker.PutBlob(stream, digest, expectedSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *openshiftImageDestination) PutManifest(m []byte) error {
|
||||||
|
// FIXME? Can this eventually just call d.docker.PutManifest()?
|
||||||
|
// Right now we need this as a skeleton to attach signatures to, and
|
||||||
|
// to workaround our inability to change tags when uploading v2s1 manifests.
|
||||||
|
|
||||||
|
// Note: This does absolutely no kind/version checking or conversions.
|
||||||
|
manifestDigest, err := manifest.Digest(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.imageStreamImageName = manifestDigest
|
||||||
|
// FIXME: We can't do what respositorymiddleware.go does because we don't know the internal address. Does any of this matter?
|
||||||
|
dockerImageReference := fmt.Sprintf("%s/%s/%s@%s", d.client.ref.dockerReference.Hostname(), d.client.ref.namespace, d.client.ref.stream, manifestDigest)
|
||||||
|
ism := imageStreamMapping{
|
||||||
|
typeMeta: typeMeta{
|
||||||
|
Kind: "ImageStreamMapping",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
objectMeta: objectMeta{
|
||||||
|
Namespace: d.client.ref.namespace,
|
||||||
|
Name: d.client.ref.stream,
|
||||||
|
},
|
||||||
|
Image: image{
|
||||||
|
objectMeta: objectMeta{
|
||||||
|
Name: manifestDigest,
|
||||||
|
},
|
||||||
|
DockerImageReference: dockerImageReference,
|
||||||
|
DockerImageManifest: string(m),
|
||||||
|
},
|
||||||
|
Tag: d.client.ref.dockerReference.Tag(),
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(ism)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: validate components per validation.IsValidPathSegmentName?
|
||||||
|
path := fmt.Sprintf("/oapi/v1/namespaces/%s/imagestreammappings", d.client.ref.namespace)
|
||||||
|
body, err = d.client.doRequest("POST", path, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *openshiftImageDestination) PutSignatures(signatures [][]byte) error {
|
||||||
|
if d.imageStreamImageName == "" {
|
||||||
|
return fmt.Errorf("Internal error: Unknown manifest digest, can't add signatures")
|
||||||
|
}
|
||||||
|
// Because image signatures are a shared resource in Atomic Registry, the default upload
|
||||||
|
// always adds signatures. Eventually we should also allow removing signatures.
|
||||||
|
|
||||||
|
if len(signatures) == 0 {
|
||||||
|
return nil // No need to even read the old state.
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := d.client.getImage(d.imageStreamImageName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
existingSigNames := map[string]struct{}{}
|
||||||
|
for _, sig := range image.Signatures {
|
||||||
|
existingSigNames[sig.objectMeta.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
sigExists:
|
||||||
|
for _, newSig := range signatures {
|
||||||
|
for _, existingSig := range image.Signatures {
|
||||||
|
if existingSig.Type == imageSignatureTypeAtomic && bytes.Equal(existingSig.Content, newSig) {
|
||||||
|
continue sigExists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The API expect us to invent a new unique name. This is racy, but hopefully good enough.
|
||||||
|
var signatureName string
|
||||||
|
for {
|
||||||
|
randBytes := make([]byte, 16)
|
||||||
|
n, err := rand.Read(randBytes)
|
||||||
|
if err != nil || n != 16 {
|
||||||
|
return fmt.Errorf("Error generating random signature ID: %v, len %d", err, n)
|
||||||
|
}
|
||||||
|
signatureName = fmt.Sprintf("%s@%032x", d.imageStreamImageName, randBytes)
|
||||||
|
if _, ok := existingSigNames[signatureName]; !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Note: This does absolutely no kind/version checking or conversions.
|
||||||
|
sig := imageSignature{
|
||||||
|
typeMeta: typeMeta{
|
||||||
|
Kind: "ImageSignature",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
objectMeta: objectMeta{Name: signatureName},
|
||||||
|
Type: imageSignatureTypeAtomic,
|
||||||
|
Content: newSig,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(sig)
|
||||||
|
_, err = d.client.doRequest("POST", "/oapi/v1/imagesignatures", body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit marks the process of storing the image as successful and asks for the image to be persisted.
|
||||||
|
// WARNING: This does not have any transactional semantics:
|
||||||
|
// - Uploaded data MAY be visible to others before Commit() is called
|
||||||
|
// - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed)
|
||||||
|
func (d *openshiftImageDestination) Commit() error {
|
||||||
|
return d.docker.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// These structs are subsets of github.com/openshift/origin/pkg/image/api/v1 and its dependencies.
|
||||||
|
type imageStream struct {
|
||||||
|
Status imageStreamStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
type imageStreamStatus struct {
|
||||||
|
DockerImageRepository string `json:"dockerImageRepository"`
|
||||||
|
Tags []namedTagEventList `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
type namedTagEventList struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Items []tagEvent `json:"items"`
|
||||||
|
}
|
||||||
|
type tagEvent struct {
|
||||||
|
DockerImageReference string `json:"dockerImageReference"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
}
|
||||||
|
type imageStreamImage struct {
|
||||||
|
Image image `json:"image"`
|
||||||
|
}
|
||||||
|
type image struct {
|
||||||
|
objectMeta `json:"metadata,omitempty"`
|
||||||
|
DockerImageReference string `json:"dockerImageReference,omitempty"`
|
||||||
|
// DockerImageMetadata runtime.RawExtension `json:"dockerImageMetadata,omitempty"`
|
||||||
|
DockerImageMetadataVersion string `json:"dockerImageMetadataVersion,omitempty"`
|
||||||
|
DockerImageManifest string `json:"dockerImageManifest,omitempty"`
|
||||||
|
// DockerImageLayers []ImageLayer `json:"dockerImageLayers"`
|
||||||
|
Signatures []imageSignature `json:"signatures,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageSignatureTypeAtomic string = "atomic"
|
||||||
|
|
||||||
|
type imageSignature struct {
|
||||||
|
typeMeta `json:",inline"`
|
||||||
|
objectMeta `json:"metadata,omitempty"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Content []byte `json:"content"`
|
||||||
|
// Conditions []SignatureCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
|
||||||
|
// ImageIdentity string `json:"imageIdentity,omitempty"`
|
||||||
|
// SignedClaims map[string]string `json:"signedClaims,omitempty"`
|
||||||
|
// Created *unversioned.Time `json:"created,omitempty"`
|
||||||
|
// IssuedBy SignatureIssuer `json:"issuedBy,omitempty"`
|
||||||
|
// IssuedTo SignatureSubject `json:"issuedTo,omitempty"`
|
||||||
|
}
|
||||||
|
type imageStreamMapping struct {
|
||||||
|
typeMeta `json:",inline"`
|
||||||
|
objectMeta `json:"metadata,omitempty"`
|
||||||
|
Image image `json:"image"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
}
|
||||||
|
type typeMeta struct {
|
||||||
|
Kind string `json:"kind,omitempty"`
|
||||||
|
APIVersion string `json:"apiVersion,omitempty"`
|
||||||
|
}
|
||||||
|
type objectMeta struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
GenerateName string `json:"generateName,omitempty"`
|
||||||
|
Namespace string `json:"namespace,omitempty"`
|
||||||
|
SelfLink string `json:"selfLink,omitempty"`
|
||||||
|
ResourceVersion string `json:"resourceVersion,omitempty"`
|
||||||
|
Generation int64 `json:"generation,omitempty"`
|
||||||
|
DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty"`
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A subset of k8s.io/kubernetes/pkg/api/unversioned/Status
|
||||||
|
type status struct {
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
// Reason StatusReason `json:"reason,omitempty"`
|
||||||
|
// Details *StatusDetails `json:"details,omitempty"`
|
||||||
|
Code int32 `json:"code,omitempty"`
|
||||||
|
}
|
144
vendor/github.com/containers/image/openshift/openshift_transport.go
generated
vendored
Normal file
144
vendor/github.com/containers/image/openshift/openshift_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package openshift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/docker/policyconfiguration"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/docker/docker/reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transport is an ImageTransport for OpenShift registry-hosted images.
|
||||||
|
var Transport = openshiftTransport{}
|
||||||
|
|
||||||
|
type openshiftTransport struct{}
|
||||||
|
|
||||||
|
func (t openshiftTransport) Name() string {
|
||||||
|
return "atomic"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
|
||||||
|
func (t openshiftTransport) ParseReference(reference string) (types.ImageReference, error) {
|
||||||
|
return ParseReference(reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that imageNameRegexp is namespace/stream:tag, this
|
||||||
|
// is HOSTNAME/namespace/stream:tag or parent prefixes.
|
||||||
|
// Keep this in sync with imageNameRegexp!
|
||||||
|
var scopeRegexp = regexp.MustCompile("^[^/]*(/[^:/]*(/[^:/]*(:[^:/]*)?)?)?$")
|
||||||
|
|
||||||
|
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
||||||
|
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
|
||||||
|
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
|
||||||
|
// scope passed to this function will not be "", that value is always allowed.
|
||||||
|
func (t openshiftTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
|
if scopeRegexp.FindStringIndex(scope) == nil {
|
||||||
|
return fmt.Errorf("Invalid scope name %s", scope)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// openshiftReference is an ImageReference for OpenShift images.
|
||||||
|
type openshiftReference struct {
|
||||||
|
dockerReference reference.NamedTagged
|
||||||
|
namespace string // Computed from dockerReference in advance.
|
||||||
|
stream string // Computed from dockerReference in advance.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OpenShift ImageReference.
|
||||||
|
func ParseReference(ref string) (types.ImageReference, error) {
|
||||||
|
r, err := reference.ParseNamed(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse image reference %q, %v", ref, err)
|
||||||
|
}
|
||||||
|
tagged, ok := r.(reference.NamedTagged)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid image reference %s, %#v", ref, r)
|
||||||
|
}
|
||||||
|
return NewReference(tagged)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReference returns an OpenShift reference for a reference.NamedTagged
|
||||||
|
func NewReference(dockerRef reference.NamedTagged) (types.ImageReference, error) {
|
||||||
|
r := strings.SplitN(dockerRef.RemoteName(), "/", 3)
|
||||||
|
if len(r) != 2 {
|
||||||
|
return nil, fmt.Errorf("invalid image reference %s", dockerRef.String())
|
||||||
|
}
|
||||||
|
return openshiftReference{
|
||||||
|
namespace: r[0],
|
||||||
|
stream: r[1],
|
||||||
|
dockerReference: dockerRef,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ref openshiftReference) Transport() types.ImageTransport {
|
||||||
|
return Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringWithinTransport returns a string representation of the reference, which MUST be such that
|
||||||
|
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
|
||||||
|
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
|
||||||
|
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
|
||||||
|
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix.
|
||||||
|
func (ref openshiftReference) StringWithinTransport() string {
|
||||||
|
return ref.dockerReference.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerReference returns a Docker reference associated with this reference
|
||||||
|
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
|
||||||
|
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
|
||||||
|
func (ref openshiftReference) DockerReference() reference.Named {
|
||||||
|
return ref.dockerReference
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
|
||||||
|
// This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases;
|
||||||
|
// The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical
|
||||||
|
// (i.e. various references with exactly the same semantics should return the same configuration identity)
|
||||||
|
// It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but
|
||||||
|
// not required/guaranteed that it will be a valid input to Transport().ParseReference().
|
||||||
|
// Returns "" if configuration identities for these references are not supported.
|
||||||
|
func (ref openshiftReference) PolicyConfigurationIdentity() string {
|
||||||
|
res, err := policyconfiguration.DockerReferenceIdentity(ref.dockerReference)
|
||||||
|
if res == "" || err != nil { // Coverage: Should never happen, NewReference constructs a valid tagged reference.
|
||||||
|
panic(fmt.Sprintf("Internal inconsistency: policyconfiguration.DockerReferenceIdentity returned %#v, %v", res, err))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
|
||||||
|
// for if explicit configuration for PolicyConfigurationIdentity() is not set. The list will be processed
|
||||||
|
// in order, terminating on first match, and an implicit "" is always checked at the end.
|
||||||
|
// It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(),
|
||||||
|
// and each following element to be a prefix of the element preceding it.
|
||||||
|
func (ref openshiftReference) PolicyConfigurationNamespaces() []string {
|
||||||
|
return policyconfiguration.DockerReferenceNamespaces(ref.dockerReference)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImage returns a types.Image for this reference.
|
||||||
|
// The caller must call .Close() on the returned Image.
|
||||||
|
func (ref openshiftReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
||||||
|
return nil, errors.New("Full Image support not implemented for atomic: image names")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageSource returns a types.ImageSource for this reference,
|
||||||
|
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
|
||||||
|
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||||
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
|
func (ref openshiftReference) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||||
|
return newImageSource(ctx, ref, requestedManifestMIMETypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||||
|
// The caller must call .Close() on the returned ImageDestination.
|
||||||
|
func (ref openshiftReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
|
return newImageDestination(ctx, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteImage deletes the named image from the registry, if supported.
|
||||||
|
func (ref openshiftReference) DeleteImage(ctx *types.SystemContext) error {
|
||||||
|
return fmt.Errorf("Deleting images not implemented for atomic: images")
|
||||||
|
}
|
55
vendor/github.com/containers/image/transports/transports.go
generated
vendored
Normal file
55
vendor/github.com/containers/image/transports/transports.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package transports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/directory"
|
||||||
|
"github.com/containers/image/docker"
|
||||||
|
ociLayout "github.com/containers/image/oci/layout"
|
||||||
|
"github.com/containers/image/openshift"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KnownTransports is a registry of known ImageTransport instances.
|
||||||
|
var KnownTransports map[string]types.ImageTransport
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
KnownTransports = make(map[string]types.ImageTransport)
|
||||||
|
for _, t := range []types.ImageTransport{
|
||||||
|
directory.Transport,
|
||||||
|
docker.Transport,
|
||||||
|
ociLayout.Transport,
|
||||||
|
openshift.Transport,
|
||||||
|
} {
|
||||||
|
name := t.Name()
|
||||||
|
if _, ok := KnownTransports[name]; ok {
|
||||||
|
panic(fmt.Sprintf("Duplicate image transport name %s", name))
|
||||||
|
}
|
||||||
|
KnownTransports[name] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseImageName converts a URL-like image name to a types.ImageReference.
|
||||||
|
func ParseImageName(imgName string) (types.ImageReference, error) {
|
||||||
|
parts := strings.SplitN(imgName, ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf(`Invalid image name "%s", expected colon-separated transport:reference`, imgName)
|
||||||
|
}
|
||||||
|
transport, ok := KnownTransports[parts[0]]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(`Invalid image name "%s", unknown transport "%s"`, imgName, parts[0])
|
||||||
|
}
|
||||||
|
return transport.ParseReference(parts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageName converts a types.ImageReference into an URL-like image name, which MUST be such that
|
||||||
|
// ParseImageName(ImageName(reference)) returns an equivalent reference.
|
||||||
|
//
|
||||||
|
// This is the generally recommended way to refer to images in the UI.
|
||||||
|
//
|
||||||
|
// NOTE: The returned string is not promised to be equal to the original input to ParseImageName;
|
||||||
|
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
|
||||||
|
func ImageName(ref types.ImageReference) string {
|
||||||
|
return ref.Transport().Name() + ":" + ref.StringWithinTransport()
|
||||||
|
}
|
205
vendor/github.com/containers/image/types/types.go
generated
vendored
Normal file
205
vendor/github.com/containers/image/types/types.go
generated
vendored
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImageTransport is a top-level namespace for ways to to store/load an image.
|
||||||
|
// It should generally correspond to ImageSource/ImageDestination implementations.
|
||||||
|
//
|
||||||
|
// Note that ImageTransport is based on "ways the users refer to image storage", not necessarily on the underlying physical transport.
|
||||||
|
// For example, all Docker References would be used within a single "docker" transport, regardless of whether the images are pulled over HTTP or HTTPS
|
||||||
|
// (or, even, IPv4 or IPv6).
|
||||||
|
//
|
||||||
|
// OTOH all images using the same transport should (apart from versions of the image format), be interoperable.
|
||||||
|
// For example, several different ImageTransport implementations may be based on local filesystem paths,
|
||||||
|
// but using completely different formats for the contents of that path (a single tar file, a directory containing tarballs, a fully expanded container filesystem, ...)
|
||||||
|
//
|
||||||
|
// See also transports.KnownTransports.
|
||||||
|
type ImageTransport interface {
|
||||||
|
// Name returns the name of the transport, which must be unique among other transports.
|
||||||
|
Name() string
|
||||||
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
|
||||||
|
ParseReference(reference string) (ImageReference, error)
|
||||||
|
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
||||||
|
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
|
||||||
|
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
|
||||||
|
// scope passed to this function will not be "", that value is always allowed.
|
||||||
|
ValidatePolicyConfigurationScope(scope string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageReference is an abstracted way to refer to an image location, namespaced within an ImageTransport.
|
||||||
|
//
|
||||||
|
// The object should preferably be immutable after creation, with any parsing/state-dependent resolving happening
|
||||||
|
// within an ImageTransport.ParseReference() or equivalent API creating the reference object.
|
||||||
|
// That's also why the various identification/formatting methods of this type do not support returning errors.
|
||||||
|
//
|
||||||
|
// WARNING: While this design freezes the content of the reference within this process, it can not freeze the outside
|
||||||
|
// world: paths may be replaced by symlinks elsewhere, HTTP APIs may start returning different results, and so on.
|
||||||
|
type ImageReference interface {
|
||||||
|
Transport() ImageTransport
|
||||||
|
// StringWithinTransport returns a string representation of the reference, which MUST be such that
|
||||||
|
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
|
||||||
|
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
|
||||||
|
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
|
||||||
|
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix;
|
||||||
|
// instead, see transports.ImageName().
|
||||||
|
StringWithinTransport() string
|
||||||
|
|
||||||
|
// DockerReference returns a Docker reference associated with this reference
|
||||||
|
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
|
||||||
|
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
|
||||||
|
DockerReference() reference.Named
|
||||||
|
|
||||||
|
// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
|
||||||
|
// This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases;
|
||||||
|
// The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical
|
||||||
|
// (i.e. various references with exactly the same semantics should return the same configuration identity)
|
||||||
|
// It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but
|
||||||
|
// not required/guaranteed that it will be a valid input to Transport().ParseReference().
|
||||||
|
// Returns "" if configuration identities for these references are not supported.
|
||||||
|
PolicyConfigurationIdentity() string
|
||||||
|
|
||||||
|
// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
|
||||||
|
// for if explicit configuration for PolicyConfigurationIdentity() is not set. The list will be processed
|
||||||
|
// in order, terminating on first match, and an implicit "" is always checked at the end.
|
||||||
|
// It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(),
|
||||||
|
// and each following element to be a prefix of the element preceding it.
|
||||||
|
PolicyConfigurationNamespaces() []string
|
||||||
|
|
||||||
|
// NewImage returns a types.Image for this reference.
|
||||||
|
// The caller must call .Close() on the returned Image.
|
||||||
|
NewImage(ctx *SystemContext) (Image, error)
|
||||||
|
// NewImageSource returns a types.ImageSource for this reference,
|
||||||
|
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
|
||||||
|
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||||
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
|
NewImageSource(ctx *SystemContext, requestedManifestMIMETypes []string) (ImageSource, error)
|
||||||
|
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||||
|
// The caller must call .Close() on the returned ImageDestination.
|
||||||
|
NewImageDestination(ctx *SystemContext) (ImageDestination, error)
|
||||||
|
|
||||||
|
// DeleteImage deletes the named image from the registry, if supported.
|
||||||
|
DeleteImage(ctx *SystemContext) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageSource is a service, possibly remote (= slow), to download components of a single image.
|
||||||
|
// This is primarily useful for copying images around; for examining their properties, Image (below)
|
||||||
|
// is usually more useful.
|
||||||
|
// Each ImageSource should eventually be closed by calling Close().
|
||||||
|
type ImageSource interface {
|
||||||
|
// Reference returns the reference used to set up this source, _as specified by the user_
|
||||||
|
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
|
||||||
|
Reference() ImageReference
|
||||||
|
// Close removes resources associated with an initialized ImageSource, if any.
|
||||||
|
Close()
|
||||||
|
// GetManifest returns the image's manifest along with its MIME type. The empty string is returned if the MIME type is unknown.
|
||||||
|
// It may use a remote (= slow) service.
|
||||||
|
GetManifest() ([]byte, string, error)
|
||||||
|
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
||||||
|
GetBlob(digest string) (io.ReadCloser, int64, error)
|
||||||
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
|
GetSignatures() ([][]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageDestination is a service, possibly remote (= slow), to store components of a single image.
|
||||||
|
//
|
||||||
|
// There is a specific required order for some of the calls:
|
||||||
|
// PutBlob on the various blobs, if any, MUST be called before PutManifest (manifest references blobs, which may be created or compressed only at push time)
|
||||||
|
// PutSignatures, if called, MUST be called after PutManifest (signatures reference manifest contents)
|
||||||
|
// Finally, Commit MUST be called if the caller wants the image, as formed by the components saved above, to persist.
|
||||||
|
//
|
||||||
|
// Each ImageDestination should eventually be closed by calling Close().
|
||||||
|
type ImageDestination interface {
|
||||||
|
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
||||||
|
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
|
||||||
|
Reference() ImageReference
|
||||||
|
// Close removes resources associated with an initialized ImageDestination, if any.
|
||||||
|
Close()
|
||||||
|
|
||||||
|
// SupportedManifestMIMETypes tells which manifest mime types the destination supports
|
||||||
|
// If an empty slice or nil it's returned, then any mime type can be tried to upload
|
||||||
|
SupportedManifestMIMETypes() []string
|
||||||
|
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
|
||||||
|
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
|
||||||
|
SupportsSignatures() error
|
||||||
|
|
||||||
|
// PutBlob writes contents of stream and returns its computed digest and size.
|
||||||
|
// A digest can be optionally provided if known, the specific image destination can decide to play with it or not.
|
||||||
|
// The length of stream is expected to be expectedSize; if expectedSize == -1, it is not known.
|
||||||
|
// WARNING: The contents of stream are being verified on the fly. Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
|
||||||
|
// to any other readers for download using the supplied digest.
|
||||||
|
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
||||||
|
PutBlob(stream io.Reader, digest string, expectedSize int64) (string, int64, error)
|
||||||
|
// FIXME? This should also receive a MIME type if known, to differentiate between schema versions.
|
||||||
|
PutManifest([]byte) error
|
||||||
|
PutSignatures(signatures [][]byte) error
|
||||||
|
// Commit marks the process of storing the image as successful and asks for the image to be persisted.
|
||||||
|
// WARNING: This does not have any transactional semantics:
|
||||||
|
// - Uploaded data MAY be visible to others before Commit() is called
|
||||||
|
// - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed)
|
||||||
|
Commit() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image is the primary API for inspecting properties of images.
|
||||||
|
// Each Image should eventually be closed by calling Close().
|
||||||
|
type Image interface {
|
||||||
|
// Reference returns the reference used to set up this source, _as specified by the user_
|
||||||
|
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
|
||||||
|
Reference() ImageReference
|
||||||
|
// Close removes resources associated with an initialized Image, if any.
|
||||||
|
Close()
|
||||||
|
// ref to repository?
|
||||||
|
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
|
||||||
|
// NOTE: It is essential for signature verification that Manifest returns the manifest from which BlobDigests is computed.
|
||||||
|
Manifest() ([]byte, string, error)
|
||||||
|
// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
||||||
|
Signatures() ([][]byte, error)
|
||||||
|
// BlobDigests returns a list of blob digests referenced by this image.
|
||||||
|
// The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image.
|
||||||
|
// NOTE: It is essential for signature verification that BlobDigests is computed from the same manifest which is returned by Manifest().
|
||||||
|
BlobDigests() ([]string, error)
|
||||||
|
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
||||||
|
Inspect() (*ImageInspectInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageInspectInfo is a set of metadata describing Docker images, primarily their manifest and configuration.
|
||||||
|
// The Tag field is a legacy field which is here just for the Docker v2s1 manifest. It won't be supported
|
||||||
|
// for other manifest types.
|
||||||
|
type ImageInspectInfo struct {
|
||||||
|
Tag string
|
||||||
|
Created time.Time
|
||||||
|
DockerVersion string
|
||||||
|
Labels map[string]string
|
||||||
|
Architecture string
|
||||||
|
Os string
|
||||||
|
Layers []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemContext allows parametrizing access to implicitly-accessed resources,
|
||||||
|
// like configuration files in /etc and users' login state in their home directory.
|
||||||
|
// Various components can share the same field only if their semantics is exactly
|
||||||
|
// the same; if in doubt, add a new field.
|
||||||
|
// It is always OK to pass nil instead of a SystemContext.
|
||||||
|
type SystemContext struct {
|
||||||
|
// If not "", prefixed to any absolute paths used by default by the library (e.g. in /etc/).
|
||||||
|
// Not used for any of the more specific path overrides available in this struct.
|
||||||
|
// Not used for any paths specified by users in config files (even if the location of the config file _was_ affected by it).
|
||||||
|
// NOTE: If this is set, environment-variable overrides of paths are ignored (to keep the semantics simple: to create an /etc replacement, just set RootForImplicitAbsolutePaths .
|
||||||
|
// and there is no need to worry about the environment.)
|
||||||
|
// NOTE: This does NOT affect paths starting by $HOME.
|
||||||
|
RootForImplicitAbsolutePaths string
|
||||||
|
|
||||||
|
// === Global configuration overrides ===
|
||||||
|
// If not "", overrides the system's default path for signature.Policy configuration.
|
||||||
|
SignaturePolicyPath string
|
||||||
|
// If not "", overrides the system's default path for registries.d (Docker signature storage configuration)
|
||||||
|
RegistriesDirPath string
|
||||||
|
|
||||||
|
// === docker.Transport overrides ===
|
||||||
|
DockerCertPath string // If not "", a directory containing "cert.pem" and "key.pem" used when talking to a Docker Registry
|
||||||
|
DockerInsecureSkipTLSVerify bool // Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
|
||||||
|
}
|
18
vendor/github.com/containers/image/version/version.go
generated
vendored
Normal file
18
vendor/github.com/containers/image/version/version.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package version
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// VersionMajor is for an API incompatible changes
|
||||||
|
VersionMajor = 0
|
||||||
|
// VersionMinor is for functionality in a backwards-compatible manner
|
||||||
|
VersionMinor = 1
|
||||||
|
// VersionPatch is for backwards-compatible bug fixes
|
||||||
|
VersionPatch = 0
|
||||||
|
|
||||||
|
// VersionDev indicates development branch. Releases will be empty string.
|
||||||
|
VersionDev = "-dev"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version is the specification version that the package types support.
|
||||||
|
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
|
191
vendor/github.com/docker/docker/LICENSE
generated
vendored
Normal file
191
vendor/github.com/docker/docker/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2013-2016 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
23
vendor/github.com/docker/docker/api/types/blkiodev/blkio.go
generated
vendored
Normal file
23
vendor/github.com/docker/docker/api/types/blkiodev/blkio.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package blkiodev
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// WeightDevice is a structure that holds device:weight pair
|
||||||
|
type WeightDevice struct {
|
||||||
|
Path string
|
||||||
|
Weight uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WeightDevice) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", w.Path, w.Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThrottleDevice is a structure that holds device:rate_per_second pair
|
||||||
|
type ThrottleDevice struct {
|
||||||
|
Path string
|
||||||
|
Rate uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ThrottleDevice) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", t.Path, t.Rate)
|
||||||
|
}
|
62
vendor/github.com/docker/docker/api/types/container/config.go
generated
vendored
Normal file
62
vendor/github.com/docker/docker/api/types/container/config.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
|
||||||
|
type HealthConfig struct {
|
||||||
|
// Test is the test to perform to check that the container is healthy.
|
||||||
|
// An empty slice means to inherit the default.
|
||||||
|
// The options are:
|
||||||
|
// {} : inherit healthcheck
|
||||||
|
// {"NONE"} : disable healthcheck
|
||||||
|
// {"CMD", args...} : exec arguments directly
|
||||||
|
// {"CMD-SHELL", command} : run command with system's default shell
|
||||||
|
Test []string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Zero means to inherit. Durations are expressed as integer nanoseconds.
|
||||||
|
Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
|
||||||
|
Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.
|
||||||
|
|
||||||
|
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
|
||||||
|
// Zero means inherit.
|
||||||
|
Retries int `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config contains the configuration data about a container.
|
||||||
|
// It should hold only portable information about the container.
|
||||||
|
// Here, "portable" means "independent from the host we are running on".
|
||||||
|
// Non-portable information *should* appear in HostConfig.
|
||||||
|
// All fields added to this struct must be marked `omitempty` to keep getting
|
||||||
|
// predictable hashes from the old `v1Compatibility` configuration.
|
||||||
|
type Config struct {
|
||||||
|
Hostname string // Hostname
|
||||||
|
Domainname string // Domainname
|
||||||
|
User string // User that will run the command(s) inside the container, also support user:group
|
||||||
|
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||||
|
AttachStdout bool // Attach the standard output
|
||||||
|
AttachStderr bool // Attach the standard error
|
||||||
|
ExposedPorts map[nat.Port]struct{} `json:",omitempty"` // List of exposed ports
|
||||||
|
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||||
|
OpenStdin bool // Open stdin
|
||||||
|
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||||
|
Env []string // List of environment variable to set in the container
|
||||||
|
Cmd strslice.StrSlice // Command to run when starting the container
|
||||||
|
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
|
||||||
|
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
|
||||||
|
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
|
||||||
|
Volumes map[string]struct{} // List of volumes (mounts) used for the container
|
||||||
|
WorkingDir string // Current directory (PWD) in the command will be launched
|
||||||
|
Entrypoint strslice.StrSlice // Entrypoint to run when starting the container
|
||||||
|
NetworkDisabled bool `json:",omitempty"` // Is network disabled
|
||||||
|
MacAddress string `json:",omitempty"` // Mac Address of the container
|
||||||
|
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
|
||||||
|
Labels map[string]string // List of labels set to this container
|
||||||
|
StopSignal string `json:",omitempty"` // Signal to stop a container
|
||||||
|
StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container
|
||||||
|
Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
|
||||||
|
}
|
333
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
333
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/blkiodev"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetworkMode represents the container network stack.
|
||||||
|
type NetworkMode string
|
||||||
|
|
||||||
|
// Isolation represents the isolation technology of a container. The supported
|
||||||
|
// values are platform specific
|
||||||
|
type Isolation string
|
||||||
|
|
||||||
|
// IsDefault indicates the default isolation technology of a container. On Linux this
|
||||||
|
// is the native driver. On Windows, this is a Windows Server Container.
|
||||||
|
func (i Isolation) IsDefault() bool {
|
||||||
|
return strings.ToLower(string(i)) == "default" || string(i) == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IpcMode represents the container ipc stack.
|
||||||
|
type IpcMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its private ipc stack.
|
||||||
|
func (n IpcMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's ipc stack.
|
||||||
|
func (n IpcMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's ipc stack.
|
||||||
|
func (n IpcMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the ipc stack is valid.
|
||||||
|
func (n IpcMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container ipc stack is going to be used.
|
||||||
|
func (n IpcMode) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsernsMode represents userns mode in the container.
|
||||||
|
type UsernsMode string
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's userns.
|
||||||
|
func (n UsernsMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses the a private userns.
|
||||||
|
func (n UsernsMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the userns is valid.
|
||||||
|
func (n UsernsMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CgroupSpec represents the cgroup to use for the container.
|
||||||
|
type CgroupSpec string
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container is using another container cgroup
|
||||||
|
func (c CgroupSpec) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(c), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the cgroup spec is valid.
|
||||||
|
func (c CgroupSpec) Valid() bool {
|
||||||
|
return c.IsContainer() || c == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose cgroup will be used.
|
||||||
|
func (c CgroupSpec) Container() string {
|
||||||
|
parts := strings.SplitN(string(c), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTSMode represents the UTS namespace of the container.
|
||||||
|
type UTSMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its private UTS namespace.
|
||||||
|
func (n UTSMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's UTS namespace.
|
||||||
|
func (n UTSMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the UTS namespace is valid.
|
||||||
|
func (n UTSMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidMode represents the pid namespace of the container.
|
||||||
|
type PidMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its own new pid namespace.
|
||||||
|
func (n PidMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's pid namespace.
|
||||||
|
func (n PidMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||||
|
func (n PidMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the pid namespace is valid.
|
||||||
|
func (n PidMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose pid namespace is going to be used.
|
||||||
|
func (n PidMode) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceMapping represents the device mapping between the host and the container.
|
||||||
|
type DeviceMapping struct {
|
||||||
|
PathOnHost string
|
||||||
|
PathInContainer string
|
||||||
|
CgroupPermissions string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartPolicy represents the restart policies of the container.
|
||||||
|
type RestartPolicy struct {
|
||||||
|
Name string
|
||||||
|
MaximumRetryCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether the container has the "no" restart policy.
|
||||||
|
// This means the container will not automatically restart when exiting.
|
||||||
|
func (rp *RestartPolicy) IsNone() bool {
|
||||||
|
return rp.Name == "no" || rp.Name == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlways indicates whether the container has the "always" restart policy.
|
||||||
|
// This means the container will automatically restart regardless of the exit status.
|
||||||
|
func (rp *RestartPolicy) IsAlways() bool {
|
||||||
|
return rp.Name == "always"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOnFailure indicates whether the container has the "on-failure" restart policy.
|
||||||
|
// This means the container will automatically restart of exiting with a non-zero exit status.
|
||||||
|
func (rp *RestartPolicy) IsOnFailure() bool {
|
||||||
|
return rp.Name == "on-failure"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUnlessStopped indicates whether the container has the
|
||||||
|
// "unless-stopped" restart policy. This means the container will
|
||||||
|
// automatically restart unless user has put it to stopped state.
|
||||||
|
func (rp *RestartPolicy) IsUnlessStopped() bool {
|
||||||
|
return rp.Name == "unless-stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSame compares two RestartPolicy to see if they are the same
|
||||||
|
func (rp *RestartPolicy) IsSame(tp *RestartPolicy) bool {
|
||||||
|
return rp.Name == tp.Name && rp.MaximumRetryCount == tp.MaximumRetryCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogConfig represents the logging configuration of the container.
|
||||||
|
type LogConfig struct {
|
||||||
|
Type string
|
||||||
|
Config map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resources contains container's resources (cgroups config, ulimits...)
|
||||||
|
type Resources struct {
|
||||||
|
// Applicable to all platforms
|
||||||
|
CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers)
|
||||||
|
Memory int64 // Memory limit (in bytes)
|
||||||
|
|
||||||
|
// Applicable to UNIX platforms
|
||||||
|
CgroupParent string // Parent cgroup.
|
||||||
|
BlkioWeight uint16 // Block IO weight (relative weight vs. other containers)
|
||||||
|
BlkioWeightDevice []*blkiodev.WeightDevice
|
||||||
|
BlkioDeviceReadBps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceWriteBps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceReadIOps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceWriteIOps []*blkiodev.ThrottleDevice
|
||||||
|
CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period
|
||||||
|
CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota
|
||||||
|
CpusetCpus string // CpusetCpus 0-2, 0,1
|
||||||
|
CpusetMems string // CpusetMems 0-2, 0,1
|
||||||
|
Devices []DeviceMapping // List of devices to map inside the container
|
||||||
|
DiskQuota int64 // Disk limit (in bytes)
|
||||||
|
KernelMemory int64 // Kernel memory limit (in bytes)
|
||||||
|
MemoryReservation int64 // Memory soft limit (in bytes)
|
||||||
|
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||||
|
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
||||||
|
OomKillDisable *bool // Whether to disable OOM Killer or not
|
||||||
|
PidsLimit int64 // Setting pids limit for a container
|
||||||
|
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
||||||
|
|
||||||
|
// Applicable to Windows
|
||||||
|
CPUCount int64 `json:"CpuCount"` // CPU count
|
||||||
|
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
||||||
|
IOMaximumIOps uint64 // Maximum IOps for the container system drive
|
||||||
|
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig holds the mutable attributes of a Container.
|
||||||
|
// Those attributes can be updated at runtime.
|
||||||
|
type UpdateConfig struct {
|
||||||
|
// Contains container's resources (cgroups, ulimits)
|
||||||
|
Resources
|
||||||
|
RestartPolicy RestartPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostConfig the non-portable Config structure of a container.
|
||||||
|
// Here, "non-portable" means "dependent of the host we are running on".
|
||||||
|
// Portable information *should* appear in Config.
|
||||||
|
type HostConfig struct {
|
||||||
|
// Applicable to all platforms
|
||||||
|
Binds []string // List of volume bindings for this container
|
||||||
|
ContainerIDFile string // File (path) where the containerId is written
|
||||||
|
LogConfig LogConfig // Configuration of the logs for this container
|
||||||
|
NetworkMode NetworkMode // Network mode to use for the container
|
||||||
|
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||||
|
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||||
|
AutoRemove bool // Automatically remove container when it exits
|
||||||
|
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||||
|
VolumesFrom []string // List of volumes to take from other container
|
||||||
|
|
||||||
|
// Applicable to UNIX platforms
|
||||||
|
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||||
|
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
|
||||||
|
DNS []string `json:"Dns"` // List of DNS server to lookup
|
||||||
|
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
||||||
|
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
||||||
|
ExtraHosts []string // List of extra hosts
|
||||||
|
GroupAdd []string // List of additional groups that the container process will run as
|
||||||
|
IpcMode IpcMode // IPC namespace to use for the container
|
||||||
|
Cgroup CgroupSpec // Cgroup to use for the container
|
||||||
|
Links []string // List of links (in the name:alias form)
|
||||||
|
OomScoreAdj int // Container preference for OOM-killing
|
||||||
|
PidMode PidMode // PID namespace to use for the container
|
||||||
|
Privileged bool // Is the container in privileged mode
|
||||||
|
PublishAllPorts bool // Should docker publish all exposed port for the container
|
||||||
|
ReadonlyRootfs bool // Is the container root filesystem in read-only
|
||||||
|
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux.
|
||||||
|
StorageOpt map[string]string `json:",omitempty"` // Storage driver options per container.
|
||||||
|
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
|
||||||
|
UTSMode UTSMode // UTS namespace to use for the container
|
||||||
|
UsernsMode UsernsMode // The user namespace to use for the container
|
||||||
|
ShmSize int64 // Total shm memory usage
|
||||||
|
Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
|
||||||
|
Runtime string `json:",omitempty"` // Runtime to use with this container
|
||||||
|
|
||||||
|
// Applicable to Windows
|
||||||
|
ConsoleSize Box // Initial console size
|
||||||
|
Isolation Isolation // Isolation technology of the container (eg default, hyperv)
|
||||||
|
|
||||||
|
// Contains container's resources (cgroups, ulimits)
|
||||||
|
Resources
|
||||||
|
|
||||||
|
// Mounts specs used by the container
|
||||||
|
Mounts []mount.Mount `json:",omitempty"`
|
||||||
|
|
||||||
|
// Run a custom init inside the container, if null, use the daemon's configured settings
|
||||||
|
Init *bool `json:",om itempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Box specifies height and width dimensions. Used for sizing of a console.
|
||||||
|
type Box struct {
|
||||||
|
Height uint
|
||||||
|
Width uint
|
||||||
|
}
|
81
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
81
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package container
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// IsValid indicates if an isolation technology is valid
|
||||||
|
func (i Isolation) IsValid() bool {
|
||||||
|
return i.IsDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether container uses it's private network stack.
|
||||||
|
func (n NetworkMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDefault indicates whether container uses the default network stack.
|
||||||
|
func (n NetworkMode) IsDefault() bool {
|
||||||
|
return n == "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkName returns the name of the network stack.
|
||||||
|
func (n NetworkMode) NetworkName() string {
|
||||||
|
if n.IsBridge() {
|
||||||
|
return "bridge"
|
||||||
|
} else if n.IsHost() {
|
||||||
|
return "host"
|
||||||
|
} else if n.IsContainer() {
|
||||||
|
return "container"
|
||||||
|
} else if n.IsNone() {
|
||||||
|
return "none"
|
||||||
|
} else if n.IsDefault() {
|
||||||
|
return "default"
|
||||||
|
} else if n.IsUserDefined() {
|
||||||
|
return n.UserDefined()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBridge indicates whether container uses the bridge network stack
|
||||||
|
func (n NetworkMode) IsBridge() bool {
|
||||||
|
return n == "bridge"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether container uses the host network stack.
|
||||||
|
func (n NetworkMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether container uses a container network stack.
|
||||||
|
func (n NetworkMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether container isn't using a network stack.
|
||||||
|
func (n NetworkMode) IsNone() bool {
|
||||||
|
return n == "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||||
|
func (n NetworkMode) ConnectedContainer() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) IsUserDefined() bool {
|
||||||
|
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
//UserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) UserDefined() string {
|
||||||
|
if n.IsUserDefined() {
|
||||||
|
return string(n)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
87
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
87
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsDefault indicates whether container uses the default network stack.
|
||||||
|
func (n NetworkMode) IsDefault() bool {
|
||||||
|
return n == "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether container isn't using a network stack.
|
||||||
|
func (n NetworkMode) IsNone() bool {
|
||||||
|
return n == "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether container uses a container network stack.
|
||||||
|
// Returns false as windows doesn't support this mode
|
||||||
|
func (n NetworkMode) IsContainer() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBridge indicates whether container uses the bridge network stack
|
||||||
|
// in windows it is given the name NAT
|
||||||
|
func (n NetworkMode) IsBridge() bool {
|
||||||
|
return n == "nat"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether container uses the host network stack.
|
||||||
|
// returns false as this is not supported by windows
|
||||||
|
func (n NetworkMode) IsHost() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether container uses its private network stack.
|
||||||
|
func (n NetworkMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||||
|
// Returns blank string on windows
|
||||||
|
func (n NetworkMode) ConnectedContainer() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) IsUserDefined() bool {
|
||||||
|
return !n.IsDefault() && !n.IsNone() && !n.IsBridge()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
||||||
|
func (i Isolation) IsHyperV() bool {
|
||||||
|
return strings.ToLower(string(i)) == "hyperv"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProcess indicates the use of process isolation
|
||||||
|
func (i Isolation) IsProcess() bool {
|
||||||
|
return strings.ToLower(string(i)) == "process"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid indicates if an isolation technology is valid
|
||||||
|
func (i Isolation) IsValid() bool {
|
||||||
|
return i.IsDefault() || i.IsHyperV() || i.IsProcess()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkName returns the name of the network stack.
|
||||||
|
func (n NetworkMode) NetworkName() string {
|
||||||
|
if n.IsDefault() {
|
||||||
|
return "default"
|
||||||
|
} else if n.IsBridge() {
|
||||||
|
return "nat"
|
||||||
|
} else if n.IsNone() {
|
||||||
|
return "none"
|
||||||
|
} else if n.IsUserDefined() {
|
||||||
|
return n.UserDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
//UserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) UserDefined() string {
|
||||||
|
if n.IsUserDefined() {
|
||||||
|
return string(n)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
58
vendor/github.com/docker/docker/api/types/mount/mount.go
generated
vendored
Normal file
58
vendor/github.com/docker/docker/api/types/mount/mount.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
// Type represents the type of a mount.
|
||||||
|
type Type string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TypeBind BIND
|
||||||
|
TypeBind Type = "bind"
|
||||||
|
// TypeVolume VOLUME
|
||||||
|
TypeVolume Type = "volume"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mount represents a mount (volume).
|
||||||
|
type Mount struct {
|
||||||
|
Type Type `json:",omitempty"`
|
||||||
|
Source string `json:",omitempty"`
|
||||||
|
Target string `json:",omitempty"`
|
||||||
|
ReadOnly bool `json:",omitempty"`
|
||||||
|
|
||||||
|
BindOptions *BindOptions `json:",omitempty"`
|
||||||
|
VolumeOptions *VolumeOptions `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagation represents the propagation of a mount.
|
||||||
|
type Propagation string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PropagationRPrivate RPRIVATE
|
||||||
|
PropagationRPrivate Propagation = "rprivate"
|
||||||
|
// PropagationPrivate PRIVATE
|
||||||
|
PropagationPrivate Propagation = "private"
|
||||||
|
// PropagationRShared RSHARED
|
||||||
|
PropagationRShared Propagation = "rshared"
|
||||||
|
// PropagationShared SHARED
|
||||||
|
PropagationShared Propagation = "shared"
|
||||||
|
// PropagationRSlave RSLAVE
|
||||||
|
PropagationRSlave Propagation = "rslave"
|
||||||
|
// PropagationSlave SLAVE
|
||||||
|
PropagationSlave Propagation = "slave"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BindOptions defines options specific to mounts of type "bind".
|
||||||
|
type BindOptions struct {
|
||||||
|
Propagation Propagation `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeOptions represents the options for a mount of type volume.
|
||||||
|
type VolumeOptions struct {
|
||||||
|
NoCopy bool `json:",omitempty"`
|
||||||
|
Labels map[string]string `json:",omitempty"`
|
||||||
|
DriverConfig *Driver `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Driver represents a volume driver.
|
||||||
|
type Driver struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Options map[string]string `json:",omitempty"`
|
||||||
|
}
|
30
vendor/github.com/docker/docker/api/types/strslice/strslice.go
generated
vendored
Normal file
30
vendor/github.com/docker/docker/api/types/strslice/strslice.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package strslice
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// StrSlice represents a string or an array of strings.
|
||||||
|
// We need to override the json decoder to accept both options.
|
||||||
|
type StrSlice []string
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes the byte slice whether it's a string or an array of
|
||||||
|
// strings. This method is needed to implement json.Unmarshaler.
|
||||||
|
func (e *StrSlice) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) == 0 {
|
||||||
|
// With no input, we preserve the existing value by returning nil and
|
||||||
|
// leaving the target alone. This allows defining default values for
|
||||||
|
// the type.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p := make([]string, 0, 1)
|
||||||
|
if err := json.Unmarshal(b, &p); err != nil {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p = append(p, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = p
|
||||||
|
return nil
|
||||||
|
}
|
14
vendor/github.com/docker/docker/api/types/versions/README.md
generated
vendored
Normal file
14
vendor/github.com/docker/docker/api/types/versions/README.md
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
## Legacy API type versions
|
||||||
|
|
||||||
|
This package includes types for legacy API versions. The stable version of the API types live in `api/types/*.go`.
|
||||||
|
|
||||||
|
Consider moving a type here when you need to keep backwards compatibility in the API. This legacy types are organized by the latest API version they appear in. For instance, types in the `v1p19` package are valid for API versions below or equal `1.19`. Types in the `v1p20` package are valid for the API version `1.20`, since the versions below that will use the legacy types in `v1p19`.
|
||||||
|
|
||||||
|
### Package name conventions
|
||||||
|
|
||||||
|
The package name convention is to use `v` as a prefix for the version number and `p`(patch) as a separator. We use this nomenclature due to a few restrictions in the Go package name convention:
|
||||||
|
|
||||||
|
1. We cannot use `.` because it's interpreted by the language, think of `v1.20.CallFunction`.
|
||||||
|
2. We cannot use `_` because golint complains about it. The code is actually valid, but it looks probably more weird: `v1_20.CallFunction`.
|
||||||
|
|
||||||
|
For instance, if you want to modify a type that was available in the version `1.21` of the API but it will have different fields in the version `1.22`, you want to create a new package under `api/types/versions/v1p21`.
|
62
vendor/github.com/docker/docker/api/types/versions/compare.go
generated
vendored
Normal file
62
vendor/github.com/docker/docker/api/types/versions/compare.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package versions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// compare compares two version strings
|
||||||
|
// returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise.
|
||||||
|
func compare(v1, v2 string) int {
|
||||||
|
var (
|
||||||
|
currTab = strings.Split(v1, ".")
|
||||||
|
otherTab = strings.Split(v2, ".")
|
||||||
|
)
|
||||||
|
|
||||||
|
max := len(currTab)
|
||||||
|
if len(otherTab) > max {
|
||||||
|
max = len(otherTab)
|
||||||
|
}
|
||||||
|
for i := 0; i < max; i++ {
|
||||||
|
var currInt, otherInt int
|
||||||
|
|
||||||
|
if len(currTab) > i {
|
||||||
|
currInt, _ = strconv.Atoi(currTab[i])
|
||||||
|
}
|
||||||
|
if len(otherTab) > i {
|
||||||
|
otherInt, _ = strconv.Atoi(otherTab[i])
|
||||||
|
}
|
||||||
|
if currInt > otherInt {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if otherInt > currInt {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThan checks if a version is less than another
|
||||||
|
func LessThan(v, other string) bool {
|
||||||
|
return compare(v, other) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThanOrEqualTo checks if a version is less than or equal to another
|
||||||
|
func LessThanOrEqualTo(v, other string) bool {
|
||||||
|
return compare(v, other) <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThan checks if a version is greater than another
|
||||||
|
func GreaterThan(v, other string) bool {
|
||||||
|
return compare(v, other) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThanOrEqualTo checks if a version is greater than or equal to another
|
||||||
|
func GreaterThanOrEqualTo(v, other string) bool {
|
||||||
|
return compare(v, other) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks if a version is equal to another
|
||||||
|
func Equal(v, other string) bool {
|
||||||
|
return compare(v, other) == 0
|
||||||
|
}
|
27
vendor/github.com/docker/docker/client/transport/cancellable/LICENSE
generated
vendored
Normal file
27
vendor/github.com/docker/docker/client/transport/cancellable/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
339
vendor/github.com/docker/docker/contrib/selinux-fedora-24/docker-engine-selinux/LICENSE
generated
vendored
Normal file
339
vendor/github.com/docker/docker/contrib/selinux-fedora-24/docker-engine-selinux/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 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.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, 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 or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
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 give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
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 Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
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 Program, 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 Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) 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; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, 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 executable. However, as a
|
||||||
|
special exception, the source code 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.
|
||||||
|
|
||||||
|
If distribution of executable or 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 counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program 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.
|
||||||
|
|
||||||
|
5. 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 Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program 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 to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. 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 Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program 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 Program.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program 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.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the 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 Program
|
||||||
|
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 Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, 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
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), 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 Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; 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.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
339
vendor/github.com/docker/docker/contrib/selinux-oraclelinux-7/docker-engine-selinux/LICENSE
generated
vendored
Normal file
339
vendor/github.com/docker/docker/contrib/selinux-oraclelinux-7/docker-engine-selinux/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 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.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, 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 or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
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 give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
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 Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
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 Program, 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 Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) 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; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, 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 executable. However, as a
|
||||||
|
special exception, the source code 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.
|
||||||
|
|
||||||
|
If distribution of executable or 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 counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program 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.
|
||||||
|
|
||||||
|
5. 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 Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program 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 to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. 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 Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program 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 Program.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program 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.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the 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 Program
|
||||||
|
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 Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, 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
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), 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 Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; 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.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
340
vendor/github.com/docker/docker/contrib/selinux/docker-engine-selinux/LICENSE
generated
vendored
Normal file
340
vendor/github.com/docker/docker/contrib/selinux/docker-engine-selinux/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Library General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, 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 or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
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 give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
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 Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
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 Program, 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 Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) 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; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, 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 executable. However, as a
|
||||||
|
special exception, the source code 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.
|
||||||
|
|
||||||
|
If distribution of executable or 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 counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program 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.
|
||||||
|
|
||||||
|
5. 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 Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program 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 to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. 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 Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program 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 Program.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program 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.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the 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 Program
|
||||||
|
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 Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, 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
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), 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 Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Library General
|
||||||
|
Public License instead of this License.
|
22
vendor/github.com/docker/docker/contrib/syntax/vim/LICENSE
generated
vendored
Normal file
22
vendor/github.com/docker/docker/contrib/syntax/vim/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 2013 Honza Pokorny
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
67
vendor/github.com/docker/docker/daemon/graphdriver/counter.go
generated
vendored
Normal file
67
vendor/github.com/docker/docker/daemon/graphdriver/counter.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
type minfo struct {
|
||||||
|
check bool
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefCounter is a generic counter for use by graphdriver Get/Put calls
|
||||||
|
type RefCounter struct {
|
||||||
|
counts map[string]*minfo
|
||||||
|
mu sync.Mutex
|
||||||
|
checker Checker
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRefCounter returns a new RefCounter
|
||||||
|
func NewRefCounter(c Checker) *RefCounter {
|
||||||
|
return &RefCounter{
|
||||||
|
checker: c,
|
||||||
|
counts: make(map[string]*minfo),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment increaes the ref count for the given id and returns the current count
|
||||||
|
func (c *RefCounter) Increment(path string) int {
|
||||||
|
c.mu.Lock()
|
||||||
|
m := c.counts[path]
|
||||||
|
if m == nil {
|
||||||
|
m = &minfo{}
|
||||||
|
c.counts[path] = m
|
||||||
|
}
|
||||||
|
// if we are checking this path for the first time check to make sure
|
||||||
|
// if it was already mounted on the system and make sure we have a correct ref
|
||||||
|
// count if it is mounted as it is in use.
|
||||||
|
if !m.check {
|
||||||
|
m.check = true
|
||||||
|
if c.checker.IsMounted(path) {
|
||||||
|
m.count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.count++
|
||||||
|
c.mu.Unlock()
|
||||||
|
return m.count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement decreases the ref count for the given id and returns the current count
|
||||||
|
func (c *RefCounter) Decrement(path string) int {
|
||||||
|
c.mu.Lock()
|
||||||
|
m := c.counts[path]
|
||||||
|
if m == nil {
|
||||||
|
m = &minfo{}
|
||||||
|
c.counts[path] = m
|
||||||
|
}
|
||||||
|
// if we are checking this path for the first time check to make sure
|
||||||
|
// if it was already mounted on the system and make sure we have a correct ref
|
||||||
|
// count if it is mounted as it is in use.
|
||||||
|
if !m.check {
|
||||||
|
m.check = true
|
||||||
|
if c.checker.IsMounted(path) {
|
||||||
|
m.count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.count--
|
||||||
|
c.mu.Unlock()
|
||||||
|
return m.count
|
||||||
|
}
|
243
vendor/github.com/docker/docker/daemon/graphdriver/driver.go
generated
vendored
Normal file
243
vendor/github.com/docker/docker/daemon/graphdriver/driver.go
generated
vendored
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/vbatts/tar-split/tar/storage"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FsMagic unsigned id of the filesystem in use.
|
||||||
|
type FsMagic uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FsMagicUnsupported is a predefined constant value other than a valid filesystem id.
|
||||||
|
FsMagicUnsupported = FsMagic(0x00000000)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// All registered drivers
|
||||||
|
drivers map[string]InitFunc
|
||||||
|
|
||||||
|
// ErrNotSupported returned when driver is not supported.
|
||||||
|
ErrNotSupported = errors.New("driver not supported")
|
||||||
|
// ErrPrerequisites retuned when driver does not meet prerequisites.
|
||||||
|
ErrPrerequisites = errors.New("prerequisites for driver not satisfied (wrong filesystem?)")
|
||||||
|
// ErrIncompatibleFS returned when file system is not supported.
|
||||||
|
ErrIncompatibleFS = fmt.Errorf("backing file system is unsupported for this graph driver")
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitFunc initializes the storage driver.
|
||||||
|
type InitFunc func(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error)
|
||||||
|
|
||||||
|
// ProtoDriver defines the basic capabilities of a driver.
|
||||||
|
// This interface exists solely to be a minimum set of methods
|
||||||
|
// for client code which choose not to implement the entire Driver
|
||||||
|
// interface and use the NaiveDiffDriver wrapper constructor.
|
||||||
|
//
|
||||||
|
// Use of ProtoDriver directly by client code is not recommended.
|
||||||
|
type ProtoDriver interface {
|
||||||
|
// String returns a string representation of this driver.
|
||||||
|
String() string
|
||||||
|
// CreateReadWrite creates a new, empty filesystem layer that is ready
|
||||||
|
// to be used as the storage for a container.
|
||||||
|
CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error
|
||||||
|
// Create creates a new, empty, filesystem layer with the
|
||||||
|
// specified id and parent and mountLabel. Parent and mountLabel may be "".
|
||||||
|
Create(id, parent, mountLabel string, storageOpt map[string]string) error
|
||||||
|
// Remove attempts to remove the filesystem layer with this id.
|
||||||
|
Remove(id string) error
|
||||||
|
// Get returns the mountpoint for the layered filesystem referred
|
||||||
|
// to by this id. You can optionally specify a mountLabel or "".
|
||||||
|
// Returns the absolute path to the mounted layered filesystem.
|
||||||
|
Get(id, mountLabel string) (dir string, err error)
|
||||||
|
// Put releases the system resources for the specified id,
|
||||||
|
// e.g, unmounting layered filesystem.
|
||||||
|
Put(id string) error
|
||||||
|
// Exists returns whether a filesystem layer with the specified
|
||||||
|
// ID exists on this driver.
|
||||||
|
Exists(id string) bool
|
||||||
|
// Status returns a set of key-value pairs which give low
|
||||||
|
// level diagnostic status about this driver.
|
||||||
|
Status() [][2]string
|
||||||
|
// Returns a set of key-value pairs which give low level information
|
||||||
|
// about the image/container driver is managing.
|
||||||
|
GetMetadata(id string) (map[string]string, error)
|
||||||
|
// Cleanup performs necessary tasks to release resources
|
||||||
|
// held by the driver, e.g., unmounting all layered filesystems
|
||||||
|
// known to this driver.
|
||||||
|
Cleanup() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Driver is the interface for layered/snapshot file system drivers.
|
||||||
|
type Driver interface {
|
||||||
|
ProtoDriver
|
||||||
|
// Diff produces an archive of the changes between the specified
|
||||||
|
// layer and its parent layer which may be "".
|
||||||
|
Diff(id, parent string) (archive.Archive, error)
|
||||||
|
// Changes produces a list of changes between the specified layer
|
||||||
|
// and its parent layer. If parent is "", then all changes will be ADD changes.
|
||||||
|
Changes(id, parent string) ([]archive.Change, error)
|
||||||
|
// ApplyDiff extracts the changeset from the given diff into the
|
||||||
|
// layer with the specified id and parent, returning the size of the
|
||||||
|
// new layer in bytes.
|
||||||
|
// The archive.Reader must be an uncompressed stream.
|
||||||
|
ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error)
|
||||||
|
// DiffSize calculates the changes between the specified id
|
||||||
|
// and its parent and returns the size in bytes of the changes
|
||||||
|
// relative to its base filesystem directory.
|
||||||
|
DiffSize(id, parent string) (size int64, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffGetterDriver is the interface for layered file system drivers that
|
||||||
|
// provide a specialized function for getting file contents for tar-split.
|
||||||
|
type DiffGetterDriver interface {
|
||||||
|
Driver
|
||||||
|
// DiffGetter returns an interface to efficiently retrieve the contents
|
||||||
|
// of files in a layer.
|
||||||
|
DiffGetter(id string) (FileGetCloser, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileGetCloser extends the storage.FileGetter interface with a Close method
|
||||||
|
// for cleaning up.
|
||||||
|
type FileGetCloser interface {
|
||||||
|
storage.FileGetter
|
||||||
|
// Close cleans up any resources associated with the FileGetCloser.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checker makes checks on specified filesystems.
|
||||||
|
type Checker interface {
|
||||||
|
// IsMounted returns true if the provided path is mounted for the specific checker
|
||||||
|
IsMounted(path string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
drivers = make(map[string]InitFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register registers an InitFunc for the driver.
|
||||||
|
func Register(name string, initFunc InitFunc) error {
|
||||||
|
if _, exists := drivers[name]; exists {
|
||||||
|
return fmt.Errorf("Name already registered %s", name)
|
||||||
|
}
|
||||||
|
drivers[name] = initFunc
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDriver initializes and returns the registered driver
|
||||||
|
func GetDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
|
||||||
|
if initFunc, exists := drivers[name]; exists {
|
||||||
|
return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps)
|
||||||
|
}
|
||||||
|
if pluginDriver, err := lookupPlugin(name, home, options); err == nil {
|
||||||
|
return pluginDriver, nil
|
||||||
|
}
|
||||||
|
logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBuiltinDriver initializes and returns the registered driver, but does not try to load from plugins
|
||||||
|
func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
|
||||||
|
if initFunc, exists := drivers[name]; exists {
|
||||||
|
return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps)
|
||||||
|
}
|
||||||
|
logrus.Errorf("Failed to built-in GetDriver graph %s %s", name, home)
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates the driver and initializes it at the specified root.
|
||||||
|
func New(root string, name string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
|
||||||
|
if name != "" {
|
||||||
|
logrus.Debugf("[graphdriver] trying provided driver: %s", name) // so the logs show specified driver
|
||||||
|
return GetDriver(name, root, options, uidMaps, gidMaps)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guess for prior driver
|
||||||
|
driversMap := scanPriorDrivers(root)
|
||||||
|
for _, name := range priority {
|
||||||
|
if name == "vfs" {
|
||||||
|
// don't use vfs even if there is state present.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, prior := driversMap[name]; prior {
|
||||||
|
// of the state found from prior drivers, check in order of our priority
|
||||||
|
// which we would prefer
|
||||||
|
driver, err := getBuiltinDriver(name, root, options, uidMaps, gidMaps)
|
||||||
|
if err != nil {
|
||||||
|
// unlike below, we will return error here, because there is prior
|
||||||
|
// state, and now it is no longer supported/prereq/compatible, so
|
||||||
|
// something changed and needs attention. Otherwise the daemon's
|
||||||
|
// images would just "disappear".
|
||||||
|
logrus.Errorf("[graphdriver] prior storage driver %s failed: %s", name, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// abort starting when there are other prior configured drivers
|
||||||
|
// to ensure the user explicitly selects the driver to load
|
||||||
|
if len(driversMap)-1 > 0 {
|
||||||
|
var driversSlice []string
|
||||||
|
for name := range driversMap {
|
||||||
|
driversSlice = append(driversSlice, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("%s contains several valid graphdrivers: %s; Please cleanup or explicitly choose storage driver (-s <DRIVER>)", root, strings.Join(driversSlice, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("[graphdriver] using prior storage driver: %s", name)
|
||||||
|
return driver, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for priority drivers first
|
||||||
|
for _, name := range priority {
|
||||||
|
driver, err := getBuiltinDriver(name, root, options, uidMaps, gidMaps)
|
||||||
|
if err != nil {
|
||||||
|
if isDriverNotSupported(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return driver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all registered drivers if no priority driver is found
|
||||||
|
for name, initFunc := range drivers {
|
||||||
|
driver, err := initFunc(filepath.Join(root, name), options, uidMaps, gidMaps)
|
||||||
|
if err != nil {
|
||||||
|
if isDriverNotSupported(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return driver, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("No supported storage backend found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDriverNotSupported returns true if the error initializing
|
||||||
|
// the graph driver is a non-supported error.
|
||||||
|
func isDriverNotSupported(err error) bool {
|
||||||
|
return err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanPriorDrivers returns an un-ordered scan of directories of prior storage drivers
|
||||||
|
func scanPriorDrivers(root string) map[string]bool {
|
||||||
|
driversMap := make(map[string]bool)
|
||||||
|
|
||||||
|
for driver := range drivers {
|
||||||
|
p := filepath.Join(root, driver)
|
||||||
|
if _, err := os.Stat(p); err == nil && driver != "vfs" {
|
||||||
|
driversMap[driver] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return driversMap
|
||||||
|
}
|
19
vendor/github.com/docker/docker/daemon/graphdriver/driver_freebsd.go
generated
vendored
Normal file
19
vendor/github.com/docker/docker/daemon/graphdriver/driver_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Slice of drivers that should be used in an order
|
||||||
|
priority = []string{
|
||||||
|
"zfs",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mounted checks if the given path is mounted as the fs type
|
||||||
|
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
|
||||||
|
var buf syscall.Statfs_t
|
||||||
|
if err := syscall.Statfs(mountPath, &buf); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return FsMagic(buf.Type) == fsType, nil
|
||||||
|
}
|
133
vendor/github.com/docker/docker/daemon/graphdriver/driver_linux.go
generated
vendored
Normal file
133
vendor/github.com/docker/docker/daemon/graphdriver/driver_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/mount"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FsMagicAufs filesystem id for Aufs
|
||||||
|
FsMagicAufs = FsMagic(0x61756673)
|
||||||
|
// FsMagicBtrfs filesystem id for Btrfs
|
||||||
|
FsMagicBtrfs = FsMagic(0x9123683E)
|
||||||
|
// FsMagicCramfs filesystem id for Cramfs
|
||||||
|
FsMagicCramfs = FsMagic(0x28cd3d45)
|
||||||
|
// FsMagicEcryptfs filesystem id for eCryptfs
|
||||||
|
FsMagicEcryptfs = FsMagic(0xf15f)
|
||||||
|
// FsMagicExtfs filesystem id for Extfs
|
||||||
|
FsMagicExtfs = FsMagic(0x0000EF53)
|
||||||
|
// FsMagicF2fs filesystem id for F2fs
|
||||||
|
FsMagicF2fs = FsMagic(0xF2F52010)
|
||||||
|
// FsMagicGPFS filesystem id for GPFS
|
||||||
|
FsMagicGPFS = FsMagic(0x47504653)
|
||||||
|
// FsMagicJffs2Fs filesystem if for Jffs2Fs
|
||||||
|
FsMagicJffs2Fs = FsMagic(0x000072b6)
|
||||||
|
// FsMagicJfs filesystem id for Jfs
|
||||||
|
FsMagicJfs = FsMagic(0x3153464a)
|
||||||
|
// FsMagicNfsFs filesystem id for NfsFs
|
||||||
|
FsMagicNfsFs = FsMagic(0x00006969)
|
||||||
|
// FsMagicRAMFs filesystem id for RamFs
|
||||||
|
FsMagicRAMFs = FsMagic(0x858458f6)
|
||||||
|
// FsMagicReiserFs filesystem id for ReiserFs
|
||||||
|
FsMagicReiserFs = FsMagic(0x52654973)
|
||||||
|
// FsMagicSmbFs filesystem id for SmbFs
|
||||||
|
FsMagicSmbFs = FsMagic(0x0000517B)
|
||||||
|
// FsMagicSquashFs filesystem id for SquashFs
|
||||||
|
FsMagicSquashFs = FsMagic(0x73717368)
|
||||||
|
// FsMagicTmpFs filesystem id for TmpFs
|
||||||
|
FsMagicTmpFs = FsMagic(0x01021994)
|
||||||
|
// FsMagicVxFS filesystem id for VxFs
|
||||||
|
FsMagicVxFS = FsMagic(0xa501fcf5)
|
||||||
|
// FsMagicXfs filesystem id for Xfs
|
||||||
|
FsMagicXfs = FsMagic(0x58465342)
|
||||||
|
// FsMagicZfs filesystem id for Zfs
|
||||||
|
FsMagicZfs = FsMagic(0x2fc12fc1)
|
||||||
|
// FsMagicOverlay filesystem id for overlay
|
||||||
|
FsMagicOverlay = FsMagic(0x794C7630)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Slice of drivers that should be used in an order
|
||||||
|
priority = []string{
|
||||||
|
"aufs",
|
||||||
|
"btrfs",
|
||||||
|
"zfs",
|
||||||
|
"devicemapper",
|
||||||
|
"overlay",
|
||||||
|
"vfs",
|
||||||
|
}
|
||||||
|
|
||||||
|
// FsNames maps filesystem id to name of the filesystem.
|
||||||
|
FsNames = map[FsMagic]string{
|
||||||
|
FsMagicAufs: "aufs",
|
||||||
|
FsMagicBtrfs: "btrfs",
|
||||||
|
FsMagicCramfs: "cramfs",
|
||||||
|
FsMagicExtfs: "extfs",
|
||||||
|
FsMagicF2fs: "f2fs",
|
||||||
|
FsMagicGPFS: "gpfs",
|
||||||
|
FsMagicJffs2Fs: "jffs2",
|
||||||
|
FsMagicJfs: "jfs",
|
||||||
|
FsMagicNfsFs: "nfs",
|
||||||
|
FsMagicRAMFs: "ramfs",
|
||||||
|
FsMagicReiserFs: "reiserfs",
|
||||||
|
FsMagicSmbFs: "smb",
|
||||||
|
FsMagicSquashFs: "squashfs",
|
||||||
|
FsMagicTmpFs: "tmpfs",
|
||||||
|
FsMagicUnsupported: "unsupported",
|
||||||
|
FsMagicVxFS: "vxfs",
|
||||||
|
FsMagicXfs: "xfs",
|
||||||
|
FsMagicZfs: "zfs",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFSMagic returns the filesystem id given the path.
|
||||||
|
func GetFSMagic(rootpath string) (FsMagic, error) {
|
||||||
|
var buf syscall.Statfs_t
|
||||||
|
if err := syscall.Statfs(filepath.Dir(rootpath), &buf); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return FsMagic(buf.Type), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFsChecker returns a checker configured for the provied FsMagic
|
||||||
|
func NewFsChecker(t FsMagic) Checker {
|
||||||
|
return &fsChecker{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type fsChecker struct {
|
||||||
|
t FsMagic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fsChecker) IsMounted(path string) bool {
|
||||||
|
m, _ := Mounted(c.t, path)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultChecker returns a check that parses /proc/mountinfo to check
|
||||||
|
// if the specified path is mounted.
|
||||||
|
func NewDefaultChecker() Checker {
|
||||||
|
return &defaultChecker{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultChecker struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *defaultChecker) IsMounted(path string) bool {
|
||||||
|
m, _ := mount.Mounted(path)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounted checks if the given path is mounted as the fs type
|
||||||
|
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
|
||||||
|
var buf syscall.Statfs_t
|
||||||
|
if err := syscall.Statfs(mountPath, &buf); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return FsMagic(buf.Type) == fsType, nil
|
||||||
|
}
|
65
vendor/github.com/docker/docker/daemon/graphdriver/driver_solaris.go
generated
vendored
Normal file
65
vendor/github.com/docker/docker/daemon/graphdriver/driver_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// +build solaris,cgo
|
||||||
|
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static inline struct statvfs *getstatfs(char *s) {
|
||||||
|
struct statvfs *buf;
|
||||||
|
int err;
|
||||||
|
buf = (struct statvfs *)malloc(sizeof(struct statvfs));
|
||||||
|
err = statvfs(s, buf);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FsMagicZfs filesystem id for Zfs
|
||||||
|
FsMagicZfs = FsMagic(0x2fc12fc1)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Slice of drivers that should be used in an order
|
||||||
|
priority = []string{
|
||||||
|
"zfs",
|
||||||
|
}
|
||||||
|
|
||||||
|
// FsNames maps filesystem id to name of the filesystem.
|
||||||
|
FsNames = map[FsMagic]string{
|
||||||
|
FsMagicZfs: "zfs",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFSMagic returns the filesystem id given the path.
|
||||||
|
func GetFSMagic(rootpath string) (FsMagic, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounted checks if the given path is mounted as the fs type
|
||||||
|
//Solaris supports only ZFS for now
|
||||||
|
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
|
||||||
|
|
||||||
|
cs := C.CString(filepath.Dir(mountPath))
|
||||||
|
buf := C.getstatfs(cs)
|
||||||
|
|
||||||
|
// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
|
||||||
|
if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
|
||||||
|
(buf.f_basetype[3] != 0) {
|
||||||
|
logrus.Debugf("[zfs] no zfs dataset found for rootdir '%s'", mountPath)
|
||||||
|
C.free(unsafe.Pointer(buf))
|
||||||
|
return false, ErrPrerequisites
|
||||||
|
}
|
||||||
|
|
||||||
|
C.free(unsafe.Pointer(buf))
|
||||||
|
C.free(unsafe.Pointer(cs))
|
||||||
|
return true, nil
|
||||||
|
}
|
15
vendor/github.com/docker/docker/daemon/graphdriver/driver_unsupported.go
generated
vendored
Normal file
15
vendor/github.com/docker/docker/daemon/graphdriver/driver_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// +build !linux,!windows,!freebsd,!solaris
|
||||||
|
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Slice of drivers that should be used in an order
|
||||||
|
priority = []string{
|
||||||
|
"unsupported",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFSMagic returns the filesystem id given the path.
|
||||||
|
func GetFSMagic(rootpath string) (FsMagic, error) {
|
||||||
|
return FsMagicUnsupported, nil
|
||||||
|
}
|
14
vendor/github.com/docker/docker/daemon/graphdriver/driver_windows.go
generated
vendored
Normal file
14
vendor/github.com/docker/docker/daemon/graphdriver/driver_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Slice of drivers that should be used in order
|
||||||
|
priority = []string{
|
||||||
|
"windowsfilter",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFSMagic returns the filesystem id given the path.
|
||||||
|
func GetFSMagic(rootpath string) (FsMagic, error) {
|
||||||
|
// Note it is OK to return FsMagicUnsupported on Windows.
|
||||||
|
return FsMagicUnsupported, nil
|
||||||
|
}
|
162
vendor/github.com/docker/docker/daemon/graphdriver/fsdiff.go
generated
vendored
Normal file
162
vendor/github.com/docker/docker/daemon/graphdriver/fsdiff.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/chrootarchive"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ApplyUncompressedLayer defines the unpack method used by the graph
|
||||||
|
// driver.
|
||||||
|
ApplyUncompressedLayer = chrootarchive.ApplyUncompressedLayer
|
||||||
|
)
|
||||||
|
|
||||||
|
// NaiveDiffDriver takes a ProtoDriver and adds the
|
||||||
|
// capability of the Diffing methods which it may or may not
|
||||||
|
// support on its own. See the comment on the exported
|
||||||
|
// NewNaiveDiffDriver function below.
|
||||||
|
// Notably, the AUFS driver doesn't need to be wrapped like this.
|
||||||
|
type NaiveDiffDriver struct {
|
||||||
|
ProtoDriver
|
||||||
|
uidMaps []idtools.IDMap
|
||||||
|
gidMaps []idtools.IDMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNaiveDiffDriver returns a fully functional driver that wraps the
|
||||||
|
// given ProtoDriver and adds the capability of the following methods which
|
||||||
|
// it may or may not support on its own:
|
||||||
|
// Diff(id, parent string) (archive.Archive, error)
|
||||||
|
// Changes(id, parent string) ([]archive.Change, error)
|
||||||
|
// ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error)
|
||||||
|
// DiffSize(id, parent string) (size int64, err error)
|
||||||
|
func NewNaiveDiffDriver(driver ProtoDriver, uidMaps, gidMaps []idtools.IDMap) Driver {
|
||||||
|
return &NaiveDiffDriver{ProtoDriver: driver,
|
||||||
|
uidMaps: uidMaps,
|
||||||
|
gidMaps: gidMaps}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diff produces an archive of the changes between the specified
|
||||||
|
// layer and its parent layer which may be "".
|
||||||
|
func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch archive.Archive, err error) {
|
||||||
|
driver := gdw.ProtoDriver
|
||||||
|
|
||||||
|
layerFs, err := driver.Get(id, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
driver.Put(id)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if parent == "" {
|
||||||
|
archive, err := archive.Tar(layerFs, archive.Uncompressed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ioutils.NewReadCloserWrapper(archive, func() error {
|
||||||
|
err := archive.Close()
|
||||||
|
driver.Put(id)
|
||||||
|
return err
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parentFs, err := driver.Get(parent, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer driver.Put(parent)
|
||||||
|
|
||||||
|
changes, err := archive.ChangesDirs(layerFs, parentFs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
archive, err := archive.ExportChanges(layerFs, changes, gdw.uidMaps, gdw.gidMaps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutils.NewReadCloserWrapper(archive, func() error {
|
||||||
|
err := archive.Close()
|
||||||
|
driver.Put(id)
|
||||||
|
return err
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changes produces a list of changes between the specified layer
|
||||||
|
// and its parent layer. If parent is "", then all changes will be ADD changes.
|
||||||
|
func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) {
|
||||||
|
driver := gdw.ProtoDriver
|
||||||
|
|
||||||
|
layerFs, err := driver.Get(id, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer driver.Put(id)
|
||||||
|
|
||||||
|
parentFs := ""
|
||||||
|
|
||||||
|
if parent != "" {
|
||||||
|
parentFs, err = driver.Get(parent, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer driver.Put(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return archive.ChangesDirs(layerFs, parentFs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyDiff extracts the changeset from the given diff into the
|
||||||
|
// layer with the specified id and parent, returning the size of the
|
||||||
|
// new layer in bytes.
|
||||||
|
func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) {
|
||||||
|
driver := gdw.ProtoDriver
|
||||||
|
|
||||||
|
// Mount the root filesystem so we can apply the diff/layer.
|
||||||
|
layerFs, err := driver.Get(id, "")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer driver.Put(id)
|
||||||
|
|
||||||
|
options := &archive.TarOptions{UIDMaps: gdw.uidMaps,
|
||||||
|
GIDMaps: gdw.gidMaps}
|
||||||
|
start := time.Now().UTC()
|
||||||
|
logrus.Debug("Start untar layer")
|
||||||
|
if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffSize calculates the changes between the specified layer
|
||||||
|
// and its parent and returns the size in bytes of the changes
|
||||||
|
// relative to its base filesystem directory.
|
||||||
|
func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error) {
|
||||||
|
driver := gdw.ProtoDriver
|
||||||
|
|
||||||
|
changes, err := gdw.Changes(id, parent)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
layerFs, err := driver.Get(id, "")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer driver.Put(id)
|
||||||
|
|
||||||
|
return archive.ChangesSize(layerFs, changes), nil
|
||||||
|
}
|
32
vendor/github.com/docker/docker/daemon/graphdriver/plugin.go
generated
vendored
Normal file
32
vendor/github.com/docker/docker/daemon/graphdriver/plugin.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/plugins"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pluginClient interface {
|
||||||
|
// Call calls the specified method with the specified arguments for the plugin.
|
||||||
|
Call(string, interface{}, interface{}) error
|
||||||
|
// Stream calls the specified method with the specified arguments for the plugin and returns the response IO stream
|
||||||
|
Stream(string, interface{}) (io.ReadCloser, error)
|
||||||
|
// SendFile calls the specified method, and passes through the IO stream
|
||||||
|
SendFile(string, io.Reader, interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPlugin(name, home string, opts []string) (Driver, error) {
|
||||||
|
pl, err := plugins.Get(name, "GraphDriver")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
|
||||||
|
}
|
||||||
|
return newPluginDriver(name, home, opts, pl.Client())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPluginDriver(name, home string, opts []string, c pluginClient) (Driver, error) {
|
||||||
|
proxy := &graphDriverProxy{name, c}
|
||||||
|
return proxy, proxy.Init(home, opts)
|
||||||
|
}
|
7
vendor/github.com/docker/docker/daemon/graphdriver/plugin_unsupported.go
generated
vendored
Normal file
7
vendor/github.com/docker/docker/daemon/graphdriver/plugin_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !experimental
|
||||||
|
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
func lookupPlugin(name, home string, opts []string) (Driver, error) {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
225
vendor/github.com/docker/docker/daemon/graphdriver/proxy.go
generated
vendored
Normal file
225
vendor/github.com/docker/docker/daemon/graphdriver/proxy.go
generated
vendored
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package graphdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type graphDriverProxy struct {
|
||||||
|
name string
|
||||||
|
client pluginClient
|
||||||
|
}
|
||||||
|
|
||||||
|
type graphDriverRequest struct {
|
||||||
|
ID string `json:",omitempty"`
|
||||||
|
Parent string `json:",omitempty"`
|
||||||
|
MountLabel string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type graphDriverResponse struct {
|
||||||
|
Err string `json:",omitempty"`
|
||||||
|
Dir string `json:",omitempty"`
|
||||||
|
Exists bool `json:",omitempty"`
|
||||||
|
Status [][2]string `json:",omitempty"`
|
||||||
|
Changes []archive.Change `json:",omitempty"`
|
||||||
|
Size int64 `json:",omitempty"`
|
||||||
|
Metadata map[string]string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type graphDriverInitRequest struct {
|
||||||
|
Home string
|
||||||
|
Opts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Init(home string, opts []string) error {
|
||||||
|
args := &graphDriverInitRequest{
|
||||||
|
Home: home,
|
||||||
|
Opts: opts,
|
||||||
|
}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Init", args, &ret); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) String() string {
|
||||||
|
return d.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
|
||||||
|
args := &graphDriverRequest{
|
||||||
|
ID: id,
|
||||||
|
Parent: parent,
|
||||||
|
MountLabel: mountLabel,
|
||||||
|
}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.CreateReadWrite", args, &ret); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
|
||||||
|
args := &graphDriverRequest{
|
||||||
|
ID: id,
|
||||||
|
Parent: parent,
|
||||||
|
MountLabel: mountLabel,
|
||||||
|
}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Create", args, &ret); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Remove(id string) error {
|
||||||
|
args := &graphDriverRequest{ID: id}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Remove", args, &ret); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Get(id, mountLabel string) (string, error) {
|
||||||
|
args := &graphDriverRequest{
|
||||||
|
ID: id,
|
||||||
|
MountLabel: mountLabel,
|
||||||
|
}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Get", args, &ret); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if ret.Err != "" {
|
||||||
|
err = errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return ret.Dir, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Put(id string) error {
|
||||||
|
args := &graphDriverRequest{ID: id}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Put", args, &ret); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Exists(id string) bool {
|
||||||
|
args := &graphDriverRequest{ID: id}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Exists", args, &ret); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ret.Exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Status() [][2]string {
|
||||||
|
args := &graphDriverRequest{}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Status", args, &ret); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ret.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) GetMetadata(id string) (map[string]string, error) {
|
||||||
|
args := &graphDriverRequest{
|
||||||
|
ID: id,
|
||||||
|
}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.GetMetadata", args, &ret); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return nil, errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return ret.Metadata, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Cleanup() error {
|
||||||
|
args := &graphDriverRequest{}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Cleanup", args, &ret); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Diff(id, parent string) (archive.Archive, error) {
|
||||||
|
args := &graphDriverRequest{
|
||||||
|
ID: id,
|
||||||
|
Parent: parent,
|
||||||
|
}
|
||||||
|
body, err := d.client.Stream("GraphDriver.Diff", args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return archive.Archive(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) Changes(id, parent string) ([]archive.Change, error) {
|
||||||
|
args := &graphDriverRequest{
|
||||||
|
ID: id,
|
||||||
|
Parent: parent,
|
||||||
|
}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.Changes", args, &ret); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return nil, errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret.Changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) {
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.SendFile(fmt.Sprintf("GraphDriver.ApplyDiff?id=%s&parent=%s", id, parent), diff, &ret); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return -1, errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return ret.Size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *graphDriverProxy) DiffSize(id, parent string) (int64, error) {
|
||||||
|
args := &graphDriverRequest{
|
||||||
|
ID: id,
|
||||||
|
Parent: parent,
|
||||||
|
}
|
||||||
|
var ret graphDriverResponse
|
||||||
|
if err := d.client.Call("GraphDriver.DiffSize", args, &ret); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
if ret.Err != "" {
|
||||||
|
return -1, errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
return ret.Size, nil
|
||||||
|
}
|
173
vendor/github.com/docker/docker/image/fs.go
generated
vendored
Normal file
173
vendor/github.com/docker/docker/image/fs.go
generated
vendored
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
package image
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DigestWalkFunc is function called by StoreBackend.Walk
|
||||||
|
type DigestWalkFunc func(id digest.Digest) error
|
||||||
|
|
||||||
|
// StoreBackend provides interface for image.Store persistence
|
||||||
|
type StoreBackend interface {
|
||||||
|
Walk(f DigestWalkFunc) error
|
||||||
|
Get(id digest.Digest) ([]byte, error)
|
||||||
|
Set(data []byte) (digest.Digest, error)
|
||||||
|
Delete(id digest.Digest) error
|
||||||
|
SetMetadata(id digest.Digest, key string, data []byte) error
|
||||||
|
GetMetadata(id digest.Digest, key string) ([]byte, error)
|
||||||
|
DeleteMetadata(id digest.Digest, key string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// fs implements StoreBackend using the filesystem.
|
||||||
|
type fs struct {
|
||||||
|
sync.RWMutex
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
contentDirName = "content"
|
||||||
|
metadataDirName = "metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewFSStoreBackend returns new filesystem based backend for image.Store
|
||||||
|
func NewFSStoreBackend(root string) (StoreBackend, error) {
|
||||||
|
return newFSStore(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFSStore(root string) (*fs, error) {
|
||||||
|
s := &fs{
|
||||||
|
root: root,
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(root, contentDirName, string(digest.Canonical)), 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(root, metadataDirName, string(digest.Canonical)), 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fs) contentFile(dgst digest.Digest) string {
|
||||||
|
return filepath.Join(s.root, contentDirName, string(dgst.Algorithm()), dgst.Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fs) metadataDir(dgst digest.Digest) string {
|
||||||
|
return filepath.Join(s.root, metadataDirName, string(dgst.Algorithm()), dgst.Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk calls the supplied callback for each image ID in the storage backend.
|
||||||
|
func (s *fs) Walk(f DigestWalkFunc) error {
|
||||||
|
// Only Canonical digest (sha256) is currently supported
|
||||||
|
s.RLock()
|
||||||
|
dir, err := ioutil.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical)))
|
||||||
|
s.RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, v := range dir {
|
||||||
|
dgst := digest.NewDigestFromHex(string(digest.Canonical), v.Name())
|
||||||
|
if err := dgst.Validate(); err != nil {
|
||||||
|
logrus.Debugf("Skipping invalid digest %s: %s", dgst, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := f(dgst); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the content stored under a given digest.
|
||||||
|
func (s *fs) Get(dgst digest.Digest) ([]byte, error) {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.get(dgst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fs) get(dgst digest.Digest) ([]byte, error) {
|
||||||
|
content, err := ioutil.ReadFile(s.contentFile(dgst))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: maybe optional
|
||||||
|
if digest.FromBytes(content) != dgst {
|
||||||
|
return nil, fmt.Errorf("failed to verify: %v", dgst)
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stores content by checksum.
|
||||||
|
func (s *fs) Set(data []byte) (digest.Digest, error) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
return "", fmt.Errorf("Invalid empty data")
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst := digest.FromBytes(data)
|
||||||
|
if err := ioutils.AtomicWriteFile(s.contentFile(dgst), data, 0600); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dgst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes content and metadata files associated with the digest.
|
||||||
|
func (s *fs) Delete(dgst digest.Digest) error {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
if err := os.RemoveAll(s.metadataDir(dgst)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.Remove(s.contentFile(dgst)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMetadata sets metadata for a given ID. It fails if there's no base file.
|
||||||
|
func (s *fs) SetMetadata(dgst digest.Digest, key string, data []byte) error {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
if _, err := s.get(dgst); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDir := filepath.Join(s.metadataDir(dgst))
|
||||||
|
if err := os.MkdirAll(baseDir, 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutils.AtomicWriteFile(filepath.Join(s.metadataDir(dgst), key), data, 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetadata returns metadata for a given digest.
|
||||||
|
func (s *fs) GetMetadata(dgst digest.Digest, key string) ([]byte, error) {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
if _, err := s.get(dgst); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ioutil.ReadFile(filepath.Join(s.metadataDir(dgst), key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMetadata removes the metadata associated with a digest.
|
||||||
|
func (s *fs) DeleteMetadata(dgst digest.Digest, key string) error {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
return os.RemoveAll(filepath.Join(s.metadataDir(dgst), key))
|
||||||
|
}
|
150
vendor/github.com/docker/docker/image/image.go
generated
vendored
Normal file
150
vendor/github.com/docker/docker/image/image.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package image
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ID is the content-addressable ID of an image.
|
||||||
|
type ID digest.Digest
|
||||||
|
|
||||||
|
func (id ID) String() string {
|
||||||
|
return id.Digest().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Digest converts ID into a digest
|
||||||
|
func (id ID) Digest() digest.Digest {
|
||||||
|
return digest.Digest(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDFromDigest creates an ID from a digest
|
||||||
|
func IDFromDigest(digest digest.Digest) ID {
|
||||||
|
return ID(digest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V1Image stores the V1 image configuration.
|
||||||
|
type V1Image struct {
|
||||||
|
// ID a unique 64 character identifier of the image
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
// Parent id of the image
|
||||||
|
Parent string `json:"parent,omitempty"`
|
||||||
|
// Comment user added comment
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
// Created timestamp when image was created
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// Container is the id of the container used to commit
|
||||||
|
Container string `json:"container,omitempty"`
|
||||||
|
// ContainerConfig is the configuration of the container that is committed into the image
|
||||||
|
ContainerConfig container.Config `json:"container_config,omitempty"`
|
||||||
|
// DockerVersion specifies version on which image is built
|
||||||
|
DockerVersion string `json:"docker_version,omitempty"`
|
||||||
|
// Author of the image
|
||||||
|
Author string `json:"author,omitempty"`
|
||||||
|
// Config is the configuration of the container received from the client
|
||||||
|
Config *container.Config `json:"config,omitempty"`
|
||||||
|
// Architecture is the hardware that the image is build and runs on
|
||||||
|
Architecture string `json:"architecture,omitempty"`
|
||||||
|
// OS is the operating system used to build and run the image
|
||||||
|
OS string `json:"os,omitempty"`
|
||||||
|
// Size is the total size of the image including all layers it is composed of
|
||||||
|
Size int64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image stores the image configuration
|
||||||
|
type Image struct {
|
||||||
|
V1Image
|
||||||
|
Parent ID `json:"parent,omitempty"`
|
||||||
|
RootFS *RootFS `json:"rootfs,omitempty"`
|
||||||
|
History []History `json:"history,omitempty"`
|
||||||
|
OSVersion string `json:"os.version,omitempty"`
|
||||||
|
OSFeatures []string `json:"os.features,omitempty"`
|
||||||
|
|
||||||
|
// rawJSON caches the immutable JSON associated with this image.
|
||||||
|
rawJSON []byte
|
||||||
|
|
||||||
|
// computedID is the ID computed from the hash of the image config.
|
||||||
|
// Not to be confused with the legacy V1 ID in V1Image.
|
||||||
|
computedID ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawJSON returns the immutable JSON associated with the image.
|
||||||
|
func (img *Image) RawJSON() []byte {
|
||||||
|
return img.rawJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the image's content-addressable ID.
|
||||||
|
func (img *Image) ID() ID {
|
||||||
|
return img.computedID
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageID stringifies ID.
|
||||||
|
func (img *Image) ImageID() string {
|
||||||
|
return img.ID().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunConfig returns the image's container config.
|
||||||
|
func (img *Image) RunConfig() *container.Config {
|
||||||
|
return img.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON serializes the image to JSON. It sorts the top-level keys so
|
||||||
|
// that JSON that's been manipulated by a push/pull cycle with a legacy
|
||||||
|
// registry won't end up with a different key order.
|
||||||
|
func (img *Image) MarshalJSON() ([]byte, error) {
|
||||||
|
type MarshalImage Image
|
||||||
|
|
||||||
|
pass1, err := json.Marshal(MarshalImage(*img))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var c map[string]*json.RawMessage
|
||||||
|
if err := json.Unmarshal(pass1, &c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// History stores build commands that were used to create an image
|
||||||
|
type History struct {
|
||||||
|
// Created timestamp for build point
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// Author of the build point
|
||||||
|
Author string `json:"author,omitempty"`
|
||||||
|
// CreatedBy keeps the Dockerfile command used while building image.
|
||||||
|
CreatedBy string `json:"created_by,omitempty"`
|
||||||
|
// Comment is custom message set by the user when creating the image.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
// EmptyLayer is set to true if this history item did not generate a
|
||||||
|
// layer. Otherwise, the history item is associated with the next
|
||||||
|
// layer in the RootFS section.
|
||||||
|
EmptyLayer bool `json:"empty_layer,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exporter provides interface for exporting and importing images
|
||||||
|
type Exporter interface {
|
||||||
|
Load(io.ReadCloser, io.Writer, bool) error
|
||||||
|
// TODO: Load(net.Context, io.ReadCloser, <- chan StatusMessage) error
|
||||||
|
Save([]string, io.Writer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromJSON creates an Image configuration from json.
|
||||||
|
func NewFromJSON(src []byte) (*Image, error) {
|
||||||
|
img := &Image{}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(src, img); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if img.RootFS == nil {
|
||||||
|
return nil, errors.New("Invalid image JSON, no RootFS key.")
|
||||||
|
}
|
||||||
|
|
||||||
|
img.rawJSON = src
|
||||||
|
|
||||||
|
return img, nil
|
||||||
|
}
|
44
vendor/github.com/docker/docker/image/rootfs.go
generated
vendored
Normal file
44
vendor/github.com/docker/docker/image/rootfs.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package image
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/layer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TypeLayers is used for RootFS.Type for filesystems organized into layers.
|
||||||
|
const TypeLayers = "layers"
|
||||||
|
|
||||||
|
// typeLayersWithBase is an older format used by Windows up to v1.12. We
|
||||||
|
// explicitly handle this as an error case to ensure that a daemon which still
|
||||||
|
// has an older image like this on disk can still start, even though the
|
||||||
|
// image itself is not usable. See https://github.com/docker/docker/pull/25806.
|
||||||
|
const typeLayersWithBase = "layers+base"
|
||||||
|
|
||||||
|
// RootFS describes images root filesystem
|
||||||
|
// This is currently a placeholder that only supports layers. In the future
|
||||||
|
// this can be made into an interface that supports different implementations.
|
||||||
|
type RootFS struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
DiffIDs []layer.DiffID `json:"diff_ids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRootFS returns empty RootFS struct
|
||||||
|
func NewRootFS() *RootFS {
|
||||||
|
return &RootFS{Type: TypeLayers}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append appends a new diffID to rootfs
|
||||||
|
func (r *RootFS) Append(id layer.DiffID) {
|
||||||
|
r.DiffIDs = append(r.DiffIDs, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainID returns the ChainID for the top layer in RootFS.
|
||||||
|
func (r *RootFS) ChainID() layer.ChainID {
|
||||||
|
if runtime.GOOS == "windows" && r.Type == typeLayersWithBase {
|
||||||
|
logrus.Warnf("Layer type is unsupported on this platform. DiffIDs: '%v'", r.DiffIDs)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return layer.CreateChainID(r.DiffIDs)
|
||||||
|
}
|
295
vendor/github.com/docker/docker/image/store.go
generated
vendored
Normal file
295
vendor/github.com/docker/docker/image/store.go
generated
vendored
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
package image
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/layer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Store is an interface for creating and accessing images
|
||||||
|
type Store interface {
|
||||||
|
Create(config []byte) (ID, error)
|
||||||
|
Get(id ID) (*Image, error)
|
||||||
|
Delete(id ID) ([]layer.Metadata, error)
|
||||||
|
Search(partialID string) (ID, error)
|
||||||
|
SetParent(id ID, parent ID) error
|
||||||
|
GetParent(id ID) (ID, error)
|
||||||
|
Children(id ID) []ID
|
||||||
|
Map() map[ID]*Image
|
||||||
|
Heads() map[ID]*Image
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerGetReleaser is a minimal interface for getting and releasing images.
|
||||||
|
type LayerGetReleaser interface {
|
||||||
|
Get(layer.ChainID) (layer.Layer, error)
|
||||||
|
Release(layer.Layer) ([]layer.Metadata, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type imageMeta struct {
|
||||||
|
layer layer.Layer
|
||||||
|
children map[ID]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type store struct {
|
||||||
|
sync.Mutex
|
||||||
|
ls LayerGetReleaser
|
||||||
|
images map[ID]*imageMeta
|
||||||
|
fs StoreBackend
|
||||||
|
digestSet *digest.Set
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageStore returns new store object for given layer store
|
||||||
|
func NewImageStore(fs StoreBackend, ls LayerGetReleaser) (Store, error) {
|
||||||
|
is := &store{
|
||||||
|
ls: ls,
|
||||||
|
images: make(map[ID]*imageMeta),
|
||||||
|
fs: fs,
|
||||||
|
digestSet: digest.NewSet(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// load all current images and retain layers
|
||||||
|
if err := is.restore(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return is, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) restore() error {
|
||||||
|
err := is.fs.Walk(func(dgst digest.Digest) error {
|
||||||
|
img, err := is.Get(IDFromDigest(dgst))
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("invalid image %v, %v", dgst, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var l layer.Layer
|
||||||
|
if chainID := img.RootFS.ChainID(); chainID != "" {
|
||||||
|
l, err = is.ls.Get(chainID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := is.digestSet.Add(dgst); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
imageMeta := &imageMeta{
|
||||||
|
layer: l,
|
||||||
|
children: make(map[ID]struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
is.images[IDFromDigest(dgst)] = imageMeta
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass to fill in children maps
|
||||||
|
for id := range is.images {
|
||||||
|
if parent, err := is.GetParent(id); err == nil {
|
||||||
|
if parentMeta := is.images[parent]; parentMeta != nil {
|
||||||
|
parentMeta.children[id] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) Create(config []byte) (ID, error) {
|
||||||
|
var img Image
|
||||||
|
err := json.Unmarshal(config, &img)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must reject any config that references diffIDs from the history
|
||||||
|
// which aren't among the rootfs layers.
|
||||||
|
rootFSLayers := make(map[layer.DiffID]struct{})
|
||||||
|
for _, diffID := range img.RootFS.DiffIDs {
|
||||||
|
rootFSLayers[diffID] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
layerCounter := 0
|
||||||
|
for _, h := range img.History {
|
||||||
|
if !h.EmptyLayer {
|
||||||
|
layerCounter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if layerCounter > len(img.RootFS.DiffIDs) {
|
||||||
|
return "", errors.New("too many non-empty layers in History section")
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst, err := is.fs.Set(config)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
imageID := IDFromDigest(dgst)
|
||||||
|
|
||||||
|
is.Lock()
|
||||||
|
defer is.Unlock()
|
||||||
|
|
||||||
|
if _, exists := is.images[imageID]; exists {
|
||||||
|
return imageID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
layerID := img.RootFS.ChainID()
|
||||||
|
|
||||||
|
var l layer.Layer
|
||||||
|
if layerID != "" {
|
||||||
|
l, err = is.ls.Get(layerID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageMeta := &imageMeta{
|
||||||
|
layer: l,
|
||||||
|
children: make(map[ID]struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
is.images[imageID] = imageMeta
|
||||||
|
if err := is.digestSet.Add(imageID.Digest()); err != nil {
|
||||||
|
delete(is.images, imageID)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) Search(term string) (ID, error) {
|
||||||
|
is.Lock()
|
||||||
|
defer is.Unlock()
|
||||||
|
|
||||||
|
dgst, err := is.digestSet.Lookup(term)
|
||||||
|
if err != nil {
|
||||||
|
if err == digest.ErrDigestNotFound {
|
||||||
|
err = fmt.Errorf("No such image: %s", term)
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return IDFromDigest(dgst), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) Get(id ID) (*Image, error) {
|
||||||
|
// todo: Check if image is in images
|
||||||
|
// todo: Detect manual insertions and start using them
|
||||||
|
config, err := is.fs.Get(id.Digest())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
img, err := NewFromJSON(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
img.computedID = id
|
||||||
|
|
||||||
|
img.Parent, err = is.GetParent(id)
|
||||||
|
if err != nil {
|
||||||
|
img.Parent = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) Delete(id ID) ([]layer.Metadata, error) {
|
||||||
|
is.Lock()
|
||||||
|
defer is.Unlock()
|
||||||
|
|
||||||
|
imageMeta := is.images[id]
|
||||||
|
if imageMeta == nil {
|
||||||
|
return nil, fmt.Errorf("unrecognized image ID %s", id.String())
|
||||||
|
}
|
||||||
|
for id := range imageMeta.children {
|
||||||
|
is.fs.DeleteMetadata(id.Digest(), "parent")
|
||||||
|
}
|
||||||
|
if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
|
||||||
|
delete(is.images[parent].children, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := is.digestSet.Remove(id.Digest()); err != nil {
|
||||||
|
logrus.Errorf("error removing %s from digest set: %q", id, err)
|
||||||
|
}
|
||||||
|
delete(is.images, id)
|
||||||
|
is.fs.Delete(id.Digest())
|
||||||
|
|
||||||
|
if imageMeta.layer != nil {
|
||||||
|
return is.ls.Release(imageMeta.layer)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) SetParent(id, parent ID) error {
|
||||||
|
is.Lock()
|
||||||
|
defer is.Unlock()
|
||||||
|
parentMeta := is.images[parent]
|
||||||
|
if parentMeta == nil {
|
||||||
|
return fmt.Errorf("unknown parent image ID %s", parent.String())
|
||||||
|
}
|
||||||
|
if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
|
||||||
|
delete(is.images[parent].children, id)
|
||||||
|
}
|
||||||
|
parentMeta.children[id] = struct{}{}
|
||||||
|
return is.fs.SetMetadata(id.Digest(), "parent", []byte(parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) GetParent(id ID) (ID, error) {
|
||||||
|
d, err := is.fs.GetMetadata(id.Digest(), "parent")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ID(d), nil // todo: validate?
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) Children(id ID) []ID {
|
||||||
|
is.Lock()
|
||||||
|
defer is.Unlock()
|
||||||
|
|
||||||
|
return is.children(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) children(id ID) []ID {
|
||||||
|
var ids []ID
|
||||||
|
if is.images[id] != nil {
|
||||||
|
for id := range is.images[id].children {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) Heads() map[ID]*Image {
|
||||||
|
return is.imagesMap(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) Map() map[ID]*Image {
|
||||||
|
return is.imagesMap(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *store) imagesMap(all bool) map[ID]*Image {
|
||||||
|
is.Lock()
|
||||||
|
defer is.Unlock()
|
||||||
|
|
||||||
|
images := make(map[ID]*Image)
|
||||||
|
|
||||||
|
for id := range is.images {
|
||||||
|
if !all && len(is.children(id)) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
img, err := is.Get(id)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("invalid image access: %q, error: %q", id, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
images[id] = img
|
||||||
|
}
|
||||||
|
return images
|
||||||
|
}
|
156
vendor/github.com/docker/docker/image/v1/imagev1.go
generated
vendored
Normal file
156
vendor/github.com/docker/docker/image/v1/imagev1.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
"github.com/docker/docker/image"
|
||||||
|
"github.com/docker/docker/layer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
||||||
|
|
||||||
|
// noFallbackMinVersion is the minimum version for which v1compatibility
|
||||||
|
// information will not be marshaled through the Image struct to remove
|
||||||
|
// blank fields.
|
||||||
|
var noFallbackMinVersion = "1.8.3"
|
||||||
|
|
||||||
|
// HistoryFromConfig creates a History struct from v1 configuration JSON
|
||||||
|
func HistoryFromConfig(imageJSON []byte, emptyLayer bool) (image.History, error) {
|
||||||
|
h := image.History{}
|
||||||
|
var v1Image image.V1Image
|
||||||
|
if err := json.Unmarshal(imageJSON, &v1Image); err != nil {
|
||||||
|
return h, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return image.History{
|
||||||
|
Author: v1Image.Author,
|
||||||
|
Created: v1Image.Created,
|
||||||
|
CreatedBy: strings.Join(v1Image.ContainerConfig.Cmd, " "),
|
||||||
|
Comment: v1Image.Comment,
|
||||||
|
EmptyLayer: emptyLayer,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateID creates an ID from v1 image, layerID and parent ID.
|
||||||
|
// Used for backwards compatibility with old clients.
|
||||||
|
func CreateID(v1Image image.V1Image, layerID layer.ChainID, parent digest.Digest) (digest.Digest, error) {
|
||||||
|
v1Image.ID = ""
|
||||||
|
v1JSON, err := json.Marshal(v1Image)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var config map[string]*json.RawMessage
|
||||||
|
if err := json.Unmarshal(v1JSON, &config); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: note that this is slightly incompatible with RootFS logic
|
||||||
|
config["layer_id"] = rawJSON(layerID)
|
||||||
|
if parent != "" {
|
||||||
|
config["parent"] = rawJSON(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
configJSON, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
logrus.Debugf("CreateV1ID %s", configJSON)
|
||||||
|
|
||||||
|
return digest.FromBytes(configJSON), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeConfigFromV1Config creates an image config from the legacy V1 config format.
|
||||||
|
func MakeConfigFromV1Config(imageJSON []byte, rootfs *image.RootFS, history []image.History) ([]byte, error) {
|
||||||
|
var dver struct {
|
||||||
|
DockerVersion string `json:"docker_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(imageJSON, &dver); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
useFallback := versions.LessThan(dver.DockerVersion, noFallbackMinVersion)
|
||||||
|
|
||||||
|
if useFallback {
|
||||||
|
var v1Image image.V1Image
|
||||||
|
err := json.Unmarshal(imageJSON, &v1Image)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imageJSON, err = json.Marshal(v1Image)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var c map[string]*json.RawMessage
|
||||||
|
if err := json.Unmarshal(imageJSON, &c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(c, "id")
|
||||||
|
delete(c, "parent")
|
||||||
|
delete(c, "Size") // Size is calculated from data on disk and is inconsistent
|
||||||
|
delete(c, "parent_id")
|
||||||
|
delete(c, "layer_id")
|
||||||
|
delete(c, "throwaway")
|
||||||
|
|
||||||
|
c["rootfs"] = rawJSON(rootfs)
|
||||||
|
c["history"] = rawJSON(history)
|
||||||
|
|
||||||
|
return json.Marshal(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeV1ConfigFromConfig creates an legacy V1 image config from an Image struct
|
||||||
|
func MakeV1ConfigFromConfig(img *image.Image, v1ID, parentV1ID string, throwaway bool) ([]byte, error) {
|
||||||
|
// Top-level v1compatibility string should be a modified version of the
|
||||||
|
// image config.
|
||||||
|
var configAsMap map[string]*json.RawMessage
|
||||||
|
if err := json.Unmarshal(img.RawJSON(), &configAsMap); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete fields that didn't exist in old manifest
|
||||||
|
imageType := reflect.TypeOf(img).Elem()
|
||||||
|
for i := 0; i < imageType.NumField(); i++ {
|
||||||
|
f := imageType.Field(i)
|
||||||
|
jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
|
||||||
|
// Parent is handled specially below.
|
||||||
|
if jsonName != "" && jsonName != "parent" {
|
||||||
|
delete(configAsMap, jsonName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configAsMap["id"] = rawJSON(v1ID)
|
||||||
|
if parentV1ID != "" {
|
||||||
|
configAsMap["parent"] = rawJSON(parentV1ID)
|
||||||
|
}
|
||||||
|
if throwaway {
|
||||||
|
configAsMap["throwaway"] = rawJSON(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(configAsMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rawJSON(value interface{}) *json.RawMessage {
|
||||||
|
jsonval, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (*json.RawMessage)(&jsonval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateID checks whether an ID string is a valid image ID.
|
||||||
|
func ValidateID(id string) error {
|
||||||
|
if ok := validHex.MatchString(id); !ok {
|
||||||
|
return fmt.Errorf("image ID %q is invalid", id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
48
vendor/github.com/docker/docker/layer/empty.go
generated
vendored
Normal file
48
vendor/github.com/docker/docker/layer/empty.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DigestSHA256EmptyTar is the canonical sha256 digest of empty tar file -
|
||||||
|
// (1024 NULL bytes)
|
||||||
|
const DigestSHA256EmptyTar = DiffID("sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef")
|
||||||
|
|
||||||
|
type emptyLayer struct{}
|
||||||
|
|
||||||
|
// EmptyLayer is a layer that corresponds to empty tar.
|
||||||
|
var EmptyLayer = &emptyLayer{}
|
||||||
|
|
||||||
|
func (el *emptyLayer) TarStream() (io.ReadCloser, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
tarWriter := tar.NewWriter(buf)
|
||||||
|
tarWriter.Close()
|
||||||
|
return ioutil.NopCloser(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *emptyLayer) ChainID() ChainID {
|
||||||
|
return ChainID(DigestSHA256EmptyTar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *emptyLayer) DiffID() DiffID {
|
||||||
|
return DigestSHA256EmptyTar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *emptyLayer) Parent() Layer {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *emptyLayer) Size() (size int64, err error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *emptyLayer) DiffSize() (size int64, err error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *emptyLayer) Metadata() (map[string]string, error) {
|
||||||
|
return make(map[string]string), nil
|
||||||
|
}
|
354
vendor/github.com/docker/docker/layer/filestore.go
generated
vendored
Normal file
354
vendor/github.com/docker/docker/layer/filestore.go
generated
vendored
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
stringIDRegexp = regexp.MustCompile(`^[a-f0-9]{64}(-init)?$`)
|
||||||
|
supportedAlgorithms = []digest.Algorithm{
|
||||||
|
digest.SHA256,
|
||||||
|
// digest.SHA384, // Currently not used
|
||||||
|
// digest.SHA512, // Currently not used
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileMetadataStore struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileMetadataTransaction struct {
|
||||||
|
store *fileMetadataStore
|
||||||
|
ws *ioutils.AtomicWriteSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFSMetadataStore returns an instance of a metadata store
|
||||||
|
// which is backed by files on disk using the provided root
|
||||||
|
// as the root of metadata files.
|
||||||
|
func NewFSMetadataStore(root string) (MetadataStore, error) {
|
||||||
|
if err := os.MkdirAll(root, 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &fileMetadataStore{
|
||||||
|
root: root,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) getLayerDirectory(layer ChainID) string {
|
||||||
|
dgst := digest.Digest(layer)
|
||||||
|
return filepath.Join(fms.root, string(dgst.Algorithm()), dgst.Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) getLayerFilename(layer ChainID, filename string) string {
|
||||||
|
return filepath.Join(fms.getLayerDirectory(layer), filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) getMountDirectory(mount string) string {
|
||||||
|
return filepath.Join(fms.root, "mounts", mount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) getMountFilename(mount, filename string) string {
|
||||||
|
return filepath.Join(fms.getMountDirectory(mount), filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) StartTransaction() (MetadataTransaction, error) {
|
||||||
|
tmpDir := filepath.Join(fms.root, "tmp")
|
||||||
|
if err := os.MkdirAll(tmpDir, 0755); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ws, err := ioutils.NewAtomicWriteSet(tmpDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &fileMetadataTransaction{
|
||||||
|
store: fms,
|
||||||
|
ws: ws,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) SetSize(size int64) error {
|
||||||
|
content := fmt.Sprintf("%d", size)
|
||||||
|
return fm.ws.WriteFile("size", []byte(content), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) SetParent(parent ChainID) error {
|
||||||
|
return fm.ws.WriteFile("parent", []byte(digest.Digest(parent).String()), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) SetDiffID(diff DiffID) error {
|
||||||
|
return fm.ws.WriteFile("diff", []byte(digest.Digest(diff).String()), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error {
|
||||||
|
return fm.ws.WriteFile("cache-id", []byte(cacheID), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) error {
|
||||||
|
jsonRef, err := json.Marshal(ref)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fm.ws.WriteFile("descriptor.json", jsonRef, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) {
|
||||||
|
f, err := fm.ws.FileWriter("tar-split.json.gz", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var wc io.WriteCloser
|
||||||
|
if compressInput {
|
||||||
|
wc = gzip.NewWriter(f)
|
||||||
|
} else {
|
||||||
|
wc = f
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutils.NewWriteCloserWrapper(wc, func() error {
|
||||||
|
wc.Close()
|
||||||
|
return f.Close()
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) Commit(layer ChainID) error {
|
||||||
|
finalDir := fm.store.getLayerDirectory(layer)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(finalDir), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fm.ws.Commit(finalDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) Cancel() error {
|
||||||
|
return fm.ws.Cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *fileMetadataTransaction) String() string {
|
||||||
|
return fm.ws.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) {
|
||||||
|
content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "size"))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := strconv.ParseInt(string(content), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
|
||||||
|
content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "parent"))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChainID(dgst), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
|
||||||
|
content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "diff"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return DiffID(dgst), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
|
||||||
|
contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "cache-id"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
content := strings.TrimSpace(string(contentBytes))
|
||||||
|
|
||||||
|
if !stringIDRegexp.MatchString(content) {
|
||||||
|
return "", errors.New("invalid cache id value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) GetDescriptor(layer ChainID) (distribution.Descriptor, error) {
|
||||||
|
content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// only return empty descriptor to represent what is stored
|
||||||
|
return distribution.Descriptor{}, nil
|
||||||
|
}
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ref distribution.Descriptor
|
||||||
|
err = json.Unmarshal(content, &ref)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
return ref, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, error) {
|
||||||
|
fz, err := os.Open(fms.getLayerFilename(layer, "tar-split.json.gz"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f, err := gzip.NewReader(fz)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutils.NewReadCloserWrapper(f, func() error {
|
||||||
|
f.Close()
|
||||||
|
return fz.Close()
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error {
|
||||||
|
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) SetInitID(mount string, init string) error {
|
||||||
|
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error {
|
||||||
|
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
|
||||||
|
contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "mount-id"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
content := strings.TrimSpace(string(contentBytes))
|
||||||
|
|
||||||
|
if !stringIDRegexp.MatchString(content) {
|
||||||
|
return "", errors.New("invalid mount id value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) GetInitID(mount string) (string, error) {
|
||||||
|
contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "init-id"))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
content := strings.TrimSpace(string(contentBytes))
|
||||||
|
|
||||||
|
if !stringIDRegexp.MatchString(content) {
|
||||||
|
return "", errors.New("invalid init id value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
|
||||||
|
content, err := ioutil.ReadFile(fms.getMountFilename(mount, "parent"))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChainID(dgst), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
|
||||||
|
var ids []ChainID
|
||||||
|
for _, algorithm := range supportedAlgorithms {
|
||||||
|
fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, string(algorithm)))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fi := range fileInfos {
|
||||||
|
if fi.IsDir() && fi.Name() != "mounts" {
|
||||||
|
dgst := digest.NewDigestFromHex(string(algorithm), fi.Name())
|
||||||
|
if err := dgst.Validate(); err != nil {
|
||||||
|
logrus.Debugf("Ignoring invalid digest %s:%s", algorithm, fi.Name())
|
||||||
|
} else {
|
||||||
|
ids = append(ids, ChainID(dgst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, "mounts"))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return ids, []string{}, nil
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var mounts []string
|
||||||
|
for _, fi := range fileInfos {
|
||||||
|
if fi.IsDir() {
|
||||||
|
mounts = append(mounts, fi.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, mounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) Remove(layer ChainID) error {
|
||||||
|
return os.RemoveAll(fms.getLayerDirectory(layer))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fms *fileMetadataStore) RemoveMount(mount string) error {
|
||||||
|
return os.RemoveAll(fms.getMountDirectory(mount))
|
||||||
|
}
|
270
vendor/github.com/docker/docker/layer/layer.go
generated
vendored
Normal file
270
vendor/github.com/docker/docker/layer/layer.go
generated
vendored
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
// Package layer is package for managing read-only
|
||||||
|
// and read-write mounts on the union file system
|
||||||
|
// driver. Read-only mounts are referenced using a
|
||||||
|
// content hash and are protected from mutation in
|
||||||
|
// the exposed interface. The tar format is used
|
||||||
|
// to create read-only layers and export both
|
||||||
|
// read-only and writable layers. The exported
|
||||||
|
// tar data for a read-only layer should match
|
||||||
|
// the tar used to create the layer.
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrLayerDoesNotExist is used when an operation is
|
||||||
|
// attempted on a layer which does not exist.
|
||||||
|
ErrLayerDoesNotExist = errors.New("layer does not exist")
|
||||||
|
|
||||||
|
// ErrLayerNotRetained is used when a release is
|
||||||
|
// attempted on a layer which is not retained.
|
||||||
|
ErrLayerNotRetained = errors.New("layer not retained")
|
||||||
|
|
||||||
|
// ErrMountDoesNotExist is used when an operation is
|
||||||
|
// attempted on a mount layer which does not exist.
|
||||||
|
ErrMountDoesNotExist = errors.New("mount does not exist")
|
||||||
|
|
||||||
|
// ErrMountNameConflict is used when a mount is attempted
|
||||||
|
// to be created but there is already a mount with the name
|
||||||
|
// used for creation.
|
||||||
|
ErrMountNameConflict = errors.New("mount already exists with name")
|
||||||
|
|
||||||
|
// ErrActiveMount is used when an operation on a
|
||||||
|
// mount is attempted but the layer is still
|
||||||
|
// mounted and the operation cannot be performed.
|
||||||
|
ErrActiveMount = errors.New("mount still active")
|
||||||
|
|
||||||
|
// ErrNotMounted is used when requesting an active
|
||||||
|
// mount but the layer is not mounted.
|
||||||
|
ErrNotMounted = errors.New("not mounted")
|
||||||
|
|
||||||
|
// ErrMaxDepthExceeded is used when a layer is attempted
|
||||||
|
// to be created which would result in a layer depth
|
||||||
|
// greater than the 125 max.
|
||||||
|
ErrMaxDepthExceeded = errors.New("max depth exceeded")
|
||||||
|
|
||||||
|
// ErrNotSupported is used when the action is not supported
|
||||||
|
// on the current platform
|
||||||
|
ErrNotSupported = errors.New("not support on this platform")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChainID is the content-addressable ID of a layer.
|
||||||
|
type ChainID digest.Digest
|
||||||
|
|
||||||
|
// String returns a string rendition of a layer ID
|
||||||
|
func (id ChainID) String() string {
|
||||||
|
return string(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffID is the hash of an individual layer tar.
|
||||||
|
type DiffID digest.Digest
|
||||||
|
|
||||||
|
// String returns a string rendition of a layer DiffID
|
||||||
|
func (diffID DiffID) String() string {
|
||||||
|
return string(diffID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TarStreamer represents an object which may
|
||||||
|
// have its contents exported as a tar stream.
|
||||||
|
type TarStreamer interface {
|
||||||
|
// TarStream returns a tar archive stream
|
||||||
|
// for the contents of a layer.
|
||||||
|
TarStream() (io.ReadCloser, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer represents a read-only layer
|
||||||
|
type Layer interface {
|
||||||
|
TarStreamer
|
||||||
|
|
||||||
|
// ChainID returns the content hash of the entire layer chain. The hash
|
||||||
|
// chain is made up of DiffID of top layer and all of its parents.
|
||||||
|
ChainID() ChainID
|
||||||
|
|
||||||
|
// DiffID returns the content hash of the layer
|
||||||
|
// tar stream used to create this layer.
|
||||||
|
DiffID() DiffID
|
||||||
|
|
||||||
|
// Parent returns the next layer in the layer chain.
|
||||||
|
Parent() Layer
|
||||||
|
|
||||||
|
// Size returns the size of the entire layer chain. The size
|
||||||
|
// is calculated from the total size of all files in the layers.
|
||||||
|
Size() (int64, error)
|
||||||
|
|
||||||
|
// DiffSize returns the size difference of the top layer
|
||||||
|
// from parent layer.
|
||||||
|
DiffSize() (int64, error)
|
||||||
|
|
||||||
|
// Metadata returns the low level storage metadata associated
|
||||||
|
// with layer.
|
||||||
|
Metadata() (map[string]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RWLayer represents a layer which is
|
||||||
|
// read and writable
|
||||||
|
type RWLayer interface {
|
||||||
|
TarStreamer
|
||||||
|
|
||||||
|
// Name of mounted layer
|
||||||
|
Name() string
|
||||||
|
|
||||||
|
// Parent returns the layer which the writable
|
||||||
|
// layer was created from.
|
||||||
|
Parent() Layer
|
||||||
|
|
||||||
|
// Mount mounts the RWLayer and returns the filesystem path
|
||||||
|
// the to the writable layer.
|
||||||
|
Mount(mountLabel string) (string, error)
|
||||||
|
|
||||||
|
// Unmount unmounts the RWLayer. This should be called
|
||||||
|
// for every mount. If there are multiple mount calls
|
||||||
|
// this operation will only decrement the internal mount counter.
|
||||||
|
Unmount() error
|
||||||
|
|
||||||
|
// Size represents the size of the writable layer
|
||||||
|
// as calculated by the total size of the files
|
||||||
|
// changed in the mutable layer.
|
||||||
|
Size() (int64, error)
|
||||||
|
|
||||||
|
// Changes returns the set of changes for the mutable layer
|
||||||
|
// from the base layer.
|
||||||
|
Changes() ([]archive.Change, error)
|
||||||
|
|
||||||
|
// Metadata returns the low level metadata for the mutable layer
|
||||||
|
Metadata() (map[string]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata holds information about a
|
||||||
|
// read-only layer
|
||||||
|
type Metadata struct {
|
||||||
|
// ChainID is the content hash of the layer
|
||||||
|
ChainID ChainID
|
||||||
|
|
||||||
|
// DiffID is the hash of the tar data used to
|
||||||
|
// create the layer
|
||||||
|
DiffID DiffID
|
||||||
|
|
||||||
|
// Size is the size of the layer and all parents
|
||||||
|
Size int64
|
||||||
|
|
||||||
|
// DiffSize is the size of the top layer
|
||||||
|
DiffSize int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountInit is a function to initialize a
|
||||||
|
// writable mount. Changes made here will
|
||||||
|
// not be included in the Tar stream of the
|
||||||
|
// RWLayer.
|
||||||
|
type MountInit func(root string) error
|
||||||
|
|
||||||
|
// Store represents a backend for managing both
|
||||||
|
// read-only and read-write layers.
|
||||||
|
type Store interface {
|
||||||
|
Register(io.Reader, ChainID) (Layer, error)
|
||||||
|
Get(ChainID) (Layer, error)
|
||||||
|
Release(Layer) ([]Metadata, error)
|
||||||
|
|
||||||
|
CreateRWLayer(id string, parent ChainID, mountLabel string, initFunc MountInit, storageOpt map[string]string) (RWLayer, error)
|
||||||
|
GetRWLayer(id string) (RWLayer, error)
|
||||||
|
GetMountID(id string) (string, error)
|
||||||
|
ReleaseRWLayer(RWLayer) ([]Metadata, error)
|
||||||
|
|
||||||
|
Cleanup() error
|
||||||
|
DriverStatus() [][2]string
|
||||||
|
DriverName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescribableStore represents a layer store capable of storing
|
||||||
|
// descriptors for layers.
|
||||||
|
type DescribableStore interface {
|
||||||
|
RegisterWithDescriptor(io.Reader, ChainID, distribution.Descriptor) (Layer, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataTransaction represents functions for setting layer metadata
|
||||||
|
// with a single transaction.
|
||||||
|
type MetadataTransaction interface {
|
||||||
|
SetSize(int64) error
|
||||||
|
SetParent(parent ChainID) error
|
||||||
|
SetDiffID(DiffID) error
|
||||||
|
SetCacheID(string) error
|
||||||
|
SetDescriptor(distribution.Descriptor) error
|
||||||
|
TarSplitWriter(compressInput bool) (io.WriteCloser, error)
|
||||||
|
|
||||||
|
Commit(ChainID) error
|
||||||
|
Cancel() error
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataStore represents a backend for persisting
|
||||||
|
// metadata about layers and providing the metadata
|
||||||
|
// for restoring a Store.
|
||||||
|
type MetadataStore interface {
|
||||||
|
// StartTransaction starts an update for new metadata
|
||||||
|
// which will be used to represent an ID on commit.
|
||||||
|
StartTransaction() (MetadataTransaction, error)
|
||||||
|
|
||||||
|
GetSize(ChainID) (int64, error)
|
||||||
|
GetParent(ChainID) (ChainID, error)
|
||||||
|
GetDiffID(ChainID) (DiffID, error)
|
||||||
|
GetCacheID(ChainID) (string, error)
|
||||||
|
GetDescriptor(ChainID) (distribution.Descriptor, error)
|
||||||
|
TarSplitReader(ChainID) (io.ReadCloser, error)
|
||||||
|
|
||||||
|
SetMountID(string, string) error
|
||||||
|
SetInitID(string, string) error
|
||||||
|
SetMountParent(string, ChainID) error
|
||||||
|
|
||||||
|
GetMountID(string) (string, error)
|
||||||
|
GetInitID(string) (string, error)
|
||||||
|
GetMountParent(string) (ChainID, error)
|
||||||
|
|
||||||
|
// List returns the full list of referenced
|
||||||
|
// read-only and read-write layers
|
||||||
|
List() ([]ChainID, []string, error)
|
||||||
|
|
||||||
|
Remove(ChainID) error
|
||||||
|
RemoveMount(string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateChainID returns ID for a layerDigest slice
|
||||||
|
func CreateChainID(dgsts []DiffID) ChainID {
|
||||||
|
return createChainIDFromParent("", dgsts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createChainIDFromParent(parent ChainID, dgsts ...DiffID) ChainID {
|
||||||
|
if len(dgsts) == 0 {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
if parent == "" {
|
||||||
|
return createChainIDFromParent(ChainID(dgsts[0]), dgsts[1:]...)
|
||||||
|
}
|
||||||
|
// H = "H(n-1) SHA256(n)"
|
||||||
|
dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0])))
|
||||||
|
return createChainIDFromParent(ChainID(dgst), dgsts[1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseAndLog releases the provided layer from the given layer
|
||||||
|
// store, logging any error and release metadata
|
||||||
|
func ReleaseAndLog(ls Store, l Layer) {
|
||||||
|
metadata, err := ls.Release(l)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Error releasing layer %s: %v", l.ChainID(), err)
|
||||||
|
}
|
||||||
|
LogReleaseMetadata(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogReleaseMetadata logs a metadata array, uses this to
|
||||||
|
// ensure consistent logging for release metadata
|
||||||
|
func LogReleaseMetadata(metadatas []Metadata) {
|
||||||
|
for _, metadata := range metadatas {
|
||||||
|
logrus.Infof("Layer %s cleaned up", metadata.ChainID)
|
||||||
|
}
|
||||||
|
}
|
659
vendor/github.com/docker/docker/layer/layer_store.go
generated
vendored
Normal file
659
vendor/github.com/docker/docker/layer/layer_store.go
generated
vendored
Normal file
|
@ -0,0 +1,659 @@
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
"github.com/vbatts/tar-split/tar/asm"
|
||||||
|
"github.com/vbatts/tar-split/tar/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
// maxLayerDepth represents the maximum number of
|
||||||
|
// layers which can be chained together. 125 was
|
||||||
|
// chosen to account for the 127 max in some
|
||||||
|
// graphdrivers plus the 2 additional layers
|
||||||
|
// used to create a rwlayer.
|
||||||
|
const maxLayerDepth = 125
|
||||||
|
|
||||||
|
type layerStore struct {
|
||||||
|
store MetadataStore
|
||||||
|
driver graphdriver.Driver
|
||||||
|
|
||||||
|
layerMap map[ChainID]*roLayer
|
||||||
|
layerL sync.Mutex
|
||||||
|
|
||||||
|
mounts map[string]*mountedLayer
|
||||||
|
mountL sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreOptions are the options used to create a new Store instance
|
||||||
|
type StoreOptions struct {
|
||||||
|
StorePath string
|
||||||
|
MetadataStorePathTemplate string
|
||||||
|
GraphDriver string
|
||||||
|
GraphDriverOptions []string
|
||||||
|
UIDMaps []idtools.IDMap
|
||||||
|
GIDMaps []idtools.IDMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStoreFromOptions creates a new Store instance
|
||||||
|
func NewStoreFromOptions(options StoreOptions) (Store, error) {
|
||||||
|
driver, err := graphdriver.New(
|
||||||
|
options.StorePath,
|
||||||
|
options.GraphDriver,
|
||||||
|
options.GraphDriverOptions,
|
||||||
|
options.UIDMaps,
|
||||||
|
options.GIDMaps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error initializing graphdriver: %v", err)
|
||||||
|
}
|
||||||
|
logrus.Debugf("Using graph driver %s", driver)
|
||||||
|
|
||||||
|
fms, err := NewFSMetadataStore(fmt.Sprintf(options.MetadataStorePathTemplate, driver))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewStoreFromGraphDriver(fms, driver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStoreFromGraphDriver creates a new Store instance using the provided
|
||||||
|
// metadata store and graph driver. The metadata store will be used to restore
|
||||||
|
// the Store.
|
||||||
|
func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver) (Store, error) {
|
||||||
|
ls := &layerStore{
|
||||||
|
store: store,
|
||||||
|
driver: driver,
|
||||||
|
layerMap: map[ChainID]*roLayer{},
|
||||||
|
mounts: map[string]*mountedLayer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
ids, mounts, err := store.List()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
l, err := ls.loadLayer(id)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Failed to load layer %s: %s", id, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if l.parent != nil {
|
||||||
|
l.parent.referenceCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mount := range mounts {
|
||||||
|
if err := ls.loadMount(mount); err != nil {
|
||||||
|
logrus.Debugf("Failed to load mount %s: %s", mount, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ls, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
|
||||||
|
cl, ok := ls.layerMap[layer]
|
||||||
|
if ok {
|
||||||
|
return cl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
diff, err := ls.store.GetDiffID(layer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get diff id for %s: %s", layer, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := ls.store.GetSize(layer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get size for %s: %s", layer, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheID, err := ls.store.GetCacheID(layer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get cache id for %s: %s", layer, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parent, err := ls.store.GetParent(layer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get parent for %s: %s", layer, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor, err := ls.store.GetDescriptor(layer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get descriptor for %s: %s", layer, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cl = &roLayer{
|
||||||
|
chainID: layer,
|
||||||
|
diffID: diff,
|
||||||
|
size: size,
|
||||||
|
cacheID: cacheID,
|
||||||
|
layerStore: ls,
|
||||||
|
references: map[Layer]struct{}{},
|
||||||
|
descriptor: descriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent != "" {
|
||||||
|
p, err := ls.loadLayer(parent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cl.parent = p
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.layerMap[cl.chainID] = cl
|
||||||
|
|
||||||
|
return cl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) loadMount(mount string) error {
|
||||||
|
if _, ok := ls.mounts[mount]; ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mountID, err := ls.store.GetMountID(mount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
initID, err := ls.store.GetInitID(mount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
parent, err := ls.store.GetMountParent(mount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ml := &mountedLayer{
|
||||||
|
name: mount,
|
||||||
|
mountID: mountID,
|
||||||
|
initID: initID,
|
||||||
|
layerStore: ls,
|
||||||
|
references: map[RWLayer]*referencedRWLayer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent != "" {
|
||||||
|
p, err := ls.loadLayer(parent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ml.parent = p
|
||||||
|
|
||||||
|
p.referenceCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.mounts[ml.name] = ml
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent string, layer *roLayer) error {
|
||||||
|
digester := digest.Canonical.New()
|
||||||
|
tr := io.TeeReader(ts, digester.Hash())
|
||||||
|
|
||||||
|
tsw, err := tx.TarSplitWriter(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metaPacker := storage.NewJSONPacker(tsw)
|
||||||
|
defer tsw.Close()
|
||||||
|
|
||||||
|
// we're passing nil here for the file putter, because the ApplyDiff will
|
||||||
|
// handle the extraction of the archive
|
||||||
|
rdr, err := asm.NewInputTarStream(tr, metaPacker, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, archive.Reader(rdr))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard trailing data but ensure metadata is picked up to reconstruct stream
|
||||||
|
io.Copy(ioutil.Discard, rdr) // ignore error as reader may be closed
|
||||||
|
|
||||||
|
layer.size = applySize
|
||||||
|
layer.diffID = DiffID(digester.Digest())
|
||||||
|
|
||||||
|
logrus.Debugf("Applied tar %s to %s, size: %d", layer.diffID, layer.cacheID, applySize)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) Register(ts io.Reader, parent ChainID) (Layer, error) {
|
||||||
|
return ls.registerWithDescriptor(ts, parent, distribution.Descriptor{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descriptor distribution.Descriptor) (Layer, error) {
|
||||||
|
// err is used to hold the error which will always trigger
|
||||||
|
// cleanup of creates sources but may not be an error returned
|
||||||
|
// to the caller (already exists).
|
||||||
|
var err error
|
||||||
|
var pid string
|
||||||
|
var p *roLayer
|
||||||
|
if string(parent) != "" {
|
||||||
|
p = ls.get(parent)
|
||||||
|
if p == nil {
|
||||||
|
return nil, ErrLayerDoesNotExist
|
||||||
|
}
|
||||||
|
pid = p.cacheID
|
||||||
|
// Release parent chain if error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
ls.layerL.Lock()
|
||||||
|
ls.releaseLayer(p)
|
||||||
|
ls.layerL.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if p.depth() >= maxLayerDepth {
|
||||||
|
err = ErrMaxDepthExceeded
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new roLayer
|
||||||
|
layer := &roLayer{
|
||||||
|
parent: p,
|
||||||
|
cacheID: stringid.GenerateRandomID(),
|
||||||
|
referenceCount: 1,
|
||||||
|
layerStore: ls,
|
||||||
|
references: map[Layer]struct{}{},
|
||||||
|
descriptor: descriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ls.driver.Create(layer.cacheID, pid, "", nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := ls.store.StartTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Cleaning up layer %s: %v", layer.cacheID, err)
|
||||||
|
if err := ls.driver.Remove(layer.cacheID); err != nil {
|
||||||
|
logrus.Errorf("Error cleaning up cache layer %s: %v", layer.cacheID, err)
|
||||||
|
}
|
||||||
|
if err := tx.Cancel(); err != nil {
|
||||||
|
logrus.Errorf("Error canceling metadata transaction %q: %s", tx.String(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = ls.applyTar(tx, ts, pid, layer); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if layer.parent == nil {
|
||||||
|
layer.chainID = ChainID(layer.diffID)
|
||||||
|
} else {
|
||||||
|
layer.chainID = createChainIDFromParent(layer.parent.chainID, layer.diffID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = storeLayer(tx, layer); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.layerL.Lock()
|
||||||
|
defer ls.layerL.Unlock()
|
||||||
|
|
||||||
|
if existingLayer := ls.getWithoutLock(layer.chainID); existingLayer != nil {
|
||||||
|
// Set error for cleanup, but do not return the error
|
||||||
|
err = errors.New("layer already exists")
|
||||||
|
return existingLayer.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(layer.chainID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.layerMap[layer.chainID] = layer
|
||||||
|
|
||||||
|
return layer.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) getWithoutLock(layer ChainID) *roLayer {
|
||||||
|
l, ok := ls.layerMap[layer]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l.referenceCount++
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) get(l ChainID) *roLayer {
|
||||||
|
ls.layerL.Lock()
|
||||||
|
defer ls.layerL.Unlock()
|
||||||
|
return ls.getWithoutLock(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) Get(l ChainID) (Layer, error) {
|
||||||
|
ls.layerL.Lock()
|
||||||
|
defer ls.layerL.Unlock()
|
||||||
|
|
||||||
|
layer := ls.getWithoutLock(l)
|
||||||
|
if layer == nil {
|
||||||
|
return nil, ErrLayerDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return layer.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) deleteLayer(layer *roLayer, metadata *Metadata) error {
|
||||||
|
err := ls.driver.Remove(layer.cacheID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ls.store.Remove(layer.chainID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metadata.DiffID = layer.diffID
|
||||||
|
metadata.ChainID = layer.chainID
|
||||||
|
metadata.Size, err = layer.Size()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metadata.DiffSize = layer.size
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) releaseLayer(l *roLayer) ([]Metadata, error) {
|
||||||
|
depth := 0
|
||||||
|
removed := []Metadata{}
|
||||||
|
for {
|
||||||
|
if l.referenceCount == 0 {
|
||||||
|
panic("layer not retained")
|
||||||
|
}
|
||||||
|
l.referenceCount--
|
||||||
|
if l.referenceCount != 0 {
|
||||||
|
return removed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(removed) == 0 && depth > 0 {
|
||||||
|
panic("cannot remove layer with child")
|
||||||
|
}
|
||||||
|
if l.hasReferences() {
|
||||||
|
panic("cannot delete referenced layer")
|
||||||
|
}
|
||||||
|
var metadata Metadata
|
||||||
|
if err := ls.deleteLayer(l, &metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(ls.layerMap, l.chainID)
|
||||||
|
removed = append(removed, metadata)
|
||||||
|
|
||||||
|
if l.parent == nil {
|
||||||
|
return removed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
depth++
|
||||||
|
l = l.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) Release(l Layer) ([]Metadata, error) {
|
||||||
|
ls.layerL.Lock()
|
||||||
|
defer ls.layerL.Unlock()
|
||||||
|
layer, ok := ls.layerMap[l.ChainID()]
|
||||||
|
if !ok {
|
||||||
|
return []Metadata{}, nil
|
||||||
|
}
|
||||||
|
if !layer.hasReference(l) {
|
||||||
|
return nil, ErrLayerNotRetained
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.deleteReference(l)
|
||||||
|
|
||||||
|
return ls.releaseLayer(layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) CreateRWLayer(name string, parent ChainID, mountLabel string, initFunc MountInit, storageOpt map[string]string) (RWLayer, error) {
|
||||||
|
ls.mountL.Lock()
|
||||||
|
defer ls.mountL.Unlock()
|
||||||
|
m, ok := ls.mounts[name]
|
||||||
|
if ok {
|
||||||
|
return nil, ErrMountNameConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var pid string
|
||||||
|
var p *roLayer
|
||||||
|
if string(parent) != "" {
|
||||||
|
p = ls.get(parent)
|
||||||
|
if p == nil {
|
||||||
|
return nil, ErrLayerDoesNotExist
|
||||||
|
}
|
||||||
|
pid = p.cacheID
|
||||||
|
|
||||||
|
// Release parent chain if error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
ls.layerL.Lock()
|
||||||
|
ls.releaseLayer(p)
|
||||||
|
ls.layerL.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
m = &mountedLayer{
|
||||||
|
name: name,
|
||||||
|
parent: p,
|
||||||
|
mountID: ls.mountID(name),
|
||||||
|
layerStore: ls,
|
||||||
|
references: map[RWLayer]*referencedRWLayer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if initFunc != nil {
|
||||||
|
pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc, storageOpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m.initID = pid
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ls.driver.CreateReadWrite(m.mountID, pid, "", storageOpt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ls.saveMount(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) GetRWLayer(id string) (RWLayer, error) {
|
||||||
|
ls.mountL.Lock()
|
||||||
|
defer ls.mountL.Unlock()
|
||||||
|
mount, ok := ls.mounts[id]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrMountDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return mount.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) GetMountID(id string) (string, error) {
|
||||||
|
ls.mountL.Lock()
|
||||||
|
defer ls.mountL.Unlock()
|
||||||
|
mount, ok := ls.mounts[id]
|
||||||
|
if !ok {
|
||||||
|
return "", ErrMountDoesNotExist
|
||||||
|
}
|
||||||
|
logrus.Debugf("GetMountID id: %s -> mountID: %s", id, mount.mountID)
|
||||||
|
|
||||||
|
return mount.mountID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) ReleaseRWLayer(l RWLayer) ([]Metadata, error) {
|
||||||
|
ls.mountL.Lock()
|
||||||
|
defer ls.mountL.Unlock()
|
||||||
|
m, ok := ls.mounts[l.Name()]
|
||||||
|
if !ok {
|
||||||
|
return []Metadata{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.deleteReference(l); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.hasReferences() {
|
||||||
|
return []Metadata{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ls.driver.Remove(m.mountID); err != nil {
|
||||||
|
logrus.Errorf("Error removing mounted layer %s: %s", m.name, err)
|
||||||
|
m.retakeReference(l)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.initID != "" {
|
||||||
|
if err := ls.driver.Remove(m.initID); err != nil {
|
||||||
|
logrus.Errorf("Error removing init layer %s: %s", m.name, err)
|
||||||
|
m.retakeReference(l)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ls.store.RemoveMount(m.name); err != nil {
|
||||||
|
logrus.Errorf("Error removing mount metadata: %s: %s", m.name, err)
|
||||||
|
m.retakeReference(l)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(ls.mounts, m.Name())
|
||||||
|
|
||||||
|
ls.layerL.Lock()
|
||||||
|
defer ls.layerL.Unlock()
|
||||||
|
if m.parent != nil {
|
||||||
|
return ls.releaseLayer(m.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Metadata{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) saveMount(mount *mountedLayer) error {
|
||||||
|
if err := ls.store.SetMountID(mount.name, mount.mountID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mount.initID != "" {
|
||||||
|
if err := ls.store.SetInitID(mount.name, mount.initID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mount.parent != nil {
|
||||||
|
if err := ls.store.SetMountParent(mount.name, mount.parent.chainID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.mounts[mount.name] = mount
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (string, error) {
|
||||||
|
// Use "<graph-id>-init" to maintain compatibility with graph drivers
|
||||||
|
// which are expecting this layer with this special name. If all
|
||||||
|
// graph drivers can be updated to not rely on knowing about this layer
|
||||||
|
// then the initID should be randomly generated.
|
||||||
|
initID := fmt.Sprintf("%s-init", graphID)
|
||||||
|
|
||||||
|
if err := ls.driver.Create(initID, parent, mountLabel, storageOpt); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
p, err := ls.driver.Get(initID, "")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := initFunc(p); err != nil {
|
||||||
|
ls.driver.Put(initID)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ls.driver.Put(initID); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return initID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) assembleTarTo(graphID string, metadata io.ReadCloser, size *int64, w io.Writer) error {
|
||||||
|
diffDriver, ok := ls.driver.(graphdriver.DiffGetterDriver)
|
||||||
|
if !ok {
|
||||||
|
diffDriver = &naiveDiffPathDriver{ls.driver}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer metadata.Close()
|
||||||
|
|
||||||
|
// get our relative path to the container
|
||||||
|
fileGetCloser, err := diffDriver.DiffGetter(graphID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fileGetCloser.Close()
|
||||||
|
|
||||||
|
metaUnpacker := storage.NewJSONUnpacker(metadata)
|
||||||
|
upackerCounter := &unpackSizeCounter{metaUnpacker, size}
|
||||||
|
logrus.Debugf("Assembling tar data for %s", graphID)
|
||||||
|
return asm.WriteOutputTarStream(fileGetCloser, upackerCounter, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) Cleanup() error {
|
||||||
|
return ls.driver.Cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) DriverStatus() [][2]string {
|
||||||
|
return ls.driver.Status()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) DriverName() string {
|
||||||
|
return ls.driver.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type naiveDiffPathDriver struct {
|
||||||
|
graphdriver.Driver
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileGetPutter struct {
|
||||||
|
storage.FileGetter
|
||||||
|
driver graphdriver.Driver
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *fileGetPutter) Close() error {
|
||||||
|
return w.driver.Put(w.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *naiveDiffPathDriver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
|
||||||
|
p, err := n.Driver.Get(id, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &fileGetPutter{storage.NewPathFileGetter(p), n.Driver, id}, nil
|
||||||
|
}
|
11
vendor/github.com/docker/docker/layer/layer_store_windows.go
generated
vendored
Normal file
11
vendor/github.com/docker/docker/layer/layer_store_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, descriptor distribution.Descriptor) (Layer, error) {
|
||||||
|
return ls.registerWithDescriptor(ts, parent, descriptor)
|
||||||
|
}
|
9
vendor/github.com/docker/docker/layer/layer_unix.go
generated
vendored
Normal file
9
vendor/github.com/docker/docker/layer/layer_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// +build linux freebsd darwin openbsd solaris
|
||||||
|
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import "github.com/docker/docker/pkg/stringid"
|
||||||
|
|
||||||
|
func (ls *layerStore) mountID(name string) string {
|
||||||
|
return stringid.GenerateRandomID()
|
||||||
|
}
|
98
vendor/github.com/docker/docker/layer/layer_windows.go
generated
vendored
Normal file
98
vendor/github.com/docker/docker/layer/layer_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetLayerPath returns the path to a layer
|
||||||
|
func GetLayerPath(s Store, layer ChainID) (string, error) {
|
||||||
|
ls, ok := s.(*layerStore)
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("unsupported layer store")
|
||||||
|
}
|
||||||
|
ls.layerL.Lock()
|
||||||
|
defer ls.layerL.Unlock()
|
||||||
|
|
||||||
|
rl, ok := ls.layerMap[layer]
|
||||||
|
if !ok {
|
||||||
|
return "", ErrLayerDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := ls.driver.Get(rl.cacheID, "")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ls.driver.Put(rl.cacheID); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) RegisterDiffID(graphID string, size int64) (Layer, error) {
|
||||||
|
var err error // this is used for cleanup in existingLayer case
|
||||||
|
diffID := digest.FromBytes([]byte(graphID))
|
||||||
|
|
||||||
|
// Create new roLayer
|
||||||
|
layer := &roLayer{
|
||||||
|
cacheID: graphID,
|
||||||
|
diffID: DiffID(diffID),
|
||||||
|
referenceCount: 1,
|
||||||
|
layerStore: ls,
|
||||||
|
references: map[Layer]struct{}{},
|
||||||
|
size: size,
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := ls.store.StartTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if err := tx.Cancel(); err != nil {
|
||||||
|
logrus.Errorf("Error canceling metadata transaction %q: %s", tx.String(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
layer.chainID = createChainIDFromParent("", layer.diffID)
|
||||||
|
|
||||||
|
if !ls.driver.Exists(layer.cacheID) {
|
||||||
|
return nil, fmt.Errorf("layer %q is unknown to driver", layer.cacheID)
|
||||||
|
}
|
||||||
|
if err = storeLayer(tx, layer); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.layerL.Lock()
|
||||||
|
defer ls.layerL.Unlock()
|
||||||
|
|
||||||
|
if existingLayer := ls.getWithoutLock(layer.chainID); existingLayer != nil {
|
||||||
|
// Set error for cleanup, but do not return
|
||||||
|
err = errors.New("layer already exists")
|
||||||
|
return existingLayer.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(layer.chainID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.layerMap[layer.chainID] = layer
|
||||||
|
|
||||||
|
return layer.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) mountID(name string) string {
|
||||||
|
// windows has issues if container ID doesn't match mount ID
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) GraphDriver() graphdriver.Driver {
|
||||||
|
return ls.driver
|
||||||
|
}
|
256
vendor/github.com/docker/docker/layer/migration.go
generated
vendored
Normal file
256
vendor/github.com/docker/docker/layer/migration.go
generated
vendored
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/vbatts/tar-split/tar/asm"
|
||||||
|
"github.com/vbatts/tar-split/tar/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateRWLayerByGraphID creates a RWLayer in the layer store using
|
||||||
|
// the provided name with the given graphID. To get the RWLayer
|
||||||
|
// after migration the layer may be retrieved by the given name.
|
||||||
|
func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent ChainID) (err error) {
|
||||||
|
ls.mountL.Lock()
|
||||||
|
defer ls.mountL.Unlock()
|
||||||
|
m, ok := ls.mounts[name]
|
||||||
|
if ok {
|
||||||
|
if m.parent.chainID != parent {
|
||||||
|
return errors.New("name conflict, mismatched parent")
|
||||||
|
}
|
||||||
|
if m.mountID != graphID {
|
||||||
|
return errors.New("mount already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ls.driver.Exists(graphID) {
|
||||||
|
return fmt.Errorf("graph ID does not exist: %q", graphID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var p *roLayer
|
||||||
|
if string(parent) != "" {
|
||||||
|
p = ls.get(parent)
|
||||||
|
if p == nil {
|
||||||
|
return ErrLayerDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release parent chain if error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
ls.layerL.Lock()
|
||||||
|
ls.releaseLayer(p)
|
||||||
|
ls.layerL.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Ensure graphID has correct parent
|
||||||
|
|
||||||
|
m = &mountedLayer{
|
||||||
|
name: name,
|
||||||
|
parent: p,
|
||||||
|
mountID: graphID,
|
||||||
|
layerStore: ls,
|
||||||
|
references: map[RWLayer]*referencedRWLayer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for existing init layer
|
||||||
|
initID := fmt.Sprintf("%s-init", graphID)
|
||||||
|
if ls.driver.Exists(initID) {
|
||||||
|
m.initID = initID
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ls.saveMount(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID DiffID, size int64, err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("could not get checksum for %q with tar-split: %q", id, err)
|
||||||
|
diffID, size, err = ls.checksumForGraphIDNoTarsplit(id, parent, newTarDataPath)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if oldTarDataPath == "" {
|
||||||
|
err = errors.New("no tar-split file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tarDataFile, err := os.Open(oldTarDataPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tarDataFile.Close()
|
||||||
|
uncompressed, err := gzip.NewReader(tarDataFile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst := digest.Canonical.New()
|
||||||
|
err = ls.assembleTarTo(id, uncompressed, &size, dgst.Hash())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diffID = DiffID(dgst.Digest())
|
||||||
|
err = os.RemoveAll(newTarDataPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = os.Link(oldTarDataPath, newTarDataPath)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) checksumForGraphIDNoTarsplit(id, parent, newTarDataPath string) (diffID DiffID, size int64, err error) {
|
||||||
|
rawarchive, err := ls.driver.Diff(id, parent)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rawarchive.Close()
|
||||||
|
|
||||||
|
f, err := os.Create(newTarDataPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
mfz := gzip.NewWriter(f)
|
||||||
|
defer mfz.Close()
|
||||||
|
metaPacker := storage.NewJSONPacker(mfz)
|
||||||
|
|
||||||
|
packerCounter := &packSizeCounter{metaPacker, &size}
|
||||||
|
|
||||||
|
archive, err := asm.NewInputTarStream(rawarchive, packerCounter, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dgst, err := digest.FromReader(archive)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
diffID = DiffID(dgst)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *layerStore) RegisterByGraphID(graphID string, parent ChainID, diffID DiffID, tarDataFile string, size int64) (Layer, error) {
|
||||||
|
// err is used to hold the error which will always trigger
|
||||||
|
// cleanup of creates sources but may not be an error returned
|
||||||
|
// to the caller (already exists).
|
||||||
|
var err error
|
||||||
|
var p *roLayer
|
||||||
|
if string(parent) != "" {
|
||||||
|
p = ls.get(parent)
|
||||||
|
if p == nil {
|
||||||
|
return nil, ErrLayerDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release parent chain if error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
ls.layerL.Lock()
|
||||||
|
ls.releaseLayer(p)
|
||||||
|
ls.layerL.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new roLayer
|
||||||
|
layer := &roLayer{
|
||||||
|
parent: p,
|
||||||
|
cacheID: graphID,
|
||||||
|
referenceCount: 1,
|
||||||
|
layerStore: ls,
|
||||||
|
references: map[Layer]struct{}{},
|
||||||
|
diffID: diffID,
|
||||||
|
size: size,
|
||||||
|
chainID: createChainIDFromParent(parent, diffID),
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.layerL.Lock()
|
||||||
|
defer ls.layerL.Unlock()
|
||||||
|
|
||||||
|
if existingLayer := ls.getWithoutLock(layer.chainID); existingLayer != nil {
|
||||||
|
// Set error for cleanup, but do not return
|
||||||
|
err = errors.New("layer already exists")
|
||||||
|
return existingLayer.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := ls.store.StartTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Cleaning up transaction after failed migration for %s: %v", graphID, err)
|
||||||
|
if err := tx.Cancel(); err != nil {
|
||||||
|
logrus.Errorf("Error canceling metadata transaction %q: %s", tx.String(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
tsw, err := tx.TarSplitWriter(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer tsw.Close()
|
||||||
|
tdf, err := os.Open(tarDataFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer tdf.Close()
|
||||||
|
_, err = io.Copy(tsw, tdf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = storeLayer(tx, layer); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(layer.chainID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ls.layerMap[layer.chainID] = layer
|
||||||
|
|
||||||
|
return layer.getReference(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type unpackSizeCounter struct {
|
||||||
|
unpacker storage.Unpacker
|
||||||
|
size *int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *unpackSizeCounter) Next() (*storage.Entry, error) {
|
||||||
|
e, err := u.unpacker.Next()
|
||||||
|
if err == nil && u.size != nil {
|
||||||
|
*u.size += e.Size
|
||||||
|
}
|
||||||
|
return e, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type packSizeCounter struct {
|
||||||
|
packer storage.Packer
|
||||||
|
size *int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *packSizeCounter) AddEntry(e storage.Entry) (int, error) {
|
||||||
|
n, err := p.packer.AddEntry(e)
|
||||||
|
if err == nil && p.size != nil {
|
||||||
|
*p.size += e.Size
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
103
vendor/github.com/docker/docker/layer/mounted_layer.go
generated
vendored
Normal file
103
vendor/github.com/docker/docker/layer/mounted_layer.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mountedLayer struct {
|
||||||
|
name string
|
||||||
|
mountID string
|
||||||
|
initID string
|
||||||
|
parent *roLayer
|
||||||
|
path string
|
||||||
|
layerStore *layerStore
|
||||||
|
|
||||||
|
references map[RWLayer]*referencedRWLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) cacheParent() string {
|
||||||
|
if ml.initID != "" {
|
||||||
|
return ml.initID
|
||||||
|
}
|
||||||
|
if ml.parent != nil {
|
||||||
|
return ml.parent.cacheID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) TarStream() (io.ReadCloser, error) {
|
||||||
|
archiver, err := ml.layerStore.driver.Diff(ml.mountID, ml.cacheParent())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return archiver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) Name() string {
|
||||||
|
return ml.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) Parent() Layer {
|
||||||
|
if ml.parent != nil {
|
||||||
|
return ml.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a nil interface instead of an interface wrapping a nil
|
||||||
|
// pointer.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) Size() (int64, error) {
|
||||||
|
return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) Changes() ([]archive.Change, error) {
|
||||||
|
return ml.layerStore.driver.Changes(ml.mountID, ml.cacheParent())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) Metadata() (map[string]string, error) {
|
||||||
|
return ml.layerStore.driver.GetMetadata(ml.mountID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) getReference() RWLayer {
|
||||||
|
ref := &referencedRWLayer{
|
||||||
|
mountedLayer: ml,
|
||||||
|
}
|
||||||
|
ml.references[ref] = ref
|
||||||
|
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) hasReferences() bool {
|
||||||
|
return len(ml.references) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) deleteReference(ref RWLayer) error {
|
||||||
|
if _, ok := ml.references[ref]; !ok {
|
||||||
|
return ErrLayerNotRetained
|
||||||
|
}
|
||||||
|
delete(ml.references, ref)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ml *mountedLayer) retakeReference(r RWLayer) {
|
||||||
|
if ref, ok := r.(*referencedRWLayer); ok {
|
||||||
|
ml.references[ref] = ref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type referencedRWLayer struct {
|
||||||
|
*mountedLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rl *referencedRWLayer) Mount(mountLabel string) (string, error) {
|
||||||
|
return rl.layerStore.driver.Get(rl.mountedLayer.mountID, mountLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount decrements the activity count and unmounts the underlying layer
|
||||||
|
// Callers should only call `Unmount` once per call to `Mount`, even on error.
|
||||||
|
func (rl *referencedRWLayer) Unmount() error {
|
||||||
|
return rl.layerStore.driver.Put(rl.mountedLayer.mountID)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue