Merge pull request #2097 from Random-Liu/vendor-cri-plugin
Vendor cri plugin into containerd.
This commit is contained in:
		
							
								
								
									
										13
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -26,6 +26,7 @@ addons: | ||||
|       - libaio-dev | ||||
|       - libprotobuf-c0-dev | ||||
|       - libprotobuf-dev | ||||
|       - socat | ||||
|  | ||||
| env: | ||||
|   - TRAVIS_GOOS=linux TRAVIS_CGO_ENABLED=1 | ||||
| @@ -35,6 +36,8 @@ before_install: | ||||
|   - uname -r | ||||
|   - sudo apt-get -q update | ||||
|   - sudo apt-get install -y libseccomp-dev/trusty-backports | ||||
|   # Use jpetazzo/nsenter to install nsenter on ubuntu trusty. | ||||
|   - docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter | ||||
|  | ||||
| install: | ||||
|   - sudo PATH=$PATH GOPATH=$GOPATH script/setup/install-protobuf | ||||
| @@ -44,6 +47,8 @@ install: | ||||
|   - protoc --version | ||||
|   - go get -u github.com/vbatts/git-validation | ||||
|   - sudo PATH=$PATH GOPATH=$GOPATH script/setup/install-runc | ||||
|   - sudo PATH=$PATH GOPATH=$GOPATH script/setup/install-cni | ||||
|   - sudo PATH=$PATH GOPATH=$GOPATH script/setup/install-critools | ||||
|   - wget https://github.com/xemul/criu/archive/v3.0.tar.gz -O /tmp/criu.tar.gz | ||||
|   - tar -C /tmp/ -zxf /tmp/criu.tar.gz | ||||
|   - cd /tmp/criu-3.0 && sudo make install-criu | ||||
| @@ -66,6 +71,14 @@ script: | ||||
|   - if [ "$GOOS" = "linux" ]; then sudo PATH=$PATH GOPATH=$GOPATH make integration ; fi | ||||
|   # Run the integration suite a second time. See discussion in github.com/containerd/containerd/pull/1759 | ||||
|   - if [ "$GOOS" = "linux" ]; then sudo PATH=$PATH GOPATH=$GOPATH TESTFLAGS_PARALLEL=1 make integration ; fi | ||||
|   - if [ "$GOOS" = "linux" ]; then | ||||
|       sudo PATH=$PATH containerd -log-level debug &> /tmp/containerd-cri.log & | ||||
|       sudo PATH=$PATH GOPATH=$GOPATH critest --runtime-endpoint=/var/run/containerd/containerd.sock validation ; | ||||
|       exit_code=$? ; | ||||
|       test $exit_code -ne 0 && cat /tmp/containerd-cri.log ; | ||||
|       sudo pkill containerd ; | ||||
|       exit $exit_code ; | ||||
|     fi | ||||
|  | ||||
| after_success: | ||||
|   - bash <(curl -s https://codecov.io/bash) -F linux | ||||
|   | ||||
| @@ -69,8 +69,12 @@ compiler to regenerate the API generated code packages with: | ||||
| make generate | ||||
| ``` | ||||
|  | ||||
| > *Note*: A build tag is currently available to disable building the btrfs snapshot driver. | ||||
| > Adding `BUILDTAGS=no_btrfs` to your environment before calling the **binaries** | ||||
| > *Note*: Several build tags are currently available: | ||||
| > * `no_btrfs`: A build tag disables building the btrfs snapshot driver. | ||||
| > * `no_cri`: A build tag disables building Kubernetes [CRI](http://blog.kubernetes.io/2016/12/container-runtime-interface-cri-in-kubernetes.html) support into containerd. | ||||
| > See [here](https://github.com/containerd/cri-containerd#build-tags) for build tags of CRI plugin. | ||||
| > | ||||
| > For example, adding `BUILDTAGS=no_btrfs` to your environment before calling the **binaries** | ||||
| > Makefile target will disable the btrfs driver within the containerd Go build. | ||||
|  | ||||
| Vendoring of external imports uses the [`vndr` tool](https://github.com/LK4D4/vndr) which uses a simple config file, `vendor.conf`, to provide the URL and version or hash details for each vendored import. After modifying `vendor.conf` run the `vndr` tool to update the `vendor/` directory contents. Combining the `vendor.conf` update with the changeset in `vendor/` after running `vndr` should become a single commit for a PR which relies on vendored updates. | ||||
|   | ||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -74,6 +74,8 @@ TEST_REQUIRES_ROOT_PACKAGES=$(filter \ | ||||
| COMMANDS=ctr containerd containerd-stress containerd-release | ||||
| MANPAGES=ctr.1 containerd.1 config.toml.5 containerd-config.1 | ||||
|  | ||||
| # Build tags seccomp and apparmor are needed by CRI plugin. | ||||
| BUILDTAGS ?= seccomp apparmor | ||||
| GO_TAGS=$(if $(BUILDTAGS),-tags "$(BUILDTAGS)",) | ||||
| GO_LDFLAGS=-ldflags '-s -w -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PKG) $(EXTRA_LDFLAGS)' | ||||
| SHIM_GO_LDFLAGS=-ldflags '-s -w -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PKG) -extldflags "-static"' | ||||
|   | ||||
							
								
								
									
										5
									
								
								cmd/containerd/builtins_cri_linux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cmd/containerd/builtins_cri_linux.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| // +build !no_cri | ||||
|  | ||||
| package main | ||||
|  | ||||
| import _ "github.com/containerd/cri-containerd" | ||||
							
								
								
									
										44
									
								
								script/setup/install-cni
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										44
									
								
								script/setup/install-cni
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| #!/usr/bin/env bash | ||||
| # | ||||
| # Builds and installs cni plugins to /opt/cni/bin, | ||||
| # and create basic cni config in /etc/cni/net.d. | ||||
| # The commit defined in vendor.conf | ||||
| # | ||||
| set -eu -o pipefail | ||||
|  | ||||
| CNI_COMMIT=$(grep containernetworking/plugins ${GOPATH}/src/github.com/containerd/containerd/vendor.conf | cut -d " " -f 2) | ||||
| CNI_DIR=/opt/cni | ||||
| CNI_CONFIG_DIR=/etc/cni/net.d | ||||
|  | ||||
| go get -d github.com/containernetworking/plugins/... | ||||
| cd $GOPATH/src/github.com/containernetworking/plugins | ||||
| git checkout $CNI_COMMIT | ||||
| FASTBUILD=true ./build.sh | ||||
| mkdir -p $CNI_DIR | ||||
| cp -r ./bin $CNI_DIR | ||||
| mkdir -p $CNI_CONFIG_DIR | ||||
| bash -c 'cat >'$CNI_CONFIG_DIR'/10-containerd-net.conflist <<EOF | ||||
| { | ||||
|   "cniVersion": "0.3.1", | ||||
|   "name": "containerd-net", | ||||
|   "plugins": [ | ||||
|     { | ||||
|       "type": "bridge", | ||||
|       "bridge": "cni0", | ||||
|       "isGateway": true, | ||||
|       "ipMasq": true, | ||||
|       "ipam": { | ||||
|         "type": "host-local", | ||||
|         "subnet": "10.88.0.0/16", | ||||
|         "routes": [ | ||||
|           { "dst": "0.0.0.0/0" } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "type": "portmap", | ||||
|       "capabilities": {"portMappings": true} | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| EOF' | ||||
							
								
								
									
										13
									
								
								script/setup/install-critools
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										13
									
								
								script/setup/install-critools
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| #!/usr/bin/env bash | ||||
| # | ||||
| # Builds and installs critools including critest and crictl | ||||
| # to /usr/local/bin. | ||||
| # | ||||
| set -eu -o pipefail | ||||
|  | ||||
| CRITEST_COMMIT=240a840375cdabb5860c75c99e8b0d0a776006b4 | ||||
| go get -d github.com/kubernetes-incubator/cri-tools/... | ||||
| cd $GOPATH/src/github.com/kubernetes-incubator/cri-tools | ||||
| git checkout $CRITEST_COMMIT | ||||
| make | ||||
| make install | ||||
| @@ -23,7 +23,7 @@ set -eu -o pipefail | ||||
|  | ||||
| RUNC_COMMIT=$(grep opencontainers/runc ${GOPATH}/src/github.com/containerd/containerd/vendor.conf | cut -d " " -f 2) | ||||
|  | ||||
| go get -u github.com/opencontainers/runc | ||||
| go get -d github.com/opencontainers/runc | ||||
| cd $GOPATH/src/github.com/opencontainers/runc | ||||
| git checkout $RUNC_COMMIT | ||||
| make BUILDTAGS="apparmor seccomp" runc install | ||||
|   | ||||
							
								
								
									
										32
									
								
								vendor.conf
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								vendor.conf
									
									
									
									
									
								
							| @@ -42,3 +42,35 @@ github.com/stevvooe/ttrpc d4528379866b0ce7e9d71f3eb96f0582fc374577 | ||||
| github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 | ||||
| github.com/gotestyourself/gotestyourself 44dbf532bbf5767611f6f2a61bded572e337010a | ||||
| github.com/google/go-cmp v0.1.0 | ||||
| # cri dependencies | ||||
| github.com/containerd/cri-containerd 83573155645882403ef2da86df8ea95e2d031f28 | ||||
| github.com/blang/semver v3.1.0 | ||||
| github.com/containernetworking/cni v0.6.0 | ||||
| github.com/containernetworking/plugins v0.6.0 | ||||
| github.com/cri-o/ocicni 9b451e26eb7c694d564991fbf44f77d0afb9b03c | ||||
| github.com/davecgh/go-spew v1.1.0 | ||||
| github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 | ||||
| github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 | ||||
| github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 | ||||
| github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46 | ||||
| github.com/fsnotify/fsnotify 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1 | ||||
| github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee | ||||
| github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed | ||||
| github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c | ||||
| github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 | ||||
| github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f | ||||
| github.com/json-iterator/go 1.0.4 | ||||
| github.com/opencontainers/runtime-tools 6073aff4ac61897f75895123f7e24135204a404d | ||||
| github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206 | ||||
| github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 | ||||
| github.com/spf13/pflag v1.0.0 | ||||
| github.com/tchap/go-patricia 5ad6cdb7538b0097d5598c7e57f0a24072adf7dc | ||||
| golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 | ||||
| gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 | ||||
| gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 | ||||
| k8s.io/api a1d6dce6736a6c75929bb75111e89077e35a5856 | ||||
| k8s.io/apimachinery 8259d997cf059cd83dc47e5f8074b7a7d7967c09 | ||||
| k8s.io/apiserver 8e45eac9dff86447a5c2effe6a3d2cba70121ebf | ||||
| k8s.io/client-go 33bd23f75b6de861994706a322b0afab824b2171 | ||||
| k8s.io/kubernetes 05944b1d2ca7f60b09762a330425108f48f6b603 | ||||
| k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e | ||||
|   | ||||
							
								
								
									
										22
									
								
								vendor/github.com/blang/semver/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/blang/semver/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| The MIT License | ||||
|  | ||||
| Copyright (c) 2014 Benedikt Lang <github at benediktlang.de> | ||||
|  | ||||
| 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. | ||||
|  | ||||
							
								
								
									
										191
									
								
								vendor/github.com/blang/semver/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								vendor/github.com/blang/semver/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| semver for golang [](https://drone.io/github.com/blang/semver/latest) [](https://godoc.org/github.com/blang/semver) [](https://coveralls.io/r/blang/semver?branch=master) | ||||
| ====== | ||||
|  | ||||
| semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`. | ||||
|  | ||||
| Usage | ||||
| ----- | ||||
| ```bash | ||||
| $ go get github.com/blang/semver | ||||
| ``` | ||||
| Note: Always vendor your dependencies or fix on a specific version tag. | ||||
|  | ||||
| ```go | ||||
| import github.com/blang/semver | ||||
| v1, err := semver.Make("1.0.0-beta") | ||||
| v2, err := semver.Make("2.0.0-beta") | ||||
| v1.Compare(v2) | ||||
| ``` | ||||
|  | ||||
| Also check the [GoDocs](http://godoc.org/github.com/blang/semver). | ||||
|  | ||||
| Why should I use this lib? | ||||
| ----- | ||||
|  | ||||
| - Fully spec compatible | ||||
| - No reflection | ||||
| - No regex | ||||
| - Fully tested (Coverage >99%) | ||||
| - Readable parsing/validation errors | ||||
| - Fast (See [Benchmarks](#benchmarks)) | ||||
| - Only Stdlib | ||||
| - Uses values instead of pointers | ||||
| - Many features, see below | ||||
|  | ||||
|  | ||||
| Features | ||||
| ----- | ||||
|  | ||||
| - Parsing and validation at all levels | ||||
| - Comparator-like comparisons | ||||
| - Compare Helper Methods | ||||
| - InPlace manipulation | ||||
| - Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1` | ||||
| - Sortable (implements sort.Interface) | ||||
| - database/sql compatible (sql.Scanner/Valuer) | ||||
| - encoding/json compatible (json.Marshaler/Unmarshaler) | ||||
|  | ||||
| Ranges | ||||
| ------ | ||||
|  | ||||
| A `Range` is a set of conditions which specify which versions satisfy the range. | ||||
|  | ||||
| A condition is composed of an operator and a version. The supported operators are: | ||||
|  | ||||
| - `<1.0.0` Less than `1.0.0` | ||||
| - `<=1.0.0` Less than or equal to `1.0.0` | ||||
| - `>1.0.0` Greater than `1.0.0` | ||||
| - `>=1.0.0` Greater than or equal to `1.0.0` | ||||
| - `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0` | ||||
| - `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`. | ||||
|  | ||||
| A `Range` can link multiple `Ranges` separated by space: | ||||
|  | ||||
| Ranges can be linked by logical AND: | ||||
|  | ||||
|   - `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0` | ||||
|   - `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2` | ||||
|  | ||||
| Ranges can also be linked by logical OR: | ||||
|  | ||||
|   - `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x` | ||||
|  | ||||
| AND has a higher precedence than OR. It's not possible to use brackets. | ||||
|  | ||||
| Ranges can be combined by both AND and OR | ||||
|  | ||||
|   - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` | ||||
|  | ||||
| Range usage: | ||||
|  | ||||
| ``` | ||||
| v, err := semver.Parse("1.2.3") | ||||
| range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0") | ||||
| if range(v) { | ||||
|     //valid | ||||
| } | ||||
|  | ||||
| ``` | ||||
|  | ||||
| Example | ||||
| ----- | ||||
|  | ||||
| Have a look at full examples in [examples/main.go](examples/main.go) | ||||
|  | ||||
| ```go | ||||
| import github.com/blang/semver | ||||
|  | ||||
| v, err := semver.Make("0.0.1-alpha.preview+123.github") | ||||
| fmt.Printf("Major: %d\n", v.Major) | ||||
| fmt.Printf("Minor: %d\n", v.Minor) | ||||
| fmt.Printf("Patch: %d\n", v.Patch) | ||||
| fmt.Printf("Pre: %s\n", v.Pre) | ||||
| fmt.Printf("Build: %s\n", v.Build) | ||||
|  | ||||
| // Prerelease versions array | ||||
| if len(v.Pre) > 0 { | ||||
|     fmt.Println("Prerelease versions:") | ||||
|     for i, pre := range v.Pre { | ||||
|         fmt.Printf("%d: %q\n", i, pre) | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Build meta data array | ||||
| if len(v.Build) > 0 { | ||||
|     fmt.Println("Build meta data:") | ||||
|     for i, build := range v.Build { | ||||
|         fmt.Printf("%d: %q\n", i, build) | ||||
|     } | ||||
| } | ||||
|  | ||||
| v001, err := semver.Make("0.0.1") | ||||
| // Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE | ||||
| v001.GT(v) == true | ||||
| v.LT(v001) == true | ||||
| v.GTE(v) == true | ||||
| v.LTE(v) == true | ||||
|  | ||||
| // Or use v.Compare(v2) for comparisons (-1, 0, 1): | ||||
| v001.Compare(v) == 1 | ||||
| v.Compare(v001) == -1 | ||||
| v.Compare(v) == 0 | ||||
|  | ||||
| // Manipulate Version in place: | ||||
| v.Pre[0], err = semver.NewPRVersion("beta") | ||||
| if err != nil { | ||||
|     fmt.Printf("Error parsing pre release version: %q", err) | ||||
| } | ||||
|  | ||||
| fmt.Println("\nValidate versions:") | ||||
| v.Build[0] = "?" | ||||
|  | ||||
| err = v.Validate() | ||||
| if err != nil { | ||||
|     fmt.Printf("Validation failed: %s\n", err) | ||||
| } | ||||
| ``` | ||||
|  | ||||
|  | ||||
| Benchmarks | ||||
| ----- | ||||
|  | ||||
|     BenchmarkParseSimple-4           5000000    390    ns/op    48 B/op   1 allocs/op | ||||
|     BenchmarkParseComplex-4          1000000   1813    ns/op   256 B/op   7 allocs/op | ||||
|     BenchmarkParseAverage-4          1000000   1171    ns/op   163 B/op   4 allocs/op | ||||
|     BenchmarkStringSimple-4         20000000    119    ns/op    16 B/op   1 allocs/op | ||||
|     BenchmarkStringLarger-4         10000000    206    ns/op    32 B/op   2 allocs/op | ||||
|     BenchmarkStringComplex-4         5000000    324    ns/op    80 B/op   3 allocs/op | ||||
|     BenchmarkStringAverage-4         5000000    273    ns/op    53 B/op   2 allocs/op | ||||
|     BenchmarkValidateSimple-4      200000000      9.33 ns/op     0 B/op   0 allocs/op | ||||
|     BenchmarkValidateComplex-4       3000000    469    ns/op     0 B/op   0 allocs/op | ||||
|     BenchmarkValidateAverage-4       5000000    256    ns/op     0 B/op   0 allocs/op | ||||
|     BenchmarkCompareSimple-4       100000000     11.8  ns/op     0 B/op   0 allocs/op | ||||
|     BenchmarkCompareComplex-4       50000000     30.8  ns/op     0 B/op   0 allocs/op | ||||
|     BenchmarkCompareAverage-4       30000000     41.5  ns/op     0 B/op   0 allocs/op | ||||
|     BenchmarkSort-4                  3000000    419    ns/op   256 B/op   2 allocs/op | ||||
|     BenchmarkRangeParseSimple-4      2000000    850    ns/op   192 B/op   5 allocs/op | ||||
|     BenchmarkRangeParseAverage-4     1000000   1677    ns/op   400 B/op  10 allocs/op | ||||
|     BenchmarkRangeParseComplex-4      300000   5214    ns/op  1440 B/op  30 allocs/op | ||||
|     BenchmarkRangeMatchSimple-4     50000000     25.6  ns/op     0 B/op   0 allocs/op | ||||
|     BenchmarkRangeMatchAverage-4    30000000     56.4  ns/op     0 B/op   0 allocs/op | ||||
|     BenchmarkRangeMatchComplex-4    10000000    153    ns/op     0 B/op   0 allocs/op | ||||
|  | ||||
| See benchmark cases at [semver_test.go](semver_test.go) | ||||
|  | ||||
|  | ||||
| Motivation | ||||
| ----- | ||||
|  | ||||
| I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like. | ||||
|  | ||||
|  | ||||
| Contribution | ||||
| ----- | ||||
|  | ||||
| Feel free to make a pull request. For bigger changes create a issue first to discuss about it. | ||||
|  | ||||
|  | ||||
| License | ||||
| ----- | ||||
|  | ||||
| See [LICENSE](LICENSE) file. | ||||
							
								
								
									
										23
									
								
								vendor/github.com/blang/semver/json.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/blang/semver/json.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| package semver | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| ) | ||||
|  | ||||
| // MarshalJSON implements the encoding/json.Marshaler interface. | ||||
| func (v Version) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(v.String()) | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON implements the encoding/json.Unmarshaler interface. | ||||
| func (v *Version) UnmarshalJSON(data []byte) (err error) { | ||||
| 	var versionString string | ||||
|  | ||||
| 	if err = json.Unmarshal(data, &versionString); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	*v, err = Parse(versionString) | ||||
|  | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										224
									
								
								vendor/github.com/blang/semver/range.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								vendor/github.com/blang/semver/range.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,224 @@ | ||||
| package semver | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
| ) | ||||
|  | ||||
| type comparator func(Version, Version) bool | ||||
|  | ||||
| var ( | ||||
| 	compEQ comparator = func(v1 Version, v2 Version) bool { | ||||
| 		return v1.Compare(v2) == 0 | ||||
| 	} | ||||
| 	compNE = func(v1 Version, v2 Version) bool { | ||||
| 		return v1.Compare(v2) != 0 | ||||
| 	} | ||||
| 	compGT = func(v1 Version, v2 Version) bool { | ||||
| 		return v1.Compare(v2) == 1 | ||||
| 	} | ||||
| 	compGE = func(v1 Version, v2 Version) bool { | ||||
| 		return v1.Compare(v2) >= 0 | ||||
| 	} | ||||
| 	compLT = func(v1 Version, v2 Version) bool { | ||||
| 		return v1.Compare(v2) == -1 | ||||
| 	} | ||||
| 	compLE = func(v1 Version, v2 Version) bool { | ||||
| 		return v1.Compare(v2) <= 0 | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| type versionRange struct { | ||||
| 	v Version | ||||
| 	c comparator | ||||
| } | ||||
|  | ||||
| // rangeFunc creates a Range from the given versionRange. | ||||
| func (vr *versionRange) rangeFunc() Range { | ||||
| 	return Range(func(v Version) bool { | ||||
| 		return vr.c(v, vr.v) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Range represents a range of versions. | ||||
| // A Range can be used to check if a Version satisfies it: | ||||
| // | ||||
| //     range, err := semver.ParseRange(">1.0.0 <2.0.0") | ||||
| //     range(semver.MustParse("1.1.1") // returns true | ||||
| type Range func(Version) bool | ||||
|  | ||||
| // OR combines the existing Range with another Range using logical OR. | ||||
| func (rf Range) OR(f Range) Range { | ||||
| 	return Range(func(v Version) bool { | ||||
| 		return rf(v) || f(v) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // AND combines the existing Range with another Range using logical AND. | ||||
| func (rf Range) AND(f Range) Range { | ||||
| 	return Range(func(v Version) bool { | ||||
| 		return rf(v) && f(v) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // ParseRange parses a range and returns a Range. | ||||
| // If the range could not be parsed an error is returned. | ||||
| // | ||||
| // Valid ranges are: | ||||
| //   - "<1.0.0" | ||||
| //   - "<=1.0.0" | ||||
| //   - ">1.0.0" | ||||
| //   - ">=1.0.0" | ||||
| //   - "1.0.0", "=1.0.0", "==1.0.0" | ||||
| //   - "!1.0.0", "!=1.0.0" | ||||
| // | ||||
| // A Range can consist of multiple ranges separated by space: | ||||
| // Ranges can be linked by logical AND: | ||||
| //   - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0" | ||||
| //   - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2 | ||||
| // | ||||
| // Ranges can also be linked by logical OR: | ||||
| //   - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x" | ||||
| // | ||||
| // AND has a higher precedence than OR. It's not possible to use brackets. | ||||
| // | ||||
| // Ranges can be combined by both AND and OR | ||||
| // | ||||
| //  - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` | ||||
| func ParseRange(s string) (Range, error) { | ||||
| 	parts := splitAndTrim(s) | ||||
| 	orParts, err := splitORParts(parts) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var orFn Range | ||||
| 	for _, p := range orParts { | ||||
| 		var andFn Range | ||||
| 		for _, ap := range p { | ||||
| 			opStr, vStr, err := splitComparatorVersion(ap) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			vr, err := buildVersionRange(opStr, vStr) | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err) | ||||
| 			} | ||||
| 			rf := vr.rangeFunc() | ||||
|  | ||||
| 			// Set function | ||||
| 			if andFn == nil { | ||||
| 				andFn = rf | ||||
| 			} else { // Combine with existing function | ||||
| 				andFn = andFn.AND(rf) | ||||
| 			} | ||||
| 		} | ||||
| 		if orFn == nil { | ||||
| 			orFn = andFn | ||||
| 		} else { | ||||
| 			orFn = orFn.OR(andFn) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| 	return orFn, nil | ||||
| } | ||||
|  | ||||
| // splitORParts splits the already cleaned parts by '||'. | ||||
| // Checks for invalid positions of the operator and returns an | ||||
| // error if found. | ||||
| func splitORParts(parts []string) ([][]string, error) { | ||||
| 	var ORparts [][]string | ||||
| 	last := 0 | ||||
| 	for i, p := range parts { | ||||
| 		if p == "||" { | ||||
| 			if i == 0 { | ||||
| 				return nil, fmt.Errorf("First element in range is '||'") | ||||
| 			} | ||||
| 			ORparts = append(ORparts, parts[last:i]) | ||||
| 			last = i + 1 | ||||
| 		} | ||||
| 	} | ||||
| 	if last == len(parts) { | ||||
| 		return nil, fmt.Errorf("Last element in range is '||'") | ||||
| 	} | ||||
| 	ORparts = append(ORparts, parts[last:]) | ||||
| 	return ORparts, nil | ||||
| } | ||||
|  | ||||
| // buildVersionRange takes a slice of 2: operator and version | ||||
| // and builds a versionRange, otherwise an error. | ||||
| func buildVersionRange(opStr, vStr string) (*versionRange, error) { | ||||
| 	c := parseComparator(opStr) | ||||
| 	if c == nil { | ||||
| 		return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, "")) | ||||
| 	} | ||||
| 	v, err := Parse(vStr) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err) | ||||
| 	} | ||||
|  | ||||
| 	return &versionRange{ | ||||
| 		v: v, | ||||
| 		c: c, | ||||
| 	}, nil | ||||
|  | ||||
| } | ||||
|  | ||||
| // splitAndTrim splits a range string by spaces and cleans leading and trailing spaces | ||||
| func splitAndTrim(s string) (result []string) { | ||||
| 	last := 0 | ||||
| 	for i := 0; i < len(s); i++ { | ||||
| 		if s[i] == ' ' { | ||||
| 			if last < i-1 { | ||||
| 				result = append(result, s[last:i]) | ||||
| 			} | ||||
| 			last = i + 1 | ||||
| 		} | ||||
| 	} | ||||
| 	if last < len(s)-1 { | ||||
| 		result = append(result, s[last:]) | ||||
| 	} | ||||
| 	// parts := strings.Split(s, " ") | ||||
| 	// for _, x := range parts { | ||||
| 	// 	if s := strings.TrimSpace(x); len(s) != 0 { | ||||
| 	// 		result = append(result, s) | ||||
| 	// 	} | ||||
| 	// } | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // splitComparatorVersion splits the comparator from the version. | ||||
| // Spaces between the comparator and the version are not allowed. | ||||
| // Input must be free of leading or trailing spaces. | ||||
| func splitComparatorVersion(s string) (string, string, error) { | ||||
| 	i := strings.IndexFunc(s, unicode.IsDigit) | ||||
| 	if i == -1 { | ||||
| 		return "", "", fmt.Errorf("Could not get version from string: %q", s) | ||||
| 	} | ||||
| 	return strings.TrimSpace(s[0:i]), s[i:], nil | ||||
| } | ||||
|  | ||||
| func parseComparator(s string) comparator { | ||||
| 	switch s { | ||||
| 	case "==": | ||||
| 		fallthrough | ||||
| 	case "": | ||||
| 		fallthrough | ||||
| 	case "=": | ||||
| 		return compEQ | ||||
| 	case ">": | ||||
| 		return compGT | ||||
| 	case ">=": | ||||
| 		return compGE | ||||
| 	case "<": | ||||
| 		return compLT | ||||
| 	case "<=": | ||||
| 		return compLE | ||||
| 	case "!": | ||||
| 		fallthrough | ||||
| 	case "!=": | ||||
| 		return compNE | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										395
									
								
								vendor/github.com/blang/semver/semver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								vendor/github.com/blang/semver/semver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,395 @@ | ||||
| package semver | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	numbers  string = "0123456789" | ||||
| 	alphas          = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" | ||||
| 	alphanum        = alphas + numbers | ||||
| ) | ||||
|  | ||||
| // SpecVersion is the latest fully supported spec version of semver | ||||
| var SpecVersion = Version{ | ||||
| 	Major: 2, | ||||
| 	Minor: 0, | ||||
| 	Patch: 0, | ||||
| } | ||||
|  | ||||
| // Version represents a semver compatible version | ||||
| type Version struct { | ||||
| 	Major uint64 | ||||
| 	Minor uint64 | ||||
| 	Patch uint64 | ||||
| 	Pre   []PRVersion | ||||
| 	Build []string //No Precendence | ||||
| } | ||||
|  | ||||
| // Version to string | ||||
| func (v Version) String() string { | ||||
| 	b := make([]byte, 0, 5) | ||||
| 	b = strconv.AppendUint(b, v.Major, 10) | ||||
| 	b = append(b, '.') | ||||
| 	b = strconv.AppendUint(b, v.Minor, 10) | ||||
| 	b = append(b, '.') | ||||
| 	b = strconv.AppendUint(b, v.Patch, 10) | ||||
|  | ||||
| 	if len(v.Pre) > 0 { | ||||
| 		b = append(b, '-') | ||||
| 		b = append(b, v.Pre[0].String()...) | ||||
|  | ||||
| 		for _, pre := range v.Pre[1:] { | ||||
| 			b = append(b, '.') | ||||
| 			b = append(b, pre.String()...) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(v.Build) > 0 { | ||||
| 		b = append(b, '+') | ||||
| 		b = append(b, v.Build[0]...) | ||||
|  | ||||
| 		for _, build := range v.Build[1:] { | ||||
| 			b = append(b, '.') | ||||
| 			b = append(b, build...) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return string(b) | ||||
| } | ||||
|  | ||||
| // Equals checks if v is equal to o. | ||||
| func (v Version) Equals(o Version) bool { | ||||
| 	return (v.Compare(o) == 0) | ||||
| } | ||||
|  | ||||
| // EQ checks if v is equal to o. | ||||
| func (v Version) EQ(o Version) bool { | ||||
| 	return (v.Compare(o) == 0) | ||||
| } | ||||
|  | ||||
| // NE checks if v is not equal to o. | ||||
| func (v Version) NE(o Version) bool { | ||||
| 	return (v.Compare(o) != 0) | ||||
| } | ||||
|  | ||||
| // GT checks if v is greater than o. | ||||
| func (v Version) GT(o Version) bool { | ||||
| 	return (v.Compare(o) == 1) | ||||
| } | ||||
|  | ||||
| // GTE checks if v is greater than or equal to o. | ||||
| func (v Version) GTE(o Version) bool { | ||||
| 	return (v.Compare(o) >= 0) | ||||
| } | ||||
|  | ||||
| // GE checks if v is greater than or equal to o. | ||||
| func (v Version) GE(o Version) bool { | ||||
| 	return (v.Compare(o) >= 0) | ||||
| } | ||||
|  | ||||
| // LT checks if v is less than o. | ||||
| func (v Version) LT(o Version) bool { | ||||
| 	return (v.Compare(o) == -1) | ||||
| } | ||||
|  | ||||
| // LTE checks if v is less than or equal to o. | ||||
| func (v Version) LTE(o Version) bool { | ||||
| 	return (v.Compare(o) <= 0) | ||||
| } | ||||
|  | ||||
| // LE checks if v is less than or equal to o. | ||||
| func (v Version) LE(o Version) bool { | ||||
| 	return (v.Compare(o) <= 0) | ||||
| } | ||||
|  | ||||
| // Compare compares Versions v to o: | ||||
| // -1 == v is less than o | ||||
| // 0 == v is equal to o | ||||
| // 1 == v is greater than o | ||||
| func (v Version) Compare(o Version) int { | ||||
| 	if v.Major != o.Major { | ||||
| 		if v.Major > o.Major { | ||||
| 			return 1 | ||||
| 		} | ||||
| 		return -1 | ||||
| 	} | ||||
| 	if v.Minor != o.Minor { | ||||
| 		if v.Minor > o.Minor { | ||||
| 			return 1 | ||||
| 		} | ||||
| 		return -1 | ||||
| 	} | ||||
| 	if v.Patch != o.Patch { | ||||
| 		if v.Patch > o.Patch { | ||||
| 			return 1 | ||||
| 		} | ||||
| 		return -1 | ||||
| 	} | ||||
|  | ||||
| 	// Quick comparison if a version has no prerelease versions | ||||
| 	if len(v.Pre) == 0 && len(o.Pre) == 0 { | ||||
| 		return 0 | ||||
| 	} else if len(v.Pre) == 0 && len(o.Pre) > 0 { | ||||
| 		return 1 | ||||
| 	} else if len(v.Pre) > 0 && len(o.Pre) == 0 { | ||||
| 		return -1 | ||||
| 	} | ||||
|  | ||||
| 	i := 0 | ||||
| 	for ; i < len(v.Pre) && i < len(o.Pre); i++ { | ||||
| 		if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 { | ||||
| 			continue | ||||
| 		} else if comp == 1 { | ||||
| 			return 1 | ||||
| 		} else { | ||||
| 			return -1 | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// If all pr versions are the equal but one has further prversion, this one greater | ||||
| 	if i == len(v.Pre) && i == len(o.Pre) { | ||||
| 		return 0 | ||||
| 	} else if i == len(v.Pre) && i < len(o.Pre) { | ||||
| 		return -1 | ||||
| 	} else { | ||||
| 		return 1 | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // Validate validates v and returns error in case | ||||
| func (v Version) Validate() error { | ||||
| 	// Major, Minor, Patch already validated using uint64 | ||||
|  | ||||
| 	for _, pre := range v.Pre { | ||||
| 		if !pre.IsNum { //Numeric prerelease versions already uint64 | ||||
| 			if len(pre.VersionStr) == 0 { | ||||
| 				return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr) | ||||
| 			} | ||||
| 			if !containsOnly(pre.VersionStr, alphanum) { | ||||
| 				return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for _, build := range v.Build { | ||||
| 		if len(build) == 0 { | ||||
| 			return fmt.Errorf("Build meta data can not be empty %q", build) | ||||
| 		} | ||||
| 		if !containsOnly(build, alphanum) { | ||||
| 			return fmt.Errorf("Invalid character(s) found in build meta data %q", build) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error | ||||
| func New(s string) (vp *Version, err error) { | ||||
| 	v, err := Parse(s) | ||||
| 	vp = &v | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Make is an alias for Parse, parses version string and returns a validated Version or error | ||||
| func Make(s string) (Version, error) { | ||||
| 	return Parse(s) | ||||
| } | ||||
|  | ||||
| // Parse parses version string and returns a validated Version or error | ||||
| func Parse(s string) (Version, error) { | ||||
| 	if len(s) == 0 { | ||||
| 		return Version{}, errors.New("Version string empty") | ||||
| 	} | ||||
|  | ||||
| 	// Split into major.minor.(patch+pr+meta) | ||||
| 	parts := strings.SplitN(s, ".", 3) | ||||
| 	if len(parts) != 3 { | ||||
| 		return Version{}, errors.New("No Major.Minor.Patch elements found") | ||||
| 	} | ||||
|  | ||||
| 	// Major | ||||
| 	if !containsOnly(parts[0], numbers) { | ||||
| 		return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0]) | ||||
| 	} | ||||
| 	if hasLeadingZeroes(parts[0]) { | ||||
| 		return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0]) | ||||
| 	} | ||||
| 	major, err := strconv.ParseUint(parts[0], 10, 64) | ||||
| 	if err != nil { | ||||
| 		return Version{}, err | ||||
| 	} | ||||
|  | ||||
| 	// Minor | ||||
| 	if !containsOnly(parts[1], numbers) { | ||||
| 		return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1]) | ||||
| 	} | ||||
| 	if hasLeadingZeroes(parts[1]) { | ||||
| 		return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1]) | ||||
| 	} | ||||
| 	minor, err := strconv.ParseUint(parts[1], 10, 64) | ||||
| 	if err != nil { | ||||
| 		return Version{}, err | ||||
| 	} | ||||
|  | ||||
| 	v := Version{} | ||||
| 	v.Major = major | ||||
| 	v.Minor = minor | ||||
|  | ||||
| 	var build, prerelease []string | ||||
| 	patchStr := parts[2] | ||||
|  | ||||
| 	if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 { | ||||
| 		build = strings.Split(patchStr[buildIndex+1:], ".") | ||||
| 		patchStr = patchStr[:buildIndex] | ||||
| 	} | ||||
|  | ||||
| 	if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 { | ||||
| 		prerelease = strings.Split(patchStr[preIndex+1:], ".") | ||||
| 		patchStr = patchStr[:preIndex] | ||||
| 	} | ||||
|  | ||||
| 	if !containsOnly(patchStr, numbers) { | ||||
| 		return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr) | ||||
| 	} | ||||
| 	if hasLeadingZeroes(patchStr) { | ||||
| 		return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr) | ||||
| 	} | ||||
| 	patch, err := strconv.ParseUint(patchStr, 10, 64) | ||||
| 	if err != nil { | ||||
| 		return Version{}, err | ||||
| 	} | ||||
|  | ||||
| 	v.Patch = patch | ||||
|  | ||||
| 	// Prerelease | ||||
| 	for _, prstr := range prerelease { | ||||
| 		parsedPR, err := NewPRVersion(prstr) | ||||
| 		if err != nil { | ||||
| 			return Version{}, err | ||||
| 		} | ||||
| 		v.Pre = append(v.Pre, parsedPR) | ||||
| 	} | ||||
|  | ||||
| 	// Build meta data | ||||
| 	for _, str := range build { | ||||
| 		if len(str) == 0 { | ||||
| 			return Version{}, errors.New("Build meta data is empty") | ||||
| 		} | ||||
| 		if !containsOnly(str, alphanum) { | ||||
| 			return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str) | ||||
| 		} | ||||
| 		v.Build = append(v.Build, str) | ||||
| 	} | ||||
|  | ||||
| 	return v, nil | ||||
| } | ||||
|  | ||||
| // MustParse is like Parse but panics if the version cannot be parsed. | ||||
| func MustParse(s string) Version { | ||||
| 	v, err := Parse(s) | ||||
| 	if err != nil { | ||||
| 		panic(`semver: Parse(` + s + `): ` + err.Error()) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // PRVersion represents a PreRelease Version | ||||
| type PRVersion struct { | ||||
| 	VersionStr string | ||||
| 	VersionNum uint64 | ||||
| 	IsNum      bool | ||||
| } | ||||
|  | ||||
| // NewPRVersion creates a new valid prerelease version | ||||
| func NewPRVersion(s string) (PRVersion, error) { | ||||
| 	if len(s) == 0 { | ||||
| 		return PRVersion{}, errors.New("Prerelease is empty") | ||||
| 	} | ||||
| 	v := PRVersion{} | ||||
| 	if containsOnly(s, numbers) { | ||||
| 		if hasLeadingZeroes(s) { | ||||
| 			return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s) | ||||
| 		} | ||||
| 		num, err := strconv.ParseUint(s, 10, 64) | ||||
|  | ||||
| 		// Might never be hit, but just in case | ||||
| 		if err != nil { | ||||
| 			return PRVersion{}, err | ||||
| 		} | ||||
| 		v.VersionNum = num | ||||
| 		v.IsNum = true | ||||
| 	} else if containsOnly(s, alphanum) { | ||||
| 		v.VersionStr = s | ||||
| 		v.IsNum = false | ||||
| 	} else { | ||||
| 		return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s) | ||||
| 	} | ||||
| 	return v, nil | ||||
| } | ||||
|  | ||||
| // IsNumeric checks if prerelease-version is numeric | ||||
| func (v PRVersion) IsNumeric() bool { | ||||
| 	return v.IsNum | ||||
| } | ||||
|  | ||||
| // Compare compares two PreRelease Versions v and o: | ||||
| // -1 == v is less than o | ||||
| // 0 == v is equal to o | ||||
| // 1 == v is greater than o | ||||
| func (v PRVersion) Compare(o PRVersion) int { | ||||
| 	if v.IsNum && !o.IsNum { | ||||
| 		return -1 | ||||
| 	} else if !v.IsNum && o.IsNum { | ||||
| 		return 1 | ||||
| 	} else if v.IsNum && o.IsNum { | ||||
| 		if v.VersionNum == o.VersionNum { | ||||
| 			return 0 | ||||
| 		} else if v.VersionNum > o.VersionNum { | ||||
| 			return 1 | ||||
| 		} else { | ||||
| 			return -1 | ||||
| 		} | ||||
| 	} else { // both are Alphas | ||||
| 		if v.VersionStr == o.VersionStr { | ||||
| 			return 0 | ||||
| 		} else if v.VersionStr > o.VersionStr { | ||||
| 			return 1 | ||||
| 		} else { | ||||
| 			return -1 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // PreRelease version to string | ||||
| func (v PRVersion) String() string { | ||||
| 	if v.IsNum { | ||||
| 		return strconv.FormatUint(v.VersionNum, 10) | ||||
| 	} | ||||
| 	return v.VersionStr | ||||
| } | ||||
|  | ||||
| func containsOnly(s string, set string) bool { | ||||
| 	return strings.IndexFunc(s, func(r rune) bool { | ||||
| 		return !strings.ContainsRune(set, r) | ||||
| 	}) == -1 | ||||
| } | ||||
|  | ||||
| func hasLeadingZeroes(s string) bool { | ||||
| 	return len(s) > 1 && s[0] == '0' | ||||
| } | ||||
|  | ||||
| // NewBuildVersion creates a new valid build version | ||||
| func NewBuildVersion(s string) (string, error) { | ||||
| 	if len(s) == 0 { | ||||
| 		return "", errors.New("Buildversion is empty") | ||||
| 	} | ||||
| 	if !containsOnly(s, alphanum) { | ||||
| 		return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s) | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
							
								
								
									
										28
									
								
								vendor/github.com/blang/semver/sort.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/blang/semver/sort.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package semver | ||||
|  | ||||
| import ( | ||||
| 	"sort" | ||||
| ) | ||||
|  | ||||
| // Versions represents multiple versions. | ||||
| type Versions []Version | ||||
|  | ||||
| // Len returns length of version collection | ||||
| func (s Versions) Len() int { | ||||
| 	return len(s) | ||||
| } | ||||
|  | ||||
| // Swap swaps two versions inside the collection by its indices | ||||
| func (s Versions) Swap(i, j int) { | ||||
| 	s[i], s[j] = s[j], s[i] | ||||
| } | ||||
|  | ||||
| // Less checks if version at index i is less than version at index j | ||||
| func (s Versions) Less(i, j int) bool { | ||||
| 	return s[i].LT(s[j]) | ||||
| } | ||||
|  | ||||
| // Sort sorts a slice of versions | ||||
| func Sort(versions []Version) { | ||||
| 	sort.Sort(Versions(versions)) | ||||
| } | ||||
							
								
								
									
										30
									
								
								vendor/github.com/blang/semver/sql.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/blang/semver/sql.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| package semver | ||||
|  | ||||
| import ( | ||||
| 	"database/sql/driver" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // Scan implements the database/sql.Scanner interface. | ||||
| func (v *Version) Scan(src interface{}) (err error) { | ||||
| 	var str string | ||||
| 	switch src := src.(type) { | ||||
| 	case string: | ||||
| 		str = src | ||||
| 	case []byte: | ||||
| 		str = string(src) | ||||
| 	default: | ||||
| 		return fmt.Errorf("Version.Scan: cannot convert %T to string.", src) | ||||
| 	} | ||||
|  | ||||
| 	if t, err := Parse(str); err == nil { | ||||
| 		*v = t | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Value implements the database/sql/driver.Valuer interface. | ||||
| func (v Version) Value() (driver.Value, error) { | ||||
| 	return v.String(), nil | ||||
| } | ||||
							
								
								
									
										201
									
								
								vendor/github.com/containerd/cri-containerd/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/containerd/cri-containerd/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. | ||||
							
								
								
									
										162
									
								
								vendor/github.com/containerd/cri-containerd/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								vendor/github.com/containerd/cri-containerd/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| # cri-containerd | ||||
| <p align="center"> | ||||
| <img src="https://github.com/kubernetes/kubernetes/blob/master/logo/logo.png" width="50" height="50"> | ||||
| <img src="https://github.com/containerd/containerd/blob/master/docs/images/containerd-dark.png" width="200" > | ||||
| </p> | ||||
|  | ||||
| [](https://travis-ci.org/containerd/cri-containerd) | ||||
| [](https://goreportcard.com/report/github.com/containerd/cri-containerd) | ||||
|  | ||||
| `cri-containerd` is a [containerd](https://containerd.io/) based implementation of Kubernetes [container runtime interface (CRI)](https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto). | ||||
|  | ||||
| With it, you could run Kubernetes using containerd as the container runtime. | ||||
|  | ||||
| ## Current Status | ||||
| `cri-containerd` is in beta: | ||||
| * It is feature complete. | ||||
| * It (the beta version) works with Kubernetes >= 1.9. | ||||
| * It has passed all [CRI validation tests](https://github.com/kubernetes/community/blob/master/contributors/devel/cri-validation.md). | ||||
| * It has passed all regular [node e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-node-tests.md). | ||||
| * It has passed all regular [e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-tests.md). | ||||
|  | ||||
| See [test dashboard](https://k8s-testgrid.appspot.com/sig-node-containerd) | ||||
| ## Support Metrics | ||||
| | CRI-Containerd Version | Kubernetes Version | | ||||
| |:----------------------:|:------------------:| | ||||
| |     v1.0.0-alpha.x     |      1.7, 1.8      | | ||||
| |      v1.0.0-beta.x     |        1.9+        | | ||||
| ## Production Quality Cluster on GCE | ||||
| For a production quality cluster on GCE brought up with `kube-up.sh` refer [here](docs/kube-up.md). | ||||
| ## Installing with Ansible and Kubeadm | ||||
| For a multi node cluster installer and bring up steps using ansible and kubeadm refer [here](contrib/ansible/README.md). | ||||
| ## Custom Installation | ||||
| For non ansible users, you can download the `cri-containerd` release tarball and deploy | ||||
| kubernetes cluster using kubeadm as described [here](docs/installation.md). | ||||
| ## Getting Started for Developers | ||||
| ### Binary Dependencies and Specifications | ||||
| The current release of `cri-containerd` has the following dependencies: | ||||
| * [containerd](https://github.com/containerd/containerd) | ||||
| * [runc](https://github.com/opencontainers/runc) | ||||
| * [CNI](https://github.com/containernetworking/cni) | ||||
|  | ||||
| See [versions](./hack/versions) of these dependencies `cri-containerd` is tested with. | ||||
|  | ||||
| As containerd and runc move to their respective general availability releases, | ||||
| we will do our best to rebase/retest `cri-containerd` with these releases on a | ||||
| weekly/monthly basis. Similarly, given that `cri-containerd` uses the Open | ||||
| Container Initiative (OCI) [image](https://github.com/opencontainers/image-spec) | ||||
| and [runtime](https://github.com/opencontainers/runtime-spec) specifications, we | ||||
| will also do our best to update `cri-containerd` to the latest releases of these | ||||
| specifications as appropriate. | ||||
| ### Install Dependencies | ||||
| 1. Install development libraries: | ||||
| * **libseccomp development library.** Required by cri-containerd and runc seccomp support. `libseccomp-dev` (Ubuntu, Debian) / `libseccomp-devel` | ||||
| (Fedora, CentOS, RHEL). On releases of Ubuntu <=Trusty and Debian <=jessie a | ||||
| backport version of `libseccomp-dev` is required. See [travis.yml](.travis.yml) for an example on trusty. | ||||
| * **libapparmor development library.** Required by cri-containerd and runc apparmor support. To use apparmor on Debian, Ubuntu, and related distributions the installation of `libapparmor-dev` is required. | ||||
| * **btrfs development library.** Required by containerd btrfs support. `btrfs-tools`(Ubuntu, Debian) / `btrfs-progs-devel`(Fedora, CentOS, RHEL) | ||||
| 2. Install other dependencies: | ||||
| * **`nsenter`**: Required by CNI and portforward. | ||||
| * **`socat`**: Required by portforward. | ||||
| 3. Install and setup a go 1.9.x development environment. | ||||
| 4. Make a local clone of this repository. | ||||
| 5. Install binary dependencies by running the following command from your cloned `cri-containerd/` project directory: | ||||
| ```bash | ||||
| # Note: install.deps installs the above mentioned runc, containerd, and CNI | ||||
| # binary dependencies. install.deps is only provided for general use and ease of | ||||
| # testing. To customize `runc` and `containerd` build tags and/or to configure | ||||
| # `cni`, please follow instructions in their documents. | ||||
| make install.deps | ||||
| ``` | ||||
| ### Build and Install cri-containerd | ||||
| To build and install `cri-containerd` enter the following commands from your `cri-containerd` project directory: | ||||
| ```bash | ||||
| make | ||||
| sudo make install | ||||
| ``` | ||||
| #### Build Tags | ||||
| `cri-containerd` supports optional build tags for compiling support of various features. | ||||
| To add build tags to the make option the `BUILDTAGS` variable must be set. | ||||
|  | ||||
| ```bash | ||||
| make BUILD_TAGS='seccomp apparmor' | ||||
| ``` | ||||
|  | ||||
| | Build Tag | Feature                            | Dependency                      | | ||||
| |-----------|------------------------------------|---------------------------------| | ||||
| | seccomp   | syscall filtering                  | libseccomp development library  | | ||||
| | selinux   | selinux process and mount labeling | <none>                          | | ||||
| | apparmor  | apparmor profile support           | libapparmor development library | | ||||
| ### Validate Your cri-containerd Setup | ||||
| A Kubernetes incubator project called [cri-tools](https://github.com/kubernetes-incubator/cri-tools) | ||||
| includes programs for exercising CRI implementations such as `cri-containerd`. | ||||
| More importantly, cri-tools includes the program `critest` which is used for running | ||||
| [CRI Validation Testing](https://github.com/kubernetes/community/blob/master/contributors/devel/cri-validation.md). | ||||
|  | ||||
| Run the CRI Validation test to validate your installation of `cri-containerd`: | ||||
| ```bash | ||||
| make test-cri | ||||
| ``` | ||||
| ### Running a Kubernetes local cluster | ||||
| If you already have a working development environment for supported Kubernetes | ||||
| version, you can try `cri-containerd` in a local cluster: | ||||
|  | ||||
| 1. Start `containerd` as root in a first terminal: | ||||
| ```bash | ||||
| sudo containerd | ||||
| ``` | ||||
| 2. Start `cri-containerd` as root in a second terminal: | ||||
| ```bash | ||||
| sudo cri-containerd | ||||
| ``` | ||||
| 3. From the Kubernetes project directory startup a local cluster using `cri-containerd`: | ||||
| ```bash | ||||
| CONTAINER_RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT='/var/run/cri-containerd.sock' ./hack/local-up-cluster.sh | ||||
| ``` | ||||
| ### Test | ||||
| See [here](./docs/testing.md) for information about test. | ||||
| ## Using crictl | ||||
| See [here](./docs/crictl.md) for information about using `crictl` to debug | ||||
| pods, containers, and images. | ||||
| ## Documentation | ||||
| See [here](./docs) for additional documentation. | ||||
| ## Contributing | ||||
| Interested in contributing? Check out the [documentation](./CONTRIBUTING.md). | ||||
|  | ||||
| ## Communication | ||||
| This project was originally established in April of 2017 in the Kubernetes | ||||
| Incubator program. After reaching the Beta stage, In January of 2018, the | ||||
| project was merged into [containerd](https://github.com/containerd/containerd). | ||||
|  | ||||
| For async communication and long running discussions please use issues and pull | ||||
| requests on this github repo. This will be the best place to discuss design and | ||||
| implementation. | ||||
|  | ||||
| For sync communication we have a community slack with a #containerd channel that | ||||
| everyone is welcome to join and chat about development. | ||||
|  | ||||
| **Slack:** https://dockr.ly/community | ||||
|  | ||||
| ## Other Communications | ||||
| As this project is tightly coupled to CRI and CRI-Tools and they are Kubernetes | ||||
| projects, some of our project communications take place in the Kubernetes' SIG: | ||||
| `sig-node.` | ||||
|  | ||||
| For more information about `sig-node`, `CRI`, and the `CRI-Tools` projects: | ||||
| * [sig-node community site](https://github.com/kubernetes/community/tree/master/sig-node) | ||||
| * Slack: `#sig-node` channel in Kubernetes (kubernetes.slack.com) | ||||
| * Mailing List: https://groups.google.com/forum/#!forum/kubernetes-sig-node | ||||
|  | ||||
| ### Reporting Security Issues | ||||
|  | ||||
| __If you are reporting a security issue, please reach out discreetly at security@containerd.io__. | ||||
|  | ||||
| ## Licenses | ||||
| The containerd codebase is released under the [Apache 2.0 license](https://github.com/containerd/containerd/blob/master/LICENSE.code). | ||||
| The README.md file, and files in the "docs" folder are licensed under the | ||||
| Creative Commons Attribution 4.0 International License under the terms and | ||||
| conditions set forth in the file "[LICENSE.docs](https://github.com/containerd/containerd/blob/master/LICENSE.docs)". You may obtain a duplicate | ||||
| copy of the same license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/. | ||||
|  | ||||
| ## Code of Conduct | ||||
| This project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). | ||||
							
								
								
									
										269
									
								
								vendor/github.com/containerd/cri-containerd/cmd/cri-containerd/options/options.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								vendor/github.com/containerd/cri-containerd/cmd/cri-containerd/options/options.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,269 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 options | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/BurntSushi/toml" | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/spf13/pflag" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// configFilePathArgName is the path to the config file. | ||||
| 	configFilePathArgName = "config" | ||||
| 	// defaultConfigFilePath is the default config file path. | ||||
| 	defaultConfigFilePath = "/etc/cri-containerd/config.toml" | ||||
| ) | ||||
|  | ||||
| // ContainerdConfig contains toml config related to containerd | ||||
| type ContainerdConfig struct { | ||||
| 	// RootDir is the root directory path for containerd. | ||||
| 	// TODO(random-liu): Remove this field when no longer support cri-containerd standalone mode. | ||||
| 	RootDir string `toml:"root_dir" json:"rootDir,omitempty"` | ||||
| 	// Snapshotter is the snapshotter used by containerd. | ||||
| 	Snapshotter string `toml:"snapshotter" json:"snapshotter,omitempty"` | ||||
| 	// Endpoint is the containerd endpoint path. | ||||
| 	// TODO(random-liu): Remove this field when no longer support cri-containerd standalone mode. | ||||
| 	Endpoint string `toml:"endpoint" json:"endpoint,omitempty"` | ||||
| 	// Runtime is the runtime to use in containerd. We may support | ||||
| 	// other runtimes in the future. | ||||
| 	Runtime string `toml:"runtime" json:"runtime,omitempty"` | ||||
| 	// RuntimeEngine is the name of the runtime engine used by containerd. | ||||
| 	// Containerd default should be "runc" | ||||
| 	// We may support other runtime engines in the future. | ||||
| 	RuntimeEngine string `toml:"runtime_engine" json:"runtimeEngine,omitempty"` | ||||
| 	// RuntimeRoot is the directory used by containerd for runtime state. | ||||
| 	// Containerd default should be "/run/containerd/runc" | ||||
| 	RuntimeRoot string `toml:"runtime_root" json:"runtimeRoot,omitempty"` | ||||
| } | ||||
|  | ||||
| // CniConfig contains toml config related to cni | ||||
| type CniConfig struct { | ||||
| 	// NetworkPluginBinDir is the directory in which the binaries for the plugin is kept. | ||||
| 	NetworkPluginBinDir string `toml:"bin_dir" json:"binDir,omitempty"` | ||||
| 	// NetworkPluginConfDir is the directory in which the admin places a CNI conf. | ||||
| 	NetworkPluginConfDir string `toml:"conf_dir" json:"confDir,omitempty"` | ||||
| } | ||||
|  | ||||
| // PluginConfig contains toml config related to CRI plugin, | ||||
| // it is a subset of Config. | ||||
| type PluginConfig struct { | ||||
| 	// ContainerdConfig contains config related to containerd | ||||
| 	ContainerdConfig `toml:"containerd" json:"containerd,omitempty"` | ||||
| 	// CniConfig contains config related to cni | ||||
| 	CniConfig `toml:"cni" json:"cni,omitempty"` | ||||
| 	// Registry contains config related to the registry | ||||
| 	Registry `toml:"registry" json:"registry,omitempty"` | ||||
| 	// StreamServerAddress is the ip address streaming server is listening on. | ||||
| 	StreamServerAddress string `toml:"stream_server_address" json:"streamServerAddress,omitempty"` | ||||
| 	// StreamServerPort is the port streaming server is listening on. | ||||
| 	StreamServerPort string `toml:"stream_server_port" json:"streamServerPort,omitempty"` | ||||
| 	// EnableSelinux indicates to enable the selinux support. | ||||
| 	EnableSelinux bool `toml:"enable_selinux" json:"enableSelinux,omitempty"` | ||||
| 	// SandboxImage is the image used by sandbox container. | ||||
| 	SandboxImage string `toml:"sandbox_image" json:"sandboxImage,omitempty"` | ||||
| 	// StatsCollectPeriod is the period (in seconds) of snapshots stats collection. | ||||
| 	StatsCollectPeriod int `toml:"stats_collect_period" json:"statsCollectPeriod,omitempty"` | ||||
| 	// SystemdCgroup enables systemd cgroup support. | ||||
| 	SystemdCgroup bool `toml:"systemd_cgroup" json:"systemdCgroup,omitempty"` | ||||
| 	// EnableIPv6DAD enables IPv6 DAD. | ||||
| 	// TODO(random-liu): Use optimistic_dad when it's GA. | ||||
| 	EnableIPv6DAD bool `toml:"enable_ipv6_dad" json:"enableIPv6DAD,omitempty"` | ||||
| } | ||||
|  | ||||
| // Config contains toml config related cri-containerd daemon. | ||||
| // TODO(random-liu): Make this an internal config object when we no longer support cri-containerd | ||||
| // standalone mode. At that time, we can clean this up. | ||||
| type Config struct { | ||||
| 	// PluginConfig is the config for CRI plugin. | ||||
| 	PluginConfig | ||||
| 	// ContainerdRootDir is the root directory path for containerd. | ||||
| 	ContainerdRootDir string `toml:"-" json:"containerdRootDir,omitempty"` | ||||
| 	// ContainerdEndpoint is the containerd endpoint path. | ||||
| 	ContainerdEndpoint string `toml:"-" json:"containerdEndpoint,omitempty"` | ||||
| 	// SocketPath is the path to the socket which cri-containerd serves on. | ||||
| 	// TODO(random-liu): Remove SocketPath when no longer support cri-containerd | ||||
| 	// standalone mode. | ||||
| 	SocketPath string `toml:"socket_path" json:"socketPath,omitempty"` | ||||
| 	// RootDir is the root directory path for managing cri-containerd files | ||||
| 	// (metadata checkpoint etc.) | ||||
| 	RootDir string `toml:"root_dir" json:"rootDir,omitempty"` | ||||
| 	// TODO(random-liu): Remove following fields when we no longer support cri-containerd | ||||
| 	// standalone mode. | ||||
| 	// CgroupPath is the path for the cgroup that cri-containerd is placed in. | ||||
| 	CgroupPath string `toml:"cgroup_path" json:"cgroupPath,omitempty"` | ||||
| 	// OOMScore adjust the cri-containerd's oom score | ||||
| 	OOMScore int `toml:"oom_score" json:"oomScore,omitempty"` | ||||
| 	// EnableProfiling is used for enable profiling via host:port/debug/pprof/ | ||||
| 	EnableProfiling bool `toml:"profiling" json:"enableProfiling,omitempty"` | ||||
| 	// ProfilingPort is the port for profiling via host:port/debug/pprof/ | ||||
| 	ProfilingPort string `toml:"profiling_port" json:"profilingPort,omitempty"` | ||||
| 	// ProfilingAddress is address for profiling via host:port/debug/pprof/ | ||||
| 	ProfilingAddress string `toml:"profiling_addr" json:"profilingAddress,omitempty"` | ||||
| 	// LogLevel is the logrus log level. | ||||
| 	LogLevel string `toml:"log_level" json:"logLevel,omitempty"` | ||||
| } | ||||
|  | ||||
| // CRIContainerdOptions contains cri-containerd command line and toml options. | ||||
| type CRIContainerdOptions struct { | ||||
| 	// Config contains cri-containerd toml config | ||||
| 	Config | ||||
| 	// ConfigFilePath is the path to the TOML config file. | ||||
| 	ConfigFilePath string `toml:"-"` | ||||
| } | ||||
|  | ||||
| // NewCRIContainerdOptions returns a reference to CRIContainerdOptions | ||||
| func NewCRIContainerdOptions() *CRIContainerdOptions { | ||||
| 	return &CRIContainerdOptions{} | ||||
| } | ||||
|  | ||||
| // AddFlags adds cri-containerd command line options to pflag. | ||||
| func (c *CRIContainerdOptions) AddFlags(fs *pflag.FlagSet) { | ||||
| 	defaults := DefaultConfig() | ||||
| 	fs.StringVar(&c.ConfigFilePath, configFilePathArgName, | ||||
| 		defaultConfigFilePath, "Path to the config file.") | ||||
| 	fs.StringVar(&c.LogLevel, "log-level", | ||||
| 		defaults.LogLevel, "Set the logging level [trace, debug, info, warn, error, fatal, panic].") | ||||
| 	fs.StringVar(&c.SocketPath, "socket-path", | ||||
| 		defaults.SocketPath, "Path to the socket which cri-containerd serves on.") | ||||
| 	fs.StringVar(&c.RootDir, "root-dir", | ||||
| 		defaults.RootDir, "Root directory path for cri-containerd managed files (metadata checkpoint etc).") | ||||
| 	fs.StringVar(&c.ContainerdRootDir, "containerd-root-dir", | ||||
| 		defaults.ContainerdRootDir, "Root directory path where containerd stores persistent data.") | ||||
| 	fs.StringVar(&c.ContainerdEndpoint, "containerd-endpoint", | ||||
| 		defaults.ContainerdEndpoint, "Path to the containerd endpoint.") | ||||
| 	fs.StringVar(&c.ContainerdConfig.Snapshotter, "containerd-snapshotter", | ||||
| 		defaults.ContainerdConfig.Snapshotter, "The snapshotter used by containerd.") | ||||
| 	fs.StringVar(&c.ContainerdConfig.Runtime, "containerd-runtime", | ||||
| 		defaults.ContainerdConfig.Runtime, "The runtime used by containerd.") | ||||
| 	fs.StringVar(&c.ContainerdConfig.RuntimeEngine, "containerd-runtime-engine", | ||||
| 		defaults.ContainerdConfig.RuntimeEngine, "Runtime engine used by containerd. Defaults to containerd's default if not specified.") | ||||
| 	fs.StringVar(&c.ContainerdConfig.RuntimeRoot, "containerd-runtime-root", | ||||
| 		defaults.ContainerdConfig.RuntimeRoot, "The directory used by containerd for runtime state. Defaults to containerd's default if not specified.") | ||||
| 	fs.StringVar(&c.NetworkPluginBinDir, "network-bin-dir", | ||||
| 		defaults.NetworkPluginBinDir, "The directory for putting network binaries.") | ||||
| 	fs.StringVar(&c.NetworkPluginConfDir, "network-conf-dir", | ||||
| 		defaults.NetworkPluginConfDir, "The directory for putting network plugin configuration files.") | ||||
| 	fs.StringVar(&c.StreamServerAddress, "stream-addr", | ||||
| 		defaults.StreamServerAddress, "The ip address streaming server is listening on. The default host interface is used if not specified.") | ||||
| 	fs.StringVar(&c.StreamServerPort, "stream-port", | ||||
| 		defaults.StreamServerPort, "The port streaming server is listening on.") | ||||
| 	fs.StringVar(&c.CgroupPath, "cgroup-path", | ||||
| 		defaults.CgroupPath, "The cgroup that cri-containerd is part of. Cri-containerd is not placed in a cgroup if none is specified.") | ||||
| 	fs.BoolVar(&c.EnableSelinux, "enable-selinux", | ||||
| 		defaults.EnableSelinux, "Enable selinux support. By default not enabled.") | ||||
| 	fs.StringVar(&c.SandboxImage, "sandbox-image", | ||||
| 		defaults.SandboxImage, "The image used by sandbox container.") | ||||
| 	fs.IntVar(&c.StatsCollectPeriod, "stats-collect-period", | ||||
| 		defaults.StatsCollectPeriod, "The period (in seconds) of snapshots stats collection.") | ||||
| 	fs.BoolVar(&c.SystemdCgroup, "systemd-cgroup", | ||||
| 		defaults.SystemdCgroup, "Enables systemd cgroup support. By default not enabled.") | ||||
| 	fs.IntVar(&c.OOMScore, "oom-score", | ||||
| 		defaults.OOMScore, "Adjust the cri-containerd's oom score.") | ||||
| 	fs.BoolVar(&c.EnableProfiling, "profiling", | ||||
| 		defaults.EnableProfiling, "Enable profiling via web interface host:port/debug/pprof/.") | ||||
| 	fs.StringVar(&c.ProfilingPort, "profiling-port", | ||||
| 		defaults.ProfilingPort, "Profiling port for web interface host:port/debug/pprof/.") | ||||
| 	fs.StringVar(&c.ProfilingAddress, "profiling-addr", | ||||
| 		defaults.ProfilingAddress, "Profiling address for web interface host:port/debug/pprof/.") | ||||
| 	fs.BoolVar(&c.EnableIPv6DAD, "enable-ipv6-dad", | ||||
| 		defaults.EnableIPv6DAD, "Enable IPv6 DAD (duplicate address detection) for pod sandbox network. Enabling this will increase pod sandbox start latency by several seconds.") | ||||
| 	fs.Var(&c.Registry, "registry", | ||||
| 		"Registry config for image pull eg --registry=myregistry.io=https://mymirror.io/ --registry=myregistry2.io=https://mymirror2.io/") | ||||
| } | ||||
|  | ||||
| // InitFlags load configurations from config file, and then overwrite with flags. | ||||
| // This function must be called inside `Run`, at that time flags should have been | ||||
| // parsed once. | ||||
| // precedence:  commandline > configfile > default | ||||
| func (c *CRIContainerdOptions) InitFlags(fs *pflag.FlagSet) error { | ||||
| 	// Load default config file if none provided | ||||
| 	if _, err := toml.DecodeFile(c.ConfigFilePath, &c.Config); err != nil { | ||||
| 		// the absence of default config file is normal case. | ||||
| 		if !fs.Changed(configFilePathArgName) && os.IsNotExist(err) { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
| 	// Add this for backward compatibility. | ||||
| 	// TODO(random-liu): Remove this when we no longer support cri-containerd standalone mode. | ||||
| 	c.ContainerdRootDir = c.ContainerdConfig.RootDir | ||||
| 	c.ContainerdEndpoint = c.ContainerdConfig.Endpoint | ||||
|  | ||||
| 	// What is the reason for applying the command line twice? | ||||
| 	// Because the values from command line have the highest priority. | ||||
| 	// The path of toml configuration file if from the command line, | ||||
| 	// and triggers the first parse. | ||||
| 	// The first parse generates the default value and the value from command line at the same time. | ||||
| 	// But the priority of the toml config value is higher than the default value, | ||||
| 	// Without a way to insert the toml config value between the default value and the command line value. | ||||
| 	// We parse twice one for default value, one for commandline value. | ||||
| 	return fs.Parse(os.Args[1:]) | ||||
| } | ||||
|  | ||||
| // PrintDefaultTomlConfig print default toml config of cri-containerd. | ||||
| func PrintDefaultTomlConfig() { | ||||
| 	if err := toml.NewEncoder(os.Stdout).Encode(DefaultConfig()); err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // DefaultConfig returns default configurations of cri-containerd. | ||||
| func DefaultConfig() Config { | ||||
| 	return Config{ | ||||
| 		PluginConfig: PluginConfig{ | ||||
| 			CniConfig: CniConfig{ | ||||
| 				NetworkPluginBinDir:  "/opt/cni/bin", | ||||
| 				NetworkPluginConfDir: "/etc/cni/net.d", | ||||
| 			}, | ||||
| 			ContainerdConfig: ContainerdConfig{ | ||||
| 				Snapshotter:   containerd.DefaultSnapshotter, | ||||
| 				Runtime:       "io.containerd.runtime.v1.linux", | ||||
| 				RuntimeEngine: "", | ||||
| 				RuntimeRoot:   "", | ||||
| 			}, | ||||
| 			StreamServerAddress: "", | ||||
| 			StreamServerPort:    "10010", | ||||
| 			EnableSelinux:       false, | ||||
| 			SandboxImage:        "gcr.io/google_containers/pause:3.0", | ||||
| 			StatsCollectPeriod:  10, | ||||
| 			SystemdCgroup:       false, | ||||
| 			EnableIPv6DAD:       false, | ||||
| 			Registry: Registry{ | ||||
| 				Mirrors: map[string]Mirror{ | ||||
| 					"docker.io": { | ||||
| 						Endpoints: []string{"https://registry-1.docker.io"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		ContainerdRootDir:  "/var/lib/containerd", | ||||
| 		ContainerdEndpoint: "/run/containerd/containerd.sock", | ||||
| 		SocketPath:         "/var/run/cri-containerd.sock", | ||||
| 		RootDir:            "/var/lib/cri-containerd", | ||||
| 		CgroupPath:         "", | ||||
| 		OOMScore:           -999, | ||||
| 		EnableProfiling:    true, | ||||
| 		ProfilingPort:      "10011", | ||||
| 		ProfilingAddress:   "127.0.0.1", | ||||
| 		LogLevel:           "info", | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										88
									
								
								vendor/github.com/containerd/cri-containerd/cmd/cri-containerd/options/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								vendor/github.com/containerd/cri-containerd/cmd/cri-containerd/options/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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 options | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Mirror contains the config related to the registry mirror | ||||
| type Mirror struct { | ||||
| 	Endpoints []string `toml:"endpoint" json:"endpoint,omitempty"` | ||||
| 	// TODO (Abhi) We might need to add auth per namespace. Looks like | ||||
| 	// image auth information is passed by kube itself. | ||||
| } | ||||
|  | ||||
| // Registry is registry settings configured | ||||
| type Registry struct { | ||||
| 	Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors,omitempty"` | ||||
| } | ||||
|  | ||||
| // String returns the string format of registry type | ||||
| func (r *Registry) String() string { | ||||
| 	// Its not used hence return empty string | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // Set validates and converts into the internal registry struct | ||||
| func (r *Registry) Set(s string) error { | ||||
| 	// --registry docker.io=https://mymirror.io,http://mymirror2.io | ||||
| 	// If no option is set then return format error | ||||
| 	if len(s) == 0 { | ||||
| 		return fmt.Errorf("incomplete registry mirror option") | ||||
| 	} | ||||
| 	var mirrors []string | ||||
| 	host := "docker.io" | ||||
| 	opt := strings.Split(s, "=") | ||||
| 	if len(opt) > 1 { | ||||
| 		// If option is set in the format "mynamespace.io=https://mymirror.io,https://mymirror2.io" | ||||
| 		// Then associate the mirror urls for the namespace only" | ||||
| 		host = opt[0] | ||||
| 		mirrors = strings.Split(opt[1], ",") | ||||
| 	} else { | ||||
| 		// If option is set in the format "https://mymirror.io,https://mymirror.io" | ||||
| 		// Then associate mirror against default docker.io namespace | ||||
| 		mirrors = strings.Split(opt[0], ",") | ||||
| 	} | ||||
|  | ||||
| 	// Validate the format of the urls passed | ||||
| 	for _, u := range mirrors { | ||||
| 		_, err := url.Parse(u) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("invalid registry mirror url format %v: %v", u, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if r.Mirrors == nil { | ||||
| 		r.Mirrors = make(map[string]Mirror) | ||||
| 	} | ||||
| 	if _, ok := r.Mirrors[host]; !ok { | ||||
| 		r.Mirrors[host] = Mirror{} | ||||
| 	} | ||||
| 	m := r.Mirrors[host] | ||||
| 	m.Endpoints = append(m.Endpoints, mirrors...) | ||||
| 	r.Mirrors[host] = m | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Type returns a string name for the option type | ||||
| func (r *Registry) Type() string { | ||||
| 	return "list" | ||||
| } | ||||
							
								
								
									
										79
									
								
								vendor/github.com/containerd/cri-containerd/cri.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								vendor/github.com/containerd/cri-containerd/cri.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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 cri | ||||
|  | ||||
| import ( | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	"github.com/containerd/containerd/plugin" | ||||
| 	"github.com/pkg/errors" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/cmd/cri-containerd/options" | ||||
| 	"github.com/containerd/cri-containerd/pkg/server" | ||||
| ) | ||||
|  | ||||
| // TODO(random-liu): Use github.com/pkg/errors for our errors. | ||||
| // Register CRI service plugin | ||||
| func init() { | ||||
| 	config := options.DefaultConfig().PluginConfig | ||||
| 	plugin.Register(&plugin.Registration{ | ||||
| 		Type:   plugin.GRPCPlugin, | ||||
| 		ID:     "cri", | ||||
| 		Config: &config, | ||||
| 		Requires: []plugin.Type{ | ||||
| 			plugin.RuntimePlugin, | ||||
| 			plugin.SnapshotPlugin, | ||||
| 			plugin.TaskMonitorPlugin, | ||||
| 			plugin.DiffPlugin, | ||||
| 			plugin.MetadataPlugin, | ||||
| 			plugin.ContentPlugin, | ||||
| 			plugin.GCPlugin, | ||||
| 		}, | ||||
| 		InitFn: initCRIService, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func initCRIService(ic *plugin.InitContext) (interface{}, error) { | ||||
| 	ctx := ic.Context | ||||
| 	pluginConfig := ic.Config.(*options.PluginConfig) | ||||
| 	c := options.Config{ | ||||
| 		PluginConfig: *pluginConfig, | ||||
| 		// This is a hack. We assume that containerd root directory | ||||
| 		// is one level above plugin directory. | ||||
| 		// TODO(random-liu): Expose containerd config to plugin. | ||||
| 		ContainerdRootDir:  filepath.Dir(ic.Root), | ||||
| 		ContainerdEndpoint: ic.Address, | ||||
| 		RootDir:            ic.Root, | ||||
| 	} | ||||
| 	log.G(ctx).Infof("Start cri plugin with config %+v", c) | ||||
|  | ||||
| 	s, err := server.NewCRIContainerdService(c) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "failed to create CRI service") | ||||
| 	} | ||||
|  | ||||
| 	// Use a goroutine to initialize cri service. The reason is that currently | ||||
| 	// cri service requires containerd to be initialize. | ||||
| 	go func() { | ||||
| 		if err := s.Run(false); err != nil { | ||||
| 			log.G(ctx).WithError(err).Fatal("Failed to run CRI service") | ||||
| 		} | ||||
| 		// TODO(random-liu): Whether and how we can stop containerd. | ||||
| 	}() | ||||
| 	return s, nil | ||||
| } | ||||
							
								
								
									
										34
									
								
								vendor/github.com/containerd/cri-containerd/pkg/annotations/annotations.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/containerd/cri-containerd/pkg/annotations/annotations.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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 annotations | ||||
|  | ||||
| // ContainerType values | ||||
| // Following OCI annotations are used by katacontainers now. | ||||
| // We'll switch to standard secure pod API after it is defined in CRI. | ||||
| const ( | ||||
| 	// ContainerTypeSandbox represents a pod sandbox container | ||||
| 	ContainerTypeSandbox = "sandbox" | ||||
|  | ||||
| 	// ContainerTypeContainer represents a container running within a pod | ||||
| 	ContainerTypeContainer = "container" | ||||
|  | ||||
| 	// ContainerType is the container type (sandbox or container) annotation | ||||
| 	ContainerType = "io.kubernetes.cri.container-type" | ||||
|  | ||||
| 	// SandboxID is the sandbox ID annotation | ||||
| 	SandboxID = "io.kubernetes.cri.sandbox-id" | ||||
| ) | ||||
							
								
								
									
										598
									
								
								vendor/github.com/containerd/cri-containerd/pkg/api/v1/api.pb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										598
									
								
								vendor/github.com/containerd/cri-containerd/pkg/api/v1/api.pb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,598 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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. | ||||
| */ | ||||
|  | ||||
| // Code generated by protoc-gen-gogo. | ||||
| // source: api.proto | ||||
| // DO NOT EDIT! | ||||
|  | ||||
| /* | ||||
| Package api_v1 is a generated protocol buffer package. | ||||
|  | ||||
| It is generated from these files: | ||||
| 	api.proto | ||||
|  | ||||
| It has these top-level messages: | ||||
| 	LoadImageRequest | ||||
| 	LoadImageResponse | ||||
| */ | ||||
| package api_v1 | ||||
|  | ||||
| import proto "github.com/gogo/protobuf/proto" | ||||
| import fmt "fmt" | ||||
| import math "math" | ||||
| import _ "github.com/gogo/protobuf/gogoproto" | ||||
|  | ||||
| import ( | ||||
| 	context "golang.org/x/net/context" | ||||
| 	grpc "google.golang.org/grpc" | ||||
| ) | ||||
|  | ||||
| import strings "strings" | ||||
| import reflect "reflect" | ||||
|  | ||||
| import io "io" | ||||
|  | ||||
| // Reference imports to suppress errors if they are not otherwise used. | ||||
| var _ = proto.Marshal | ||||
| var _ = fmt.Errorf | ||||
| var _ = math.Inf | ||||
|  | ||||
| // This is a compile-time assertion to ensure that this generated file | ||||
| // is compatible with the proto package it is being compiled against. | ||||
| // A compilation error at this line likely means your copy of the | ||||
| // proto package needs to be updated. | ||||
| const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package | ||||
|  | ||||
| type LoadImageRequest struct { | ||||
| 	// FilePath is the absolute path of docker image tarball. | ||||
| 	FilePath string `protobuf:"bytes,1,opt,name=FilePath,proto3" json:"FilePath,omitempty"` | ||||
| } | ||||
|  | ||||
| func (m *LoadImageRequest) Reset()                    { *m = LoadImageRequest{} } | ||||
| func (*LoadImageRequest) ProtoMessage()               {} | ||||
| func (*LoadImageRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{0} } | ||||
|  | ||||
| func (m *LoadImageRequest) GetFilePath() string { | ||||
| 	if m != nil { | ||||
| 		return m.FilePath | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| type LoadImageResponse struct { | ||||
| 	// Images have been loaded. | ||||
| 	Images []string `protobuf:"bytes,1,rep,name=Images" json:"Images,omitempty"` | ||||
| } | ||||
|  | ||||
| func (m *LoadImageResponse) Reset()                    { *m = LoadImageResponse{} } | ||||
| func (*LoadImageResponse) ProtoMessage()               {} | ||||
| func (*LoadImageResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{1} } | ||||
|  | ||||
| func (m *LoadImageResponse) GetImages() []string { | ||||
| 	if m != nil { | ||||
| 		return m.Images | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	proto.RegisterType((*LoadImageRequest)(nil), "api.v1.LoadImageRequest") | ||||
| 	proto.RegisterType((*LoadImageResponse)(nil), "api.v1.LoadImageResponse") | ||||
| } | ||||
|  | ||||
| // Reference imports to suppress errors if they are not otherwise used. | ||||
| var _ context.Context | ||||
| var _ grpc.ClientConn | ||||
|  | ||||
| // This is a compile-time assertion to ensure that this generated file | ||||
| // is compatible with the grpc package it is being compiled against. | ||||
| const _ = grpc.SupportPackageIsVersion4 | ||||
|  | ||||
| // Client API for CRIContainerdService service | ||||
|  | ||||
| type CRIContainerdServiceClient interface { | ||||
| 	// LoadImage loads a image into containerd. | ||||
| 	LoadImage(ctx context.Context, in *LoadImageRequest, opts ...grpc.CallOption) (*LoadImageResponse, error) | ||||
| } | ||||
|  | ||||
| type cRIContainerdServiceClient struct { | ||||
| 	cc *grpc.ClientConn | ||||
| } | ||||
|  | ||||
| func NewCRIContainerdServiceClient(cc *grpc.ClientConn) CRIContainerdServiceClient { | ||||
| 	return &cRIContainerdServiceClient{cc} | ||||
| } | ||||
|  | ||||
| func (c *cRIContainerdServiceClient) LoadImage(ctx context.Context, in *LoadImageRequest, opts ...grpc.CallOption) (*LoadImageResponse, error) { | ||||
| 	out := new(LoadImageResponse) | ||||
| 	err := grpc.Invoke(ctx, "/api.v1.CRIContainerdService/LoadImage", in, out, c.cc, opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return out, nil | ||||
| } | ||||
|  | ||||
| // Server API for CRIContainerdService service | ||||
|  | ||||
| type CRIContainerdServiceServer interface { | ||||
| 	// LoadImage loads a image into containerd. | ||||
| 	LoadImage(context.Context, *LoadImageRequest) (*LoadImageResponse, error) | ||||
| } | ||||
|  | ||||
| func RegisterCRIContainerdServiceServer(s *grpc.Server, srv CRIContainerdServiceServer) { | ||||
| 	s.RegisterService(&_CRIContainerdService_serviceDesc, srv) | ||||
| } | ||||
|  | ||||
| func _CRIContainerdService_LoadImage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||
| 	in := new(LoadImageRequest) | ||||
| 	if err := dec(in); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if interceptor == nil { | ||||
| 		return srv.(CRIContainerdServiceServer).LoadImage(ctx, in) | ||||
| 	} | ||||
| 	info := &grpc.UnaryServerInfo{ | ||||
| 		Server:     srv, | ||||
| 		FullMethod: "/api.v1.CRIContainerdService/LoadImage", | ||||
| 	} | ||||
| 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||
| 		return srv.(CRIContainerdServiceServer).LoadImage(ctx, req.(*LoadImageRequest)) | ||||
| 	} | ||||
| 	return interceptor(ctx, in, info, handler) | ||||
| } | ||||
|  | ||||
| var _CRIContainerdService_serviceDesc = grpc.ServiceDesc{ | ||||
| 	ServiceName: "api.v1.CRIContainerdService", | ||||
| 	HandlerType: (*CRIContainerdServiceServer)(nil), | ||||
| 	Methods: []grpc.MethodDesc{ | ||||
| 		{ | ||||
| 			MethodName: "LoadImage", | ||||
| 			Handler:    _CRIContainerdService_LoadImage_Handler, | ||||
| 		}, | ||||
| 	}, | ||||
| 	Streams:  []grpc.StreamDesc{}, | ||||
| 	Metadata: "api.proto", | ||||
| } | ||||
|  | ||||
| func (m *LoadImageRequest) Marshal() (dAtA []byte, err error) { | ||||
| 	size := m.Size() | ||||
| 	dAtA = make([]byte, size) | ||||
| 	n, err := m.MarshalTo(dAtA) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return dAtA[:n], nil | ||||
| } | ||||
|  | ||||
| func (m *LoadImageRequest) MarshalTo(dAtA []byte) (int, error) { | ||||
| 	var i int | ||||
| 	_ = i | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	if len(m.FilePath) > 0 { | ||||
| 		dAtA[i] = 0xa | ||||
| 		i++ | ||||
| 		i = encodeVarintApi(dAtA, i, uint64(len(m.FilePath))) | ||||
| 		i += copy(dAtA[i:], m.FilePath) | ||||
| 	} | ||||
| 	return i, nil | ||||
| } | ||||
|  | ||||
| func (m *LoadImageResponse) Marshal() (dAtA []byte, err error) { | ||||
| 	size := m.Size() | ||||
| 	dAtA = make([]byte, size) | ||||
| 	n, err := m.MarshalTo(dAtA) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return dAtA[:n], nil | ||||
| } | ||||
|  | ||||
| func (m *LoadImageResponse) MarshalTo(dAtA []byte) (int, error) { | ||||
| 	var i int | ||||
| 	_ = i | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	if len(m.Images) > 0 { | ||||
| 		for _, s := range m.Images { | ||||
| 			dAtA[i] = 0xa | ||||
| 			i++ | ||||
| 			l = len(s) | ||||
| 			for l >= 1<<7 { | ||||
| 				dAtA[i] = uint8(uint64(l)&0x7f | 0x80) | ||||
| 				l >>= 7 | ||||
| 				i++ | ||||
| 			} | ||||
| 			dAtA[i] = uint8(l) | ||||
| 			i++ | ||||
| 			i += copy(dAtA[i:], s) | ||||
| 		} | ||||
| 	} | ||||
| 	return i, nil | ||||
| } | ||||
|  | ||||
| func encodeFixed64Api(dAtA []byte, offset int, v uint64) int { | ||||
| 	dAtA[offset] = uint8(v) | ||||
| 	dAtA[offset+1] = uint8(v >> 8) | ||||
| 	dAtA[offset+2] = uint8(v >> 16) | ||||
| 	dAtA[offset+3] = uint8(v >> 24) | ||||
| 	dAtA[offset+4] = uint8(v >> 32) | ||||
| 	dAtA[offset+5] = uint8(v >> 40) | ||||
| 	dAtA[offset+6] = uint8(v >> 48) | ||||
| 	dAtA[offset+7] = uint8(v >> 56) | ||||
| 	return offset + 8 | ||||
| } | ||||
| func encodeFixed32Api(dAtA []byte, offset int, v uint32) int { | ||||
| 	dAtA[offset] = uint8(v) | ||||
| 	dAtA[offset+1] = uint8(v >> 8) | ||||
| 	dAtA[offset+2] = uint8(v >> 16) | ||||
| 	dAtA[offset+3] = uint8(v >> 24) | ||||
| 	return offset + 4 | ||||
| } | ||||
| func encodeVarintApi(dAtA []byte, offset int, v uint64) int { | ||||
| 	for v >= 1<<7 { | ||||
| 		dAtA[offset] = uint8(v&0x7f | 0x80) | ||||
| 		v >>= 7 | ||||
| 		offset++ | ||||
| 	} | ||||
| 	dAtA[offset] = uint8(v) | ||||
| 	return offset + 1 | ||||
| } | ||||
| func (m *LoadImageRequest) Size() (n int) { | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	l = len(m.FilePath) | ||||
| 	if l > 0 { | ||||
| 		n += 1 + l + sovApi(uint64(l)) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
|  | ||||
| func (m *LoadImageResponse) Size() (n int) { | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	if len(m.Images) > 0 { | ||||
| 		for _, s := range m.Images { | ||||
| 			l = len(s) | ||||
| 			n += 1 + l + sovApi(uint64(l)) | ||||
| 		} | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
|  | ||||
| func sovApi(x uint64) (n int) { | ||||
| 	for { | ||||
| 		n++ | ||||
| 		x >>= 7 | ||||
| 		if x == 0 { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| func sozApi(x uint64) (n int) { | ||||
| 	return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63)))) | ||||
| } | ||||
| func (this *LoadImageRequest) String() string { | ||||
| 	if this == nil { | ||||
| 		return "nil" | ||||
| 	} | ||||
| 	s := strings.Join([]string{`&LoadImageRequest{`, | ||||
| 		`FilePath:` + fmt.Sprintf("%v", this.FilePath) + `,`, | ||||
| 		`}`, | ||||
| 	}, "") | ||||
| 	return s | ||||
| } | ||||
| func (this *LoadImageResponse) String() string { | ||||
| 	if this == nil { | ||||
| 		return "nil" | ||||
| 	} | ||||
| 	s := strings.Join([]string{`&LoadImageResponse{`, | ||||
| 		`Images:` + fmt.Sprintf("%v", this.Images) + `,`, | ||||
| 		`}`, | ||||
| 	}, "") | ||||
| 	return s | ||||
| } | ||||
| func valueToStringApi(v interface{}) string { | ||||
| 	rv := reflect.ValueOf(v) | ||||
| 	if rv.IsNil() { | ||||
| 		return "nil" | ||||
| 	} | ||||
| 	pv := reflect.Indirect(rv).Interface() | ||||
| 	return fmt.Sprintf("*%v", pv) | ||||
| } | ||||
| func (m *LoadImageRequest) Unmarshal(dAtA []byte) error { | ||||
| 	l := len(dAtA) | ||||
| 	iNdEx := 0 | ||||
| 	for iNdEx < l { | ||||
| 		preIndex := iNdEx | ||||
| 		var wire uint64 | ||||
| 		for shift := uint(0); ; shift += 7 { | ||||
| 			if shift >= 64 { | ||||
| 				return ErrIntOverflowApi | ||||
| 			} | ||||
| 			if iNdEx >= l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			b := dAtA[iNdEx] | ||||
| 			iNdEx++ | ||||
| 			wire |= (uint64(b) & 0x7F) << shift | ||||
| 			if b < 0x80 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		fieldNum := int32(wire >> 3) | ||||
| 		wireType := int(wire & 0x7) | ||||
| 		if wireType == 4 { | ||||
| 			return fmt.Errorf("proto: LoadImageRequest: wiretype end group for non-group") | ||||
| 		} | ||||
| 		if fieldNum <= 0 { | ||||
| 			return fmt.Errorf("proto: LoadImageRequest: illegal tag %d (wire type %d)", fieldNum, wire) | ||||
| 		} | ||||
| 		switch fieldNum { | ||||
| 		case 1: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field FilePath", wireType) | ||||
| 			} | ||||
| 			var stringLen uint64 | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowApi | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				stringLen |= (uint64(b) & 0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			intStringLen := int(stringLen) | ||||
| 			if intStringLen < 0 { | ||||
| 				return ErrInvalidLengthApi | ||||
| 			} | ||||
| 			postIndex := iNdEx + intStringLen | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.FilePath = string(dAtA[iNdEx:postIndex]) | ||||
| 			iNdEx = postIndex | ||||
| 		default: | ||||
| 			iNdEx = preIndex | ||||
| 			skippy, err := skipApi(dAtA[iNdEx:]) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if skippy < 0 { | ||||
| 				return ErrInvalidLengthApi | ||||
| 			} | ||||
| 			if (iNdEx + skippy) > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			iNdEx += skippy | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if iNdEx > l { | ||||
| 		return io.ErrUnexpectedEOF | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| func (m *LoadImageResponse) Unmarshal(dAtA []byte) error { | ||||
| 	l := len(dAtA) | ||||
| 	iNdEx := 0 | ||||
| 	for iNdEx < l { | ||||
| 		preIndex := iNdEx | ||||
| 		var wire uint64 | ||||
| 		for shift := uint(0); ; shift += 7 { | ||||
| 			if shift >= 64 { | ||||
| 				return ErrIntOverflowApi | ||||
| 			} | ||||
| 			if iNdEx >= l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			b := dAtA[iNdEx] | ||||
| 			iNdEx++ | ||||
| 			wire |= (uint64(b) & 0x7F) << shift | ||||
| 			if b < 0x80 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		fieldNum := int32(wire >> 3) | ||||
| 		wireType := int(wire & 0x7) | ||||
| 		if wireType == 4 { | ||||
| 			return fmt.Errorf("proto: LoadImageResponse: wiretype end group for non-group") | ||||
| 		} | ||||
| 		if fieldNum <= 0 { | ||||
| 			return fmt.Errorf("proto: LoadImageResponse: illegal tag %d (wire type %d)", fieldNum, wire) | ||||
| 		} | ||||
| 		switch fieldNum { | ||||
| 		case 1: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Images", wireType) | ||||
| 			} | ||||
| 			var stringLen uint64 | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowApi | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				stringLen |= (uint64(b) & 0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			intStringLen := int(stringLen) | ||||
| 			if intStringLen < 0 { | ||||
| 				return ErrInvalidLengthApi | ||||
| 			} | ||||
| 			postIndex := iNdEx + intStringLen | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.Images = append(m.Images, string(dAtA[iNdEx:postIndex])) | ||||
| 			iNdEx = postIndex | ||||
| 		default: | ||||
| 			iNdEx = preIndex | ||||
| 			skippy, err := skipApi(dAtA[iNdEx:]) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if skippy < 0 { | ||||
| 				return ErrInvalidLengthApi | ||||
| 			} | ||||
| 			if (iNdEx + skippy) > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			iNdEx += skippy | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if iNdEx > l { | ||||
| 		return io.ErrUnexpectedEOF | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| func skipApi(dAtA []byte) (n int, err error) { | ||||
| 	l := len(dAtA) | ||||
| 	iNdEx := 0 | ||||
| 	for iNdEx < l { | ||||
| 		var wire uint64 | ||||
| 		for shift := uint(0); ; shift += 7 { | ||||
| 			if shift >= 64 { | ||||
| 				return 0, ErrIntOverflowApi | ||||
| 			} | ||||
| 			if iNdEx >= l { | ||||
| 				return 0, io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			b := dAtA[iNdEx] | ||||
| 			iNdEx++ | ||||
| 			wire |= (uint64(b) & 0x7F) << shift | ||||
| 			if b < 0x80 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		wireType := int(wire & 0x7) | ||||
| 		switch wireType { | ||||
| 		case 0: | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return 0, ErrIntOverflowApi | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return 0, io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				iNdEx++ | ||||
| 				if dAtA[iNdEx-1] < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			return iNdEx, nil | ||||
| 		case 1: | ||||
| 			iNdEx += 8 | ||||
| 			return iNdEx, nil | ||||
| 		case 2: | ||||
| 			var length int | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return 0, ErrIntOverflowApi | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return 0, io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				length |= (int(b) & 0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			iNdEx += length | ||||
| 			if length < 0 { | ||||
| 				return 0, ErrInvalidLengthApi | ||||
| 			} | ||||
| 			return iNdEx, nil | ||||
| 		case 3: | ||||
| 			for { | ||||
| 				var innerWire uint64 | ||||
| 				var start int = iNdEx | ||||
| 				for shift := uint(0); ; shift += 7 { | ||||
| 					if shift >= 64 { | ||||
| 						return 0, ErrIntOverflowApi | ||||
| 					} | ||||
| 					if iNdEx >= l { | ||||
| 						return 0, io.ErrUnexpectedEOF | ||||
| 					} | ||||
| 					b := dAtA[iNdEx] | ||||
| 					iNdEx++ | ||||
| 					innerWire |= (uint64(b) & 0x7F) << shift | ||||
| 					if b < 0x80 { | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 				innerWireType := int(innerWire & 0x7) | ||||
| 				if innerWireType == 4 { | ||||
| 					break | ||||
| 				} | ||||
| 				next, err := skipApi(dAtA[start:]) | ||||
| 				if err != nil { | ||||
| 					return 0, err | ||||
| 				} | ||||
| 				iNdEx = start + next | ||||
| 			} | ||||
| 			return iNdEx, nil | ||||
| 		case 4: | ||||
| 			return iNdEx, nil | ||||
| 		case 5: | ||||
| 			iNdEx += 4 | ||||
| 			return iNdEx, nil | ||||
| 		default: | ||||
| 			return 0, fmt.Errorf("proto: illegal wireType %d", wireType) | ||||
| 		} | ||||
| 	} | ||||
| 	panic("unreachable") | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling") | ||||
| 	ErrIntOverflowApi   = fmt.Errorf("proto: integer overflow") | ||||
| ) | ||||
|  | ||||
| func init() { proto.RegisterFile("api.proto", fileDescriptorApi) } | ||||
|  | ||||
| var fileDescriptorApi = []byte{ | ||||
| 	// 223 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x2c, 0xc8, 0xd4, | ||||
| 	0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x03, 0x31, 0xcb, 0x0c, 0xa5, 0x74, 0xd3, 0x33, 0x4b, | ||||
| 	0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0xd3, 0xf3, 0xd3, 0xf3, 0xf5, 0xc1, 0xd2, 0x49, | ||||
| 	0xa5, 0x69, 0x60, 0x1e, 0x98, 0x03, 0x66, 0x41, 0xb4, 0x29, 0xe9, 0x71, 0x09, 0xf8, 0xe4, 0x27, | ||||
| 	0xa6, 0x78, 0xe6, 0x26, 0xa6, 0xa7, 0x06, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0x49, 0x71, | ||||
| 	0x71, 0xb8, 0x65, 0xe6, 0xa4, 0x06, 0x24, 0x96, 0x64, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, | ||||
| 	0xc1, 0xf9, 0x4a, 0xda, 0x5c, 0x82, 0x48, 0xea, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0xc4, | ||||
| 	0xb8, 0xd8, 0xc0, 0x02, 0xc5, 0x12, 0x8c, 0x0a, 0xcc, 0x1a, 0x9c, 0x41, 0x50, 0x9e, 0x51, 0x14, | ||||
| 	0x97, 0x88, 0x73, 0x90, 0xa7, 0x73, 0x7e, 0x5e, 0x49, 0x62, 0x66, 0x5e, 0x6a, 0x51, 0x4a, 0x70, | ||||
| 	0x6a, 0x51, 0x59, 0x66, 0x72, 0xaa, 0x90, 0x13, 0x17, 0x27, 0xdc, 0x10, 0x21, 0x09, 0x3d, 0x88, | ||||
| 	0xcb, 0xf5, 0xd0, 0xdd, 0x21, 0x25, 0x89, 0x45, 0x06, 0x62, 0xa3, 0x12, 0x83, 0x93, 0xcc, 0x89, | ||||
| 	0x87, 0x72, 0x8c, 0x37, 0x1e, 0xca, 0x31, 0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, | ||||
| 	0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0x48, 0x62, 0x03, 0xfb, | ||||
| 	0xce, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x6a, 0xfe, 0x35, 0x81, 0x21, 0x01, 0x00, 0x00, | ||||
| } | ||||
							
								
								
									
										30
									
								
								vendor/github.com/containerd/cri-containerd/pkg/api/v1/api.proto
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/containerd/cri-containerd/pkg/api/v1/api.proto
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| // To regenerate api.pb.go run `make proto`hack/update-generated-runtime.sh | ||||
| syntax = 'proto3'; | ||||
|  | ||||
| package api.v1; | ||||
|  | ||||
| import "github.com/gogo/protobuf/gogoproto/gogo.proto"; | ||||
|  | ||||
| option (gogoproto.goproto_stringer_all) = false; | ||||
| option (gogoproto.stringer_all) =  true; | ||||
| option (gogoproto.goproto_getters_all) = true; | ||||
| option (gogoproto.marshaler_all) = true; | ||||
| option (gogoproto.sizer_all) = true; | ||||
| option (gogoproto.unmarshaler_all) = true; | ||||
| option (gogoproto.goproto_unrecognized_all) = false; | ||||
|  | ||||
| // CRIContainerdService defines non-CRI APIs for cri-containerd. | ||||
| service CRIContainerdService{ | ||||
|     // LoadImage loads a image into containerd. | ||||
|     rpc LoadImage(LoadImageRequest) returns (LoadImageResponse) {} | ||||
| } | ||||
|  | ||||
| message LoadImageRequest { | ||||
|     // FilePath is the absolute path of docker image tarball. | ||||
|     string FilePath = 1; | ||||
| } | ||||
|  | ||||
| message LoadImageResponse { | ||||
|     // Images have been loaded. | ||||
|     repeated string Images = 1; | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/github.com/containerd/cri-containerd/pkg/atomic/atomic_boolean.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/containerd/cri-containerd/pkg/atomic/atomic_boolean.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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 atomic | ||||
|  | ||||
| import "sync/atomic" | ||||
|  | ||||
| // Bool is an atomic Boolean, | ||||
| // Its methods are all atomic, thus safe to be called by | ||||
| // multiple goroutines simultaneously. | ||||
| type Bool interface { | ||||
| 	Set() | ||||
| 	Unset() | ||||
| 	IsSet() bool | ||||
| } | ||||
|  | ||||
| // NewBool creates an Bool with given default value | ||||
| func NewBool(ok bool) Bool { | ||||
| 	ab := new(atomicBool) | ||||
| 	if ok { | ||||
| 		ab.Set() | ||||
| 	} | ||||
| 	return ab | ||||
| } | ||||
|  | ||||
| type atomicBool int32 | ||||
|  | ||||
| // Set sets the Boolean to true | ||||
| func (ab *atomicBool) Set() { | ||||
| 	atomic.StoreInt32((*int32)(ab), 1) | ||||
| } | ||||
|  | ||||
| // Unset sets the Boolean to false | ||||
| func (ab *atomicBool) Unset() { | ||||
| 	atomic.StoreInt32((*int32)(ab), 0) | ||||
| } | ||||
|  | ||||
| // IsSet returns whether the Boolean is true | ||||
| func (ab *atomicBool) IsSet() bool { | ||||
| 	return atomic.LoadInt32((*int32)(ab)) == 1 | ||||
| } | ||||
							
								
								
									
										283
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/importer/importer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/importer/importer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,283 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 importer | ||||
|  | ||||
| import ( | ||||
| 	"archive/tar" | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/content" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/images" | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	"github.com/opencontainers/go-digest" | ||||
| 	"github.com/opencontainers/image-spec/specs-go" | ||||
| 	ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"github.com/pkg/errors" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/util" | ||||
| ) | ||||
|  | ||||
| // This code reuses the docker import code from containerd/containerd#1602. | ||||
| // It has been simplified a bit and garbage collection support was added. | ||||
| // If a library/helper is added to containerd in the future, we should switch to it. | ||||
|  | ||||
| // manifestDotJSON is an entry in manifest.json. | ||||
| type manifestDotJSON struct { | ||||
| 	Config   string | ||||
| 	RepoTags []string | ||||
| 	Layers   []string | ||||
| 	// Parent is unsupported | ||||
| 	Parent string | ||||
| } | ||||
|  | ||||
| // isLayerTar returns true if name is like "deadbeeddeadbeef/layer.tar" | ||||
| func isLayerTar(name string) bool { | ||||
| 	slashes := len(strings.Split(name, "/")) | ||||
| 	return slashes == 2 && strings.HasSuffix(name, "/layer.tar") | ||||
| } | ||||
|  | ||||
| // isDotJSON returns true if name is like "deadbeefdeadbeef.json" | ||||
| func isDotJSON(name string) bool { | ||||
| 	slashes := len(strings.Split(name, "/")) | ||||
| 	return slashes == 1 && strings.HasSuffix(name, ".json") | ||||
| } | ||||
|  | ||||
| type imageConfig struct { | ||||
| 	desc ocispec.Descriptor | ||||
| 	img  ocispec.Image | ||||
| } | ||||
|  | ||||
| // Import implements Docker Image Spec v1.1. | ||||
| // An image MUST have `manifest.json`. | ||||
| // `repositories` file in Docker Image Spec v1.0 is not supported (yet). | ||||
| // Also, the current implementation assumes the implicit file name convention, | ||||
| // which is not explicitly documented in the spec. (e.g. deadbeef/layer.tar) | ||||
| // It returns a group of image references successfully loaded. | ||||
| func Import(ctx context.Context, client *containerd.Client, reader io.Reader) (_ []string, retErr error) { | ||||
| 	ctx, done, err := client.WithLease(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer done() // nolint: errcheck | ||||
|  | ||||
| 	cs := client.ContentStore() | ||||
| 	is := client.ImageService() | ||||
|  | ||||
| 	tr := tar.NewReader(reader) | ||||
| 	var ( | ||||
| 		mfsts   []manifestDotJSON | ||||
| 		layers  = make(map[string]ocispec.Descriptor) // key: filename (deadbeeddeadbeef/layer.tar) | ||||
| 		configs = make(map[string]imageConfig)        // key: filename (deadbeeddeadbeef.json) | ||||
| 	) | ||||
| 	for { | ||||
| 		hdr, err := tr.Next() | ||||
| 		if err == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return nil, errors.Wrap(err, "get next file") | ||||
| 		} | ||||
| 		if hdr.Typeflag != tar.TypeReg && hdr.Typeflag != tar.TypeRegA { | ||||
| 			continue | ||||
| 		} | ||||
| 		if hdr.Name == "manifest.json" { | ||||
| 			mfsts, err = onUntarManifestJSON(tr) | ||||
| 			if err != nil { | ||||
| 				return nil, errors.Wrapf(err, "untar manifest %q", hdr.Name) | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		if isLayerTar(hdr.Name) { | ||||
| 			desc, err := onUntarLayerTar(ctx, tr, cs, hdr.Name, hdr.Size) | ||||
| 			if err != nil { | ||||
| 				return nil, errors.Wrapf(err, "untar layer %q", hdr.Name) | ||||
| 			} | ||||
| 			layers[hdr.Name] = *desc | ||||
| 			continue | ||||
| 		} | ||||
| 		if isDotJSON(hdr.Name) { | ||||
| 			c, err := onUntarDotJSON(ctx, tr, cs, hdr.Name, hdr.Size) | ||||
| 			if err != nil { | ||||
| 				return nil, errors.Wrapf(err, "untar config %q", hdr.Name) | ||||
| 			} | ||||
| 			configs[hdr.Name] = *c | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
| 	var refs []string | ||||
| 	defer func() { | ||||
| 		if retErr == nil { | ||||
| 			return | ||||
| 		} | ||||
| 		// TODO(random-liu): Consider whether we should keep images already imported | ||||
| 		// even when there is an error. | ||||
| 		for _, ref := range refs { | ||||
| 			if err := is.Delete(ctx, ref); err != nil { | ||||
| 				log.G(ctx).WithError(err).Errorf("Failed to remove image %q", ref) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| 	for _, mfst := range mfsts { | ||||
| 		config, ok := configs[mfst.Config] | ||||
| 		if !ok { | ||||
| 			return refs, errors.Errorf("image config %q not found", mfst.Config) | ||||
| 		} | ||||
| 		schema2Manifest, err := makeDockerSchema2Manifest(mfst, config, layers) | ||||
| 		if err != nil { | ||||
| 			return refs, errors.Wrap(err, "create docker manifest") | ||||
| 		} | ||||
| 		desc, err := writeDockerSchema2Manifest(ctx, cs, *schema2Manifest, config.img.Architecture, config.img.OS) | ||||
| 		if err != nil { | ||||
| 			return refs, errors.Wrap(err, "write docker manifest") | ||||
| 		} | ||||
|  | ||||
| 		for _, ref := range mfst.RepoTags { | ||||
| 			normalized, err := util.NormalizeImageRef(ref) | ||||
| 			if err != nil { | ||||
| 				return refs, errors.Wrapf(err, "normalize image ref %q", ref) | ||||
| 			} | ||||
| 			ref = normalized.String() | ||||
| 			imgrec := images.Image{ | ||||
| 				Name:   ref, | ||||
| 				Target: *desc, | ||||
| 			} | ||||
| 			if _, err := is.Create(ctx, imgrec); err != nil { | ||||
| 				if !errdefs.IsAlreadyExists(err) { | ||||
| 					return refs, errors.Wrapf(err, "create image ref %+v", imgrec) | ||||
| 				} | ||||
|  | ||||
| 				_, err := is.Update(ctx, imgrec) | ||||
| 				if err != nil { | ||||
| 					return refs, errors.Wrapf(err, "update image ref %+v", imgrec) | ||||
| 				} | ||||
| 			} | ||||
| 			refs = append(refs, ref) | ||||
| 		} | ||||
| 	} | ||||
| 	return refs, nil | ||||
| } | ||||
|  | ||||
| func makeDockerSchema2Manifest(mfst manifestDotJSON, config imageConfig, layers map[string]ocispec.Descriptor) (*ocispec.Manifest, error) { | ||||
| 	manifest := ocispec.Manifest{ | ||||
| 		Versioned: specs.Versioned{ | ||||
| 			SchemaVersion: 2, | ||||
| 		}, | ||||
| 		Config: config.desc, | ||||
| 	} | ||||
| 	for _, f := range mfst.Layers { | ||||
| 		desc, ok := layers[f] | ||||
| 		if !ok { | ||||
| 			return nil, errors.Errorf("layer %q not found", f) | ||||
| 		} | ||||
| 		manifest.Layers = append(manifest.Layers, desc) | ||||
| 	} | ||||
| 	return &manifest, nil | ||||
| } | ||||
|  | ||||
| func writeDockerSchema2Manifest(ctx context.Context, cs content.Ingester, manifest ocispec.Manifest, arch, os string) (*ocispec.Descriptor, error) { | ||||
| 	manifestBytes, err := json.Marshal(manifest) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	manifestBytesR := bytes.NewReader(manifestBytes) | ||||
| 	manifestDigest := digest.FromBytes(manifestBytes) | ||||
| 	labels := map[string]string{} | ||||
| 	labels["containerd.io/gc.ref.content.0"] = manifest.Config.Digest.String() | ||||
| 	for i, ch := range manifest.Layers { | ||||
| 		labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i+1)] = ch.Digest.String() | ||||
| 	} | ||||
| 	if err := content.WriteBlob(ctx, cs, "manifest-"+manifestDigest.String(), manifestBytesR, | ||||
| 		int64(len(manifestBytes)), manifestDigest, content.WithLabels(labels)); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	desc := &ocispec.Descriptor{ | ||||
| 		MediaType: images.MediaTypeDockerSchema2Manifest, | ||||
| 		Digest:    manifestDigest, | ||||
| 		Size:      int64(len(manifestBytes)), | ||||
| 	} | ||||
| 	if arch != "" || os != "" { | ||||
| 		desc.Platform = &ocispec.Platform{ | ||||
| 			Architecture: arch, | ||||
| 			OS:           os, | ||||
| 		} | ||||
| 	} | ||||
| 	return desc, nil | ||||
| } | ||||
|  | ||||
| func onUntarManifestJSON(r io.Reader) ([]manifestDotJSON, error) { | ||||
| 	// name: "manifest.json" | ||||
| 	b, err := ioutil.ReadAll(r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var mfsts []manifestDotJSON | ||||
| 	if err := json.Unmarshal(b, &mfsts); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return mfsts, nil | ||||
| } | ||||
|  | ||||
| func onUntarLayerTar(ctx context.Context, r io.Reader, cs content.Ingester, name string, size int64) (*ocispec.Descriptor, error) { | ||||
| 	// name is like "deadbeeddeadbeef/layer.tar" ( guaranteed by isLayerTar() ) | ||||
| 	split := strings.Split(name, "/") | ||||
| 	// note: split[0] is not expected digest here | ||||
| 	cw, err := cs.Writer(ctx, "layer-"+split[0], size, "") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer cw.Close() | ||||
| 	if err := content.Copy(ctx, cw, r, size, ""); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &ocispec.Descriptor{ | ||||
| 		MediaType: images.MediaTypeDockerSchema2Layer, | ||||
| 		Size:      size, | ||||
| 		Digest:    cw.Digest(), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func onUntarDotJSON(ctx context.Context, r io.Reader, cs content.Ingester, name string, size int64) (*imageConfig, error) { | ||||
| 	config := imageConfig{} | ||||
| 	config.desc.MediaType = images.MediaTypeDockerSchema2Config | ||||
| 	config.desc.Size = size | ||||
| 	// name is like "deadbeeddeadbeef.json" ( guaranteed by is DotJSON() ) | ||||
| 	split := strings.Split(name, ".") | ||||
| 	cw, err := cs.Writer(ctx, "config-"+split[0], size, "") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer cw.Close() | ||||
| 	var buf bytes.Buffer | ||||
| 	tr := io.TeeReader(r, &buf) | ||||
| 	if err := content.Copy(ctx, cw, tr, size, ""); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	config.desc.Digest = cw.Digest() | ||||
| 	if err := json.Unmarshal(buf.Bytes(), &config.img); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &config, nil | ||||
| } | ||||
							
								
								
									
										127
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/opts/container.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/opts/container.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 opts | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/containers" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/mount" | ||||
| 	"github.com/containerd/continuity/fs" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // WithNewSnapshot wraps `containerd.WithNewSnapshot` so that if creating the | ||||
| // snapshot fails we make sure the image is actually unpacked and and retry. | ||||
| func WithNewSnapshot(id string, i containerd.Image) containerd.NewContainerOpts { | ||||
| 	f := containerd.WithNewSnapshot(id, i) | ||||
| 	return func(ctx context.Context, client *containerd.Client, c *containers.Container) error { | ||||
| 		if err := f(ctx, client, c); err != nil { | ||||
| 			if !errdefs.IsNotFound(err) { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			if err := i.Unpack(ctx, c.Snapshotter); err != nil { | ||||
| 				return errors.Wrap(err, "error unpacking image") | ||||
| 			} | ||||
| 			return f(ctx, client, c) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithVolumes copies ownership of volume in rootfs to its corresponding host path. | ||||
| // It doesn't update runtime spec. | ||||
| // The passed in map is a host path to container path map for all volumes. | ||||
| // TODO(random-liu): Figure out whether we need to copy volume content. | ||||
| func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts { | ||||
| 	return func(ctx context.Context, client *containerd.Client, c *containers.Container) (err error) { | ||||
| 		if c.Snapshotter == "" { | ||||
| 			return errors.New("no snapshotter set for container") | ||||
| 		} | ||||
| 		if c.SnapshotKey == "" { | ||||
| 			return errors.New("rootfs not created for container") | ||||
| 		} | ||||
| 		snapshotter := client.SnapshotService(c.Snapshotter) | ||||
| 		mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		root, err := ioutil.TempDir("", "ctd-volume") | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		// We change RemoveAll to Remove so that we either leak a temp dir | ||||
| 		// if it fails but not RM snapshot data. | ||||
| 		// refer to https://github.com/containerd/containerd/pull/1868 | ||||
| 		// https://github.com/containerd/containerd/pull/1785 | ||||
| 		defer os.Remove(root) // nolint: errcheck | ||||
| 		if err := mount.All(mounts, root); err != nil { | ||||
| 			return errors.Wrap(err, "failed to mount") | ||||
| 		} | ||||
| 		defer func() { | ||||
| 			if uerr := mount.Unmount(root, 0); uerr != nil { | ||||
| 				logrus.WithError(uerr).Errorf("Failed to unmount snapshot %q", c.SnapshotKey) | ||||
| 				if err == nil { | ||||
| 					err = uerr | ||||
| 				} | ||||
| 			} | ||||
| 		}() | ||||
|  | ||||
| 		for host, volume := range volumeMounts { | ||||
| 			src := filepath.Join(root, volume) | ||||
| 			if _, err := os.Stat(src); err != nil { | ||||
| 				if os.IsNotExist(err) { | ||||
| 					// Skip copying directory if it does not exist. | ||||
| 					continue | ||||
| 				} | ||||
| 				return errors.Wrap(err, "stat volume in rootfs") | ||||
| 			} | ||||
| 			if err := copyExistingContents(src, host); err != nil { | ||||
| 				return errors.Wrap(err, "taking runtime copy of volume") | ||||
| 			} | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // copyExistingContents copies from the source to the destination and | ||||
| // ensures the ownership is appropriately set. | ||||
| func copyExistingContents(source, destination string) error { | ||||
| 	srcList, err := ioutil.ReadDir(source) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if len(srcList) == 0 { | ||||
| 		// Skip copying if source directory is empty. | ||||
| 		return nil | ||||
| 	} | ||||
| 	dstList, err := ioutil.ReadDir(destination) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if len(dstList) != 0 { | ||||
| 		return errors.Errorf("volume at %q is not initially empty", destination) | ||||
| 	} | ||||
| 	return fs.CopyDir(destination, source) | ||||
| } | ||||
							
								
								
									
										38
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/opts/task.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/opts/task.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 opts | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/linux/runctypes" | ||||
| ) | ||||
|  | ||||
| // WithContainerdShimCgroup returns function that sets the containerd | ||||
| // shim cgroup path | ||||
| func WithContainerdShimCgroup(path string) containerd.NewTaskOpts { | ||||
| 	return func(_ context.Context, _ *containerd.Client, r *containerd.TaskInfo) error { | ||||
| 		r.Options = &runctypes.CreateOptions{ | ||||
| 			ShimCgroup: path, | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //TODO: Since Options is an interface different WithXXX will be needed to set different | ||||
| // combinations of CreateOptions. | ||||
							
								
								
									
										202
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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. | ||||
| */ | ||||
|  | ||||
| // The corresponding file is in containerd/remote/docker/ | ||||
| // This package can be removed once a more feasible and hollistic resolver | ||||
| // is finalized in containerd. | ||||
|  | ||||
| package resolver | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| type authenticationScheme byte | ||||
|  | ||||
| const ( | ||||
| 	basicAuth  authenticationScheme = 1 << iota // Defined in RFC 7617 | ||||
| 	digestAuth                                  // Defined in RFC 7616 | ||||
| 	bearerAuth                                  // Defined in RFC 6750 | ||||
| ) | ||||
|  | ||||
| // challenge carries information from a WWW-Authenticate response header. | ||||
| // See RFC 2617. | ||||
| type challenge struct { | ||||
| 	// scheme is the auth-scheme according to RFC 2617 | ||||
| 	scheme authenticationScheme | ||||
|  | ||||
| 	// parameters are the auth-params according to RFC 2617 | ||||
| 	parameters map[string]string | ||||
| } | ||||
|  | ||||
| type byScheme []challenge | ||||
|  | ||||
| func (bs byScheme) Len() int      { return len(bs) } | ||||
| func (bs byScheme) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] } | ||||
|  | ||||
| // Sort in priority order: token > digest > basic | ||||
| func (bs byScheme) Less(i, j int) bool { return bs[i].scheme > bs[j].scheme } | ||||
|  | ||||
| // Octet types from RFC 2616. | ||||
| 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.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) | ||||
| 		if strings.ContainsRune(" \t\r\n", rune(c)) { | ||||
| 			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) | ||||
| 		var s authenticationScheme | ||||
| 		switch v { | ||||
| 		case "basic": | ||||
| 			s = basicAuth | ||||
| 		case "digest": | ||||
| 			s = digestAuth | ||||
| 		case "bearer": | ||||
| 			s = bearerAuth | ||||
| 		default: | ||||
| 			continue | ||||
| 		} | ||||
| 		challenges = append(challenges, challenge{scheme: s, parameters: p}) | ||||
| 	} | ||||
| 	sort.Stable(byScheme(challenges)) | ||||
| 	return challenges | ||||
| } | ||||
|  | ||||
| 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) | ||||
| 	for { | ||||
| 		var pkey string | ||||
| 		pkey, s = expectToken(skipSpace(s)) | ||||
| 		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) | ||||
| 		if !strings.HasPrefix(s, ",") { | ||||
| 			return | ||||
| 		} | ||||
| 		s = s[1:] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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 "", "" | ||||
| } | ||||
							
								
								
									
										146
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/fetcher.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/fetcher.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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. | ||||
| */ | ||||
|  | ||||
| // The corresponding file is in containerd/remote/docker/ | ||||
| // This package can be removed once a more feasible and hollistic resolver | ||||
| // is finalized in containerd. | ||||
|  | ||||
| package resolver | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"path" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/images" | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| type dockerFetcher struct { | ||||
| 	*dockerBase | ||||
| } | ||||
|  | ||||
| func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) { | ||||
| 	var bases []string | ||||
| 	for _, b := range r.base { | ||||
| 		bases = append(bases, b.String()) | ||||
| 	} | ||||
| 	ctx = log.WithLogger(ctx, log.G(ctx).WithFields( | ||||
| 		logrus.Fields{ | ||||
| 			"base":   bases, | ||||
| 			"digest": desc.Digest, | ||||
| 		}, | ||||
| 	)) | ||||
|  | ||||
| 	urls, err := r.getV2URLPaths(ctx, desc) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	ctx, err = contextWithRepositoryScope(ctx, r.refspec, false) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return newHTTPReadSeeker(desc.Size, func(offset int64) (io.ReadCloser, error) { | ||||
| 		for _, u := range urls { | ||||
| 			rc, err := r.open(ctx, u, desc.MediaType, offset) | ||||
| 			if err != nil { | ||||
| 				if errdefs.IsNotFound(err) { | ||||
| 					continue // try one of the other urls. | ||||
| 				} | ||||
|  | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			return rc, nil | ||||
| 		} | ||||
|  | ||||
| 		return nil, errors.Wrapf(errdefs.ErrNotFound, | ||||
| 			"could not fetch content descriptor %v (%v) from remote", | ||||
| 			desc.Digest, desc.MediaType) | ||||
|  | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int64) (io.ReadCloser, error) { | ||||
| 	req, err := http.NewRequest(http.MethodGet, u, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	req.Header.Set("Accept", strings.Join([]string{mediatype, `*`}, ", ")) | ||||
|  | ||||
| 	if offset > 0 { | ||||
| 		// TODO(stevvooe): Only set this header in response to the | ||||
| 		// "Accept-Ranges: bytes" header. | ||||
| 		req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset)) | ||||
| 	} | ||||
|  | ||||
| 	resp, err := r.doRequestWithRetries(ctx, req, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if resp.StatusCode > 299 { | ||||
| 		// TODO(stevvooe): When doing a offset specific request, we should | ||||
| 		// really distinguish between a 206 and a 200. In the case of 200, we | ||||
| 		// can discard the bytes, hiding the seek behavior from the | ||||
| 		// implementation. | ||||
|  | ||||
| 		resp.Body.Close() | ||||
| 		if resp.StatusCode == http.StatusNotFound { | ||||
| 			return nil, errors.Wrapf(errdefs.ErrNotFound, "content at %v not found", u) | ||||
| 		} | ||||
| 		return nil, errors.Errorf("unexpected status code %v: %v", u, resp.Status) | ||||
| 	} | ||||
|  | ||||
| 	return resp.Body, nil | ||||
| } | ||||
|  | ||||
| // getV2URLPaths generates the candidate urls paths for the object based on the | ||||
| // set of hints and the provided object id. URLs are returned in the order of | ||||
| // most to least likely succeed. | ||||
| func (r *dockerFetcher) getV2URLPaths(ctx context.Context, desc ocispec.Descriptor) ([]string, error) { | ||||
| 	var urls []string | ||||
|  | ||||
| 	if len(desc.URLs) > 0 { | ||||
| 		// handle fetch via external urls. | ||||
| 		for _, u := range desc.URLs { | ||||
| 			log.G(ctx).WithField("url", u).Debug("adding alternative url") | ||||
| 			urls = append(urls, u) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	switch desc.MediaType { | ||||
| 	case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList, | ||||
| 		images.MediaTypeDockerSchema1Manifest, | ||||
| 		ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex: | ||||
| 		urls = append(urls, r.urls(path.Join("manifests", desc.Digest.String()))...) | ||||
| 	} | ||||
|  | ||||
| 	// always fallback to attempting to get the object out of the blobs store. | ||||
| 	urls = append(urls, r.urls(path.Join("blobs", desc.Digest.String()))...) | ||||
|  | ||||
| 	return urls, nil | ||||
| } | ||||
							
								
								
									
										148
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/httpreadseeker.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/httpreadseeker.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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. | ||||
| */ | ||||
|  | ||||
| // The corresponding file is in containerd/remote/docker/. | ||||
| // This package can be removed once a more feasible and hollistic resolver | ||||
| // is finalized in containerd. | ||||
|  | ||||
| package resolver | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
|  | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| type httpReadSeeker struct { | ||||
| 	size   int64 | ||||
| 	offset int64 | ||||
| 	rc     io.ReadCloser | ||||
| 	open   func(offset int64) (io.ReadCloser, error) | ||||
| 	closed bool | ||||
| } | ||||
|  | ||||
| func newHTTPReadSeeker(size int64, open func(offset int64) (io.ReadCloser, error)) (io.ReadCloser, error) { | ||||
| 	return &httpReadSeeker{ | ||||
| 		size: size, | ||||
| 		open: open, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (hrs *httpReadSeeker) Read(p []byte) (n int, err error) { | ||||
| 	if hrs.closed { | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
|  | ||||
| 	rd, err := hrs.reader() | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	n, err = rd.Read(p) | ||||
| 	hrs.offset += int64(n) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (hrs *httpReadSeeker) Close() error { | ||||
| 	if hrs.closed { | ||||
| 		return nil | ||||
| 	} | ||||
| 	hrs.closed = true | ||||
| 	if hrs.rc != nil { | ||||
| 		return hrs.rc.Close() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (hrs *httpReadSeeker) Seek(offset int64, whence int) (int64, error) { | ||||
| 	if hrs.closed { | ||||
| 		return 0, errors.Wrap(errdefs.ErrUnavailable, "Fetcher.Seek: closed") | ||||
| 	} | ||||
|  | ||||
| 	abs := hrs.offset | ||||
| 	switch whence { | ||||
| 	case io.SeekStart: | ||||
| 		abs = offset | ||||
| 	case io.SeekCurrent: | ||||
| 		abs += offset | ||||
| 	case io.SeekEnd: | ||||
| 		if hrs.size == -1 { | ||||
| 			return 0, errors.Wrap(errdefs.ErrUnavailable, "Fetcher.Seek: unknown size, cannot seek from end") | ||||
| 		} | ||||
| 		abs = hrs.size + offset | ||||
| 	default: | ||||
| 		return 0, errors.Wrap(errdefs.ErrInvalidArgument, "Fetcher.Seek: invalid whence") | ||||
| 	} | ||||
|  | ||||
| 	if abs < 0 { | ||||
| 		return 0, errors.Wrapf(errdefs.ErrInvalidArgument, "Fetcher.Seek: negative offset") | ||||
| 	} | ||||
|  | ||||
| 	if abs != hrs.offset { | ||||
| 		if hrs.rc != nil { | ||||
| 			if err := hrs.rc.Close(); err != nil { | ||||
| 				log.L.WithError(err).Errorf("Fetcher.Seek: failed to close ReadCloser") | ||||
| 			} | ||||
|  | ||||
| 			hrs.rc = nil | ||||
| 		} | ||||
|  | ||||
| 		hrs.offset = abs | ||||
| 	} | ||||
|  | ||||
| 	return hrs.offset, nil | ||||
| } | ||||
|  | ||||
| func (hrs *httpReadSeeker) reader() (io.Reader, error) { | ||||
| 	if hrs.rc != nil { | ||||
| 		return hrs.rc, nil | ||||
| 	} | ||||
|  | ||||
| 	if hrs.size == -1 || hrs.offset < hrs.size { | ||||
| 		// only try to reopen the body request if we are seeking to a value | ||||
| 		// less than the actual size. | ||||
| 		if hrs.open == nil { | ||||
| 			return nil, errors.Wrapf(errdefs.ErrNotImplemented, "cannot open") | ||||
| 		} | ||||
|  | ||||
| 		rc, err := hrs.open(hrs.offset) | ||||
| 		if err != nil { | ||||
| 			return nil, errors.Wrapf(err, "httpReaderSeeker: failed open") | ||||
| 		} | ||||
|  | ||||
| 		if hrs.rc != nil { | ||||
| 			if err := hrs.rc.Close(); err != nil { | ||||
| 				log.L.WithError(err).Errorf("httpReadSeeker: failed to close ReadCloser") | ||||
| 			} | ||||
| 		} | ||||
| 		hrs.rc = rc | ||||
| 	} else { | ||||
| 		// There is an edge case here where offset == size of the content. If | ||||
| 		// we seek, we will probably get an error for content that cannot be | ||||
| 		// sought (?). In that case, we should err on committing the content, | ||||
| 		// as the length is already satisified but we just return the empty | ||||
| 		// reader instead. | ||||
|  | ||||
| 		hrs.rc = ioutil.NopCloser(bytes.NewReader([]byte{})) | ||||
| 	} | ||||
|  | ||||
| 	return hrs.rc, nil | ||||
| } | ||||
							
								
								
									
										600
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/resolver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										600
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/resolver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,600 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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 resolver | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"path" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/images" | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	"github.com/containerd/containerd/reference" | ||||
| 	"github.com/containerd/containerd/remotes" | ||||
| 	digest "github.com/opencontainers/go-digest" | ||||
| 	ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context/ctxhttp" | ||||
| ) | ||||
|  | ||||
| // This file is a modified copy of containerd/remote/docker/resolver.go. | ||||
| // The changes carried over by this file includes support for image ref | ||||
| // resolution and fetching image using multiple registry mirror urls. A new | ||||
| // ResolverOption called Registry is added. Registry will contain a map | ||||
| // of namespace relevant mirror urls. The client will use the ResolverOptions | ||||
| // to set the urls associated with the namespace of the image reference. | ||||
| // This package can be removed once a more feasible and hollistic resolver | ||||
| // is finalized in containerd. The specific changes are made to the base | ||||
| // function to calculate the base urls for the image location.  urls() is | ||||
| // added to fetch the mirror urls associated with the namespace of the image | ||||
| // ResolverOptions are changed for client to set the namespace and mirror urls | ||||
| // for to pull the image. | ||||
|  | ||||
| var ( | ||||
| 	// ErrNoToken is returned if a request is successful but the body does not | ||||
| 	// contain an authorization token. | ||||
| 	ErrNoToken = errors.New("authorization server did not include a token in the response") | ||||
|  | ||||
| 	// ErrInvalidAuthorization is used when credentials are passed to a server but | ||||
| 	// those credentials are rejected. | ||||
| 	ErrInvalidAuthorization = errors.New("authorization failed") | ||||
| ) | ||||
|  | ||||
| type containerdResolver struct { | ||||
| 	credentials func(string) (string, string, error) | ||||
| 	plainHTTP   bool | ||||
| 	client      *http.Client | ||||
| 	tracker     StatusTracker | ||||
| 	registry    map[string][]string | ||||
| } | ||||
|  | ||||
| // Options are used to configured a new Docker register resolver | ||||
| type Options struct { | ||||
| 	// Credentials provides username and secret given a host. | ||||
| 	// If username is empty but a secret is given, that secret | ||||
| 	// is interpretted as a long lived token. | ||||
| 	Credentials func(string) (string, string, error) | ||||
|  | ||||
| 	// PlainHTTP specifies to use plain http and not https | ||||
| 	PlainHTTP bool | ||||
|  | ||||
| 	// Client is the http client to used when making registry requests | ||||
| 	Client *http.Client | ||||
|  | ||||
| 	// Tracker is used to track uploads to the registry. This is used | ||||
| 	// since the registry does not have upload tracking and the existing | ||||
| 	// mechanism for getting blob upload status is expensive. | ||||
| 	Tracker StatusTracker | ||||
|  | ||||
| 	Registry map[string][]string | ||||
| } | ||||
|  | ||||
| // NewResolver returns a new resolver to a Docker registry | ||||
| func NewResolver(options Options) remotes.Resolver { | ||||
| 	tracker := options.Tracker | ||||
| 	if tracker == nil { | ||||
| 		tracker = NewInMemoryTracker() | ||||
| 	} | ||||
|  | ||||
| 	return &containerdResolver{ | ||||
| 		credentials: options.Credentials, | ||||
| 		plainHTTP:   options.PlainHTTP, | ||||
| 		client:      options.Client, | ||||
| 		tracker:     tracker, | ||||
| 		registry:    options.Registry, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var _ remotes.Resolver = &containerdResolver{} | ||||
|  | ||||
| func (r *containerdResolver) Resolve(ctx context.Context, ref string) (string, ocispec.Descriptor, error) { | ||||
| 	refspec, err := reference.Parse(ref) | ||||
| 	if err != nil { | ||||
| 		return "", ocispec.Descriptor{}, err | ||||
| 	} | ||||
|  | ||||
| 	if refspec.Object == "" { | ||||
| 		return "", ocispec.Descriptor{}, reference.ErrObjectRequired | ||||
| 	} | ||||
|  | ||||
| 	base, err := r.base(refspec) | ||||
| 	if err != nil { | ||||
| 		return "", ocispec.Descriptor{}, err | ||||
| 	} | ||||
|  | ||||
| 	fetcher := dockerFetcher{ | ||||
| 		dockerBase: base, | ||||
| 	} | ||||
|  | ||||
| 	var ( | ||||
| 		urls []string | ||||
| 		dgst = refspec.Digest() | ||||
| 	) | ||||
|  | ||||
| 	if dgst != "" { | ||||
| 		if err := dgst.Validate(); err != nil { | ||||
| 			// need to fail here, since we can't actually resolve the invalid | ||||
| 			// digest. | ||||
| 			return "", ocispec.Descriptor{}, err | ||||
| 		} | ||||
|  | ||||
| 		// turns out, we have a valid digest, make a url. | ||||
| 		urls = append(urls, fetcher.urls("manifests", dgst.String())...) | ||||
|  | ||||
| 		// fallback to blobs on not found. | ||||
| 		urls = append(urls, fetcher.urls("blobs", dgst.String())...) | ||||
| 	} else { | ||||
| 		urls = append(urls, fetcher.urls("manifests", refspec.Object)...) | ||||
| 	} | ||||
|  | ||||
| 	ctx, err = contextWithRepositoryScope(ctx, refspec, false) | ||||
| 	if err != nil { | ||||
| 		return "", ocispec.Descriptor{}, err | ||||
| 	} | ||||
| 	for _, u := range urls { | ||||
| 		log.G(ctx).WithFields(logrus.Fields{ | ||||
| 			"url": u, | ||||
| 		}).Debug("Trying to fetch from url") | ||||
| 		req, err := http.NewRequest(http.MethodHead, u, nil) | ||||
| 		if err != nil { | ||||
| 			return "", ocispec.Descriptor{}, err | ||||
| 		} | ||||
|  | ||||
| 		// set headers for all the types we support for resolution. | ||||
| 		req.Header.Set("Accept", strings.Join([]string{ | ||||
| 			images.MediaTypeDockerSchema2Manifest, | ||||
| 			images.MediaTypeDockerSchema2ManifestList, | ||||
| 			ocispec.MediaTypeImageManifest, | ||||
| 			ocispec.MediaTypeImageIndex, "*"}, ", ")) | ||||
|  | ||||
| 		log.G(ctx).Info("resolving") | ||||
| 		resp, err := fetcher.doRequestWithRetries(ctx, req, nil) | ||||
| 		if err != nil { | ||||
| 			return "", ocispec.Descriptor{}, err | ||||
| 		} | ||||
| 		resp.Body.Close() // don't care about body contents. | ||||
|  | ||||
| 		if resp.StatusCode > 299 { | ||||
| 			if resp.StatusCode == http.StatusNotFound { | ||||
| 				continue | ||||
| 			} | ||||
| 			return "", ocispec.Descriptor{}, errors.Errorf("unexpected status code %v: %v", u, resp.Status) | ||||
| 		} | ||||
|  | ||||
| 		// this is the only point at which we trust the registry. we use the | ||||
| 		// content headers to assemble a descriptor for the name. when this becomes | ||||
| 		// more robust, we mostly get this information from a secure trust store. | ||||
| 		dgstHeader := digest.Digest(resp.Header.Get("Docker-Content-Digest")) | ||||
|  | ||||
| 		if dgstHeader != "" { | ||||
| 			if err := dgstHeader.Validate(); err != nil { | ||||
| 				return "", ocispec.Descriptor{}, errors.Wrapf(err, "%q in header not a valid digest", dgstHeader) | ||||
| 			} | ||||
| 			dgst = dgstHeader | ||||
| 		} | ||||
|  | ||||
| 		if dgst == "" { | ||||
| 			return "", ocispec.Descriptor{}, errors.Errorf("could not resolve digest for %v", ref) | ||||
| 		} | ||||
|  | ||||
| 		var ( | ||||
| 			size       int64 | ||||
| 			sizeHeader = resp.Header.Get("Content-Length") | ||||
| 		) | ||||
|  | ||||
| 		size, err = strconv.ParseInt(sizeHeader, 10, 64) | ||||
| 		if err != nil { | ||||
|  | ||||
| 			return "", ocispec.Descriptor{}, errors.Wrapf(err, "invalid size header: %q", sizeHeader) | ||||
| 		} | ||||
| 		if size < 0 { | ||||
| 			return "", ocispec.Descriptor{}, errors.Errorf("%q in header not a valid size", sizeHeader) | ||||
| 		} | ||||
|  | ||||
| 		desc := ocispec.Descriptor{ | ||||
| 			Digest:    dgst, | ||||
| 			MediaType: resp.Header.Get("Content-Type"), // need to strip disposition? | ||||
| 			Size:      size, | ||||
| 		} | ||||
|  | ||||
| 		log.G(ctx).WithField("desc.digest", desc.Digest).Debug("resolved") | ||||
| 		return ref, desc, nil | ||||
| 	} | ||||
|  | ||||
| 	return "", ocispec.Descriptor{}, errors.Errorf("%v not found", ref) | ||||
| } | ||||
|  | ||||
| func (r *containerdResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) { | ||||
| 	refspec, err := reference.Parse(ref) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	base, err := r.base(refspec) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return dockerFetcher{ | ||||
| 		dockerBase: base, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (r *containerdResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| type dockerBase struct { | ||||
| 	refspec reference.Spec | ||||
| 	base    []url.URL | ||||
| 	token   string | ||||
|  | ||||
| 	client   *http.Client | ||||
| 	useBasic bool | ||||
| 	username string | ||||
| 	secret   string | ||||
| } | ||||
|  | ||||
| func (r *containerdResolver) base(refspec reference.Spec) (*dockerBase, error) { | ||||
| 	var ( | ||||
| 		err              error | ||||
| 		base             []url.URL | ||||
| 		username, secret string | ||||
| 	) | ||||
|  | ||||
| 	host := refspec.Hostname() | ||||
| 	prefix := strings.TrimPrefix(refspec.Locator, host+"/") | ||||
|  | ||||
| 	if urls, ok := r.registry[host]; ok { | ||||
| 		urls, err := r.getV2Urls(urls, prefix) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to fetch v2 urls: %v", err) | ||||
| 		} | ||||
| 		base = append(base, urls...) | ||||
| 	} else if host == "docker.io" { | ||||
| 		base = append(base, []url.URL{{Host: "registry-1.docker.io", Scheme: "https", Path: path.Join("/v2", prefix)}}...) | ||||
| 	} else { | ||||
| 		scheme := "https" | ||||
| 		if r.plainHTTP || strings.HasPrefix(host, "localhost:") { | ||||
| 			scheme = "http" | ||||
| 		} | ||||
| 		base = append(base, []url.URL{{Host: host, Scheme: scheme, Path: path.Join("/v2", prefix)}}...) | ||||
| 	} | ||||
|  | ||||
| 	if r.credentials != nil { | ||||
| 		username, secret, err = r.credentials(base[0].Host) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &dockerBase{ | ||||
| 		refspec:  refspec, | ||||
| 		base:     base, | ||||
| 		client:   r.client, | ||||
| 		username: username, | ||||
| 		secret:   secret, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (r *dockerBase) urls(ps ...string) []string { | ||||
| 	urls := []string{} | ||||
| 	for _, url := range r.base { | ||||
| 		url.Path = path.Join(url.Path, path.Join(ps...)) | ||||
| 		urls = append(urls, url.String()) | ||||
| 	} | ||||
| 	return urls | ||||
| } | ||||
|  | ||||
| func (r *dockerBase) authorize(req *http.Request) { | ||||
| 	if r.useBasic { | ||||
| 		req.SetBasicAuth(r.username, r.secret) | ||||
| 	} else if r.token != "" { | ||||
| 		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.token)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *dockerBase) doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { | ||||
| 	ctx = log.WithLogger(ctx, log.G(ctx).WithField("url", req.URL.String())) | ||||
| 	log.G(ctx).WithField("request.headers", req.Header).WithField("request.method", req.Method).Debug("do request") | ||||
| 	r.authorize(req) | ||||
| 	resp, err := ctxhttp.Do(ctx, r.client, req) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "failed to do request") | ||||
| 	} | ||||
| 	log.G(ctx).WithFields(logrus.Fields{ | ||||
| 		"status":           resp.Status, | ||||
| 		"response.headers": resp.Header, | ||||
| 	}).Debug("fetch response received") | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| func (r *dockerBase) doRequestWithRetries(ctx context.Context, req *http.Request, responses []*http.Response) (*http.Response, error) { | ||||
| 	resp, err := r.doRequest(ctx, req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	responses = append(responses, resp) | ||||
| 	req, err = r.retryRequest(ctx, req, responses) | ||||
| 	if err != nil { | ||||
| 		resp.Body.Close() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if req != nil { | ||||
| 		resp.Body.Close() | ||||
| 		return r.doRequestWithRetries(ctx, req, responses) | ||||
| 	} | ||||
| 	return resp, err | ||||
| } | ||||
|  | ||||
| func (r *dockerBase) retryRequest(ctx context.Context, req *http.Request, responses []*http.Response) (*http.Request, error) { | ||||
| 	if len(responses) > 5 { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	last := responses[len(responses)-1] | ||||
| 	if last.StatusCode == http.StatusUnauthorized { | ||||
| 		log.G(ctx).WithField("header", last.Header.Get("WWW-Authenticate")).Debug("Unauthorized") | ||||
| 		for _, c := range parseAuthHeader(last.Header) { | ||||
| 			if c.scheme == bearerAuth { | ||||
| 				if err := invalidAuthorization(c, responses); err != nil { | ||||
| 					r.token = "" | ||||
| 					return nil, err | ||||
| 				} | ||||
| 				if err := r.setTokenAuth(ctx, c.parameters); err != nil { | ||||
| 					return nil, err | ||||
| 				} | ||||
| 				return copyRequest(req) | ||||
| 			} else if c.scheme == basicAuth { | ||||
| 				if r.username != "" && r.secret != "" { | ||||
| 					r.useBasic = true | ||||
| 				} | ||||
| 				return copyRequest(req) | ||||
| 			} | ||||
| 		} | ||||
| 		return nil, nil | ||||
| 	} else if last.StatusCode == http.StatusMethodNotAllowed && req.Method == http.MethodHead { | ||||
| 		// Support registries which have not properly implemented the HEAD method for | ||||
| 		// manifests endpoint | ||||
| 		if strings.Contains(req.URL.Path, "/manifests/") { | ||||
| 			// TODO: copy request? | ||||
| 			req.Method = http.MethodGet | ||||
| 			return copyRequest(req) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// TODO: Handle 50x errors accounting for attempt history | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func invalidAuthorization(c challenge, responses []*http.Response) error { | ||||
| 	errStr := c.parameters["error"] | ||||
| 	if errStr == "" { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	n := len(responses) | ||||
| 	if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return errors.Wrapf(ErrInvalidAuthorization, "server message: %s", errStr) | ||||
| } | ||||
|  | ||||
| func sameRequest(r1, r2 *http.Request) bool { | ||||
| 	if r1.Method != r2.Method { | ||||
| 		return false | ||||
| 	} | ||||
| 	if *r1.URL != *r2.URL { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func copyRequest(req *http.Request) (*http.Request, error) { | ||||
| 	ireq := *req | ||||
| 	if ireq.GetBody != nil { | ||||
| 		var err error | ||||
| 		ireq.Body, err = ireq.GetBody() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	return &ireq, nil | ||||
| } | ||||
|  | ||||
| func (r *dockerBase) setTokenAuth(ctx context.Context, params map[string]string) error { | ||||
| 	realm, ok := params["realm"] | ||||
| 	if !ok { | ||||
| 		return errors.New("no realm specified for token auth challenge") | ||||
| 	} | ||||
|  | ||||
| 	realmURL, err := url.Parse(realm) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("invalid token auth challenge realm: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	to := tokenOptions{ | ||||
| 		realm:   realmURL.String(), | ||||
| 		service: params["service"], | ||||
| 	} | ||||
|  | ||||
| 	to.scopes = getTokenScopes(ctx, params) | ||||
| 	if len(to.scopes) == 0 { | ||||
| 		return errors.Errorf("no scope specified for token auth challenge") | ||||
| 	} | ||||
| 	if r.secret != "" { | ||||
| 		// Credential information is provided, use oauth POST endpoint | ||||
| 		r.token, err = r.fetchTokenWithOAuth(ctx, to) | ||||
| 		if err != nil { | ||||
| 			return errors.Wrap(err, "failed to fetch oauth token") | ||||
| 		} | ||||
| 	} else { | ||||
| 		// Do request anonymously | ||||
| 		r.token, err = r.getToken(ctx, to) | ||||
| 		if err != nil { | ||||
| 			return errors.Wrap(err, "failed to fetch anonymous token") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type tokenOptions struct { | ||||
| 	realm   string | ||||
| 	service string | ||||
| 	scopes  []string | ||||
| } | ||||
|  | ||||
| type postTokenResponse struct { | ||||
| 	AccessToken  string    `json:"access_token"` | ||||
| 	RefreshToken string    `json:"refresh_token"` | ||||
| 	ExpiresIn    int       `json:"expires_in"` | ||||
| 	IssuedAt     time.Time `json:"issued_at"` | ||||
| 	Scope        string    `json:"scope"` | ||||
| } | ||||
|  | ||||
| func (r *dockerBase) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (string, error) { | ||||
| 	form := url.Values{} | ||||
| 	form.Set("scope", strings.Join(to.scopes, " ")) | ||||
| 	form.Set("service", to.service) | ||||
| 	// TODO: Allow setting client_id | ||||
| 	form.Set("client_id", "containerd-dist-tool") | ||||
|  | ||||
| 	if r.username == "" { | ||||
| 		form.Set("grant_type", "refresh_token") | ||||
| 		form.Set("refresh_token", r.secret) | ||||
| 	} else { | ||||
| 		form.Set("grant_type", "password") | ||||
| 		form.Set("username", r.username) | ||||
| 		form.Set("password", r.secret) | ||||
| 	} | ||||
|  | ||||
| 	resp, err := ctxhttp.PostForm(ctx, r.client, to.realm, form) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
|  | ||||
| 	// Registries without support for POST may return 404 for POST /v2/token. | ||||
| 	// As of September 2017, GCR is known to return 404. | ||||
| 	if (resp.StatusCode == 405 && r.username != "") || resp.StatusCode == 404 { | ||||
| 		return r.getToken(ctx, to) | ||||
| 	} else if resp.StatusCode < 200 || resp.StatusCode >= 400 { | ||||
| 		b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB | ||||
| 		log.G(ctx).WithFields(logrus.Fields{ | ||||
| 			"status": resp.Status, | ||||
| 			"body":   string(b), | ||||
| 		}).Debugf("token request failed") | ||||
| 		// TODO: handle error body and write debug output | ||||
| 		return "", errors.Errorf("unexpected status: %s", resp.Status) | ||||
| 	} | ||||
|  | ||||
| 	decoder := json.NewDecoder(resp.Body) | ||||
|  | ||||
| 	var tr postTokenResponse | ||||
| 	if err = decoder.Decode(&tr); err != nil { | ||||
| 		return "", fmt.Errorf("unable to decode token response: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	return tr.AccessToken, nil | ||||
| } | ||||
|  | ||||
| type getTokenResponse struct { | ||||
| 	Token        string    `json:"token"` | ||||
| 	AccessToken  string    `json:"access_token"` | ||||
| 	ExpiresIn    int       `json:"expires_in"` | ||||
| 	IssuedAt     time.Time `json:"issued_at"` | ||||
| 	RefreshToken string    `json:"refresh_token"` | ||||
| } | ||||
|  | ||||
| // getToken fetches a token using a GET request | ||||
| func (r *dockerBase) getToken(ctx context.Context, to tokenOptions) (string, error) { | ||||
| 	req, err := http.NewRequest("GET", to.realm, nil) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	reqParams := req.URL.Query() | ||||
|  | ||||
| 	if to.service != "" { | ||||
| 		reqParams.Add("service", to.service) | ||||
| 	} | ||||
|  | ||||
| 	for _, scope := range to.scopes { | ||||
| 		reqParams.Add("scope", scope) | ||||
| 	} | ||||
|  | ||||
| 	if r.secret != "" { | ||||
| 		req.SetBasicAuth(r.username, r.secret) | ||||
| 	} | ||||
|  | ||||
| 	req.URL.RawQuery = reqParams.Encode() | ||||
|  | ||||
| 	resp, err := ctxhttp.Do(ctx, r.client, req) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
|  | ||||
| 	if resp.StatusCode < 200 || resp.StatusCode >= 400 { | ||||
| 		// TODO: handle error body and write debug output | ||||
| 		return "", errors.Errorf("unexpected status: %s", resp.Status) | ||||
| 	} | ||||
|  | ||||
| 	decoder := json.NewDecoder(resp.Body) | ||||
|  | ||||
| 	var tr getTokenResponse | ||||
| 	if err = decoder.Decode(&tr); err != nil { | ||||
| 		return "", fmt.Errorf("unable to decode token response: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	// `access_token` is equivalent to `token` and if both are specified | ||||
| 	// the choice is undefined.  Canonicalize `access_token` by sticking | ||||
| 	// things in `token`. | ||||
| 	if tr.AccessToken != "" { | ||||
| 		tr.Token = tr.AccessToken | ||||
| 	} | ||||
|  | ||||
| 	if tr.Token == "" { | ||||
| 		return "", ErrNoToken | ||||
| 	} | ||||
|  | ||||
| 	return tr.Token, nil | ||||
| } | ||||
|  | ||||
| func (r *containerdResolver) getV2Urls(urls []string, imagePath string) ([]url.URL, error) { | ||||
| 	v2Urls := []url.URL{} | ||||
| 	for _, u := range urls { | ||||
| 		v2Url, err := url.Parse(u) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("Failed to parse url during getv2 urls: %+v, err:%s", u, err) | ||||
| 		} | ||||
| 		v2Url.Path = path.Join("/v2", imagePath) | ||||
| 		v2Urls = append(v2Urls, *v2Url) | ||||
| 	} | ||||
| 	return v2Urls, nil | ||||
| } | ||||
							
								
								
									
										80
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/scope.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/scope.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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. | ||||
| */ | ||||
|  | ||||
| // The corresponding file is in containerd/remote/docker/. | ||||
| // This package can be removed once a more feasible and hollistic resolver | ||||
| // is finalized in containerd. | ||||
|  | ||||
| package resolver | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/url" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/containerd/containerd/reference" | ||||
| ) | ||||
|  | ||||
| // repositoryScope returns a repository scope string such as "repository:foo/bar:pull" | ||||
| // for "host/foo/bar:baz". | ||||
| // When push is true, both pull and push are added to the scope. | ||||
| func repositoryScope(refspec reference.Spec, push bool) (string, error) { | ||||
| 	u, err := url.Parse("dummy://" + refspec.Locator) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	s := "repository:" + strings.TrimPrefix(u.Path, "/") + ":pull" | ||||
| 	if push { | ||||
| 		s += ",push" | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
|  | ||||
| // tokenScopesKey is used for the key for context.WithValue(). | ||||
| // value: []string (e.g. {"registry:foo/bar:pull"}) | ||||
| type tokenScopesKey struct{} | ||||
|  | ||||
| // contextWithRepositoryScope returns a context with tokenScopesKey{} and the repository scope value. | ||||
| func contextWithRepositoryScope(ctx context.Context, refspec reference.Spec, push bool) (context.Context, error) { | ||||
| 	s, err := repositoryScope(refspec, push) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return context.WithValue(ctx, tokenScopesKey{}, []string{s}), nil | ||||
| } | ||||
|  | ||||
| // getTokenScopes returns deduplicated and sorted scopes from ctx.Value(tokenScopesKey{}) and params["scope"]. | ||||
| func getTokenScopes(ctx context.Context, params map[string]string) []string { | ||||
| 	var scopes []string | ||||
| 	if x := ctx.Value(tokenScopesKey{}); x != nil { | ||||
| 		scopes = append(scopes, x.([]string)...) | ||||
| 	} | ||||
| 	if scope, ok := params["scope"]; ok { | ||||
| 		for _, s := range scopes { | ||||
| 			// Note: this comparison is unaware of the scope grammar (https://docs.docker.com/registry/spec/auth/scope/) | ||||
| 			// So, "repository:foo/bar:pull,push" != "repository:foo/bar:push,pull", although semantically they are equal. | ||||
| 			if s == scope { | ||||
| 				// already appended | ||||
| 				goto Sort | ||||
| 			} | ||||
| 		} | ||||
| 		scopes = append(scopes, scope) | ||||
| 	} | ||||
| Sort: | ||||
| 	sort.Strings(scopes) | ||||
| 	return scopes | ||||
| } | ||||
							
								
								
									
										71
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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. | ||||
| */ | ||||
|  | ||||
| // The corresponding file is in containerd/remote/docker/. | ||||
| // This package can be removed once a more feasible and hollistic resolver | ||||
| // is finalized in containerd. | ||||
|  | ||||
| package resolver | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd/content" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| // Status of a content operation | ||||
| type Status struct { | ||||
| 	content.Status | ||||
|  | ||||
| 	// UploadUUID is used by the Docker registry to reference blob uploads | ||||
| 	UploadUUID string | ||||
| } | ||||
|  | ||||
| // StatusTracker to track status of operations | ||||
| type StatusTracker interface { | ||||
| 	GetStatus(string) (Status, error) | ||||
| 	SetStatus(string, Status) | ||||
| } | ||||
|  | ||||
| type memoryStatusTracker struct { | ||||
| 	statuses map[string]Status | ||||
| 	m        sync.Mutex | ||||
| } | ||||
|  | ||||
| // NewInMemoryTracker returns a StatusTracker that tracks content status in-memory | ||||
| func NewInMemoryTracker() StatusTracker { | ||||
| 	return &memoryStatusTracker{ | ||||
| 		statuses: map[string]Status{}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *memoryStatusTracker) GetStatus(ref string) (Status, error) { | ||||
| 	t.m.Lock() | ||||
| 	defer t.m.Unlock() | ||||
| 	status, ok := t.statuses[ref] | ||||
| 	if !ok { | ||||
| 		return Status{}, errors.Wrapf(errdefs.ErrNotFound, "status for ref %v", ref) | ||||
| 	} | ||||
| 	return status, nil | ||||
| } | ||||
|  | ||||
| func (t *memoryStatusTracker) SetStatus(ref string, status Status) { | ||||
| 	t.m.Lock() | ||||
| 	t.statuses[ref] = status | ||||
| 	t.m.Unlock() | ||||
| } | ||||
							
								
								
									
										57
									
								
								vendor/github.com/containerd/cri-containerd/pkg/ioutil/read_closer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/containerd/cri-containerd/pkg/ioutil/read_closer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 ioutil | ||||
|  | ||||
| import "io" | ||||
|  | ||||
| // writeCloseInformer wraps a reader with a close function. | ||||
| type wrapReadCloser struct { | ||||
| 	reader *io.PipeReader | ||||
| 	writer *io.PipeWriter | ||||
| } | ||||
|  | ||||
| // NewWrapReadCloser creates a wrapReadCloser from a reader. | ||||
| // NOTE(random-liu): To avoid goroutine leakage, the reader passed in | ||||
| // must be eventually closed by the caller. | ||||
| func NewWrapReadCloser(r io.Reader) io.ReadCloser { | ||||
| 	pr, pw := io.Pipe() | ||||
| 	go func() { | ||||
| 		_, _ = io.Copy(pw, r) | ||||
| 		pr.Close() | ||||
| 		pw.Close() | ||||
| 	}() | ||||
| 	return &wrapReadCloser{ | ||||
| 		reader: pr, | ||||
| 		writer: pw, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Read reads up to len(p) bytes into p. | ||||
| func (w *wrapReadCloser) Read(p []byte) (int, error) { | ||||
| 	n, err := w.reader.Read(p) | ||||
| 	if err == io.ErrClosedPipe { | ||||
| 		return n, io.EOF | ||||
| 	} | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| // Close closes read closer. | ||||
| func (w *wrapReadCloser) Close() error { | ||||
| 	w.reader.Close() | ||||
| 	w.writer.Close() | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										68
									
								
								vendor/github.com/containerd/cri-containerd/pkg/ioutil/write_closer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								vendor/github.com/containerd/cri-containerd/pkg/ioutil/write_closer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 ioutil | ||||
|  | ||||
| import "io" | ||||
|  | ||||
| // writeCloseInformer wraps passed in write closer with a close channel. | ||||
| // Caller could wait on the close channel for the write closer to be | ||||
| // closed. | ||||
| type writeCloseInformer struct { | ||||
| 	close chan struct{} | ||||
| 	wc    io.WriteCloser | ||||
| } | ||||
|  | ||||
| // NewWriteCloseInformer creates the writeCloseInformer from a write closer. | ||||
| func NewWriteCloseInformer(wc io.WriteCloser) (io.WriteCloser, <-chan struct{}) { | ||||
| 	close := make(chan struct{}) | ||||
| 	return &writeCloseInformer{ | ||||
| 		close: close, | ||||
| 		wc:    wc, | ||||
| 	}, close | ||||
| } | ||||
|  | ||||
| // Write passes through the data into the internal write closer. | ||||
| func (w *writeCloseInformer) Write(p []byte) (int, error) { | ||||
| 	return w.wc.Write(p) | ||||
| } | ||||
|  | ||||
| // Close closes the internal write closer and inform the close channel. | ||||
| func (w *writeCloseInformer) Close() error { | ||||
| 	err := w.wc.Close() | ||||
| 	close(w.close) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // nopWriteCloser wraps passed in writer with a nop close function. | ||||
| type nopWriteCloser struct { | ||||
| 	w io.Writer | ||||
| } | ||||
|  | ||||
| // NewNopWriteCloser creates the nopWriteCloser from a writer. | ||||
| func NewNopWriteCloser(w io.Writer) io.WriteCloser { | ||||
| 	return &nopWriteCloser{w: w} | ||||
| } | ||||
|  | ||||
| // Write passes through the data into the internal writer. | ||||
| func (n *nopWriteCloser) Write(p []byte) (int, error) { | ||||
| 	return n.w.Write(p) | ||||
| } | ||||
|  | ||||
| // Close is a nop close function. | ||||
| func (n *nopWriteCloser) Close() error { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										97
									
								
								vendor/github.com/containerd/cri-containerd/pkg/ioutil/writer_group.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/github.com/containerd/cri-containerd/pkg/ioutil/writer_group.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 ioutil | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| // WriterGroup is a group of writers. Writer could be dynamically | ||||
| // added and removed. | ||||
| type WriterGroup struct { | ||||
| 	mu      sync.Mutex | ||||
| 	writers map[string]io.WriteCloser | ||||
| 	closed  bool | ||||
| } | ||||
|  | ||||
| var _ io.Writer = &WriterGroup{} | ||||
|  | ||||
| // NewWriterGroup creates an empty writer group. | ||||
| func NewWriterGroup() *WriterGroup { | ||||
| 	return &WriterGroup{ | ||||
| 		writers: make(map[string]io.WriteCloser), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Add adds a writer into the group, returns an error when writer | ||||
| // group is closed. | ||||
| func (g *WriterGroup) Add(key string, w io.WriteCloser) error { | ||||
| 	g.mu.Lock() | ||||
| 	defer g.mu.Unlock() | ||||
| 	if g.closed { | ||||
| 		return errors.New("wait group closed") | ||||
| 	} | ||||
| 	g.writers[key] = w | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove removes a writer from the group. | ||||
| func (g *WriterGroup) Remove(key string) { | ||||
| 	g.mu.Lock() | ||||
| 	defer g.mu.Unlock() | ||||
| 	w, ok := g.writers[key] | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	w.Close() | ||||
| 	delete(g.writers, key) | ||||
| } | ||||
|  | ||||
| // Write writes data into each writer. If a writer returns error, | ||||
| // it will be closed and removed from the writer group. It returns | ||||
| // error if writer group is empty. | ||||
| func (g *WriterGroup) Write(p []byte) (int, error) { | ||||
| 	g.mu.Lock() | ||||
| 	defer g.mu.Unlock() | ||||
| 	for k, w := range g.writers { | ||||
| 		n, err := w.Write(p) | ||||
| 		if err == nil && len(p) == n { | ||||
| 			continue | ||||
| 		} | ||||
| 		// The writer is closed or in bad state, remove it. | ||||
| 		w.Close() | ||||
| 		delete(g.writers, k) | ||||
| 	} | ||||
| 	if len(g.writers) == 0 { | ||||
| 		return 0, errors.New("writer group is empty") | ||||
| 	} | ||||
| 	return len(p), nil | ||||
| } | ||||
|  | ||||
| // Close closes the writer group. Write or Add will return error | ||||
| // after closed. | ||||
| func (g *WriterGroup) Close() { | ||||
| 	g.mu.Lock() | ||||
| 	defer g.mu.Unlock() | ||||
| 	for _, w := range g.writers { | ||||
| 		w.Close() | ||||
| 	} | ||||
| 	g.writers = nil | ||||
| 	g.closed = true | ||||
| } | ||||
							
								
								
									
										29
									
								
								vendor/github.com/containerd/cri-containerd/pkg/log/log.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/containerd/cri-containerd/pkg/log/log.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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 log | ||||
|  | ||||
| import "github.com/containerd/containerd/log" | ||||
|  | ||||
| // Trace logs a message at level Trace on the standard logger. | ||||
| func Trace(args ...interface{}) { | ||||
| 	log.Trace(log.L, args...) | ||||
| } | ||||
|  | ||||
| // Tracef logs a message at level Trace on the standard logger. | ||||
| func Tracef(format string, args ...interface{}) { | ||||
| 	log.Tracef(log.L, format, args...) | ||||
| } | ||||
							
								
								
									
										123
									
								
								vendor/github.com/containerd/cri-containerd/pkg/os/os.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/containerd/cri-containerd/pkg/os/os.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 os | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	containerdmount "github.com/containerd/containerd/mount" | ||||
| 	"github.com/containerd/fifo" | ||||
| 	"github.com/docker/docker/pkg/mount" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // OS collects system level operations that need to be mocked out | ||||
| // during tests. | ||||
| type OS interface { | ||||
| 	MkdirAll(path string, perm os.FileMode) error | ||||
| 	RemoveAll(path string) error | ||||
| 	OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) | ||||
| 	Stat(name string) (os.FileInfo, error) | ||||
| 	ResolveSymbolicLink(name string) (string, error) | ||||
| 	CopyFile(src, dest string, perm os.FileMode) error | ||||
| 	WriteFile(filename string, data []byte, perm os.FileMode) error | ||||
| 	Mount(source string, target string, fstype string, flags uintptr, data string) error | ||||
| 	Unmount(target string, flags int) error | ||||
| 	LookupMount(path string) (containerdmount.Info, error) | ||||
| } | ||||
|  | ||||
| // RealOS is used to dispatch the real system level operations. | ||||
| type RealOS struct{} | ||||
|  | ||||
| // MkdirAll will will call os.MkdirAll to create a directory. | ||||
| func (RealOS) MkdirAll(path string, perm os.FileMode) error { | ||||
| 	return os.MkdirAll(path, perm) | ||||
| } | ||||
|  | ||||
| // RemoveAll will call os.RemoveAll to remove the path and its children. | ||||
| func (RealOS) RemoveAll(path string) error { | ||||
| 	return os.RemoveAll(path) | ||||
| } | ||||
|  | ||||
| // OpenFifo will call fifo.OpenFifo to open a fifo. | ||||
| func (RealOS) OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { | ||||
| 	return fifo.OpenFifo(ctx, fn, flag, perm) | ||||
| } | ||||
|  | ||||
| // Stat will call os.Stat to get the status of the given file. | ||||
| func (RealOS) Stat(name string) (os.FileInfo, error) { | ||||
| 	return os.Stat(name) | ||||
| } | ||||
|  | ||||
| // ResolveSymbolicLink will follow any symbolic links | ||||
| func (RealOS) ResolveSymbolicLink(path string) (string, error) { | ||||
| 	info, err := os.Lstat(path) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if info.Mode()&os.ModeSymlink != os.ModeSymlink { | ||||
| 		return path, nil | ||||
| 	} | ||||
| 	return filepath.EvalSymlinks(path) | ||||
| } | ||||
|  | ||||
| // CopyFile copys src file to dest file | ||||
| func (RealOS) CopyFile(src, dest string, perm os.FileMode) error { | ||||
| 	in, err := os.Open(src) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer in.Close() | ||||
|  | ||||
| 	out, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer out.Close() | ||||
|  | ||||
| 	_, err = io.Copy(out, in) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // WriteFile will call ioutil.WriteFile to write data into a file. | ||||
| func (RealOS) WriteFile(filename string, data []byte, perm os.FileMode) error { | ||||
| 	return ioutil.WriteFile(filename, data, perm) | ||||
| } | ||||
|  | ||||
| // Mount will call unix.Mount to mount the file. | ||||
| func (RealOS) Mount(source string, target string, fstype string, flags uintptr, data string) error { | ||||
| 	return unix.Mount(source, target, fstype, flags, data) | ||||
| } | ||||
|  | ||||
| // Unmount will call unix.Unmount to unmount the file. The function doesn't | ||||
| // return error if target is not mounted. | ||||
| func (RealOS) Unmount(target string, flags int) error { | ||||
| 	// TODO(random-liu): Follow symlink to make sure the result is correct. | ||||
| 	if mounted, err := mount.Mounted(target); err != nil || !mounted { | ||||
| 		return err | ||||
| 	} | ||||
| 	return unix.Unmount(target, flags) | ||||
| } | ||||
|  | ||||
| // LookupMount gets mount info of a given path. | ||||
| func (RealOS) LookupMount(path string) (containerdmount.Info, error) { | ||||
| 	return containerdmount.Lookup(path) | ||||
| } | ||||
							
								
								
									
										101
									
								
								vendor/github.com/containerd/cri-containerd/pkg/registrar/registrar.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								vendor/github.com/containerd/cri-containerd/pkg/registrar/registrar.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 registrar | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| // Registrar stores one-to-one name<->key mappings. | ||||
| // Names and keys must be unique. | ||||
| // Registrar is safe for concurrent access. | ||||
| type Registrar struct { | ||||
| 	lock      sync.Mutex | ||||
| 	nameToKey map[string]string | ||||
| 	keyToName map[string]string | ||||
| } | ||||
|  | ||||
| // NewRegistrar creates a new Registrar with the empty indexes. | ||||
| func NewRegistrar() *Registrar { | ||||
| 	return &Registrar{ | ||||
| 		nameToKey: make(map[string]string), | ||||
| 		keyToName: make(map[string]string), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Reserve registers a name<->key mapping, name or key must not | ||||
| // be empty. | ||||
| // Reserve is idempotent. | ||||
| // Attempting to reserve a conflict key<->name mapping results | ||||
| // in an error. | ||||
| // A name<->key reservation is globally unique. | ||||
| func (r *Registrar) Reserve(name, key string) error { | ||||
| 	r.lock.Lock() | ||||
| 	defer r.lock.Unlock() | ||||
|  | ||||
| 	if name == "" || key == "" { | ||||
| 		return fmt.Errorf("invalid name %q or key %q", name, key) | ||||
| 	} | ||||
|  | ||||
| 	if k, exists := r.nameToKey[name]; exists { | ||||
| 		if k != key { | ||||
| 			return fmt.Errorf("name %q is reserved for %q", name, k) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if n, exists := r.keyToName[key]; exists { | ||||
| 		if n != name { | ||||
| 			return fmt.Errorf("key %q is reserved for %q", key, n) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	r.nameToKey[name] = key | ||||
| 	r.keyToName[key] = name | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ReleaseByName releases the reserved name<->key mapping by name. | ||||
| // Once released, the name and the key can be reserved again. | ||||
| func (r *Registrar) ReleaseByName(name string) { | ||||
| 	r.lock.Lock() | ||||
| 	defer r.lock.Unlock() | ||||
|  | ||||
| 	key, exists := r.nameToKey[name] | ||||
| 	if !exists { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	delete(r.nameToKey, name) | ||||
| 	delete(r.keyToName, key) | ||||
| } | ||||
|  | ||||
| // ReleaseByKey release the reserved name<->key mapping by key. | ||||
| func (r *Registrar) ReleaseByKey(key string) { | ||||
| 	r.lock.Lock() | ||||
| 	defer r.lock.Unlock() | ||||
|  | ||||
| 	name, exists := r.keyToName[key] | ||||
| 	if !exists { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	delete(r.nameToKey, name) | ||||
| 	delete(r.keyToName, key) | ||||
| } | ||||
							
								
								
									
										84
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_attach.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_attach.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/client-go/tools/remotecommand" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	cio "github.com/containerd/cri-containerd/pkg/server/io" | ||||
| ) | ||||
|  | ||||
| // Attach prepares a streaming endpoint to attach to a running container, and returns the address. | ||||
| func (c *criContainerdService) Attach(ctx context.Context, r *runtime.AttachRequest) (*runtime.AttachResponse, error) { | ||||
| 	cntr, err := c.containerStore.Get(r.GetContainerId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to find container in store: %v", err) | ||||
| 	} | ||||
| 	state := cntr.Status.Get().State() | ||||
| 	if state != runtime.ContainerState_CONTAINER_RUNNING { | ||||
| 		return nil, fmt.Errorf("container is in %s state", criContainerStateToString(state)) | ||||
| 	} | ||||
| 	return c.streamServer.GetAttach(r) | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) attachContainer(ctx context.Context, id string, stdin io.Reader, stdout, stderr io.WriteCloser, | ||||
| 	tty bool, resize <-chan remotecommand.TerminalSize) error { | ||||
| 	// Get container from our container store. | ||||
| 	cntr, err := c.containerStore.Get(id) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to find container %q in store: %v", id, err) | ||||
| 	} | ||||
| 	id = cntr.ID | ||||
|  | ||||
| 	state := cntr.Status.Get().State() | ||||
| 	if state != runtime.ContainerState_CONTAINER_RUNNING { | ||||
| 		return fmt.Errorf("container is in %s state", criContainerStateToString(state)) | ||||
| 	} | ||||
|  | ||||
| 	task, err := cntr.Container.Task(ctx, nil) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to load task: %v", err) | ||||
| 	} | ||||
| 	handleResizing(resize, func(size remotecommand.TerminalSize) { | ||||
| 		if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to resize task %q console", id) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	opts := cio.AttachOptions{ | ||||
| 		Stdin:     stdin, | ||||
| 		Stdout:    stdout, | ||||
| 		Stderr:    stderr, | ||||
| 		Tty:       tty, | ||||
| 		StdinOnce: cntr.Config.StdinOnce, | ||||
| 		CloseStdin: func() error { | ||||
| 			return task.CloseIO(ctx, containerd.WithStdinCloser) | ||||
| 		}, | ||||
| 	} | ||||
| 	// TODO(random-liu): Figure out whether we need to support historical output. | ||||
| 	if err := cntr.IO.Attach(opts); err != nil { | ||||
| 		return fmt.Errorf("failed to attach container: %v", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										860
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_create.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										860
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_create.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,860 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/containers" | ||||
| 	"github.com/containerd/containerd/contrib/apparmor" | ||||
| 	"github.com/containerd/containerd/contrib/seccomp" | ||||
| 	"github.com/containerd/containerd/linux/runctypes" | ||||
| 	"github.com/containerd/containerd/mount" | ||||
| 	"github.com/containerd/containerd/namespaces" | ||||
| 	"github.com/containerd/containerd/oci" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/davecgh/go-spew/spew" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"github.com/opencontainers/runc/libcontainer/devices" | ||||
| 	runtimespec "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/opencontainers/runtime-tools/generate" | ||||
| 	"github.com/opencontainers/runtime-tools/validate" | ||||
| 	"github.com/opencontainers/selinux/go-selinux/label" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/syndtr/gocapability/capability" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"golang.org/x/sys/unix" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/annotations" | ||||
| 	customopts "github.com/containerd/cri-containerd/pkg/containerd/opts" | ||||
| 	cio "github.com/containerd/cri-containerd/pkg/server/io" | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| 	"github.com/containerd/cri-containerd/pkg/util" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// profileNamePrefix is the prefix for loading profiles on a localhost. Eg. AppArmor localhost/profileName. | ||||
| 	profileNamePrefix = "localhost/" // TODO (mikebrow): get localhost/ & runtime/default from CRI kubernetes/kubernetes#51747 | ||||
| 	// runtimeDefault indicates that we should use or create a runtime default profile. | ||||
| 	runtimeDefault = "runtime/default" | ||||
| 	// dockerDefault indicates that we should use or create a docker default profile. | ||||
| 	dockerDefault = "docker/default" | ||||
| 	// appArmorDefaultProfileName is name to use when creating a default apparmor profile. | ||||
| 	appArmorDefaultProfileName = "cri-containerd.apparmor.d" | ||||
| 	// unconfinedProfile is a string indicating one should run a pod/containerd without a security profile | ||||
| 	unconfinedProfile = "unconfined" | ||||
| 	// seccompDefaultProfile is the default seccomp profile. | ||||
| 	seccompDefaultProfile = dockerDefault | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	typeurl.Register(&containerstore.Metadata{}, | ||||
| 		"github.com/containerd/cri-containerd/pkg/store/container", "Metadata") | ||||
| } | ||||
|  | ||||
| // CreateContainer creates a new container in the given PodSandbox. | ||||
| func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (_ *runtime.CreateContainerResponse, retErr error) { | ||||
| 	config := r.GetConfig() | ||||
| 	sandboxConfig := r.GetSandboxConfig() | ||||
| 	sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to find sandbox id %q: %v", r.GetPodSandboxId(), err) | ||||
| 	} | ||||
| 	sandboxID := sandbox.ID | ||||
| 	s, err := sandbox.Container.Task(ctx, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get sandbox container task: %v", err) | ||||
| 	} | ||||
| 	sandboxPid := s.Pid() | ||||
|  | ||||
| 	// Generate unique id and name for the container and reserve the name. | ||||
| 	// Reserve the container name to avoid concurrent `CreateContainer` request creating | ||||
| 	// the same container. | ||||
| 	id := util.GenerateID() | ||||
| 	name := makeContainerName(config.GetMetadata(), sandboxConfig.GetMetadata()) | ||||
| 	logrus.Debugf("Generated id %q for container %q", id, name) | ||||
| 	if err = c.containerNameIndex.Reserve(name, id); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to reserve container name %q: %v", name, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		// Release the name if the function returns with an error. | ||||
| 		if retErr != nil { | ||||
| 			c.containerNameIndex.ReleaseByName(name) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Create initial internal container metadata. | ||||
| 	meta := containerstore.Metadata{ | ||||
| 		ID:        id, | ||||
| 		Name:      name, | ||||
| 		SandboxID: sandboxID, | ||||
| 		Config:    config, | ||||
| 	} | ||||
|  | ||||
| 	// Prepare container image snapshot. For container, the image should have | ||||
| 	// been pulled before creating the container, so do not ensure the image. | ||||
| 	imageRef := config.GetImage().GetImage() | ||||
| 	image, err := c.localResolve(ctx, imageRef) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to resolve image %q: %v", imageRef, err) | ||||
| 	} | ||||
| 	if image == nil { | ||||
| 		return nil, fmt.Errorf("image %q not found", imageRef) | ||||
| 	} | ||||
|  | ||||
| 	// Create container root directory. | ||||
| 	containerRootDir := getContainerRootDir(c.config.RootDir, id) | ||||
| 	if err = c.os.MkdirAll(containerRootDir, 0755); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create container root directory %q: %v", | ||||
| 			containerRootDir, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			// Cleanup the container root directory. | ||||
| 			if err = c.os.RemoveAll(containerRootDir); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to remove container root directory %q", | ||||
| 					containerRootDir) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Create container volumes mounts. | ||||
| 	volumeMounts := c.generateVolumeMounts(containerRootDir, config.GetMounts(), &image.ImageSpec.Config) | ||||
|  | ||||
| 	// Generate container runtime spec. | ||||
| 	mounts := c.generateContainerMounts(getSandboxRootDir(c.config.RootDir, sandboxID), config) | ||||
|  | ||||
| 	spec, err := c.generateContainerSpec(id, sandboxID, sandboxPid, config, sandboxConfig, &image.ImageSpec.Config, append(mounts, volumeMounts...)) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to generate container %q spec: %v", id, err) | ||||
| 	} | ||||
|  | ||||
| 	logrus.Debugf("Container %q spec: %#+v", id, spew.NewFormatter(spec)) | ||||
|  | ||||
| 	// Set snapshotter before any other options. | ||||
| 	opts := []containerd.NewContainerOpts{ | ||||
| 		containerd.WithSnapshotter(c.config.ContainerdConfig.Snapshotter), | ||||
| 		// Prepare container rootfs. This is always writeable even if | ||||
| 		// the container wants a readonly rootfs since we want to give | ||||
| 		// the runtime (runc) a chance to modify (e.g. to create mount | ||||
| 		// points corresponding to spec.Mounts) before making the | ||||
| 		// rootfs readonly (requested by spec.Root.Readonly). | ||||
| 		customopts.WithNewSnapshot(id, image.Image), | ||||
| 	} | ||||
|  | ||||
| 	if len(volumeMounts) > 0 { | ||||
| 		mountMap := make(map[string]string) | ||||
| 		for _, v := range volumeMounts { | ||||
| 			mountMap[v.HostPath] = v.ContainerPath | ||||
| 		} | ||||
| 		opts = append(opts, customopts.WithVolumes(mountMap)) | ||||
| 	} | ||||
| 	meta.ImageRef = image.ID | ||||
|  | ||||
| 	// Get container log path. | ||||
| 	if config.GetLogPath() != "" { | ||||
| 		meta.LogPath = filepath.Join(sandbox.Config.GetLogDirectory(), config.GetLogPath()) | ||||
| 	} | ||||
|  | ||||
| 	containerIO, err := cio.NewContainerIO(id, | ||||
| 		cio.WithNewFIFOs(containerRootDir, config.GetTty(), config.GetStdin())) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create container io: %v", err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			if err := containerIO.Close(); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to close container io %q", id) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	var specOpts []oci.SpecOpts | ||||
| 	securityContext := config.GetLinux().GetSecurityContext() | ||||
| 	// Set container username. This could only be done by containerd, because it needs | ||||
| 	// access to the container rootfs. Pass user name to containerd, and let it overwrite | ||||
| 	// the spec for us. | ||||
| 	if uid := securityContext.GetRunAsUser(); uid != nil { | ||||
| 		specOpts = append(specOpts, oci.WithUserID(uint32(uid.GetValue()))) | ||||
| 	} | ||||
| 	if username := securityContext.GetRunAsUsername(); username != "" { | ||||
| 		specOpts = append(specOpts, oci.WithUsername(username)) | ||||
| 	} | ||||
|  | ||||
| 	apparmorSpecOpts, err := generateApparmorSpecOpts( | ||||
| 		securityContext.GetApparmorProfile(), | ||||
| 		securityContext.GetPrivileged(), | ||||
| 		c.apparmorEnabled) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to generate apparmor spec opts: %v", err) | ||||
| 	} | ||||
| 	if apparmorSpecOpts != nil { | ||||
| 		specOpts = append(specOpts, apparmorSpecOpts) | ||||
| 	} | ||||
|  | ||||
| 	seccompSpecOpts, err := generateSeccompSpecOpts( | ||||
| 		securityContext.GetSeccompProfilePath(), | ||||
| 		securityContext.GetPrivileged(), | ||||
| 		c.seccompEnabled) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to generate seccomp spec opts: %v", err) | ||||
| 	} | ||||
| 	if seccompSpecOpts != nil { | ||||
| 		specOpts = append(specOpts, seccompSpecOpts) | ||||
| 	} | ||||
| 	containerLabels := buildLabels(config.Labels, containerKindContainer) | ||||
|  | ||||
| 	opts = append(opts, | ||||
| 		containerd.WithSpec(spec, specOpts...), | ||||
| 		containerd.WithRuntime( | ||||
| 			c.config.ContainerdConfig.Runtime, | ||||
| 			&runctypes.RuncOptions{ | ||||
| 				Runtime:       c.config.ContainerdConfig.RuntimeEngine, | ||||
| 				RuntimeRoot:   c.config.ContainerdConfig.RuntimeRoot, | ||||
| 				SystemdCgroup: c.config.SystemdCgroup}), // TODO (mikebrow): add CriuPath when we add support for pause | ||||
| 		containerd.WithContainerLabels(containerLabels), | ||||
| 		containerd.WithContainerExtension(containerMetadataExtension, &meta)) | ||||
| 	var cntr containerd.Container | ||||
| 	if cntr, err = c.client.NewContainer(ctx, id, opts...); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create containerd container: %v", err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			if err := cntr.Delete(ctx, containerd.WithSnapshotCleanup); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to delete containerd container %q", id) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	status := containerstore.Status{CreatedAt: time.Now().UnixNano()} | ||||
| 	container, err := containerstore.NewContainer(meta, | ||||
| 		containerstore.WithStatus(status, containerRootDir), | ||||
| 		containerstore.WithContainer(cntr), | ||||
| 		containerstore.WithContainerIO(containerIO), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create internal container object for %q: %v", | ||||
| 			id, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			// Cleanup container checkpoint on error. | ||||
| 			if err := container.Delete(); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to cleanup container checkpoint for %q", id) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Add container into container store. | ||||
| 	if err := c.containerStore.Add(container); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to add container %q into store: %v", id, err) | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.CreateContainerResponse{ContainerId: id}, nil | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) generateContainerSpec(id string, sandboxID string, sandboxPid uint32, config *runtime.ContainerConfig, | ||||
| 	sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig, extraMounts []*runtime.Mount) (*runtimespec.Spec, error) { | ||||
| 	// Creates a spec Generator with the default spec. | ||||
| 	spec, err := defaultRuntimeSpec(id) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	g := newSpecGenerator(spec) | ||||
|  | ||||
| 	// Set the relative path to the rootfs of the container from containerd's | ||||
| 	// pre-defined directory. | ||||
| 	g.SetRootPath(relativeRootfsPath) | ||||
|  | ||||
| 	if err := setOCIProcessArgs(&g, config, imageConfig); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if config.GetWorkingDir() != "" { | ||||
| 		g.SetProcessCwd(config.GetWorkingDir()) | ||||
| 	} else if imageConfig.WorkingDir != "" { | ||||
| 		g.SetProcessCwd(imageConfig.WorkingDir) | ||||
| 	} | ||||
|  | ||||
| 	g.SetProcessTerminal(config.GetTty()) | ||||
| 	if config.GetTty() { | ||||
| 		g.AddProcessEnv("TERM", "xterm") | ||||
| 	} | ||||
|  | ||||
| 	// Apply envs from image config first, so that envs from container config | ||||
| 	// can override them. | ||||
| 	if err := addImageEnvs(&g, imageConfig.Env); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for _, e := range config.GetEnvs() { | ||||
| 		g.AddProcessEnv(e.GetKey(), e.GetValue()) | ||||
| 	} | ||||
|  | ||||
| 	securityContext := config.GetLinux().GetSecurityContext() | ||||
| 	selinuxOpt := securityContext.GetSelinuxOptions() | ||||
| 	processLabel, mountLabel, err := initSelinuxOpts(selinuxOpt) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to init selinux options %+v: %v", securityContext.GetSelinuxOptions(), err) | ||||
| 	} | ||||
|  | ||||
| 	// Add extra mounts first so that CRI specified mounts can override. | ||||
| 	mounts := append(extraMounts, config.GetMounts()...) | ||||
| 	if err := c.addOCIBindMounts(&g, mounts, mountLabel); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set OCI bind mounts %+v: %v", mounts, err) | ||||
| 	} | ||||
|  | ||||
| 	if securityContext.GetPrivileged() { | ||||
| 		if !sandboxConfig.GetLinux().GetSecurityContext().GetPrivileged() { | ||||
| 			return nil, fmt.Errorf("no privileged container allowed in sandbox") | ||||
| 		} | ||||
| 		if err := setOCIPrivileged(&g, config); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} else { // not privileged | ||||
| 		if err := c.addOCIDevices(&g, config.GetDevices()); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to set devices mapping %+v: %v", config.GetDevices(), err) | ||||
| 		} | ||||
|  | ||||
| 		if err := setOCICapabilities(&g, securityContext.GetCapabilities()); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to set capabilities %+v: %v", | ||||
| 				securityContext.GetCapabilities(), err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	g.SetProcessSelinuxLabel(processLabel) | ||||
| 	g.SetLinuxMountLabel(mountLabel) | ||||
|  | ||||
| 	// TODO: Figure out whether we should set no new privilege for sandbox container by default | ||||
| 	g.SetProcessNoNewPrivileges(securityContext.GetNoNewPrivs()) | ||||
|  | ||||
| 	// TODO(random-liu): [P1] Set selinux options (privileged or not). | ||||
|  | ||||
| 	g.SetRootReadonly(securityContext.GetReadonlyRootfs()) | ||||
|  | ||||
| 	setOCILinuxResource(&g, config.GetLinux().GetResources()) | ||||
|  | ||||
| 	if sandboxConfig.GetLinux().GetCgroupParent() != "" { | ||||
| 		cgroupsPath := getCgroupsPath(sandboxConfig.GetLinux().GetCgroupParent(), id, | ||||
| 			c.config.SystemdCgroup) | ||||
| 		g.SetLinuxCgroupsPath(cgroupsPath) | ||||
| 	} | ||||
|  | ||||
| 	// Set namespaces, share namespace with sandbox container. | ||||
| 	setOCINamespaces(&g, securityContext.GetNamespaceOptions(), sandboxPid) | ||||
|  | ||||
| 	supplementalGroups := securityContext.GetSupplementalGroups() | ||||
| 	for _, group := range supplementalGroups { | ||||
| 		g.AddProcessAdditionalGid(uint32(group)) | ||||
| 	} | ||||
|  | ||||
| 	g.AddAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer) | ||||
| 	g.AddAnnotation(annotations.SandboxID, sandboxID) | ||||
|  | ||||
| 	return g.Spec(), nil | ||||
| } | ||||
|  | ||||
| // generateVolumeMounts sets up image volumes for container. Rely on the removal of container | ||||
| // root directory to do cleanup. Note that image volume will be skipped, if there is criMounts | ||||
| // specified with the same destination. | ||||
| func (c *criContainerdService) generateVolumeMounts(containerRootDir string, criMounts []*runtime.Mount, config *imagespec.ImageConfig) []*runtime.Mount { | ||||
| 	if len(config.Volumes) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	var mounts []*runtime.Mount | ||||
| 	for dst := range config.Volumes { | ||||
| 		if isInCRIMounts(dst, criMounts) { | ||||
| 			// Skip the image volume, if there is CRI defined volume mapping. | ||||
| 			// TODO(random-liu): This should be handled by Kubelet in the future. | ||||
| 			// Kubelet should decide what to use for image volume, and also de-duplicate | ||||
| 			// the image volume and user mounts. | ||||
| 			continue | ||||
| 		} | ||||
| 		volumeID := util.GenerateID() | ||||
| 		src := filepath.Join(containerRootDir, "volumes", volumeID) | ||||
| 		// addOCIBindMounts will create these volumes. | ||||
| 		mounts = append(mounts, &runtime.Mount{ | ||||
| 			ContainerPath: dst, | ||||
| 			HostPath:      src, | ||||
| 			// Use default mount propagation. | ||||
| 			// TODO(random-liu): What about selinux relabel? | ||||
| 		}) | ||||
| 	} | ||||
| 	return mounts | ||||
| } | ||||
|  | ||||
| // generateContainerMounts sets up necessary container mounts including /dev/shm, /etc/hosts | ||||
| // and /etc/resolv.conf. | ||||
| func (c *criContainerdService) generateContainerMounts(sandboxRootDir string, config *runtime.ContainerConfig) []*runtime.Mount { | ||||
| 	var mounts []*runtime.Mount | ||||
| 	securityContext := config.GetLinux().GetSecurityContext() | ||||
| 	if !isInCRIMounts(etcHosts, config.GetMounts()) { | ||||
| 		mounts = append(mounts, &runtime.Mount{ | ||||
| 			ContainerPath: etcHosts, | ||||
| 			HostPath:      getSandboxHosts(sandboxRootDir), | ||||
| 			Readonly:      securityContext.GetReadonlyRootfs(), | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	// Mount sandbox resolv.config. | ||||
| 	// TODO: Need to figure out whether we should always mount it as read-only | ||||
| 	if !isInCRIMounts(resolvConfPath, config.GetMounts()) { | ||||
| 		mounts = append(mounts, &runtime.Mount{ | ||||
| 			ContainerPath: resolvConfPath, | ||||
| 			HostPath:      getResolvPath(sandboxRootDir), | ||||
| 			Readonly:      securityContext.GetReadonlyRootfs(), | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	if !isInCRIMounts(devShm, config.GetMounts()) { | ||||
| 		sandboxDevShm := getSandboxDevShm(sandboxRootDir) | ||||
| 		if securityContext.GetNamespaceOptions().GetIpc() == runtime.NamespaceMode_NODE { | ||||
| 			sandboxDevShm = devShm | ||||
| 		} | ||||
| 		mounts = append(mounts, &runtime.Mount{ | ||||
| 			ContainerPath: devShm, | ||||
| 			HostPath:      sandboxDevShm, | ||||
| 			Readonly:      false, | ||||
| 		}) | ||||
| 	} | ||||
| 	return mounts | ||||
| } | ||||
|  | ||||
| // setOCIProcessArgs sets process args. It returns error if the final arg list | ||||
| // is empty. | ||||
| func setOCIProcessArgs(g *generate.Generator, config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) error { | ||||
| 	command, args := config.GetCommand(), config.GetArgs() | ||||
| 	// The following logic is migrated from https://github.com/moby/moby/blob/master/daemon/commit.go | ||||
| 	// TODO(random-liu): Clearly define the commands overwrite behavior. | ||||
| 	if len(command) == 0 { | ||||
| 		// Copy array to avoid data race. | ||||
| 		if len(args) == 0 { | ||||
| 			args = append([]string{}, imageConfig.Cmd...) | ||||
| 		} | ||||
| 		if command == nil { | ||||
| 			command = append([]string{}, imageConfig.Entrypoint...) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(command) == 0 && len(args) == 0 { | ||||
| 		return fmt.Errorf("no command specified") | ||||
| 	} | ||||
| 	g.SetProcessArgs(append(command, args...)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // addImageEnvs adds environment variables from image config. It returns error if | ||||
| // an invalid environment variable is encountered. | ||||
| func addImageEnvs(g *generate.Generator, imageEnvs []string) error { | ||||
| 	for _, e := range imageEnvs { | ||||
| 		kv := strings.SplitN(e, "=", 2) | ||||
| 		if len(kv) != 2 { | ||||
| 			return fmt.Errorf("invalid environment variable %q", e) | ||||
| 		} | ||||
| 		g.AddProcessEnv(kv[0], kv[1]) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func setOCIPrivileged(g *generate.Generator, config *runtime.ContainerConfig) error { | ||||
| 	// Add all capabilities in privileged mode. | ||||
| 	g.SetupPrivileged(true) | ||||
| 	setOCIBindMountsPrivileged(g) | ||||
| 	if err := setOCIDevicesPrivileged(g); err != nil { | ||||
| 		return fmt.Errorf("failed to set devices mapping %+v: %v", config.GetDevices(), err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func clearReadOnly(m *runtimespec.Mount) { | ||||
| 	var opt []string | ||||
| 	for _, o := range m.Options { | ||||
| 		if o != "ro" { | ||||
| 			opt = append(opt, o) | ||||
| 		} | ||||
| 	} | ||||
| 	m.Options = opt | ||||
| } | ||||
|  | ||||
| // addDevices set device mapping without privilege. | ||||
| func (c *criContainerdService) addOCIDevices(g *generate.Generator, devs []*runtime.Device) error { | ||||
| 	spec := g.Spec() | ||||
| 	for _, device := range devs { | ||||
| 		path, err := c.os.ResolveSymbolicLink(device.HostPath) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		dev, err := devices.DeviceFromPath(path, device.Permissions) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		rd := runtimespec.LinuxDevice{ | ||||
| 			Path:  device.ContainerPath, | ||||
| 			Type:  string(dev.Type), | ||||
| 			Major: dev.Major, | ||||
| 			Minor: dev.Minor, | ||||
| 			UID:   &dev.Uid, | ||||
| 			GID:   &dev.Gid, | ||||
| 		} | ||||
| 		g.AddDevice(rd) | ||||
| 		spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, runtimespec.LinuxDeviceCgroup{ | ||||
| 			Allow:  true, | ||||
| 			Type:   string(dev.Type), | ||||
| 			Major:  &dev.Major, | ||||
| 			Minor:  &dev.Minor, | ||||
| 			Access: dev.Permissions, | ||||
| 		}) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // addDevices set device mapping with privilege. | ||||
| func setOCIDevicesPrivileged(g *generate.Generator) error { | ||||
| 	spec := g.Spec() | ||||
| 	hostDevices, err := devices.HostDevices() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, hostDevice := range hostDevices { | ||||
| 		rd := runtimespec.LinuxDevice{ | ||||
| 			Path:  hostDevice.Path, | ||||
| 			Type:  string(hostDevice.Type), | ||||
| 			Major: hostDevice.Major, | ||||
| 			Minor: hostDevice.Minor, | ||||
| 			UID:   &hostDevice.Uid, | ||||
| 			GID:   &hostDevice.Gid, | ||||
| 		} | ||||
| 		if hostDevice.Major == 0 && hostDevice.Minor == 0 { | ||||
| 			// Invalid device, most likely a symbolic link, skip it. | ||||
| 			continue | ||||
| 		} | ||||
| 		g.AddDevice(rd) | ||||
| 	} | ||||
| 	spec.Linux.Resources.Devices = []runtimespec.LinuxDeviceCgroup{ | ||||
| 		{ | ||||
| 			Allow:  true, | ||||
| 			Access: "rwm", | ||||
| 		}, | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // addOCIBindMounts adds bind mounts. | ||||
| func (c *criContainerdService) addOCIBindMounts(g *generate.Generator, mounts []*runtime.Mount, mountLabel string) error { | ||||
| 	// Mount cgroup into the container as readonly, which inherits docker's behavior. | ||||
| 	g.AddCgroupsMount("ro") // nolint: errcheck | ||||
| 	for _, mount := range mounts { | ||||
| 		dst := mount.GetContainerPath() | ||||
| 		src := mount.GetHostPath() | ||||
| 		// Create the host path if it doesn't exist. | ||||
| 		// TODO(random-liu): Add CRI validation test for this case. | ||||
| 		if _, err := c.os.Stat(src); err != nil { | ||||
| 			if !os.IsNotExist(err) { | ||||
| 				return fmt.Errorf("failed to stat %q: %v", src, err) | ||||
| 			} | ||||
| 			if err := c.os.MkdirAll(src, 0755); err != nil { | ||||
| 				return fmt.Errorf("failed to mkdir %q: %v", src, err) | ||||
| 			} | ||||
| 		} | ||||
| 		// TODO(random-liu): Add cri-containerd integration test or cri validation test | ||||
| 		// for this. | ||||
| 		src, err := c.os.ResolveSymbolicLink(src) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to resolve symlink %q: %v", src, err) | ||||
| 		} | ||||
|  | ||||
| 		options := []string{"rbind"} | ||||
| 		switch mount.GetPropagation() { | ||||
| 		case runtime.MountPropagation_PROPAGATION_PRIVATE: | ||||
| 			options = append(options, "rprivate") | ||||
| 			// Since default root propogation in runc is rprivate ignore | ||||
| 			// setting the root propagation | ||||
| 		case runtime.MountPropagation_PROPAGATION_BIDIRECTIONAL: | ||||
| 			if err := ensureShared(src, c.os.LookupMount); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			options = append(options, "rshared") | ||||
| 			g.SetLinuxRootPropagation("rshared") // nolint: errcheck | ||||
| 		case runtime.MountPropagation_PROPAGATION_HOST_TO_CONTAINER: | ||||
| 			if err := ensureSharedOrSlave(src, c.os.LookupMount); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			options = append(options, "rslave") | ||||
| 			if g.Spec().Linux.RootfsPropagation != "rshared" && | ||||
| 				g.Spec().Linux.RootfsPropagation != "rslave" { | ||||
| 				g.SetLinuxRootPropagation("rslave") // nolint: errcheck | ||||
| 			} | ||||
| 		default: | ||||
| 			logrus.Warnf("Unknown propagation mode for hostPath %q", mount.HostPath) | ||||
| 			options = append(options, "rprivate") | ||||
| 		} | ||||
|  | ||||
| 		// NOTE(random-liu): we don't change all mounts to `ro` when root filesystem | ||||
| 		// is readonly. This is different from docker's behavior, but make more sense. | ||||
| 		if mount.GetReadonly() { | ||||
| 			options = append(options, "ro") | ||||
| 		} else { | ||||
| 			options = append(options, "rw") | ||||
| 		} | ||||
|  | ||||
| 		if mount.GetSelinuxRelabel() { | ||||
| 			if err := label.Relabel(src, mountLabel, true); err != nil && err != unix.ENOTSUP { | ||||
| 				return fmt.Errorf("relabel %q with %q failed: %v", src, mountLabel, err) | ||||
| 			} | ||||
| 		} | ||||
| 		g.AddBindMount(src, dst, options) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func setOCIBindMountsPrivileged(g *generate.Generator) { | ||||
| 	spec := g.Spec() | ||||
| 	// clear readonly for /sys and cgroup | ||||
| 	for i, m := range spec.Mounts { | ||||
| 		if spec.Mounts[i].Destination == "/sys" && !spec.Root.Readonly { | ||||
| 			clearReadOnly(&spec.Mounts[i]) | ||||
| 		} | ||||
| 		if m.Type == "cgroup" { | ||||
| 			clearReadOnly(&spec.Mounts[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	spec.Linux.ReadonlyPaths = nil | ||||
| 	spec.Linux.MaskedPaths = nil | ||||
| } | ||||
|  | ||||
| // setOCILinuxResource set container resource limit. | ||||
| func setOCILinuxResource(g *generate.Generator, resources *runtime.LinuxContainerResources) { | ||||
| 	if resources == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	g.SetLinuxResourcesCPUPeriod(uint64(resources.GetCpuPeriod())) | ||||
| 	g.SetLinuxResourcesCPUQuota(resources.GetCpuQuota()) | ||||
| 	g.SetLinuxResourcesCPUShares(uint64(resources.GetCpuShares())) | ||||
| 	g.SetLinuxResourcesMemoryLimit(resources.GetMemoryLimitInBytes()) | ||||
| 	g.SetProcessOOMScoreAdj(int(resources.GetOomScoreAdj())) | ||||
| 	g.SetLinuxResourcesCPUCpus(resources.GetCpusetCpus()) | ||||
| 	g.SetLinuxResourcesCPUMems(resources.GetCpusetMems()) | ||||
| } | ||||
|  | ||||
| // getOCICapabilitiesList returns a list of all available capabilities. | ||||
| func getOCICapabilitiesList() []string { | ||||
| 	var caps []string | ||||
| 	for _, cap := range capability.List() { | ||||
| 		if cap > validate.LastCap() { | ||||
| 			continue | ||||
| 		} | ||||
| 		caps = append(caps, "CAP_"+strings.ToUpper(cap.String())) | ||||
| 	} | ||||
| 	return caps | ||||
| } | ||||
|  | ||||
| // setOCICapabilities adds/drops process capabilities. | ||||
| func setOCICapabilities(g *generate.Generator, capabilities *runtime.Capability) error { | ||||
| 	if capabilities == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Add/drop all capabilities if "all" is specified, so that | ||||
| 	// following individual add/drop could still work. E.g. | ||||
| 	// AddCapabilities: []string{"ALL"}, DropCapabilities: []string{"CHOWN"} | ||||
| 	// will be all capabilities without `CAP_CHOWN`. | ||||
| 	if util.InStringSlice(capabilities.GetAddCapabilities(), "ALL") { | ||||
| 		for _, c := range getOCICapabilitiesList() { | ||||
| 			if err := g.AddProcessCapability(c); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if util.InStringSlice(capabilities.GetDropCapabilities(), "ALL") { | ||||
| 		for _, c := range getOCICapabilitiesList() { | ||||
| 			if err := g.DropProcessCapability(c); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for _, c := range capabilities.GetAddCapabilities() { | ||||
| 		if strings.ToUpper(c) == "ALL" { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Capabilities in CRI doesn't have `CAP_` prefix, so add it. | ||||
| 		if err := g.AddProcessCapability("CAP_" + strings.ToUpper(c)); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for _, c := range capabilities.GetDropCapabilities() { | ||||
| 		if strings.ToUpper(c) == "ALL" { | ||||
| 			continue | ||||
| 		} | ||||
| 		if err := g.DropProcessCapability("CAP_" + strings.ToUpper(c)); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // setOCINamespaces sets namespaces. | ||||
| func setOCINamespaces(g *generate.Generator, namespaces *runtime.NamespaceOption, sandboxPid uint32) { | ||||
| 	g.AddOrReplaceLinuxNamespace(string(runtimespec.NetworkNamespace), getNetworkNamespace(sandboxPid)) // nolint: errcheck | ||||
| 	g.AddOrReplaceLinuxNamespace(string(runtimespec.IPCNamespace), getIPCNamespace(sandboxPid))         // nolint: errcheck | ||||
| 	g.AddOrReplaceLinuxNamespace(string(runtimespec.UTSNamespace), getUTSNamespace(sandboxPid))         // nolint: errcheck | ||||
| 	// Do not share pid namespace if namespace mode is CONTAINER. | ||||
| 	if namespaces.GetPid() != runtime.NamespaceMode_CONTAINER { | ||||
| 		g.AddOrReplaceLinuxNamespace(string(runtimespec.PIDNamespace), getPIDNamespace(sandboxPid)) // nolint: errcheck | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // defaultRuntimeSpec returns a default runtime spec used in cri-containerd. | ||||
| func defaultRuntimeSpec(id string) (*runtimespec.Spec, error) { | ||||
| 	// GenerateSpec needs namespace. | ||||
| 	ctx := namespaces.WithNamespace(context.Background(), k8sContainerdNamespace) | ||||
| 	spec, err := oci.GenerateSpec(ctx, nil, &containers.Container{ID: id}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// Remove `/run` mount | ||||
| 	// TODO(random-liu): Mount tmpfs for /run and handle copy-up. | ||||
| 	var mounts []runtimespec.Mount | ||||
| 	for _, mount := range spec.Mounts { | ||||
| 		if mount.Destination == "/run" { | ||||
| 			continue | ||||
| 		} | ||||
| 		mounts = append(mounts, mount) | ||||
| 	} | ||||
| 	spec.Mounts = mounts | ||||
|  | ||||
| 	// Make sure no default seccomp/apparmor is specified | ||||
| 	if spec.Process != nil { | ||||
| 		spec.Process.ApparmorProfile = "" | ||||
| 	} | ||||
| 	if spec.Linux != nil { | ||||
| 		spec.Linux.Seccomp = nil | ||||
| 	} | ||||
|  | ||||
| 	// Remove default rlimits (See issue #515) | ||||
| 	spec.Process.Rlimits = nil | ||||
|  | ||||
| 	return spec, nil | ||||
| } | ||||
|  | ||||
| // generateSeccompSpecOpts generates containerd SpecOpts for seccomp. | ||||
| func generateSeccompSpecOpts(seccompProf string, privileged, seccompEnabled bool) (oci.SpecOpts, error) { | ||||
| 	if privileged { | ||||
| 		// Do not set seccomp profile when container is privileged | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	// Set seccomp profile | ||||
| 	if seccompProf == runtimeDefault || seccompProf == dockerDefault { | ||||
| 		// use correct default profile (Eg. if not configured otherwise, the default is docker/default) | ||||
| 		seccompProf = seccompDefaultProfile | ||||
| 	} | ||||
| 	if !seccompEnabled { | ||||
| 		if seccompProf != "" && seccompProf != unconfinedProfile { | ||||
| 			return nil, fmt.Errorf("seccomp is not supported") | ||||
| 		} | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	switch seccompProf { | ||||
| 	case "", unconfinedProfile: | ||||
| 		// Do not set seccomp profile. | ||||
| 		return nil, nil | ||||
| 	case dockerDefault: | ||||
| 		// Note: WithDefaultProfile specOpts must be added after capabilities | ||||
| 		return seccomp.WithDefaultProfile(), nil | ||||
| 	default: | ||||
| 		// Require and Trim default profile name prefix | ||||
| 		if !strings.HasPrefix(seccompProf, profileNamePrefix) { | ||||
| 			return nil, fmt.Errorf("invalid seccomp profile %q", seccompProf) | ||||
| 		} | ||||
| 		return seccomp.WithProfile(strings.TrimPrefix(seccompProf, profileNamePrefix)), nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // generateApparmorSpecOpts generates containerd SpecOpts for apparmor. | ||||
| func generateApparmorSpecOpts(apparmorProf string, privileged, apparmorEnabled bool) (oci.SpecOpts, error) { | ||||
| 	if !apparmorEnabled { | ||||
| 		// Should fail loudly if user try to specify apparmor profile | ||||
| 		// but we don't support it. | ||||
| 		if apparmorProf != "" && apparmorProf != unconfinedProfile { | ||||
| 			return nil, fmt.Errorf("apparmor is not supported") | ||||
| 		} | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	switch apparmorProf { | ||||
| 	case runtimeDefault: | ||||
| 		// TODO (mikebrow): delete created apparmor default profile | ||||
| 		return apparmor.WithDefaultProfile(appArmorDefaultProfileName), nil | ||||
| 	case unconfinedProfile: | ||||
| 		return nil, nil | ||||
| 	case "": | ||||
| 		// Based on kubernetes#51746, default apparmor profile should be applied | ||||
| 		// for non-privileged container when apparmor is not specified. | ||||
| 		if privileged { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		return apparmor.WithDefaultProfile(appArmorDefaultProfileName), nil | ||||
| 	default: | ||||
| 		// Require and Trim default profile name prefix | ||||
| 		if !strings.HasPrefix(apparmorProf, profileNamePrefix) { | ||||
| 			return nil, fmt.Errorf("invalid apparmor profile %q", apparmorProf) | ||||
| 		} | ||||
| 		return apparmor.WithProfile(strings.TrimPrefix(apparmorProf, profileNamePrefix)), nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Ensure mount point on which path is mounted, is shared. | ||||
| func ensureShared(path string, lookupMount func(string) (mount.Info, error)) error { | ||||
| 	mountInfo, err := lookupMount(path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Make sure source mount point is shared. | ||||
| 	optsSplit := strings.Split(mountInfo.Optional, " ") | ||||
| 	for _, opt := range optsSplit { | ||||
| 		if strings.HasPrefix(opt, "shared:") { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Errorf("path %q is mounted on %q but it is not a shared mount", path, mountInfo.Mountpoint) | ||||
| } | ||||
|  | ||||
| // Ensure mount point on which path is mounted, is either shared or slave. | ||||
| func ensureSharedOrSlave(path string, lookupMount func(string) (mount.Info, error)) error { | ||||
| 	mountInfo, err := lookupMount(path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// Make sure source mount point is shared. | ||||
| 	optsSplit := strings.Split(mountInfo.Optional, " ") | ||||
| 	for _, opt := range optsSplit { | ||||
| 		if strings.HasPrefix(opt, "shared:") { | ||||
| 			return nil | ||||
| 		} else if strings.HasPrefix(opt, "master:") { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	return fmt.Errorf("path %q is mounted on %q but it is not a shared or slave mount", path, mountInfo.Mountpoint) | ||||
| } | ||||
							
								
								
									
										37
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_exec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_exec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // Exec prepares a streaming endpoint to execute a command in the container, and returns the address. | ||||
| func (c *criContainerdService) Exec(ctx context.Context, r *runtime.ExecRequest) (*runtime.ExecResponse, error) { | ||||
| 	cntr, err := c.containerStore.Get(r.GetContainerId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to find container %q in store: %v", r.GetContainerId(), err) | ||||
| 	} | ||||
| 	state := cntr.Status.Get().State() | ||||
| 	if state != runtime.ContainerState_CONTAINER_RUNNING { | ||||
| 		return nil, fmt.Errorf("container is in %s state", criContainerStateToString(state)) | ||||
| 	} | ||||
| 	return c.streamServer.GetExec(r) | ||||
| } | ||||
							
								
								
									
										192
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_execsync.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_execsync.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	containerdio "github.com/containerd/containerd/cio" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"golang.org/x/sys/unix" | ||||
| 	"k8s.io/client-go/tools/remotecommand" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	cioutil "github.com/containerd/cri-containerd/pkg/ioutil" | ||||
| 	cio "github.com/containerd/cri-containerd/pkg/server/io" | ||||
| 	"github.com/containerd/cri-containerd/pkg/util" | ||||
| ) | ||||
|  | ||||
| // ExecSync executes a command in the container, and returns the stdout output. | ||||
| // If command exits with a non-zero exit code, an error is returned. | ||||
| func (c *criContainerdService) ExecSync(ctx context.Context, r *runtime.ExecSyncRequest) (*runtime.ExecSyncResponse, error) { | ||||
| 	var stdout, stderr bytes.Buffer | ||||
| 	exitCode, err := c.execInContainer(ctx, r.GetContainerId(), execOptions{ | ||||
| 		cmd:     r.GetCmd(), | ||||
| 		stdout:  cioutil.NewNopWriteCloser(&stdout), | ||||
| 		stderr:  cioutil.NewNopWriteCloser(&stderr), | ||||
| 		timeout: time.Duration(r.GetTimeout()) * time.Second, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to exec in container: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.ExecSyncResponse{ | ||||
| 		Stdout:   stdout.Bytes(), | ||||
| 		Stderr:   stderr.Bytes(), | ||||
| 		ExitCode: int32(*exitCode), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // execOptions specifies how to execute command in container. | ||||
| type execOptions struct { | ||||
| 	cmd     []string | ||||
| 	stdin   io.Reader | ||||
| 	stdout  io.WriteCloser | ||||
| 	stderr  io.WriteCloser | ||||
| 	tty     bool | ||||
| 	resize  <-chan remotecommand.TerminalSize | ||||
| 	timeout time.Duration | ||||
| } | ||||
|  | ||||
| // execInContainer executes a command inside the container synchronously, and | ||||
| // redirects stdio stream properly. | ||||
| func (c *criContainerdService) execInContainer(ctx context.Context, id string, opts execOptions) (*uint32, error) { | ||||
| 	// Cancel the context before returning to ensure goroutines are stopped. | ||||
| 	// This is important, because if `Start` returns error, `Wait` will hang | ||||
| 	// forever unless we cancel the context. | ||||
| 	ctx, cancel := context.WithCancel(ctx) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	// Get container from our container store. | ||||
| 	cntr, err := c.containerStore.Get(id) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to find container %q in store: %v", id, err) | ||||
| 	} | ||||
| 	id = cntr.ID | ||||
|  | ||||
| 	state := cntr.Status.Get().State() | ||||
| 	if state != runtime.ContainerState_CONTAINER_RUNNING { | ||||
| 		return nil, fmt.Errorf("container is in %s state", criContainerStateToString(state)) | ||||
| 	} | ||||
|  | ||||
| 	container := cntr.Container | ||||
| 	spec, err := container.Spec(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get container spec: %v", err) | ||||
| 	} | ||||
| 	task, err := container.Task(ctx, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to load task: %v", err) | ||||
| 	} | ||||
| 	if opts.tty { | ||||
| 		g := newSpecGenerator(spec) | ||||
| 		g.AddProcessEnv("TERM", "xterm") | ||||
| 		spec = g.Spec() | ||||
| 	} | ||||
| 	pspec := spec.Process | ||||
| 	pspec.Args = opts.cmd | ||||
| 	pspec.Terminal = opts.tty | ||||
|  | ||||
| 	if opts.stdout == nil { | ||||
| 		opts.stdout = cio.NewDiscardLogger() | ||||
| 	} | ||||
| 	if opts.stderr == nil { | ||||
| 		opts.stderr = cio.NewDiscardLogger() | ||||
| 	} | ||||
| 	execID := util.GenerateID() | ||||
| 	logrus.Debugf("Generated exec id %q for container %q", execID, id) | ||||
| 	rootDir := getContainerRootDir(c.config.RootDir, id) | ||||
| 	var execIO *cio.ExecIO | ||||
| 	process, err := task.Exec(ctx, execID, pspec, | ||||
| 		func(id string) (containerdio.IO, error) { | ||||
| 			var err error | ||||
| 			execIO, err = cio.NewExecIO(id, rootDir, opts.tty, opts.stdin != nil) | ||||
| 			return execIO, err | ||||
| 		}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create exec %q: %v", execID, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if _, err := process.Delete(ctx); err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	exitCh, err := process.Wait(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to wait for process %q: %v", execID, err) | ||||
| 	} | ||||
| 	if err := process.Start(ctx); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to start exec %q: %v", execID, err) | ||||
| 	} | ||||
|  | ||||
| 	handleResizing(opts.resize, func(size remotecommand.TerminalSize) { | ||||
| 		if err := process.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to resize process %q console for container %q", execID, id) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	attachDone := execIO.Attach(cio.AttachOptions{ | ||||
| 		Stdin:     opts.stdin, | ||||
| 		Stdout:    opts.stdout, | ||||
| 		Stderr:    opts.stderr, | ||||
| 		Tty:       opts.tty, | ||||
| 		StdinOnce: true, | ||||
| 		CloseStdin: func() error { | ||||
| 			return process.CloseIO(ctx, containerd.WithStdinCloser) | ||||
| 		}, | ||||
| 	}) | ||||
|  | ||||
| 	var timeoutCh <-chan time.Time | ||||
| 	if opts.timeout == 0 { | ||||
| 		// Do not set timeout if it's 0. | ||||
| 		timeoutCh = make(chan time.Time) | ||||
| 	} else { | ||||
| 		timeoutCh = time.After(opts.timeout) | ||||
| 	} | ||||
| 	select { | ||||
| 	case <-timeoutCh: | ||||
| 		//TODO(Abhi) Use context.WithDeadline instead of timeout. | ||||
| 		// Ignore the not found error because the process may exit itself before killing. | ||||
| 		if err := process.Kill(ctx, unix.SIGKILL); err != nil && !errdefs.IsNotFound(err) { | ||||
| 			return nil, fmt.Errorf("failed to kill exec %q: %v", execID, err) | ||||
| 		} | ||||
| 		// Wait for the process to be killed. | ||||
| 		exitRes := <-exitCh | ||||
| 		logrus.Infof("Timeout received while waiting for exec process kill %q code %d and error %v", | ||||
| 			execID, exitRes.ExitCode(), exitRes.Error()) | ||||
| 		<-attachDone | ||||
| 		logrus.Debugf("Stream pipe for exec process %q done", execID) | ||||
| 		return nil, fmt.Errorf("timeout %v exceeded", opts.timeout) | ||||
| 	case exitRes := <-exitCh: | ||||
| 		code, _, err := exitRes.Result() | ||||
| 		logrus.Infof("Exec process %q exits with exit code %d and error %v", execID, code, err) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed while waiting for exec %q: %v", execID, err) | ||||
| 		} | ||||
| 		<-attachDone | ||||
| 		logrus.Debugf("Stream pipe for exec process %q done", execID) | ||||
| 		return &code, nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										101
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"golang.org/x/net/context" | ||||
|  | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| ) | ||||
|  | ||||
| // ListContainers lists all containers matching the filter. | ||||
| func (c *criContainerdService) ListContainers(ctx context.Context, r *runtime.ListContainersRequest) (*runtime.ListContainersResponse, error) { | ||||
| 	// List all containers from store. | ||||
| 	containersInStore := c.containerStore.List() | ||||
|  | ||||
| 	var containers []*runtime.Container | ||||
| 	for _, container := range containersInStore { | ||||
| 		containers = append(containers, toCRIContainer(container)) | ||||
| 	} | ||||
|  | ||||
| 	containers = c.filterCRIContainers(containers, r.GetFilter()) | ||||
| 	return &runtime.ListContainersResponse{Containers: containers}, nil | ||||
| } | ||||
|  | ||||
| // toCRIContainer converts internal container object into CRI container. | ||||
| func toCRIContainer(container containerstore.Container) *runtime.Container { | ||||
| 	status := container.Status.Get() | ||||
| 	return &runtime.Container{ | ||||
| 		Id:           container.ID, | ||||
| 		PodSandboxId: container.SandboxID, | ||||
| 		Metadata:     container.Config.GetMetadata(), | ||||
| 		Image:        container.Config.GetImage(), | ||||
| 		ImageRef:     container.ImageRef, | ||||
| 		State:        status.State(), | ||||
| 		CreatedAt:    status.CreatedAt, | ||||
| 		Labels:       container.Config.GetLabels(), | ||||
| 		Annotations:  container.Config.GetAnnotations(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) normalizeContainerFilter(filter *runtime.ContainerFilter) { | ||||
| 	if cntr, err := c.containerStore.Get(filter.GetId()); err == nil { | ||||
| 		filter.Id = cntr.ID | ||||
| 	} | ||||
| 	if sb, err := c.sandboxStore.Get(filter.GetPodSandboxId()); err == nil { | ||||
| 		filter.PodSandboxId = sb.ID | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // filterCRIContainers filters CRIContainers. | ||||
| func (c *criContainerdService) filterCRIContainers(containers []*runtime.Container, filter *runtime.ContainerFilter) []*runtime.Container { | ||||
| 	if filter == nil { | ||||
| 		return containers | ||||
| 	} | ||||
|  | ||||
| 	c.normalizeContainerFilter(filter) | ||||
| 	filtered := []*runtime.Container{} | ||||
| 	for _, cntr := range containers { | ||||
| 		if filter.GetId() != "" && filter.GetId() != cntr.Id { | ||||
| 			continue | ||||
| 		} | ||||
| 		if filter.GetPodSandboxId() != "" && filter.GetPodSandboxId() != cntr.PodSandboxId { | ||||
| 			continue | ||||
| 		} | ||||
| 		if filter.GetState() != nil && filter.GetState().GetState() != cntr.State { | ||||
| 			continue | ||||
| 		} | ||||
| 		if filter.GetLabelSelector() != nil { | ||||
| 			match := true | ||||
| 			for k, v := range filter.GetLabelSelector() { | ||||
| 				got, ok := cntr.Labels[k] | ||||
| 				if !ok || got != v { | ||||
| 					match = false | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			if !match { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		filtered = append(filtered, cntr) | ||||
| 	} | ||||
|  | ||||
| 	return filtered | ||||
| } | ||||
							
								
								
									
										31
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_log_reopen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_log_reopen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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 server | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"golang.org/x/net/context" | ||||
|  | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // ReopenContainerLog asks cri-containerd to reopen the stdout/stderr log file for the container. | ||||
| // This is often called after the log file has been rotated. | ||||
| // TODO(random-liu): Implement this for kubelet log rotation. | ||||
| func (c *criContainerdService) ReopenContainerLog(ctx context.Context, r *runtime.ReopenContainerLogRequest) (*runtime.ReopenContainerLogResponse, error) { | ||||
| 	return nil, errors.New("not implemented") | ||||
| } | ||||
							
								
								
									
										116
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_remove.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_remove.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/docker/docker/pkg/system" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/log" | ||||
| 	"github.com/containerd/cri-containerd/pkg/store" | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| ) | ||||
|  | ||||
| // RemoveContainer removes the container. | ||||
| // TODO(random-liu): Forcibly stop container if it's running. | ||||
| func (c *criContainerdService) RemoveContainer(ctx context.Context, r *runtime.RemoveContainerRequest) (_ *runtime.RemoveContainerResponse, retErr error) { | ||||
| 	container, err := c.containerStore.Get(r.GetContainerId()) | ||||
| 	if err != nil { | ||||
| 		if err != store.ErrNotExist { | ||||
| 			return nil, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err) | ||||
| 		} | ||||
| 		// Do not return error if container metadata doesn't exist. | ||||
| 		log.Tracef("RemoveContainer called for container %q that does not exist", r.GetContainerId()) | ||||
| 		return &runtime.RemoveContainerResponse{}, nil | ||||
| 	} | ||||
| 	id := container.ID | ||||
|  | ||||
| 	// Set removing state to prevent other start/remove operations against this container | ||||
| 	// while it's being removed. | ||||
| 	if err := setContainerRemoving(container); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set removing state for container %q: %v", id, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			// Reset removing if remove failed. | ||||
| 			if err := resetContainerRemoving(container); err != nil { | ||||
| 				logrus.WithError(err).Errorf("failed to reset removing state for container %q", id) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// NOTE(random-liu): Docker set container to "Dead" state when start removing the | ||||
| 	// container so as to avoid start/restart the container again. However, for current | ||||
| 	// kubelet implementation, we'll never start a container once we decide to remove it, | ||||
| 	// so we don't need the "Dead" state for now. | ||||
|  | ||||
| 	// Delete containerd container. | ||||
| 	if err := container.Container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil { | ||||
| 		if !errdefs.IsNotFound(err) { | ||||
| 			return nil, fmt.Errorf("failed to delete containerd container %q: %v", id, err) | ||||
| 		} | ||||
| 		log.Tracef("Remove called for containerd container %q that does not exist", id) | ||||
| 	} | ||||
|  | ||||
| 	// Delete container checkpoint. | ||||
| 	if err := container.Delete(); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to delete container checkpoint for %q: %v", id, err) | ||||
| 	} | ||||
|  | ||||
| 	containerRootDir := getContainerRootDir(c.config.RootDir, id) | ||||
| 	if err := system.EnsureRemoveAll(containerRootDir); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to remove container root directory %q: %v", | ||||
| 			containerRootDir, err) | ||||
| 	} | ||||
|  | ||||
| 	c.containerStore.Delete(id) | ||||
|  | ||||
| 	c.containerNameIndex.ReleaseByKey(id) | ||||
|  | ||||
| 	return &runtime.RemoveContainerResponse{}, nil | ||||
| } | ||||
|  | ||||
| // setContainerRemoving sets the container into removing state. In removing state, the | ||||
| // container will not be started or removed again. | ||||
| func setContainerRemoving(container containerstore.Container) error { | ||||
| 	return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) { | ||||
| 		// Do not remove container if it's still running. | ||||
| 		if status.State() == runtime.ContainerState_CONTAINER_RUNNING { | ||||
| 			return status, fmt.Errorf("container is still running") | ||||
| 		} | ||||
| 		if status.Removing { | ||||
| 			return status, fmt.Errorf("container is already in removing state") | ||||
| 		} | ||||
| 		status.Removing = true | ||||
| 		return status, nil | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // resetContainerRemoving resets the container removing state on remove failure. So | ||||
| // that we could remove the container again. | ||||
| func resetContainerRemoving(container containerstore.Container) error { | ||||
| 	return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) { | ||||
| 		status.Removing = false | ||||
| 		return status, nil | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										167
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_start.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_start.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	containerdio "github.com/containerd/containerd/cio" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	cio "github.com/containerd/cri-containerd/pkg/server/io" | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // StartContainer starts the container. | ||||
| func (c *criContainerdService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (retRes *runtime.StartContainerResponse, retErr error) { | ||||
| 	container, err := c.containerStore.Get(r.GetContainerId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err) | ||||
| 	} | ||||
|  | ||||
| 	var startErr error | ||||
| 	// update container status in one transaction to avoid race with event monitor. | ||||
| 	if err := container.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) { | ||||
| 		// Always apply status change no matter startContainer fails or not. Because startContainer | ||||
| 		// may change container state no matter it fails or succeeds. | ||||
| 		startErr = c.startContainer(ctx, container, &status) | ||||
| 		return status, nil | ||||
| 	}); startErr != nil { | ||||
| 		return nil, startErr | ||||
| 	} else if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to update container %q metadata: %v", container.ID, err) | ||||
| 	} | ||||
| 	return &runtime.StartContainerResponse{}, nil | ||||
| } | ||||
|  | ||||
| // startContainer actually starts the container. The function needs to be run in one transaction. Any updates | ||||
| // to the status passed in will be applied no matter the function returns error or not. | ||||
| func (c *criContainerdService) startContainer(ctx context.Context, | ||||
| 	cntr containerstore.Container, | ||||
| 	status *containerstore.Status) (retErr error) { | ||||
| 	id := cntr.ID | ||||
| 	meta := cntr.Metadata | ||||
| 	container := cntr.Container | ||||
| 	config := meta.Config | ||||
|  | ||||
| 	// Return error if container is not in created state. | ||||
| 	if status.State() != runtime.ContainerState_CONTAINER_CREATED { | ||||
| 		return fmt.Errorf("container %q is in %s state", id, criContainerStateToString(status.State())) | ||||
| 	} | ||||
| 	// Do not start the container when there is a removal in progress. | ||||
| 	if status.Removing { | ||||
| 		return fmt.Errorf("container %q is in removing state", id) | ||||
| 	} | ||||
|  | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			// Set container to exited if fail to start. | ||||
| 			status.Pid = 0 | ||||
| 			status.FinishedAt = time.Now().UnixNano() | ||||
| 			status.ExitCode = errorStartExitCode | ||||
| 			status.Reason = errorStartReason | ||||
| 			status.Message = retErr.Error() | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Get sandbox config from sandbox store. | ||||
| 	sandbox, err := c.sandboxStore.Get(meta.SandboxID) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("sandbox %q not found: %v", meta.SandboxID, err) | ||||
| 	} | ||||
| 	sandboxID := meta.SandboxID | ||||
| 	if sandbox.Status.Get().State != sandboxstore.StateReady { | ||||
| 		return fmt.Errorf("sandbox container %q is not running", sandboxID) | ||||
| 	} | ||||
|  | ||||
| 	ioCreation := func(id string) (_ containerdio.IO, err error) { | ||||
| 		stdoutWC, stderrWC, err := createContainerLoggers(meta.LogPath, config.GetTty()) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to create container loggers: %v", err) | ||||
| 		} | ||||
| 		defer func() { | ||||
| 			if err != nil { | ||||
| 				if stdoutWC != nil { | ||||
| 					stdoutWC.Close() | ||||
| 				} | ||||
| 				if stderrWC != nil { | ||||
| 					stderrWC.Close() | ||||
| 				} | ||||
| 			} | ||||
| 		}() | ||||
| 		if err := cio.WithOutput("log", stdoutWC, stderrWC)(cntr.IO); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to add container log: %v", err) | ||||
| 		} | ||||
| 		cntr.IO.Pipe() | ||||
| 		return cntr.IO, nil | ||||
| 	} | ||||
|  | ||||
| 	task, err := container.NewTask(ctx, ioCreation) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to create containerd task: %v", err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			// It's possible that task is deleted by event monitor. | ||||
| 			if _, err := task.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) { | ||||
| 				logrus.WithError(err).Errorf("Failed to delete containerd task %q", id) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Start containerd task. | ||||
| 	if err := task.Start(ctx); err != nil { | ||||
| 		return fmt.Errorf("failed to start containerd task %q: %v", id, err) | ||||
| 	} | ||||
|  | ||||
| 	// Update container start timestamp. | ||||
| 	status.Pid = task.Pid() | ||||
| 	status.StartedAt = time.Now().UnixNano() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Create container loggers and return write closer for stdout and stderr. | ||||
| func createContainerLoggers(logPath string, tty bool) (stdout io.WriteCloser, stderr io.WriteCloser, err error) { | ||||
| 	if logPath != "" { | ||||
| 		// Only generate container log when log path is specified. | ||||
| 		if stdout, err = cio.NewCRILogger(logPath, cio.Stdout); err != nil { | ||||
| 			return nil, nil, fmt.Errorf("failed to start container stdout logger: %v", err) | ||||
| 		} | ||||
| 		defer func() { | ||||
| 			if err != nil { | ||||
| 				stdout.Close() | ||||
| 			} | ||||
| 		}() | ||||
| 		// Only redirect stderr when there is no tty. | ||||
| 		if !tty { | ||||
| 			if stderr, err = cio.NewCRILogger(logPath, cio.Stderr); err != nil { | ||||
| 				return nil, nil, fmt.Errorf("failed to start container stderr logger: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		stdout = cio.NewDiscardLogger() | ||||
| 		stderr = cio.NewDiscardLogger() | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										48
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_stats.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_stats.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	tasks "github.com/containerd/containerd/api/services/tasks/v1" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // ContainerStats returns stats of the container. If the container does not | ||||
| // exist, the call returns an error. | ||||
| func (c *criContainerdService) ContainerStats(ctx context.Context, in *runtime.ContainerStatsRequest) (*runtime.ContainerStatsResponse, error) { | ||||
| 	cntr, err := c.containerStore.Get(in.GetContainerId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to find container: %v", err) | ||||
| 	} | ||||
| 	request := &tasks.MetricsRequest{Filters: []string{"id==" + cntr.ID}} | ||||
| 	resp, err := c.client.TaskService().Metrics(ctx, request) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to fetch metrics for task: %v", err) | ||||
| 	} | ||||
| 	if len(resp.Metrics) != 1 { | ||||
| 		return nil, fmt.Errorf("unexpected metrics response: %+v", resp.Metrics) | ||||
| 	} | ||||
|  | ||||
| 	cs, err := c.getContainerMetrics(cntr.Metadata, resp.Metrics[0]) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to decode container metrics: %v", err) | ||||
| 	} | ||||
| 	return &runtime.ContainerStatsResponse{Stats: cs}, nil | ||||
| } | ||||
							
								
								
									
										170
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_stats_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_stats_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/containerd/cgroups" | ||||
| 	tasks "github.com/containerd/containerd/api/services/tasks/v1" | ||||
| 	"github.com/containerd/containerd/api/types" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| ) | ||||
|  | ||||
| // ListContainerStats returns stats of all running containers. | ||||
| func (c *criContainerdService) ListContainerStats( | ||||
| 	ctx context.Context, | ||||
| 	in *runtime.ListContainerStatsRequest, | ||||
| ) (*runtime.ListContainerStatsResponse, error) { | ||||
| 	request, containers, err := c.buildTaskMetricsRequest(in) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to build metrics request: %v", err) | ||||
| 	} | ||||
| 	resp, err := c.client.TaskService().Metrics(ctx, &request) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to fetch metrics for tasks: %v", err) | ||||
| 	} | ||||
| 	criStats, err := c.toCRIContainerStats(resp.Metrics, containers) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to convert to cri containerd stats format: %v", err) | ||||
| 	} | ||||
| 	return criStats, nil | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) toCRIContainerStats( | ||||
| 	stats []*types.Metric, | ||||
| 	containers []containerstore.Container, | ||||
| ) (*runtime.ListContainerStatsResponse, error) { | ||||
| 	statsMap := make(map[string]*types.Metric) | ||||
| 	for _, stat := range stats { | ||||
| 		statsMap[stat.ID] = stat | ||||
| 	} | ||||
| 	containerStats := new(runtime.ListContainerStatsResponse) | ||||
| 	for _, cntr := range containers { | ||||
| 		cs, err := c.getContainerMetrics(cntr.Metadata, statsMap[cntr.ID]) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to decode container metrics for %q: %v", cntr.ID, err) | ||||
| 		} | ||||
| 		containerStats.Stats = append(containerStats.Stats, cs) | ||||
| 	} | ||||
| 	return containerStats, nil | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) getContainerMetrics( | ||||
| 	meta containerstore.Metadata, | ||||
| 	stats *types.Metric, | ||||
| ) (*runtime.ContainerStats, error) { | ||||
| 	var cs runtime.ContainerStats | ||||
| 	var usedBytes, inodesUsed uint64 | ||||
| 	sn, err := c.snapshotStore.Get(meta.ID) | ||||
| 	// If snapshotstore doesn't have cached snapshot information | ||||
| 	// set WritableLayer usage to zero | ||||
| 	if err == nil { | ||||
| 		usedBytes = sn.Size | ||||
| 		inodesUsed = sn.Inodes | ||||
| 	} | ||||
| 	cs.WritableLayer = &runtime.FilesystemUsage{ | ||||
| 		Timestamp: sn.Timestamp, | ||||
| 		FsId: &runtime.FilesystemIdentifier{ | ||||
| 			Mountpoint: c.imageFSPath, | ||||
| 		}, | ||||
| 		UsedBytes:  &runtime.UInt64Value{Value: usedBytes}, | ||||
| 		InodesUsed: &runtime.UInt64Value{Value: inodesUsed}, | ||||
| 	} | ||||
| 	cs.Attributes = &runtime.ContainerAttributes{ | ||||
| 		Id:          meta.ID, | ||||
| 		Metadata:    meta.Config.GetMetadata(), | ||||
| 		Labels:      meta.Config.GetLabels(), | ||||
| 		Annotations: meta.Config.GetAnnotations(), | ||||
| 	} | ||||
|  | ||||
| 	if stats != nil { | ||||
| 		s, err := typeurl.UnmarshalAny(stats.Data) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to extract container metrics: %v", err) | ||||
| 		} | ||||
| 		metrics := s.(*cgroups.Metrics) | ||||
| 		if metrics.CPU != nil && metrics.CPU.Usage != nil { | ||||
| 			cs.Cpu = &runtime.CpuUsage{ | ||||
| 				Timestamp:            stats.Timestamp.UnixNano(), | ||||
| 				UsageCoreNanoSeconds: &runtime.UInt64Value{Value: metrics.CPU.Usage.Total}, | ||||
| 			} | ||||
| 		} | ||||
| 		if metrics.Memory != nil && metrics.Memory.Usage != nil { | ||||
| 			cs.Memory = &runtime.MemoryUsage{ | ||||
| 				Timestamp:       stats.Timestamp.UnixNano(), | ||||
| 				WorkingSetBytes: &runtime.UInt64Value{Value: metrics.Memory.Usage.Usage}, | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &cs, nil | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) normalizeContainerStatsFilter(filter *runtime.ContainerStatsFilter) { | ||||
| 	if cntr, err := c.containerStore.Get(filter.GetId()); err == nil { | ||||
| 		filter.Id = cntr.ID | ||||
| 	} | ||||
| 	if sb, err := c.sandboxStore.Get(filter.GetPodSandboxId()); err == nil { | ||||
| 		filter.PodSandboxId = sb.ID | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // buildTaskMetricsRequest constructs a tasks.MetricsRequest based on | ||||
| // the information in the stats request and the containerStore | ||||
| func (c *criContainerdService) buildTaskMetricsRequest( | ||||
| 	r *runtime.ListContainerStatsRequest, | ||||
| ) (tasks.MetricsRequest, []containerstore.Container, error) { | ||||
| 	var req tasks.MetricsRequest | ||||
| 	if r.GetFilter() == nil { | ||||
| 		return req, nil, nil | ||||
| 	} | ||||
| 	c.normalizeContainerStatsFilter(r.GetFilter()) | ||||
| 	var containers []containerstore.Container | ||||
| 	for _, cntr := range c.containerStore.List() { | ||||
| 		if r.GetFilter().GetId() != "" && cntr.ID != r.GetFilter().GetId() { | ||||
| 			continue | ||||
| 		} | ||||
| 		if r.GetFilter().GetPodSandboxId() != "" && cntr.SandboxID != r.GetFilter().GetPodSandboxId() { | ||||
| 			continue | ||||
| 		} | ||||
| 		if r.GetFilter().GetLabelSelector() != nil && | ||||
| 			!matchLabelSelector(r.GetFilter().GetLabelSelector(), cntr.Config.GetLabels()) { | ||||
| 			continue | ||||
| 		} | ||||
| 		containers = append(containers, cntr) | ||||
| 		req.Filters = append(req.Filters, "id=="+cntr.ID) | ||||
| 	} | ||||
| 	return req, containers, nil | ||||
| } | ||||
|  | ||||
| func matchLabelSelector(selector, labels map[string]string) bool { | ||||
| 	for k, v := range selector { | ||||
| 		if val, ok := labels[k]; ok { | ||||
| 			if v != val { | ||||
| 				return false | ||||
| 			} | ||||
| 		} else { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										153
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | ||||
| 	runtimespec "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| ) | ||||
|  | ||||
| // ContainerStatus inspects the container and returns the status. | ||||
| func (c *criContainerdService) ContainerStatus(ctx context.Context, r *runtime.ContainerStatusRequest) (*runtime.ContainerStatusResponse, error) { | ||||
| 	container, err := c.containerStore.Get(r.GetContainerId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err) | ||||
| 	} | ||||
|  | ||||
| 	// TODO(random-liu): Clean up the following logic in CRI. | ||||
| 	// Current assumption: | ||||
| 	// * ImageSpec in container config is image ID. | ||||
| 	// * ImageSpec in container status is image tag. | ||||
| 	// * ImageRef in container status is repo digest. | ||||
| 	spec := container.Config.GetImage() | ||||
| 	imageRef := container.ImageRef | ||||
| 	image, err := c.imageStore.Get(imageRef) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get image %q: %v", imageRef, err) | ||||
| 	} | ||||
| 	if len(image.RepoTags) > 0 { | ||||
| 		// Based on current behavior of dockershim, this field should be | ||||
| 		// image tag. | ||||
| 		spec = &runtime.ImageSpec{Image: image.RepoTags[0]} | ||||
| 	} | ||||
| 	if len(image.RepoDigests) > 0 { | ||||
| 		// Based on the CRI definition, this field will be consumed by user. | ||||
| 		imageRef = image.RepoDigests[0] | ||||
| 	} | ||||
| 	status := toCRIContainerStatus(container, spec, imageRef) | ||||
| 	info, err := toCRIContainerInfo(ctx, container, r.GetVerbose()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get verbose container info: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.ContainerStatusResponse{ | ||||
| 		Status: status, | ||||
| 		Info:   info, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // toCRIContainerStatus converts internal container object to CRI container status. | ||||
| func toCRIContainerStatus(container containerstore.Container, spec *runtime.ImageSpec, imageRef string) *runtime.ContainerStatus { | ||||
| 	meta := container.Metadata | ||||
| 	status := container.Status.Get() | ||||
| 	reason := status.Reason | ||||
| 	if status.State() == runtime.ContainerState_CONTAINER_EXITED && reason == "" { | ||||
| 		if status.ExitCode == 0 { | ||||
| 			reason = completeExitReason | ||||
| 		} else { | ||||
| 			reason = errorExitReason | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.ContainerStatus{ | ||||
| 		Id:          meta.ID, | ||||
| 		Metadata:    meta.Config.GetMetadata(), | ||||
| 		State:       status.State(), | ||||
| 		CreatedAt:   status.CreatedAt, | ||||
| 		StartedAt:   status.StartedAt, | ||||
| 		FinishedAt:  status.FinishedAt, | ||||
| 		ExitCode:    status.ExitCode, | ||||
| 		Image:       spec, | ||||
| 		ImageRef:    imageRef, | ||||
| 		Reason:      reason, | ||||
| 		Message:     status.Message, | ||||
| 		Labels:      meta.Config.GetLabels(), | ||||
| 		Annotations: meta.Config.GetAnnotations(), | ||||
| 		Mounts:      meta.Config.GetMounts(), | ||||
| 		LogPath:     meta.LogPath, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type containerInfo struct { | ||||
| 	// TODO(random-liu): Add sandboxID in CRI container status. | ||||
| 	SandboxID   string                   `json:"sandboxID"` | ||||
| 	Pid         uint32                   `json:"pid"` | ||||
| 	Removing    bool                     `json:"removing"` | ||||
| 	SnapshotKey string                   `json:"snapshotKey"` | ||||
| 	Snapshotter string                   `json:"snapshotter"` | ||||
| 	Config      *runtime.ContainerConfig `json:"config"` | ||||
| 	RuntimeSpec *runtimespec.Spec        `json:"runtimeSpec"` | ||||
| } | ||||
|  | ||||
| // toCRIContainerInfo converts internal container object information to CRI container status response info map. | ||||
| // TODO(random-liu): Return error instead of logging. | ||||
| func toCRIContainerInfo(ctx context.Context, container containerstore.Container, verbose bool) (map[string]string, error) { | ||||
| 	if !verbose { | ||||
| 		return nil, nil | ||||
| 	} | ||||
|  | ||||
| 	meta := container.Metadata | ||||
| 	status := container.Status.Get() | ||||
|  | ||||
| 	// TODO(random-liu): Change CRI status info to use array instead of map. | ||||
| 	ci := &containerInfo{ | ||||
| 		SandboxID: container.SandboxID, | ||||
| 		Pid:       status.Pid, | ||||
| 		Removing:  status.Removing, | ||||
| 		Config:    meta.Config, | ||||
| 	} | ||||
|  | ||||
| 	spec, err := container.Container.Spec(ctx) | ||||
| 	if err == nil { | ||||
| 		ci.RuntimeSpec = spec | ||||
| 	} else { | ||||
| 		logrus.WithError(err).Errorf("Failed to get container %q spec", container.ID) | ||||
| 	} | ||||
|  | ||||
| 	ctrInfo, err := container.Container.Info(ctx) | ||||
| 	if err == nil { | ||||
| 		ci.SnapshotKey = ctrInfo.SnapshotKey | ||||
| 		ci.Snapshotter = ctrInfo.Snapshotter | ||||
| 	} else { | ||||
| 		logrus.WithError(err).Errorf("Failed to get container %q info", container.ID) | ||||
| 	} | ||||
|  | ||||
| 	infoBytes, err := json.Marshal(ci) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to marshal info %v: %v", ci, err) | ||||
| 	} | ||||
| 	return map[string]string{ | ||||
| 		"info": string(infoBytes), | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										144
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_stop.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_stop.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/docker/docker/pkg/signal" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"golang.org/x/sys/unix" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| ) | ||||
|  | ||||
| // killContainerTimeout is the timeout that we wait for the container to | ||||
| // be SIGKILLed. | ||||
| const killContainerTimeout = 2 * time.Minute | ||||
|  | ||||
| // StopContainer stops a running container with a grace period (i.e., timeout). | ||||
| func (c *criContainerdService) StopContainer(ctx context.Context, r *runtime.StopContainerRequest) (*runtime.StopContainerResponse, error) { | ||||
| 	// Get container config from container store. | ||||
| 	container, err := c.containerStore.Get(r.GetContainerId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err) | ||||
| 	} | ||||
|  | ||||
| 	if err := c.stopContainer(ctx, container, time.Duration(r.GetTimeout())*time.Second); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.StopContainerResponse{}, nil | ||||
| } | ||||
|  | ||||
| // stopContainer stops a container based on the container metadata. | ||||
| func (c *criContainerdService) stopContainer(ctx context.Context, container containerstore.Container, timeout time.Duration) error { | ||||
| 	id := container.ID | ||||
|  | ||||
| 	// Return without error if container is not running. This makes sure that | ||||
| 	// stop only takes real action after the container is started. | ||||
| 	state := container.Status.Get().State() | ||||
| 	if state != runtime.ContainerState_CONTAINER_RUNNING { | ||||
| 		logrus.Infof("Container to stop %q is not running, current state %q", | ||||
| 			id, criContainerStateToString(state)) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if timeout > 0 { | ||||
| 		stopSignal := unix.SIGTERM | ||||
| 		image, err := c.imageStore.Get(container.ImageRef) | ||||
| 		if err != nil { | ||||
| 			// NOTE(random-liu): It's possible that the container is stopped, | ||||
| 			// deleted and image is garbage collected before this point. However, | ||||
| 			// the chance is really slim, even it happens, it's still fine to return | ||||
| 			// an error here. | ||||
| 			return fmt.Errorf("failed to get image metadata %q: %v", container.ImageRef, err) | ||||
| 		} | ||||
| 		if image.ImageSpec.Config.StopSignal != "" { | ||||
| 			stopSignal, err = signal.ParseSignal(image.ImageSpec.Config.StopSignal) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("failed to parse stop signal %q: %v", | ||||
| 					image.ImageSpec.Config.StopSignal, err) | ||||
| 			} | ||||
| 		} | ||||
| 		logrus.Infof("Stop container %q with signal %v", id, stopSignal) | ||||
| 		task, err := container.Container.Task(ctx, nil) | ||||
| 		if err != nil { | ||||
| 			if !errdefs.IsNotFound(err) { | ||||
| 				return fmt.Errorf("failed to stop container, task not found for container %q: %v", id, err) | ||||
| 			} | ||||
| 			return nil | ||||
| 		} | ||||
| 		if task != nil { | ||||
| 			if err = task.Kill(ctx, stopSignal); err != nil { | ||||
| 				if !errdefs.IsNotFound(err) { | ||||
| 					return fmt.Errorf("failed to stop container %q: %v", id, err) | ||||
| 				} | ||||
| 				// Move on to make sure container status is updated. | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		err = c.waitContainerStop(ctx, container, timeout) | ||||
| 		if err == nil { | ||||
| 			return nil | ||||
| 		} | ||||
| 		logrus.WithError(err).Errorf("Stop container %q timed out", id) | ||||
| 	} | ||||
|  | ||||
| 	task, err := container.Container.Task(ctx, nil) | ||||
| 	if err != nil { | ||||
| 		if !errdefs.IsNotFound(err) { | ||||
| 			return fmt.Errorf("failed to stop container, task not found for container %q: %v", id, err) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| 	// Event handler will Delete the container from containerd after it handles the Exited event. | ||||
| 	logrus.Infof("Kill container %q", id) | ||||
| 	if task != nil { | ||||
| 		if err = task.Kill(ctx, unix.SIGKILL, containerd.WithKillAll); err != nil { | ||||
| 			if !errdefs.IsNotFound(err) { | ||||
| 				return fmt.Errorf("failed to kill container %q: %v", id, err) | ||||
| 			} | ||||
| 			// Move on to make sure container status is updated. | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Wait for a fixed timeout until container stop is observed by event monitor. | ||||
| 	if err := c.waitContainerStop(ctx, container, killContainerTimeout); err != nil { | ||||
| 		return fmt.Errorf("an error occurs during waiting for container %q to stop: %v", id, err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // waitContainerStop waits for container to be stopped until timeout exceeds or context is cancelled. | ||||
| func (c *criContainerdService) waitContainerStop(ctx context.Context, container containerstore.Container, timeout time.Duration) error { | ||||
| 	timeoutTimer := time.NewTimer(timeout) | ||||
| 	defer timeoutTimer.Stop() | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return fmt.Errorf("wait container %q is cancelled", container.ID) | ||||
| 	case <-timeoutTimer.C: | ||||
| 		return fmt.Errorf("wait container %q stop timeout", container.ID) | ||||
| 	case <-container.Stopped(): | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										158
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_update_resources.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/container_update_resources.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	gocontext "context" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/containers" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	runtimespec "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| 	"github.com/containerd/cri-containerd/pkg/util" | ||||
| ) | ||||
|  | ||||
| // UpdateContainerResources updates ContainerConfig of the container. | ||||
| func (c *criContainerdService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (retRes *runtime.UpdateContainerResourcesResponse, retErr error) { | ||||
| 	container, err := c.containerStore.Get(r.GetContainerId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to find container: %v", err) | ||||
| 	} | ||||
| 	// Update resources in status update transaction, so that: | ||||
| 	// 1) There won't be race condition with container start. | ||||
| 	// 2) There won't be concurrent resource update to the same container. | ||||
| 	if err := container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) { | ||||
| 		return status, c.updateContainerResources(ctx, container, r.GetLinux(), status) | ||||
| 	}); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to update resources: %v", err) | ||||
| 	} | ||||
| 	return &runtime.UpdateContainerResourcesResponse{}, nil | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) updateContainerResources(ctx context.Context, | ||||
| 	cntr containerstore.Container, | ||||
| 	resources *runtime.LinuxContainerResources, | ||||
| 	status containerstore.Status) (retErr error) { | ||||
| 	id := cntr.ID | ||||
| 	// Do not update the container when there is a removal in progress. | ||||
| 	if status.Removing { | ||||
| 		return fmt.Errorf("container %q is in removing state", id) | ||||
| 	} | ||||
|  | ||||
| 	// Update container spec. If the container is not started yet, updating | ||||
| 	// spec makes sure that the resource limits are correct when start; | ||||
| 	// if the container is already started, updating spec is still required, | ||||
| 	// the spec will become our source of truth for resource limits. | ||||
| 	oldSpec, err := cntr.Container.Spec(ctx) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to get container spec: %v", err) | ||||
| 	} | ||||
| 	newSpec, err := updateOCILinuxResource(oldSpec, resources) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to update resource in spec: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := updateContainerSpec(ctx, cntr.Container, newSpec); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			// Reset spec on error. | ||||
| 			if err := updateContainerSpec(ctx, cntr.Container, oldSpec); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to update spec %+v for container %q", oldSpec, id) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// If container is not running, only update spec is enough, new resource | ||||
| 	// limit will be applied when container start. | ||||
| 	if status.State() != runtime.ContainerState_CONTAINER_RUNNING { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	task, err := cntr.Container.Task(ctx, nil) | ||||
| 	if err != nil { | ||||
| 		if errdefs.IsNotFound(err) { | ||||
| 			// Task exited already. | ||||
| 			return nil | ||||
| 		} | ||||
| 		return fmt.Errorf("failed to get task: %v", err) | ||||
| 	} | ||||
| 	// newSpec.Linux won't be nil | ||||
| 	if err := task.Update(ctx, containerd.WithResources(newSpec.Linux.Resources)); err != nil { | ||||
| 		if errdefs.IsNotFound(err) { | ||||
| 			// Task exited already. | ||||
| 			return nil | ||||
| 		} | ||||
| 		return fmt.Errorf("failed to update resources: %v", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // updateContainerSpec updates container spec. | ||||
| func updateContainerSpec(ctx context.Context, cntr containerd.Container, spec *runtimespec.Spec) error { | ||||
| 	any, err := typeurl.MarshalAny(spec) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to marshal spec %+v: %v", spec, err) | ||||
| 	} | ||||
| 	if err := cntr.Update(ctx, func(ctx gocontext.Context, client *containerd.Client, c *containers.Container) error { | ||||
| 		c.Spec = any | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		return fmt.Errorf("failed to update container spec: %v", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // updateOCILinuxResource updates container resource limit. | ||||
| func updateOCILinuxResource(spec *runtimespec.Spec, new *runtime.LinuxContainerResources) (*runtimespec.Spec, error) { | ||||
| 	// Copy to make sure old spec is not changed. | ||||
| 	var cloned runtimespec.Spec | ||||
| 	if err := util.DeepCopy(&cloned, spec); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to deep copy: %v", err) | ||||
| 	} | ||||
| 	g := newSpecGenerator(&cloned) | ||||
|  | ||||
| 	if new.GetCpuPeriod() != 0 { | ||||
| 		g.SetLinuxResourcesCPUPeriod(uint64(new.GetCpuPeriod())) | ||||
| 	} | ||||
| 	if new.GetCpuQuota() != 0 { | ||||
| 		g.SetLinuxResourcesCPUQuota(new.GetCpuQuota()) | ||||
| 	} | ||||
| 	if new.GetCpuShares() != 0 { | ||||
| 		g.SetLinuxResourcesCPUShares(uint64(new.GetCpuShares())) | ||||
| 	} | ||||
| 	if new.GetMemoryLimitInBytes() != 0 { | ||||
| 		g.SetLinuxResourcesMemoryLimit(new.GetMemoryLimitInBytes()) | ||||
| 	} | ||||
| 	// OOMScore is not updatable. | ||||
| 	if new.GetCpusetCpus() != "" { | ||||
| 		g.SetLinuxResourcesCPUCpus(new.GetCpusetCpus()) | ||||
| 	} | ||||
| 	if new.GetCpusetMems() != "" { | ||||
| 		g.SetLinuxResourcesCPUMems(new.GetCpusetMems()) | ||||
| 	} | ||||
|  | ||||
| 	return g.Spec(), nil | ||||
| } | ||||
							
								
								
									
										244
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/events.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/events.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	eventtypes "github.com/containerd/containerd/api/events" | ||||
| 	"github.com/containerd/containerd/api/services/events/v1" | ||||
| 	containerdio "github.com/containerd/containerd/cio" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/store" | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // eventMonitor monitors containerd event and updates internal state correspondingly. | ||||
| // TODO(random-liu): [P1] Figure out is it possible to drop event during containerd | ||||
| // is running. If it is, we should do periodically list to sync state with containerd. | ||||
| type eventMonitor struct { | ||||
| 	containerStore *containerstore.Store | ||||
| 	sandboxStore   *sandboxstore.Store | ||||
| 	ch             <-chan *events.Envelope | ||||
| 	errCh          <-chan error | ||||
| 	ctx            context.Context | ||||
| 	cancel         context.CancelFunc | ||||
| } | ||||
|  | ||||
| // Create new event monitor. New event monitor will start subscribing containerd event. All events | ||||
| // happen after it should be monitored. | ||||
| func newEventMonitor(c *containerstore.Store, s *sandboxstore.Store) *eventMonitor { | ||||
| 	ctx, cancel := context.WithCancel(context.Background()) | ||||
| 	return &eventMonitor{ | ||||
| 		containerStore: c, | ||||
| 		sandboxStore:   s, | ||||
| 		ctx:            ctx, | ||||
| 		cancel:         cancel, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // subscribe starts to subscribe containerd events. | ||||
| func (em *eventMonitor) subscribe(client *containerd.Client) { | ||||
| 	filters := []string{ | ||||
| 		`topic=="/tasks/exit"`, | ||||
| 		`topic=="/tasks/oom"`, | ||||
| 	} | ||||
| 	em.ch, em.errCh = client.Subscribe(em.ctx, filters...) | ||||
| } | ||||
|  | ||||
| // start starts the event monitor which monitors and handles all container events. It returns | ||||
| // a channel for the caller to wait for the event monitor to stop. start must be called after | ||||
| // subscribe. | ||||
| func (em *eventMonitor) start() (<-chan struct{}, error) { | ||||
| 	if em.ch == nil || em.errCh == nil { | ||||
| 		return nil, errors.New("event channel is nil") | ||||
| 	} | ||||
| 	closeCh := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case e := <-em.ch: | ||||
| 				logrus.Debugf("Received containerd event timestamp - %v, namespace - %q, topic - %q", e.Timestamp, e.Namespace, e.Topic) | ||||
| 				em.handleEvent(e) | ||||
| 			case err := <-em.errCh: | ||||
| 				logrus.WithError(err).Error("Failed to handle event stream") | ||||
| 				close(closeCh) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| 	return closeCh, nil | ||||
| } | ||||
|  | ||||
| // stop stops the event monitor. It will close the event channel. | ||||
| // Once event monitor is stopped, it can't be started. | ||||
| func (em *eventMonitor) stop() { | ||||
| 	em.cancel() | ||||
| } | ||||
|  | ||||
| // handleEvent handles a containerd event. | ||||
| func (em *eventMonitor) handleEvent(evt *events.Envelope) { | ||||
| 	any, err := typeurl.UnmarshalAny(evt.Event) | ||||
| 	if err != nil { | ||||
| 		logrus.WithError(err).Errorf("Failed to convert event envelope %+v", evt) | ||||
| 		return | ||||
| 	} | ||||
| 	switch any.(type) { | ||||
| 	// If containerd-shim exits unexpectedly, there will be no corresponding event. | ||||
| 	// However, containerd could not retrieve container state in that case, so it's | ||||
| 	// fine to leave out that case for now. | ||||
| 	// TODO(random-liu): [P2] Handle containerd-shim exit. | ||||
| 	case *eventtypes.TaskExit: | ||||
| 		e := any.(*eventtypes.TaskExit) | ||||
| 		logrus.Infof("TaskExit event %+v", e) | ||||
| 		cntr, err := em.containerStore.Get(e.ContainerID) | ||||
| 		if err == nil { | ||||
| 			handleContainerExit(e, cntr) | ||||
| 			return | ||||
| 		} else if err != store.ErrNotExist { | ||||
| 			logrus.WithError(err).Errorf("Failed to get container %q", e.ContainerID) | ||||
| 			return | ||||
| 		} | ||||
| 		// Use GetAll to include sandbox in unknown state. | ||||
| 		sb, err := em.sandboxStore.GetAll(e.ContainerID) | ||||
| 		if err == nil { | ||||
| 			handleSandboxExit(e, sb) | ||||
| 			return | ||||
| 		} else if err != store.ErrNotExist { | ||||
| 			logrus.WithError(err).Errorf("Failed to get sandbox %q", e.ContainerID) | ||||
| 			return | ||||
| 		} | ||||
| 	case *eventtypes.TaskOOM: | ||||
| 		e := any.(*eventtypes.TaskOOM) | ||||
| 		logrus.Infof("TaskOOM event %+v", e) | ||||
| 		cntr, err := em.containerStore.Get(e.ContainerID) | ||||
| 		if err != nil { | ||||
| 			if _, err := em.sandboxStore.Get(e.ContainerID); err == nil { | ||||
| 				return | ||||
| 			} | ||||
| 			logrus.WithError(err).Errorf("Failed to get container %q", e.ContainerID) | ||||
| 		} | ||||
| 		err = cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) { | ||||
| 			status.Reason = oomExitReason | ||||
| 			return status, nil | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to update container %q oom", e.ContainerID) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // handleContainerExit handles TaskExit event for container. | ||||
| func handleContainerExit(e *eventtypes.TaskExit, cntr containerstore.Container) { | ||||
| 	if e.Pid != cntr.Status.Get().Pid { | ||||
| 		// Non-init process died, ignore the event. | ||||
| 		return | ||||
| 	} | ||||
| 	// Attach container IO so that `Delete` could cleanup the stream properly. | ||||
| 	task, err := cntr.Container.Task(context.Background(), | ||||
| 		func(*containerdio.FIFOSet) (containerdio.IO, error) { | ||||
| 			return cntr.IO, nil | ||||
| 		}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		if !errdefs.IsNotFound(err) { | ||||
| 			logrus.WithError(err).Errorf("failed to load task for container %q", e.ContainerID) | ||||
| 			return | ||||
| 		} | ||||
| 	} else { | ||||
| 		// TODO(random-liu): [P1] This may block the loop, we may want to spawn a worker | ||||
| 		if _, err = task.Delete(context.Background()); err != nil { | ||||
| 			// TODO(random-liu): [P0] Enqueue the event and retry. | ||||
| 			if !errdefs.IsNotFound(err) { | ||||
| 				logrus.WithError(err).Errorf("failed to stop container %q", e.ContainerID) | ||||
| 				return | ||||
| 			} | ||||
| 			// Move on to make sure container status is updated. | ||||
| 		} | ||||
| 	} | ||||
| 	err = cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) { | ||||
| 		// If FinishedAt has been set (e.g. with start failure), keep as | ||||
| 		// it is. | ||||
| 		if status.FinishedAt != 0 { | ||||
| 			return status, nil | ||||
| 		} | ||||
| 		status.Pid = 0 | ||||
| 		status.FinishedAt = e.ExitedAt.UnixNano() | ||||
| 		status.ExitCode = int32(e.ExitStatus) | ||||
| 		return status, nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		logrus.WithError(err).Errorf("Failed to update container %q state", e.ContainerID) | ||||
| 		// TODO(random-liu): [P0] Enqueue the event and retry. | ||||
| 		return | ||||
| 	} | ||||
| 	// Using channel to propagate the information of container stop | ||||
| 	cntr.Stop() | ||||
| } | ||||
|  | ||||
| // handleSandboxExit handles TaskExit event for sandbox. | ||||
| func handleSandboxExit(e *eventtypes.TaskExit, sb sandboxstore.Sandbox) { | ||||
| 	if e.Pid != sb.Status.Get().Pid { | ||||
| 		// Non-init process died, ignore the event. | ||||
| 		return | ||||
| 	} | ||||
| 	// No stream attached to sandbox container. | ||||
| 	task, err := sb.Container.Task(context.Background(), nil) | ||||
| 	if err != nil { | ||||
| 		if !errdefs.IsNotFound(err) { | ||||
| 			logrus.WithError(err).Errorf("failed to load task for sandbox %q", e.ContainerID) | ||||
| 			return | ||||
| 		} | ||||
| 	} else { | ||||
| 		// TODO(random-liu): [P1] This may block the loop, we may want to spawn a worker | ||||
| 		if _, err = task.Delete(context.Background()); err != nil { | ||||
| 			// TODO(random-liu): [P0] Enqueue the event and retry. | ||||
| 			if !errdefs.IsNotFound(err) { | ||||
| 				logrus.WithError(err).Errorf("failed to stop sandbox %q", e.ContainerID) | ||||
| 				return | ||||
| 			} | ||||
| 			// Move on to make sure container status is updated. | ||||
| 		} | ||||
| 	} | ||||
| 	err = sb.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) { | ||||
| 		// NOTE(random-liu): We SHOULD NOT change UNKNOWN state here. | ||||
| 		// If sandbox state is UNKNOWN when event monitor receives an TaskExit event, | ||||
| 		// it means that sandbox start has failed. In that case, `RunPodSandbox` will | ||||
| 		// cleanup everything immediately. | ||||
| 		// Once sandbox state goes out of UNKNOWN, it becomes visable to the user, which | ||||
| 		// is not what we want. | ||||
| 		if status.State != sandboxstore.StateUnknown { | ||||
| 			status.State = sandboxstore.StateNotReady | ||||
| 		} | ||||
| 		status.Pid = 0 | ||||
| 		return status, nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		logrus.WithError(err).Errorf("Failed to update sandbox %q state", e.ContainerID) | ||||
| 		// TODO(random-liu): [P0] Enqueue the event and retry. | ||||
| 		return | ||||
| 	} | ||||
| 	// Using channel to propagate the information of sandbox stop | ||||
| 	sb.Stop() | ||||
| } | ||||
							
								
								
									
										425
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										425
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,425 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"os/exec" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/content" | ||||
| 	"github.com/docker/distribution/reference" | ||||
| 	imagedigest "github.com/opencontainers/go-digest" | ||||
| 	"github.com/opencontainers/image-spec/identity" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	runtimespec "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/opencontainers/runtime-tools/generate" | ||||
| 	"github.com/opencontainers/selinux/go-selinux" | ||||
| 	"github.com/opencontainers/selinux/go-selinux/label" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| 	"k8s.io/kubernetes/pkg/util/sysctl" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/store" | ||||
| 	imagestore "github.com/containerd/cri-containerd/pkg/store/image" | ||||
| 	"github.com/containerd/cri-containerd/pkg/util" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// errorStartReason is the exit reason when fails to start container. | ||||
| 	errorStartReason = "StartError" | ||||
| 	// errorStartExitCode is the exit code when fails to start container. | ||||
| 	// 128 is the same with Docker's behavior. | ||||
| 	errorStartExitCode = 128 | ||||
| 	// completeExitReason is the exit reason when container exits with code 0. | ||||
| 	completeExitReason = "Completed" | ||||
| 	// errorExitReason is the exit reason when container exits with code non-zero. | ||||
| 	errorExitReason = "Error" | ||||
| 	// oomExitReason is the exit reason when process in container is oom killed. | ||||
| 	oomExitReason = "OOMKilled" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// defaultSandboxOOMAdj is default omm adj for sandbox container. (kubernetes#47938). | ||||
| 	defaultSandboxOOMAdj = -998 | ||||
| 	// defaultSandboxCPUshares is default cpu shares for sandbox container. | ||||
| 	defaultSandboxCPUshares = 2 | ||||
| 	// defaultShmSize is the default size of the sandbox shm. | ||||
| 	defaultShmSize = int64(1024 * 1024 * 64) | ||||
| 	// relativeRootfsPath is the rootfs path relative to bundle path. | ||||
| 	relativeRootfsPath = "rootfs" | ||||
| 	// sandboxesDir contains all sandbox root. A sandbox root is the running | ||||
| 	// directory of the sandbox, all files created for the sandbox will be | ||||
| 	// placed under this directory. | ||||
| 	sandboxesDir = "sandboxes" | ||||
| 	// containersDir contains all container root. | ||||
| 	containersDir = "containers" | ||||
| 	// According to http://man7.org/linux/man-pages/man5/resolv.conf.5.html: | ||||
| 	// "The search list is currently limited to six domains with a total of 256 characters." | ||||
| 	maxDNSSearches = 6 | ||||
| 	// Delimiter used to construct container/sandbox names. | ||||
| 	nameDelimiter = "_" | ||||
| 	// netNSFormat is the format of network namespace of a process. | ||||
| 	netNSFormat = "/proc/%v/ns/net" | ||||
| 	// ipcNSFormat is the format of ipc namespace of a process. | ||||
| 	ipcNSFormat = "/proc/%v/ns/ipc" | ||||
| 	// utsNSFormat is the format of uts namespace of a process. | ||||
| 	utsNSFormat = "/proc/%v/ns/uts" | ||||
| 	// pidNSFormat is the format of pid namespace of a process. | ||||
| 	pidNSFormat = "/proc/%v/ns/pid" | ||||
| 	// devShm is the default path of /dev/shm. | ||||
| 	devShm = "/dev/shm" | ||||
| 	// etcHosts is the default path of /etc/hosts file. | ||||
| 	etcHosts = "/etc/hosts" | ||||
| 	// resolvConfPath is the abs path of resolv.conf on host or container. | ||||
| 	resolvConfPath = "/etc/resolv.conf" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// criContainerdPrefix is common prefix for cri-containerd | ||||
| 	criContainerdPrefix = "io.cri-containerd" | ||||
| 	// containerKindLabel is a label key indicating container is sandbox container or application container | ||||
| 	containerKindLabel = criContainerdPrefix + ".kind" | ||||
| 	// containerKindSandbox is a label value indicating container is sandbox container | ||||
| 	containerKindSandbox = "sandbox" | ||||
| 	// containerKindContainer is a label value indicating container is application container | ||||
| 	containerKindContainer = "container" | ||||
| 	// sandboxMetadataExtension is an extension name that identify metadata of sandbox in CreateContainerRequest | ||||
| 	sandboxMetadataExtension = criContainerdPrefix + ".sandbox.metadata" | ||||
| 	// containerMetadataExtension is an extension name that identify metadata of container in CreateContainerRequest | ||||
| 	containerMetadataExtension = criContainerdPrefix + ".container.metadata" | ||||
| ) | ||||
|  | ||||
| // makeSandboxName generates sandbox name from sandbox metadata. The name | ||||
| // generated is unique as long as sandbox metadata is unique. | ||||
| func makeSandboxName(s *runtime.PodSandboxMetadata) string { | ||||
| 	return strings.Join([]string{ | ||||
| 		s.Name,      // 0 | ||||
| 		s.Namespace, // 1 | ||||
| 		s.Uid,       // 2 | ||||
| 		fmt.Sprintf("%d", s.Attempt), // 3 | ||||
| 	}, nameDelimiter) | ||||
| } | ||||
|  | ||||
| // makeContainerName generates container name from sandbox and container metadata. | ||||
| // The name generated is unique as long as the sandbox container combination is | ||||
| // unique. | ||||
| func makeContainerName(c *runtime.ContainerMetadata, s *runtime.PodSandboxMetadata) string { | ||||
| 	return strings.Join([]string{ | ||||
| 		c.Name,      // 0 | ||||
| 		s.Name,      // 1: sandbox name | ||||
| 		s.Namespace, // 2: sandbox namespace | ||||
| 		s.Uid,       // 3: sandbox uid | ||||
| 		fmt.Sprintf("%d", c.Attempt), // 4 | ||||
| 	}, nameDelimiter) | ||||
| } | ||||
|  | ||||
| // getCgroupsPath generates container cgroups path. | ||||
| func getCgroupsPath(cgroupsParent, id string, systemdCgroup bool) string { | ||||
| 	if systemdCgroup { | ||||
| 		// Convert a.slice/b.slice/c.slice to c.slice. | ||||
| 		p := path.Base(cgroupsParent) | ||||
| 		// runc systemd cgroup path format is "slice:prefix:name". | ||||
| 		return strings.Join([]string{p, "cri-containerd", id}, ":") | ||||
| 	} | ||||
| 	return filepath.Join(cgroupsParent, id) | ||||
| } | ||||
|  | ||||
| // getSandboxRootDir returns the root directory for managing sandbox files, | ||||
| // e.g. named pipes. | ||||
| func getSandboxRootDir(rootDir, id string) string { | ||||
| 	return filepath.Join(rootDir, sandboxesDir, id) | ||||
| } | ||||
|  | ||||
| // getContainerRootDir returns the root directory for managing container files. | ||||
| func getContainerRootDir(rootDir, id string) string { | ||||
| 	return filepath.Join(rootDir, containersDir, id) | ||||
| } | ||||
|  | ||||
| // getSandboxHosts returns the hosts file path inside the sandbox root directory. | ||||
| func getSandboxHosts(sandboxRootDir string) string { | ||||
| 	return filepath.Join(sandboxRootDir, "hosts") | ||||
| } | ||||
|  | ||||
| // getResolvPath returns resolv.conf filepath for specified sandbox. | ||||
| func getResolvPath(sandboxRoot string) string { | ||||
| 	return filepath.Join(sandboxRoot, "resolv.conf") | ||||
| } | ||||
|  | ||||
| // getSandboxDevShm returns the shm file path inside the sandbox root directory. | ||||
| func getSandboxDevShm(sandboxRootDir string) string { | ||||
| 	return filepath.Join(sandboxRootDir, "shm") | ||||
| } | ||||
|  | ||||
| // getNetworkNamespace returns the network namespace of a process. | ||||
| func getNetworkNamespace(pid uint32) string { | ||||
| 	return fmt.Sprintf(netNSFormat, pid) | ||||
| } | ||||
|  | ||||
| // getIPCNamespace returns the ipc namespace of a process. | ||||
| func getIPCNamespace(pid uint32) string { | ||||
| 	return fmt.Sprintf(ipcNSFormat, pid) | ||||
| } | ||||
|  | ||||
| // getUTSNamespace returns the uts namespace of a process. | ||||
| func getUTSNamespace(pid uint32) string { | ||||
| 	return fmt.Sprintf(utsNSFormat, pid) | ||||
| } | ||||
|  | ||||
| // getPIDNamespace returns the pid namespace of a process. | ||||
| func getPIDNamespace(pid uint32) string { | ||||
| 	return fmt.Sprintf(pidNSFormat, pid) | ||||
| } | ||||
|  | ||||
| // criContainerStateToString formats CRI container state to string. | ||||
| func criContainerStateToString(state runtime.ContainerState) string { | ||||
| 	return runtime.ContainerState_name[int32(state)] | ||||
| } | ||||
|  | ||||
| // getRepoDigestAngTag returns image repoDigest and repoTag of the named image reference. | ||||
| func getRepoDigestAndTag(namedRef reference.Named, digest imagedigest.Digest, schema1 bool) (string, string) { | ||||
| 	var repoTag, repoDigest string | ||||
| 	if _, ok := namedRef.(reference.NamedTagged); ok { | ||||
| 		repoTag = namedRef.String() | ||||
| 	} | ||||
| 	if _, ok := namedRef.(reference.Canonical); ok { | ||||
| 		repoDigest = namedRef.String() | ||||
| 	} else if !schema1 { | ||||
| 		// digest is not actual repo digest for schema1 image. | ||||
| 		repoDigest = namedRef.Name() + "@" + digest.String() | ||||
| 	} | ||||
| 	return repoDigest, repoTag | ||||
| } | ||||
|  | ||||
| // localResolve resolves image reference locally and returns corresponding image metadata. It returns | ||||
| // nil without error if the reference doesn't exist. | ||||
| func (c *criContainerdService) localResolve(ctx context.Context, refOrID string) (*imagestore.Image, error) { | ||||
| 	getImageID := func(refOrId string) string { | ||||
| 		if _, err := imagedigest.Parse(refOrID); err == nil { | ||||
| 			return refOrID | ||||
| 		} | ||||
| 		return func(ref string) string { | ||||
| 			// ref is not image id, try to resolve it locally. | ||||
| 			normalized, err := util.NormalizeImageRef(ref) | ||||
| 			if err != nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			image, err := c.client.GetImage(ctx, normalized.String()) | ||||
| 			if err != nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			desc, err := image.Config(ctx) | ||||
| 			if err != nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return desc.Digest.String() | ||||
| 		}(refOrID) | ||||
| 	} | ||||
|  | ||||
| 	imageID := getImageID(refOrID) | ||||
| 	if imageID == "" { | ||||
| 		// Try to treat ref as imageID | ||||
| 		imageID = refOrID | ||||
| 	} | ||||
| 	image, err := c.imageStore.Get(imageID) | ||||
| 	if err != nil { | ||||
| 		if err == store.ErrNotExist { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("failed to get image %q : %v", imageID, err) | ||||
| 	} | ||||
| 	return &image, nil | ||||
| } | ||||
|  | ||||
| // getUserFromImage gets uid or user name of the image user. | ||||
| // If user is numeric, it will be treated as uid; or else, it is treated as user name. | ||||
| func getUserFromImage(user string) (*int64, string) { | ||||
| 	// return both empty if user is not specified in the image. | ||||
| 	if user == "" { | ||||
| 		return nil, "" | ||||
| 	} | ||||
| 	// split instances where the id may contain user:group | ||||
| 	user = strings.Split(user, ":")[0] | ||||
| 	// user could be either uid or user name. Try to interpret as numeric uid. | ||||
| 	uid, err := strconv.ParseInt(user, 10, 64) | ||||
| 	if err != nil { | ||||
| 		// If user is non numeric, assume it's user name. | ||||
| 		return nil, user | ||||
| 	} | ||||
| 	// If user is a numeric uid. | ||||
| 	return &uid, "" | ||||
| } | ||||
|  | ||||
| // ensureImageExists returns corresponding metadata of the image reference, if image is not | ||||
| // pulled yet, the function will pull the image. | ||||
| func (c *criContainerdService) ensureImageExists(ctx context.Context, ref string) (*imagestore.Image, error) { | ||||
| 	image, err := c.localResolve(ctx, ref) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to resolve image %q: %v", ref, err) | ||||
| 	} | ||||
| 	if image != nil { | ||||
| 		return image, nil | ||||
| 	} | ||||
| 	// Pull image to ensure the image exists | ||||
| 	resp, err := c.PullImage(ctx, &runtime.PullImageRequest{Image: &runtime.ImageSpec{Image: ref}}) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to pull image %q: %v", ref, err) | ||||
| 	} | ||||
| 	imageID := resp.GetImageRef() | ||||
| 	newImage, err := c.imageStore.Get(imageID) | ||||
| 	if err != nil { | ||||
| 		// It's still possible that someone removed the image right after it is pulled. | ||||
| 		return nil, fmt.Errorf("failed to get image %q metadata after pulling: %v", imageID, err) | ||||
| 	} | ||||
| 	return &newImage, nil | ||||
| } | ||||
|  | ||||
| // imageInfo is the information about the image got from containerd. | ||||
| type imageInfo struct { | ||||
| 	id        string | ||||
| 	chainID   imagedigest.Digest | ||||
| 	size      int64 | ||||
| 	imagespec imagespec.Image | ||||
| } | ||||
|  | ||||
| // getImageInfo gets image info from containerd. | ||||
| func getImageInfo(ctx context.Context, image containerd.Image) (*imageInfo, error) { | ||||
| 	// Get image information. | ||||
| 	diffIDs, err := image.RootFS(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get image diffIDs: %v", err) | ||||
| 	} | ||||
| 	chainID := identity.ChainID(diffIDs) | ||||
|  | ||||
| 	size, err := image.Size(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get image compressed resource size: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	desc, err := image.Config(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get image config descriptor: %v", err) | ||||
| 	} | ||||
| 	id := desc.Digest.String() | ||||
|  | ||||
| 	rb, err := content.ReadBlob(ctx, image.ContentStore(), desc.Digest) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to read image config from content store: %v", err) | ||||
| 	} | ||||
| 	var ociimage imagespec.Image | ||||
| 	if err := json.Unmarshal(rb, &ociimage); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to unmarshal image config %s: %v", rb, err) | ||||
| 	} | ||||
|  | ||||
| 	return &imageInfo{ | ||||
| 		id:        id, | ||||
| 		chainID:   chainID, | ||||
| 		size:      size, | ||||
| 		imagespec: ociimage, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func initSelinuxOpts(selinuxOpt *runtime.SELinuxOption) (string, string, error) { | ||||
| 	if selinuxOpt == nil { | ||||
| 		return "", "", nil | ||||
| 	} | ||||
|  | ||||
| 	// Should ignored selinuxOpts if they are incomplete. | ||||
| 	if selinuxOpt.GetUser() == "" || | ||||
| 		selinuxOpt.GetRole() == "" || | ||||
| 		selinuxOpt.GetType() == "" || | ||||
| 		selinuxOpt.GetLevel() == "" { | ||||
| 		return "", "", nil | ||||
| 	} | ||||
|  | ||||
| 	labelOpts := fmt.Sprintf("%s:%s:%s:%s", | ||||
| 		selinuxOpt.GetUser(), | ||||
| 		selinuxOpt.GetRole(), | ||||
| 		selinuxOpt.GetType(), | ||||
| 		selinuxOpt.GetLevel()) | ||||
| 	return label.InitLabels(selinux.DupSecOpt(labelOpts)) | ||||
| } | ||||
|  | ||||
| // isInCRIMounts checks whether a destination is in CRI mount list. | ||||
| func isInCRIMounts(dst string, mounts []*runtime.Mount) bool { | ||||
| 	for _, m := range mounts { | ||||
| 		if m.ContainerPath == dst { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // filterLabel returns a label filter. Use `%q` here because containerd | ||||
| // filter needs extra quote to work properly. | ||||
| func filterLabel(k, v string) string { | ||||
| 	return fmt.Sprintf("labels.%q==%q", k, v) | ||||
| } | ||||
|  | ||||
| // buildLabel builds the labels from config to be passed to containerd | ||||
| func buildLabels(configLabels map[string]string, containerType string) map[string]string { | ||||
| 	labels := make(map[string]string) | ||||
| 	for k, v := range configLabels { | ||||
| 		labels[k] = v | ||||
| 	} | ||||
| 	labels[containerKindLabel] = containerType | ||||
| 	return labels | ||||
| } | ||||
|  | ||||
| // newSpecGenerator creates a new spec generator for the runtime spec. | ||||
| func newSpecGenerator(spec *runtimespec.Spec) generate.Generator { | ||||
| 	g := generate.NewFromSpec(spec) | ||||
| 	g.HostSpecific = true | ||||
| 	return g | ||||
| } | ||||
|  | ||||
| // disableNetNSDAD disables duplicate address detection in the network namespace. | ||||
| // DAD has a negative affect on sandbox start latency, since we have to wait | ||||
| // a second or more for the addresses to leave the "tentative" state. | ||||
| func disableNetNSDAD(ns string) error { | ||||
| 	dad := "net/ipv6/conf/default/accept_dad" | ||||
|  | ||||
| 	sysctlBin, err := exec.LookPath("sysctl") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not find sysctl binary: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	nsenterBin, err := exec.LookPath("nsenter") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not find nsenter binary: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// If the sysctl doesn't exist, it means ipv6 is disabled. | ||||
| 	if _, err := sysctl.New().GetSysctl(dad); err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	output, err := exec.Command(nsenterBin, | ||||
| 		fmt.Sprintf("--net=%s", ns), "-F", "--", | ||||
| 		sysctlBin, "-w", fmt.Sprintf("%s=%s", dad, "0"), | ||||
| 	).CombinedOutput() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to write sysctl %q - output: %s, error: %s", | ||||
| 			dad, output, err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										56
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	imagestore "github.com/containerd/cri-containerd/pkg/store/image" | ||||
| ) | ||||
|  | ||||
| // ListImages lists existing images. | ||||
| // TODO(random-liu): Add image list filters after CRI defines this more clear, and kubelet | ||||
| // actually needs it. | ||||
| func (c *criContainerdService) ListImages(ctx context.Context, r *runtime.ListImagesRequest) (*runtime.ListImagesResponse, error) { | ||||
| 	imagesInStore := c.imageStore.List() | ||||
|  | ||||
| 	var images []*runtime.Image | ||||
| 	for _, image := range imagesInStore { | ||||
| 		// TODO(random-liu): [P0] Make sure corresponding snapshot exists. What if snapshot | ||||
| 		// doesn't exist? | ||||
| 		images = append(images, toCRIImage(image)) | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.ListImagesResponse{Images: images}, nil | ||||
| } | ||||
|  | ||||
| // toCRIImage converts image to CRI image type. | ||||
| func toCRIImage(image imagestore.Image) *runtime.Image { | ||||
| 	runtimeImage := &runtime.Image{ | ||||
| 		Id:          image.ID, | ||||
| 		RepoTags:    image.RepoTags, | ||||
| 		RepoDigests: image.RepoDigests, | ||||
| 		Size_:       uint64(image.Size), | ||||
| 	} | ||||
| 	uid, username := getUserFromImage(image.ImageSpec.Config.User) | ||||
| 	if uid != nil { | ||||
| 		runtimeImage.Uid = &runtime.Int64Value{Value: *uid} | ||||
| 	} | ||||
| 	runtimeImage.Username = username | ||||
| 	return runtimeImage | ||||
| } | ||||
							
								
								
									
										80
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_load.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_load.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/sirupsen/logrus" | ||||
|  | ||||
| 	api "github.com/containerd/cri-containerd/pkg/api/v1" | ||||
| 	"github.com/containerd/cri-containerd/pkg/containerd/importer" | ||||
| 	imagestore "github.com/containerd/cri-containerd/pkg/store/image" | ||||
| ) | ||||
|  | ||||
| // LoadImage loads a image into containerd. | ||||
| func (c *criContainerdService) LoadImage(ctx context.Context, r *api.LoadImageRequest) (*api.LoadImageResponse, error) { | ||||
| 	path := r.GetFilePath() | ||||
| 	if !filepath.IsAbs(path) { | ||||
| 		return nil, fmt.Errorf("path %q is not an absolute path", path) | ||||
| 	} | ||||
| 	f, err := os.Open(path) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to open file: %v", err) | ||||
| 	} | ||||
| 	repoTags, err := importer.Import(ctx, c.client, f) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to import image: %v", err) | ||||
| 	} | ||||
| 	for _, repoTag := range repoTags { | ||||
| 		image, err := c.client.GetImage(ctx, repoTag) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to get image %q: %v", repoTag, err) | ||||
| 		} | ||||
| 		if err := image.Unpack(ctx, c.config.ContainerdConfig.Snapshotter); err != nil { | ||||
| 			logrus.WithError(err).Warnf("Failed to unpack image %q", repoTag) | ||||
| 			// Do not fail image importing. Unpack will be retried when container creation. | ||||
| 		} | ||||
| 		info, err := getImageInfo(ctx, image) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to get image %q info: %v", repoTag, err) | ||||
| 		} | ||||
| 		id := info.id | ||||
|  | ||||
| 		if err := c.createImageReference(ctx, id, image.Target()); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to create image reference %q: %v", id, err) | ||||
| 		} | ||||
|  | ||||
| 		img := imagestore.Image{ | ||||
| 			ID:        id, | ||||
| 			RepoTags:  []string{repoTag}, | ||||
| 			ChainID:   info.chainID.String(), | ||||
| 			Size:      info.size, | ||||
| 			ImageSpec: info.imagespec, | ||||
| 			Image:     image, | ||||
| 		} | ||||
|  | ||||
| 		if err := c.imageStore.Add(img); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to add image %q into store: %v", id, err) | ||||
| 		} | ||||
| 		logrus.Debugf("Imported image with id %q, repo tag %q", id, repoTag) | ||||
| 	} | ||||
| 	return &api.LoadImageResponse{Images: repoTags}, nil | ||||
| } | ||||
							
								
								
									
										221
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_pull.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_pull.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,221 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	containerdimages "github.com/containerd/containerd/images" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	containerdresolver "github.com/containerd/cri-containerd/pkg/containerd/resolver" | ||||
| 	imagestore "github.com/containerd/cri-containerd/pkg/store/image" | ||||
| 	"github.com/containerd/cri-containerd/pkg/util" | ||||
| ) | ||||
|  | ||||
| // For image management: | ||||
| // 1) We have an in-memory metadata index to: | ||||
| //   a. Maintain ImageID -> RepoTags, ImageID -> RepoDigset relationships; ImageID | ||||
| //   is the digest of image config, which conforms to oci image spec. | ||||
| //   b. Cache constant and useful information such as image chainID, config etc. | ||||
| //   c. An image will be added into the in-memory metadata only when it's successfully | ||||
| //   pulled and unpacked. | ||||
| // | ||||
| // 2) We use containerd image metadata store and content store: | ||||
| //   a. To resolve image reference (digest/tag) locally. During pulling image, we | ||||
| //   normalize the image reference provided by user, and put it into image metadata | ||||
| //   store with resolved descriptor. For the other operations, if image id is provided, | ||||
| //   we'll access the in-memory metadata index directly; if image reference is | ||||
| //   provided, we'll normalize it, resolve it in containerd image metadata store | ||||
| //   to get the image id. | ||||
| //   b. As the backup of in-memory metadata in 1). During startup, the in-memory | ||||
| //   metadata could be re-constructed from image metadata store + content store. | ||||
| // | ||||
| // Several problems with current approach: | ||||
| // 1) An entry in containerd image metadata store doesn't mean a "READY" (successfully | ||||
| // pulled and unpacked) image. E.g. during pulling, the client gets killed. In that case, | ||||
| // if we saw an image without snapshots or with in-complete contents during startup, | ||||
| // should we re-pull the image? Or should we remove the entry? | ||||
| // | ||||
| // yanxuean: We cann't delete image directly, because we don't know if the image | ||||
| // is pulled by us. There are resource leakage. | ||||
| // | ||||
| // 2) Containerd suggests user to add entry before pulling the image. However if | ||||
| // an error occurrs during the pulling, should we remove the entry from metadata | ||||
| // store? Or should we leave it there until next startup (resource leakage)? | ||||
| // | ||||
| // 3) CRI-containerd only exposes "READY" (successfully pulled and unpacked) images | ||||
| // to the user, which are maintained in the in-memory metadata index. However, it's | ||||
| // still possible that someone else removes the content or snapshot by-pass cri-containerd, | ||||
| // how do we detect that and update the in-memory metadata correspondingly? Always | ||||
| // check whether corresponding snapshot is ready when reporting image status? | ||||
| // | ||||
| // 4) Is the content important if we cached necessary information in-memory | ||||
| // after we pull the image? How to manage the disk usage of contents? If some | ||||
| // contents are missing but snapshots are ready, is the image still "READY"? | ||||
|  | ||||
| // PullImage pulls an image with authentication config. | ||||
| func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (*runtime.PullImageResponse, error) { | ||||
| 	imageRef := r.GetImage().GetImage() | ||||
| 	namedRef, err := util.NormalizeImageRef(imageRef) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to parse image reference %q: %v", imageRef, err) | ||||
| 	} | ||||
| 	ref := namedRef.String() | ||||
| 	if ref != imageRef { | ||||
| 		logrus.Debugf("PullImage using normalized image ref: %q", ref) | ||||
| 	} | ||||
| 	resolver := containerdresolver.NewResolver(containerdresolver.Options{ | ||||
| 		Credentials: func(string) (string, string, error) { return ParseAuth(r.GetAuth()) }, | ||||
| 		Client:      http.DefaultClient, | ||||
| 		Registry:    c.getResolverOptions(), | ||||
| 	}) | ||||
| 	_, desc, err := resolver.Resolve(ctx, ref) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to resolve image %q: %v", ref, err) | ||||
| 	} | ||||
| 	// We have to check schema1 here, because after `Pull`, schema1 | ||||
| 	// image has already been converted. | ||||
| 	isSchema1 := desc.MediaType == containerdimages.MediaTypeDockerSchema1Manifest | ||||
|  | ||||
| 	// TODO(mikebrow): add truncIndex for image id | ||||
| 	image, err := c.client.Pull(ctx, ref, | ||||
| 		containerd.WithSchema1Conversion, | ||||
| 		containerd.WithResolver(resolver), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to pull image %q: %v", ref, err) | ||||
| 	} | ||||
|  | ||||
| 	// Do best effort unpack. | ||||
| 	logrus.Debugf("Unpack image %q", imageRef) | ||||
| 	if err := image.Unpack(ctx, c.config.ContainerdConfig.Snapshotter); err != nil { | ||||
| 		logrus.WithError(err).Warnf("Failed to unpack image %q", imageRef) | ||||
| 		// Do not fail image pulling. Unpack will be retried before container creation. | ||||
| 	} | ||||
|  | ||||
| 	// Get image information. | ||||
| 	info, err := getImageInfo(ctx, image) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get image information: %v", err) | ||||
| 	} | ||||
| 	imageID := info.id | ||||
|  | ||||
| 	repoDigest, repoTag := getRepoDigestAndTag(namedRef, image.Target().Digest, isSchema1) | ||||
| 	for _, r := range []string{repoTag, repoDigest, imageID} { | ||||
| 		if r == "" { | ||||
| 			continue | ||||
| 		} | ||||
| 		if err := c.createImageReference(ctx, r, image.Target()); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to update image reference %q: %v", r, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	logrus.Debugf("Pulled image %q with image id %q, repo tag %q, repo digest %q", imageRef, imageID, | ||||
| 		repoTag, repoDigest) | ||||
| 	img := imagestore.Image{ | ||||
| 		ID:        imageID, | ||||
| 		ChainID:   info.chainID.String(), | ||||
| 		Size:      info.size, | ||||
| 		ImageSpec: info.imagespec, | ||||
| 		Image:     image, | ||||
| 	} | ||||
| 	if repoDigest != "" { | ||||
| 		img.RepoDigests = []string{repoDigest} | ||||
| 	} | ||||
| 	if repoTag != "" { | ||||
| 		img.RepoTags = []string{repoTag} | ||||
| 	} | ||||
|  | ||||
| 	if err := c.imageStore.Add(img); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to add image %q into store: %v", img.ID, err) | ||||
| 	} | ||||
|  | ||||
| 	// NOTE(random-liu): the actual state in containerd is the source of truth, even we maintain | ||||
| 	// in-memory image store, it's only for in-memory indexing. The image could be removed | ||||
| 	// by someone else anytime, before/during/after we create the metadata. We should always | ||||
| 	// check the actual state in containerd before using the image or returning status of the | ||||
| 	// image. | ||||
| 	return &runtime.PullImageResponse{ImageRef: img.ID}, nil | ||||
| } | ||||
|  | ||||
| // ParseAuth parses AuthConfig and returns username and password/secret required by containerd. | ||||
| func ParseAuth(auth *runtime.AuthConfig) (string, string, error) { | ||||
| 	if auth == nil { | ||||
| 		return "", "", nil | ||||
| 	} | ||||
| 	if auth.Username != "" { | ||||
| 		return auth.Username, auth.Password, nil | ||||
| 	} | ||||
| 	if auth.IdentityToken != "" { | ||||
| 		return "", auth.IdentityToken, nil | ||||
| 	} | ||||
| 	if auth.Auth != "" { | ||||
| 		decLen := base64.StdEncoding.DecodedLen(len(auth.Auth)) | ||||
| 		decoded := make([]byte, decLen) | ||||
| 		_, err := base64.StdEncoding.Decode(decoded, []byte(auth.Auth)) | ||||
| 		if err != nil { | ||||
| 			return "", "", err | ||||
| 		} | ||||
| 		fields := strings.SplitN(string(decoded), ":", 2) | ||||
| 		if len(fields) != 2 { | ||||
| 			return "", "", fmt.Errorf("invalid decoded auth: %q", decoded) | ||||
| 		} | ||||
| 		user, passwd := fields[0], fields[1] | ||||
| 		return user, strings.Trim(passwd, "\x00"), nil | ||||
| 	} | ||||
| 	// TODO(random-liu): Support RegistryToken. | ||||
| 	return "", "", fmt.Errorf("invalid auth config") | ||||
| } | ||||
|  | ||||
| // createImageReference creates image reference inside containerd image store. | ||||
| // Note that because create and update are not finished in one transaction, there could be race. E.g. | ||||
| // the image reference is deleted by someone else after create returns already exists, but before update | ||||
| // happens. | ||||
| func (c *criContainerdService) createImageReference(ctx context.Context, name string, desc imagespec.Descriptor) error { | ||||
| 	img := containerdimages.Image{ | ||||
| 		Name:   name, | ||||
| 		Target: desc, | ||||
| 	} | ||||
| 	// TODO(random-liu): Figure out which is the more performant sequence create then update or | ||||
| 	// update then create. | ||||
| 	_, err := c.client.ImageService().Create(ctx, img) | ||||
| 	if err == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if err != nil && !errdefs.IsAlreadyExists(err) { | ||||
| 		return err | ||||
| 	} | ||||
| 	_, err = c.client.ImageService().Update(ctx, img, "target") | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) getResolverOptions() map[string][]string { | ||||
| 	options := make(map[string][]string) | ||||
| 	for ns, mirror := range c.config.Mirrors { | ||||
| 		options[ns] = append(options[ns], mirror.Endpoints...) | ||||
| 	} | ||||
| 	return options | ||||
| } | ||||
							
								
								
									
										94
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_remove.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_remove.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/images" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // RemoveImage removes the image. | ||||
| // TODO(random-liu): Update CRI to pass image reference instead of ImageSpec. (See | ||||
| // kubernetes/kubernetes#46255) | ||||
| // TODO(random-liu): We should change CRI to distinguish image id and image spec. | ||||
| // Remove the whole image no matter the it's image id or reference. This is the | ||||
| // semantic defined in CRI now. | ||||
| func (c *criContainerdService) RemoveImage(ctx context.Context, r *runtime.RemoveImageRequest) (*runtime.RemoveImageResponse, error) { | ||||
| 	image, err := c.localResolve(ctx, r.GetImage().GetImage()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("can not resolve %q locally: %v", r.GetImage().GetImage(), err) | ||||
| 	} | ||||
| 	if image == nil { | ||||
| 		// return empty without error when image not found. | ||||
| 		return &runtime.RemoveImageResponse{}, nil | ||||
| 	} | ||||
|  | ||||
| 	// Exclude outdated image tag. | ||||
| 	for i, tag := range image.RepoTags { | ||||
| 		cImage, err := c.client.GetImage(ctx, tag) | ||||
| 		if err != nil { | ||||
| 			if errdefs.IsNotFound(err) { | ||||
| 				continue | ||||
| 			} | ||||
| 			return nil, fmt.Errorf("failed to get image %q: %v", tag, err) | ||||
| 		} | ||||
| 		desc, err := cImage.Config(ctx) | ||||
| 		if err != nil { | ||||
| 			// We can only get image id by reading Config from content. | ||||
| 			// If the config is missing, we will fail to get image id, | ||||
| 			// So we won't be able to remove the image forever, | ||||
| 			// and cri-containerd always report the image is ok. | ||||
| 			// But we also don't check it by manifest, | ||||
| 			// It's possible that two manifest digests have the same image ID in theory. | ||||
| 			// In theory it's possible that an image is compressed with different algorithms, | ||||
| 			// then they'll have the same uncompressed id - image id, | ||||
| 			// but different ids generated from compressed contents - manifest digest. | ||||
| 			// So we decide to leave it. | ||||
| 			// After all, the user can override the repoTag by pulling image again. | ||||
| 			logrus.WithError(err).Errorf("Can't remove image,failed to get config for Image tag %q,id %q", tag, image.ID) | ||||
| 			image.RepoTags = append(image.RepoTags[:i], image.RepoTags[i+1:]...) | ||||
| 			continue | ||||
| 		} | ||||
| 		cID := desc.Digest.String() | ||||
| 		if cID != image.ID { | ||||
| 			logrus.Debugf("Image tag %q for %q is outdated, it's currently used by %q", tag, image.ID, cID) | ||||
| 			image.RepoTags = append(image.RepoTags[:i], image.RepoTags[i+1:]...) | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Include all image references, including RepoTag, RepoDigest and id. | ||||
| 	for _, ref := range append(image.RepoTags, image.RepoDigests...) { | ||||
| 		err = c.client.ImageService().Delete(ctx, ref) | ||||
| 		if err == nil || errdefs.IsNotFound(err) { | ||||
| 			continue | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("failed to delete image reference %q for image %q: %v", ref, image.ID, err) | ||||
| 	} | ||||
| 	// Delete image id synchronously to trigger garbage collection. | ||||
| 	err = c.client.ImageService().Delete(ctx, image.ID, images.SynchronousDelete()) | ||||
| 	if err != nil && !errdefs.IsNotFound(err) { | ||||
| 		return nil, fmt.Errorf("failed to delete image id %q: %v", image.ID, err) | ||||
| 	} | ||||
| 	c.imageStore.Delete(image.ID) | ||||
| 	return &runtime.RemoveImageResponse{}, nil | ||||
| } | ||||
							
								
								
									
										103
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/image_status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	imagestore "github.com/containerd/cri-containerd/pkg/store/image" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| ) | ||||
|  | ||||
| // ImageStatus returns the status of the image, returns nil if the image isn't present. | ||||
| // TODO(random-liu): We should change CRI to distinguish image id and image spec. (See | ||||
| // kubernetes/kubernetes#46255) | ||||
| func (c *criContainerdService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (*runtime.ImageStatusResponse, error) { | ||||
| 	image, err := c.localResolve(ctx, r.GetImage().GetImage()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("can not resolve %q locally: %v", r.GetImage().GetImage(), err) | ||||
| 	} | ||||
| 	if image == nil { | ||||
| 		// return empty without error when image not found. | ||||
| 		return &runtime.ImageStatusResponse{}, nil | ||||
| 	} | ||||
| 	// TODO(random-liu): [P0] Make sure corresponding snapshot exists. What if snapshot | ||||
| 	// doesn't exist? | ||||
|  | ||||
| 	runtimeImage := toCRIRuntimeImage(image) | ||||
| 	info, err := c.toCRIImageInfo(ctx, image, r.GetVerbose()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to generate image info: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.ImageStatusResponse{ | ||||
| 		Image: runtimeImage, | ||||
| 		Info:  info, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // toCRIRuntimeImage converts internal image object to CRI runtime.Image. | ||||
| func toCRIRuntimeImage(image *imagestore.Image) *runtime.Image { | ||||
| 	runtimeImage := &runtime.Image{ | ||||
| 		Id:          image.ID, | ||||
| 		RepoTags:    image.RepoTags, | ||||
| 		RepoDigests: image.RepoDigests, | ||||
| 		Size_:       uint64(image.Size), | ||||
| 	} | ||||
| 	uid, username := getUserFromImage(image.ImageSpec.Config.User) | ||||
| 	if uid != nil { | ||||
| 		runtimeImage.Uid = &runtime.Int64Value{Value: *uid} | ||||
| 	} | ||||
| 	runtimeImage.Username = username | ||||
|  | ||||
| 	return runtimeImage | ||||
| } | ||||
|  | ||||
| // TODO (mikebrow): discuss moving this struct and / or constants for info map for some or all of these fields to CRI | ||||
| type verboseImageInfo struct { | ||||
| 	ChainID   string          `json:"chainID"` | ||||
| 	ImageSpec imagespec.Image `json:"imageSpec"` | ||||
| } | ||||
|  | ||||
| // toCRIImageInfo converts internal image object information to CRI image status response info map. | ||||
| func (c *criContainerdService) toCRIImageInfo(ctx context.Context, image *imagestore.Image, verbose bool) (map[string]string, error) { | ||||
| 	if !verbose { | ||||
| 		return nil, nil | ||||
| 	} | ||||
|  | ||||
| 	info := make(map[string]string) | ||||
|  | ||||
| 	imi := &verboseImageInfo{ | ||||
| 		ChainID:   image.ChainID, | ||||
| 		ImageSpec: image.ImageSpec, | ||||
| 	} | ||||
|  | ||||
| 	m, err := json.Marshal(imi) | ||||
| 	if err == nil { | ||||
| 		info["info"] = string(m) | ||||
| 	} else { | ||||
| 		logrus.WithError(err).Errorf("failed to marshal info %v", imi) | ||||
| 		info["info"] = err.Error() | ||||
| 	} | ||||
|  | ||||
| 	return info, nil | ||||
| } | ||||
							
								
								
									
										51
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/imagefs_info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/imagefs_info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/net/context" | ||||
|  | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // ImageFsInfo returns information of the filesystem that is used to store images. | ||||
| func (c *criContainerdService) ImageFsInfo(ctx context.Context, r *runtime.ImageFsInfoRequest) (*runtime.ImageFsInfoResponse, error) { | ||||
| 	snapshots := c.snapshotStore.List() | ||||
| 	timestamp := time.Now().UnixNano() | ||||
| 	var usedBytes, inodesUsed uint64 | ||||
| 	for _, sn := range snapshots { | ||||
| 		// Use the oldest timestamp as the timestamp of imagefs info. | ||||
| 		if sn.Timestamp < timestamp { | ||||
| 			timestamp = sn.Timestamp | ||||
| 		} | ||||
| 		usedBytes += sn.Size | ||||
| 		inodesUsed += sn.Inodes | ||||
| 	} | ||||
| 	// TODO(random-liu): Handle content store | ||||
| 	return &runtime.ImageFsInfoResponse{ | ||||
| 		ImageFilesystems: []*runtime.FilesystemUsage{ | ||||
| 			{ | ||||
| 				Timestamp:  timestamp, | ||||
| 				FsId:       &runtime.FilesystemIdentifier{Mountpoint: c.imageFSPath}, | ||||
| 				UsedBytes:  &runtime.UInt64Value{Value: usedBytes}, | ||||
| 				InodesUsed: &runtime.UInt64Value{Value: inodesUsed}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										478
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/instrumented_service.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										478
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/instrumented_service.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,478 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
|  | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	api "github.com/containerd/cri-containerd/pkg/api/v1" | ||||
| 	"github.com/containerd/cri-containerd/pkg/log" | ||||
| ) | ||||
|  | ||||
| // instrumentedService wraps service and logs each operation. | ||||
| type instrumentedService struct { | ||||
| 	c *criContainerdService | ||||
| } | ||||
|  | ||||
| func newInstrumentedService(c *criContainerdService) grpcServices { | ||||
| 	return &instrumentedService{c: c} | ||||
| } | ||||
|  | ||||
| // checkInitialized returns error if the server is not fully initialized. | ||||
| // GRPC service request handlers should return error before server is fully | ||||
| // initialized. | ||||
| // NOTE(random-liu): All following functions MUST check initialized at the beginning. | ||||
| func (in *instrumentedService) checkInitialized() error { | ||||
| 	if in.c.initialized.IsSet() { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New("server is not initialized yet") | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (res *runtime.RunPodSandboxResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("RunPodSandbox with config %+v", r.GetConfig()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("RunPodSandbox for %+v failed, error", r.GetConfig().GetMetadata()) | ||||
| 		} else { | ||||
| 			logrus.Infof("RunPodSandbox for %+v returns sandbox id %q", r.GetConfig().GetMetadata(), res.GetPodSandboxId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.RunPodSandbox(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ListPodSandbox(ctx context.Context, r *runtime.ListPodSandboxRequest) (res *runtime.ListPodSandboxResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("ListPodSandbox with filter %+v", r.GetFilter()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Error("ListPodSandbox failed") | ||||
| 		} else { | ||||
| 			log.Tracef("ListPodSandbox returns pod sandboxes %+v", res.GetItems()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ListPodSandbox(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) PodSandboxStatus(ctx context.Context, r *runtime.PodSandboxStatusRequest) (res *runtime.PodSandboxStatusResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("PodSandboxStatus for %q", r.GetPodSandboxId()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("PodSandboxStatus for %q failed", r.GetPodSandboxId()) | ||||
| 		} else { | ||||
| 			log.Tracef("PodSandboxStatus for %q returns status %+v", r.GetPodSandboxId(), res.GetStatus()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.PodSandboxStatus(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (_ *runtime.StopPodSandboxResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("StopPodSandbox for %q", r.GetPodSandboxId()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("StopPodSandbox for %q failed", r.GetPodSandboxId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("StopPodSandbox for %q returns successfully", r.GetPodSandboxId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.StopPodSandbox(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodSandboxRequest) (_ *runtime.RemovePodSandboxResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("RemovePodSandbox for %q", r.GetPodSandboxId()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("RemovePodSandbox for %q failed", r.GetPodSandboxId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("RemovePodSandbox %q returns successfully", r.GetPodSandboxId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.RemovePodSandbox(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (res *runtime.PortForwardResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("Portforward for %q port %v", r.GetPodSandboxId(), r.GetPort()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("Portforward for %q failed", r.GetPodSandboxId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("Portforward for %q returns URL %q", r.GetPodSandboxId(), res.GetUrl()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.PortForward(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (res *runtime.CreateContainerResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("CreateContainer within sandbox %q with container config %+v and sandbox config %+v", | ||||
| 		r.GetPodSandboxId(), r.GetConfig(), r.GetSandboxConfig()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("CreateContainer within sandbox %q for %+v failed", | ||||
| 				r.GetPodSandboxId(), r.GetConfig().GetMetadata()) | ||||
| 		} else { | ||||
| 			logrus.Infof("CreateContainer within sandbox %q for %+v returns container id %q", | ||||
| 				r.GetPodSandboxId(), r.GetConfig().GetMetadata(), res.GetContainerId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.CreateContainer(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (_ *runtime.StartContainerResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("StartContainer for %q", r.GetContainerId()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("StartContainer for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("StartContainer for %q returns successfully", r.GetContainerId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.StartContainer(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ListContainers(ctx context.Context, r *runtime.ListContainersRequest) (res *runtime.ListContainersResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("ListContainers with filter %+v", r.GetFilter()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("ListContainers with filter %+v failed", r.GetFilter()) | ||||
| 		} else { | ||||
| 			log.Tracef("ListContainers with filter %+v returns containers %+v", | ||||
| 				r.GetFilter(), res.GetContainers()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ListContainers(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ContainerStatus(ctx context.Context, r *runtime.ContainerStatusRequest) (res *runtime.ContainerStatusResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("ContainerStatus for %q", r.GetContainerId()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("ContainerStatus for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			log.Tracef("ContainerStatus for %q returns status %+v", r.GetContainerId(), res.GetStatus()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ContainerStatus(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) StopContainer(ctx context.Context, r *runtime.StopContainerRequest) (res *runtime.StopContainerResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("StopContainer for %q with timeout %d (s)", r.GetContainerId(), r.GetTimeout()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("StopContainer for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("StopContainer for %q returns successfully", r.GetContainerId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.StopContainer(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) RemoveContainer(ctx context.Context, r *runtime.RemoveContainerRequest) (res *runtime.RemoveContainerResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("RemoveContainer for %q", r.GetContainerId()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("RemoveContainer for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("RemoveContainer for %q returns successfully", r.GetContainerId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.RemoveContainer(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ExecSync(ctx context.Context, r *runtime.ExecSyncRequest) (res *runtime.ExecSyncResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("ExecSync for %q with command %+v and timeout %d (s)", r.GetContainerId(), r.GetCmd(), r.GetTimeout()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("ExecSync for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("ExecSync for %q returns with exit code %d", r.GetContainerId(), res.GetExitCode()) | ||||
| 			logrus.Debugf("ExecSync for %q outputs - stdout: %q, stderr: %q", r.GetContainerId(), | ||||
| 				res.GetStdout(), res.GetStderr()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ExecSync(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) Exec(ctx context.Context, r *runtime.ExecRequest) (res *runtime.ExecResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("Exec for %q with command %+v, tty %v and stdin %v", | ||||
| 		r.GetContainerId(), r.GetCmd(), r.GetTty(), r.GetStdin()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("Exec for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("Exec for %q returns URL %q", r.GetContainerId(), res.GetUrl()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.Exec(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) Attach(ctx context.Context, r *runtime.AttachRequest) (res *runtime.AttachResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("Attach for %q with tty %v and stdin %v", r.GetContainerId(), r.GetTty(), r.GetStdin()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("Attach for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("Attach for %q returns URL %q", r.GetContainerId(), res.Url) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.Attach(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (res *runtime.UpdateContainerResourcesResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("UpdateContainerResources for %q with %+v", r.GetContainerId(), r.GetLinux()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("UpdateContainerResources for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Infof("UpdateContainerResources for %q returns successfully", r.GetContainerId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.UpdateContainerResources(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (res *runtime.PullImageResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("PullImage %q with auth config %+v", r.GetImage().GetImage(), r.GetAuth()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("PullImage %q failed", r.GetImage().GetImage()) | ||||
| 		} else { | ||||
| 			logrus.Infof("PullImage %q returns image reference %q", | ||||
| 				r.GetImage().GetImage(), res.GetImageRef()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.PullImage(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ListImages(ctx context.Context, r *runtime.ListImagesRequest) (res *runtime.ListImagesResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("ListImages with filter %+v", r.GetFilter()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("ListImages with filter %+v failed", r.GetFilter()) | ||||
| 		} else { | ||||
| 			log.Tracef("ListImages with filter %+v returns image list %+v", | ||||
| 				r.GetFilter(), res.GetImages()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ListImages(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (res *runtime.ImageStatusResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("ImageStatus for %q", r.GetImage().GetImage()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("ImageStatus for %q failed", r.GetImage().GetImage()) | ||||
| 		} else { | ||||
| 			log.Tracef("ImageStatus for %q returns image status %+v", | ||||
| 				r.GetImage().GetImage(), res.GetImage()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ImageStatus(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) RemoveImage(ctx context.Context, r *runtime.RemoveImageRequest) (_ *runtime.RemoveImageResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Infof("RemoveImage %q", r.GetImage().GetImage()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("RemoveImage %q failed", r.GetImage().GetImage()) | ||||
| 		} else { | ||||
| 			logrus.Infof("RemoveImage %q returns successfully", r.GetImage().GetImage()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.RemoveImage(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ImageFsInfo(ctx context.Context, r *runtime.ImageFsInfoRequest) (res *runtime.ImageFsInfoResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Debugf("ImageFsInfo") | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Error("ImageFsInfo failed") | ||||
| 		} else { | ||||
| 			logrus.Debugf("ImageFsInfo returns filesystem info %+v", res.ImageFilesystems) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ImageFsInfo(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ContainerStats(ctx context.Context, r *runtime.ContainerStatsRequest) (res *runtime.ContainerStatsResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Debugf("ContainerStats for %q", r.GetContainerId()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("ContainerStats for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Debugf("ContainerStats for %q returns stats %+v", r.GetContainerId(), res.GetStats()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ContainerStats(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ListContainerStats(ctx context.Context, r *runtime.ListContainerStatsRequest) (res *runtime.ListContainerStatsResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("ListContainerStats with filter %+v", r.GetFilter()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Error("ListContainerStats failed") | ||||
| 		} else { | ||||
| 			log.Tracef("ListContainerStats returns stats %+v", res.GetStats()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ListContainerStats(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) Status(ctx context.Context, r *runtime.StatusRequest) (res *runtime.StatusResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("Status") | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Error("Status failed") | ||||
| 		} else { | ||||
| 			log.Tracef("Status returns status %+v", res.GetStatus()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.Status(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) Version(ctx context.Context, r *runtime.VersionRequest) (res *runtime.VersionResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Tracef("Version with client side version %q", r.GetVersion()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Error("Version failed") | ||||
| 		} else { | ||||
| 			log.Tracef("Version returns %+v", res) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.Version(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateRuntimeConfigRequest) (res *runtime.UpdateRuntimeConfigResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Debugf("UpdateRuntimeConfig with config %+v", r.GetRuntimeConfig()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Error("UpdateRuntimeConfig failed") | ||||
| 		} else { | ||||
| 			logrus.Debug("UpdateRuntimeConfig returns returns successfully") | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.UpdateRuntimeConfig(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) LoadImage(ctx context.Context, r *api.LoadImageRequest) (res *api.LoadImageResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Debugf("LoadImage from file %q", r.GetFilePath()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Error("LoadImage failed") | ||||
| 		} else { | ||||
| 			logrus.Debugf("LoadImage returns images %+v", res.GetImages()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.LoadImage(ctx, r) | ||||
| } | ||||
|  | ||||
| func (in *instrumentedService) ReopenContainerLog(ctx context.Context, r *runtime.ReopenContainerLogRequest) (res *runtime.ReopenContainerLogResponse, err error) { | ||||
| 	if err := in.checkInitialized(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logrus.Debugf("ReopenContainerLog for %q", r.GetContainerId()) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("ReopenContainerLog for %q failed", r.GetContainerId()) | ||||
| 		} else { | ||||
| 			logrus.Debugf("ReopenContainerLog for %q returns successfully", r.GetContainerId()) | ||||
| 		} | ||||
| 	}() | ||||
| 	return in.c.ReopenContainerLog(ctx, r) | ||||
| } | ||||
							
								
								
									
										239
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/io/container_io.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/io/container_io.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 io | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd/cio" | ||||
| 	"github.com/sirupsen/logrus" | ||||
|  | ||||
| 	cioutil "github.com/containerd/cri-containerd/pkg/ioutil" | ||||
| 	"github.com/containerd/cri-containerd/pkg/util" | ||||
| ) | ||||
|  | ||||
| // streamKey generates a key for the stream. | ||||
| func streamKey(id, name string, stream StreamType) string { | ||||
| 	return strings.Join([]string{id, name, string(stream)}, "-") | ||||
| } | ||||
|  | ||||
| // ContainerIO holds the container io. | ||||
| type ContainerIO struct { | ||||
| 	id string | ||||
|  | ||||
| 	fifos *cio.FIFOSet | ||||
| 	*stdioPipes | ||||
|  | ||||
| 	stdoutGroup *cioutil.WriterGroup | ||||
| 	stderrGroup *cioutil.WriterGroup | ||||
|  | ||||
| 	closer *wgCloser | ||||
| } | ||||
|  | ||||
| var _ cio.IO = &ContainerIO{} | ||||
|  | ||||
| // ContainerIOOpts sets specific information to newly created ContainerIO. | ||||
| type ContainerIOOpts func(*ContainerIO) error | ||||
|  | ||||
| // WithOutput adds output stream to the container io. | ||||
| func WithOutput(name string, stdout, stderr io.WriteCloser) ContainerIOOpts { | ||||
| 	return func(c *ContainerIO) error { | ||||
| 		if stdout != nil { | ||||
| 			if err := c.stdoutGroup.Add(streamKey(c.id, name, Stdout), stdout); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		if stderr != nil { | ||||
| 			if err := c.stderrGroup.Add(streamKey(c.id, name, Stderr), stderr); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithFIFOs specifies existing fifos for the container io. | ||||
| func WithFIFOs(fifos *cio.FIFOSet) ContainerIOOpts { | ||||
| 	return func(c *ContainerIO) error { | ||||
| 		c.fifos = fifos | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithNewFIFOs creates new fifos for the container io. | ||||
| func WithNewFIFOs(root string, tty, stdin bool) ContainerIOOpts { | ||||
| 	return func(c *ContainerIO) error { | ||||
| 		fifos, err := newFifos(root, c.id, tty, stdin) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return WithFIFOs(fifos)(c) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewContainerIO creates container io. | ||||
| func NewContainerIO(id string, opts ...ContainerIOOpts) (_ *ContainerIO, err error) { | ||||
| 	c := &ContainerIO{ | ||||
| 		id:          id, | ||||
| 		stdoutGroup: cioutil.NewWriterGroup(), | ||||
| 		stderrGroup: cioutil.NewWriterGroup(), | ||||
| 	} | ||||
| 	for _, opt := range opts { | ||||
| 		if err := opt(c); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	if c.fifos == nil { | ||||
| 		return nil, errors.New("fifos are not set") | ||||
| 	} | ||||
| 	// Create actual fifos. | ||||
| 	stdio, closer, err := newStdioPipes(c.fifos) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c.stdioPipes = stdio | ||||
| 	c.closer = closer | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| // Config returns io config. | ||||
| func (c *ContainerIO) Config() cio.Config { | ||||
| 	return c.fifos.Config | ||||
| } | ||||
|  | ||||
| // Pipe creates container fifos and pipe container output | ||||
| // to output stream. | ||||
| func (c *ContainerIO) Pipe() { | ||||
| 	wg := c.closer.wg | ||||
| 	wg.Add(1) | ||||
| 	go func() { | ||||
| 		if _, err := io.Copy(c.stdoutGroup, c.stdout); err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to pipe stdout of container %q", c.id) | ||||
| 		} | ||||
| 		c.stdout.Close() | ||||
| 		c.stdoutGroup.Close() | ||||
| 		wg.Done() | ||||
| 		logrus.Infof("Finish piping stdout of container %q", c.id) | ||||
| 	}() | ||||
|  | ||||
| 	if !c.fifos.Terminal { | ||||
| 		wg.Add(1) | ||||
| 		go func() { | ||||
| 			if _, err := io.Copy(c.stderrGroup, c.stderr); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to pipe stderr of container %q", c.id) | ||||
| 			} | ||||
| 			c.stderr.Close() | ||||
| 			c.stderrGroup.Close() | ||||
| 			wg.Done() | ||||
| 			logrus.Infof("Finish piping stderr of container %q", c.id) | ||||
| 		}() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Attach attaches container stdio. | ||||
| // TODO(random-liu): Use pools.Copy in docker to reduce memory usage? | ||||
| func (c *ContainerIO) Attach(opts AttachOptions) error { | ||||
| 	var wg sync.WaitGroup | ||||
| 	key := util.GenerateID() | ||||
| 	stdinKey := streamKey(c.id, "attach-"+key, Stdin) | ||||
| 	stdoutKey := streamKey(c.id, "attach-"+key, Stdout) | ||||
| 	stderrKey := streamKey(c.id, "attach-"+key, Stderr) | ||||
|  | ||||
| 	var stdinStreamRC io.ReadCloser | ||||
| 	if c.stdin != nil && opts.Stdin != nil { | ||||
| 		// Create a wrapper of stdin which could be closed. Note that the | ||||
| 		// wrapper doesn't close the actual stdin, it only stops io.Copy. | ||||
| 		// The actual stdin will be closed by stream server. | ||||
| 		stdinStreamRC = cioutil.NewWrapReadCloser(opts.Stdin) | ||||
| 		wg.Add(1) | ||||
| 		go func() { | ||||
| 			if _, err := io.Copy(c.stdin, stdinStreamRC); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to pipe stdin for container attach %q", c.id) | ||||
| 			} | ||||
| 			logrus.Infof("Attach stream %q closed", stdinKey) | ||||
| 			if opts.StdinOnce && !opts.Tty { | ||||
| 				// Due to kubectl requirements and current docker behavior, when (opts.StdinOnce && | ||||
| 				// opts.Tty) we have to close container stdin and keep stdout and stderr open until | ||||
| 				// container stops. | ||||
| 				c.stdin.Close() | ||||
| 				// Also closes the containerd side. | ||||
| 				if err := opts.CloseStdin(); err != nil { | ||||
| 					logrus.WithError(err).Errorf("Failed to close stdin for container %q", c.id) | ||||
| 				} | ||||
| 			} else { | ||||
| 				if opts.Stdout != nil { | ||||
| 					c.stdoutGroup.Remove(stdoutKey) | ||||
| 				} | ||||
| 				if opts.Stderr != nil { | ||||
| 					c.stderrGroup.Remove(stderrKey) | ||||
| 				} | ||||
| 			} | ||||
| 			wg.Done() | ||||
| 		}() | ||||
| 	} | ||||
|  | ||||
| 	attachStream := func(key string, close <-chan struct{}) { | ||||
| 		<-close | ||||
| 		logrus.Infof("Attach stream %q closed", key) | ||||
| 		// Make sure stdin gets closed. | ||||
| 		if stdinStreamRC != nil { | ||||
| 			stdinStreamRC.Close() | ||||
| 		} | ||||
| 		wg.Done() | ||||
| 	} | ||||
|  | ||||
| 	if opts.Stdout != nil { | ||||
| 		wg.Add(1) | ||||
| 		wc, close := cioutil.NewWriteCloseInformer(opts.Stdout) | ||||
| 		if err := c.stdoutGroup.Add(stdoutKey, wc); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		go attachStream(stdoutKey, close) | ||||
| 	} | ||||
| 	if !opts.Tty && opts.Stderr != nil { | ||||
| 		wg.Add(1) | ||||
| 		wc, close := cioutil.NewWriteCloseInformer(opts.Stderr) | ||||
| 		if err := c.stderrGroup.Add(stderrKey, wc); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		go attachStream(stderrKey, close) | ||||
| 	} | ||||
| 	wg.Wait() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Cancel cancels container io. | ||||
| func (c *ContainerIO) Cancel() { | ||||
| 	c.closer.Cancel() | ||||
| } | ||||
|  | ||||
| // Wait waits container io to finish. | ||||
| func (c *ContainerIO) Wait() { | ||||
| 	c.closer.Wait() | ||||
| } | ||||
|  | ||||
| // Close closes all FIFOs. | ||||
| func (c *ContainerIO) Close() error { | ||||
| 	c.closer.Close() | ||||
| 	if c.fifos != nil { | ||||
| 		return c.fifos.Close() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										146
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/io/exec_io.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/io/exec_io.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 io | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd/cio" | ||||
| 	"github.com/sirupsen/logrus" | ||||
|  | ||||
| 	cioutil "github.com/containerd/cri-containerd/pkg/ioutil" | ||||
| ) | ||||
|  | ||||
| // ExecIO holds the exec io. | ||||
| type ExecIO struct { | ||||
| 	id    string | ||||
| 	fifos *cio.FIFOSet | ||||
| 	*stdioPipes | ||||
| 	closer *wgCloser | ||||
| } | ||||
|  | ||||
| var _ cio.IO = &ExecIO{} | ||||
|  | ||||
| // NewExecIO creates exec io. | ||||
| func NewExecIO(id, root string, tty, stdin bool) (*ExecIO, error) { | ||||
| 	fifos, err := newFifos(root, id, tty, stdin) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	stdio, closer, err := newStdioPipes(fifos) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &ExecIO{ | ||||
| 		id:         id, | ||||
| 		fifos:      fifos, | ||||
| 		stdioPipes: stdio, | ||||
| 		closer:     closer, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // Config returns io config. | ||||
| func (e *ExecIO) Config() cio.Config { | ||||
| 	return e.fifos.Config | ||||
| } | ||||
|  | ||||
| // Attach attaches exec stdio. The logic is similar with container io attach. | ||||
| func (e *ExecIO) Attach(opts AttachOptions) <-chan struct{} { | ||||
| 	var wg sync.WaitGroup | ||||
| 	var stdinStreamRC io.ReadCloser | ||||
| 	if e.stdin != nil && opts.Stdin != nil { | ||||
| 		stdinStreamRC = cioutil.NewWrapReadCloser(opts.Stdin) | ||||
| 		wg.Add(1) | ||||
| 		go func() { | ||||
| 			if _, err := io.Copy(e.stdin, stdinStreamRC); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to redirect stdin for container exec %q", e.id) | ||||
| 			} | ||||
| 			logrus.Infof("Container exec %q stdin closed", e.id) | ||||
| 			if opts.StdinOnce && !opts.Tty { | ||||
| 				e.stdin.Close() | ||||
| 				if err := opts.CloseStdin(); err != nil { | ||||
| 					logrus.WithError(err).Errorf("Failed to close stdin for container exec %q", e.id) | ||||
| 				} | ||||
| 			} else { | ||||
| 				if e.stdout != nil { | ||||
| 					e.stdout.Close() | ||||
| 				} | ||||
| 				if e.stderr != nil { | ||||
| 					e.stderr.Close() | ||||
| 				} | ||||
| 			} | ||||
| 			wg.Done() | ||||
| 		}() | ||||
| 	} | ||||
|  | ||||
| 	attachOutput := func(t StreamType, stream io.WriteCloser, out io.ReadCloser) { | ||||
| 		if _, err := io.Copy(stream, out); err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to pipe %q for container exec %q", t, e.id) | ||||
| 		} | ||||
| 		out.Close() | ||||
| 		stream.Close() | ||||
| 		if stdinStreamRC != nil { | ||||
| 			stdinStreamRC.Close() | ||||
| 		} | ||||
| 		e.closer.wg.Done() | ||||
| 		wg.Done() | ||||
| 		logrus.Infof("Finish piping %q of container exec %q", t, e.id) | ||||
| 	} | ||||
|  | ||||
| 	if opts.Stdout != nil { | ||||
| 		wg.Add(1) | ||||
| 		// Closer should wait for this routine to be over. | ||||
| 		e.closer.wg.Add(1) | ||||
| 		go attachOutput(Stdout, opts.Stdout, e.stdout) | ||||
| 	} | ||||
|  | ||||
| 	if !opts.Tty && opts.Stderr != nil { | ||||
| 		wg.Add(1) | ||||
| 		// Closer should wait for this routine to be over. | ||||
| 		e.closer.wg.Add(1) | ||||
| 		go attachOutput(Stderr, opts.Stderr, e.stderr) | ||||
| 	} | ||||
|  | ||||
| 	done := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		wg.Wait() | ||||
| 		close(done) | ||||
| 	}() | ||||
| 	return done | ||||
| } | ||||
|  | ||||
| // Cancel cancels exec io. | ||||
| func (e *ExecIO) Cancel() { | ||||
| 	e.closer.Cancel() | ||||
| } | ||||
|  | ||||
| // Wait waits exec io to finish. | ||||
| func (e *ExecIO) Wait() { | ||||
| 	e.closer.Wait() | ||||
| } | ||||
|  | ||||
| // Close closes all FIFOs. | ||||
| func (e *ExecIO) Close() error { | ||||
| 	if e.closer != nil { | ||||
| 		e.closer.Close() | ||||
| 	} | ||||
| 	if e.fifos != nil { | ||||
| 		return e.fifos.Close() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										141
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/io/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/io/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 io | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/containerd/containerd/cio" | ||||
| 	"github.com/containerd/fifo" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // AttachOptions specifies how to attach to a container. | ||||
| type AttachOptions struct { | ||||
| 	Stdin     io.Reader | ||||
| 	Stdout    io.WriteCloser | ||||
| 	Stderr    io.WriteCloser | ||||
| 	Tty       bool | ||||
| 	StdinOnce bool | ||||
| 	// CloseStdin is the function to close container stdin. | ||||
| 	CloseStdin func() error | ||||
| } | ||||
|  | ||||
| // StreamType is the type of the stream, stdout/stderr. | ||||
| type StreamType string | ||||
|  | ||||
| const ( | ||||
| 	// Stdin stream type. | ||||
| 	Stdin StreamType = "stdin" | ||||
| 	// Stdout stream type. | ||||
| 	Stdout StreamType = StreamType(runtime.Stdout) | ||||
| 	// Stderr stream type. | ||||
| 	Stderr StreamType = StreamType(runtime.Stderr) | ||||
| ) | ||||
|  | ||||
| type wgCloser struct { | ||||
| 	ctx    context.Context | ||||
| 	wg     *sync.WaitGroup | ||||
| 	set    []io.Closer | ||||
| 	cancel context.CancelFunc | ||||
| } | ||||
|  | ||||
| func (g *wgCloser) Wait() { | ||||
| 	g.wg.Wait() | ||||
| } | ||||
|  | ||||
| func (g *wgCloser) Close() { | ||||
| 	for _, f := range g.set { | ||||
| 		f.Close() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (g *wgCloser) Cancel() { | ||||
| 	g.cancel() | ||||
| } | ||||
|  | ||||
| // newFifos creates fifos directory for a container. | ||||
| func newFifos(root, id string, tty, stdin bool) (*cio.FIFOSet, error) { | ||||
| 	root = filepath.Join(root, "io") | ||||
| 	if err := os.MkdirAll(root, 0700); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	fifos, err := cio.NewFIFOSetInDir(root, id, tty) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !stdin { | ||||
| 		fifos.Stdin = "" | ||||
| 	} | ||||
| 	return fifos, nil | ||||
| } | ||||
|  | ||||
| type stdioPipes struct { | ||||
| 	stdin  io.WriteCloser | ||||
| 	stdout io.ReadCloser | ||||
| 	stderr io.ReadCloser | ||||
| } | ||||
|  | ||||
| // newStdioPipes creates actual fifos for stdio. | ||||
| func newStdioPipes(fifos *cio.FIFOSet) (_ *stdioPipes, _ *wgCloser, err error) { | ||||
| 	var ( | ||||
| 		f           io.ReadWriteCloser | ||||
| 		set         []io.Closer | ||||
| 		ctx, cancel = context.WithCancel(context.Background()) | ||||
| 		p           = &stdioPipes{} | ||||
| 	) | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			for _, f := range set { | ||||
| 				f.Close() | ||||
| 			} | ||||
| 			cancel() | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	if fifos.Stdin != "" { | ||||
| 		if f, err = fifo.OpenFifo(ctx, fifos.Stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { | ||||
| 			return nil, nil, err | ||||
| 		} | ||||
| 		p.stdin = f | ||||
| 		set = append(set, f) | ||||
| 	} | ||||
|  | ||||
| 	if f, err = fifo.OpenFifo(ctx, fifos.Stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	p.stdout = f | ||||
| 	set = append(set, f) | ||||
|  | ||||
| 	if f, err = fifo.OpenFifo(ctx, fifos.Stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	p.stderr = f | ||||
| 	set = append(set, f) | ||||
|  | ||||
| 	return p, &wgCloser{ | ||||
| 		wg:     &sync.WaitGroup{}, | ||||
| 		set:    set, | ||||
| 		ctx:    ctx, | ||||
| 		cancel: cancel, | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										96
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/io/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/io/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 io | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	cioutil "github.com/containerd/cri-containerd/pkg/ioutil" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// delimiter used in CRI logging format. | ||||
| 	delimiter = ' ' | ||||
| 	// eof is end-of-line. | ||||
| 	eol = '\n' | ||||
| 	// timestampFormat is the timestamp format used in CRI logging format. | ||||
| 	timestampFormat = time.RFC3339Nano | ||||
| 	// pipeBufSize is the system PIPE_BUF size, on linux it is 4096 bytes. | ||||
| 	// POSIX.1 says that write less than PIPE_BUF is atmoic. | ||||
| 	pipeBufSize = 4096 | ||||
| 	// bufSize is the size of the read buffer. | ||||
| 	bufSize = pipeBufSize - len(timestampFormat) - len(Stdout) - len(runtime.LogTagPartial) - 3 /*3 delimiter*/ - 1 /*eol*/ | ||||
| ) | ||||
|  | ||||
| // NewDiscardLogger creates logger which discards all the input. | ||||
| func NewDiscardLogger() io.WriteCloser { | ||||
| 	return cioutil.NewNopWriteCloser(ioutil.Discard) | ||||
| } | ||||
|  | ||||
| // NewCRILogger returns a write closer which redirect container log into | ||||
| // log file, and decorate the log line into CRI defined format. | ||||
| func NewCRILogger(path string, stream StreamType) (io.WriteCloser, error) { | ||||
| 	logrus.Debugf("Start writing log file %q", path) | ||||
| 	prc, pwc := io.Pipe() | ||||
| 	f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0640) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to open log file: %v", err) | ||||
| 	} | ||||
| 	go redirectLogs(path, prc, f, stream) | ||||
| 	return pwc, nil | ||||
| } | ||||
|  | ||||
| func redirectLogs(path string, rc io.ReadCloser, wc io.WriteCloser, stream StreamType) { | ||||
| 	defer rc.Close() | ||||
| 	defer wc.Close() | ||||
| 	streamBytes := []byte(stream) | ||||
| 	delimiterBytes := []byte{delimiter} | ||||
| 	partialBytes := []byte(runtime.LogTagPartial) | ||||
| 	fullBytes := []byte(runtime.LogTagFull) | ||||
| 	r := bufio.NewReaderSize(rc, bufSize) | ||||
| 	for { | ||||
| 		lineBytes, isPrefix, err := r.ReadLine() | ||||
| 		if err == io.EOF { | ||||
| 			logrus.Debugf("Finish redirecting log file %q", path) | ||||
| 			return | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("An error occurred when redirecting log file %q", path) | ||||
| 			return | ||||
| 		} | ||||
| 		tagBytes := fullBytes | ||||
| 		if isPrefix { | ||||
| 			tagBytes = partialBytes | ||||
| 		} | ||||
| 		timestampBytes := time.Now().AppendFormat(nil, time.RFC3339Nano) | ||||
| 		data := bytes.Join([][]byte{timestampBytes, streamBytes, tagBytes, lineBytes}, delimiterBytes) | ||||
| 		data = append(data, eol) | ||||
| 		if _, err := wc.Write(data); err != nil { | ||||
| 			logrus.WithError(err).Errorf("Fail to write %q log to log file %q", stream, path) | ||||
| 		} | ||||
| 		// Continue on write error to drain the input. | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										507
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/restart.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										507
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/restart.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,507 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	containerdio "github.com/containerd/containerd/cio" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	containerdimages "github.com/containerd/containerd/images" | ||||
| 	"github.com/containerd/containerd/platforms" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/docker/distribution/reference" | ||||
| 	"github.com/docker/docker/pkg/system" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	cio "github.com/containerd/cri-containerd/pkg/server/io" | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| 	imagestore "github.com/containerd/cri-containerd/pkg/store/image" | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // NOTE: The recovery logic has following assumption: when cri-containerd is down: | ||||
| // 1) Files (e.g. root directory, netns) and checkpoint maintained by cri-containerd MUST NOT be | ||||
| // touched. Or else, recovery logic for those containers/sandboxes may return error. | ||||
| // 2) Containerd containers may be deleted, but SHOULD NOT be added. Or else, recovery logic | ||||
| // for the newly added container/sandbox will return error, because there is no corresponding root | ||||
| // directory created. | ||||
| // 3) Containerd container tasks may exit or be stoppped, deleted. Even though current logic could | ||||
| // tolerant tasks being created or started, we prefer that not to happen. | ||||
|  | ||||
| // recover recovers system state from containerd and status checkpoint. | ||||
| func (c *criContainerdService) recover(ctx context.Context) error { | ||||
| 	// Recover all sandboxes. | ||||
| 	sandboxes, err := c.client.Containers(ctx, filterLabel(containerKindLabel, containerKindSandbox)) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to list sandbox containers: %v", err) | ||||
| 	} | ||||
| 	for _, sandbox := range sandboxes { | ||||
| 		sb, err := loadSandbox(ctx, sandbox) | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to load sandbox %q", sandbox.ID()) | ||||
| 			continue | ||||
| 		} | ||||
| 		logrus.Debugf("Loaded sandbox %+v", sb) | ||||
| 		if err := c.sandboxStore.Add(sb); err != nil { | ||||
| 			return fmt.Errorf("failed to add sandbox %q to store: %v", sandbox.ID(), err) | ||||
| 		} | ||||
| 		if err := c.sandboxNameIndex.Reserve(sb.Name, sb.ID); err != nil { | ||||
| 			return fmt.Errorf("failed to reserve sandbox name %q: %v", sb.Name, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Recover all containers. | ||||
| 	containers, err := c.client.Containers(ctx, filterLabel(containerKindLabel, containerKindContainer)) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to list containers: %v", err) | ||||
| 	} | ||||
| 	for _, container := range containers { | ||||
| 		containerDir := getContainerRootDir(c.config.RootDir, container.ID()) | ||||
| 		cntr, err := loadContainer(ctx, container, containerDir) | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to load container %q", container.ID()) | ||||
| 			continue | ||||
| 		} | ||||
| 		logrus.Debugf("Loaded container %+v", cntr) | ||||
| 		if err := c.containerStore.Add(cntr); err != nil { | ||||
| 			return fmt.Errorf("failed to add container %q to store: %v", container.ID(), err) | ||||
| 		} | ||||
| 		if err := c.containerNameIndex.Reserve(cntr.Name, cntr.ID); err != nil { | ||||
| 			return fmt.Errorf("failed to reserve container name %q: %v", cntr.Name, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Recover all images. | ||||
| 	cImages, err := c.client.ListImages(ctx) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to list images: %v", err) | ||||
| 	} | ||||
| 	images, err := loadImages(ctx, cImages, c.config.ContainerdConfig.Snapshotter) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to load images: %v", err) | ||||
| 	} | ||||
| 	for _, image := range images { | ||||
| 		logrus.Debugf("Loaded image %+v", image) | ||||
| 		if err := c.imageStore.Add(image); err != nil { | ||||
| 			return fmt.Errorf("failed to add image %q to store: %v", image.ID, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// It's possible that containerd containers are deleted unexpectedly. In that case, | ||||
| 	// we can't even get metadata, we should cleanup orphaned sandbox/container directories | ||||
| 	// with best effort. | ||||
|  | ||||
| 	// Cleanup orphaned sandbox directories without corresponding containerd container. | ||||
| 	if err := cleanupOrphanedSandboxDirs(sandboxes, filepath.Join(c.config.RootDir, "sandboxes")); err != nil { | ||||
| 		return fmt.Errorf("failed to cleanup orphaned sandbox directories: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Cleanup orphaned container directories without corresponding containerd container. | ||||
| 	if err := cleanupOrphanedContainerDirs(containers, filepath.Join(c.config.RootDir, "containers")); err != nil { | ||||
| 		return fmt.Errorf("failed to cleanup orphaned container directories: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // loadContainer loads container from containerd and status checkpoint. | ||||
| func loadContainer(ctx context.Context, cntr containerd.Container, containerDir string) (containerstore.Container, error) { | ||||
| 	id := cntr.ID() | ||||
| 	var container containerstore.Container | ||||
| 	// Load container metadata. | ||||
| 	exts, err := cntr.Extensions(ctx) | ||||
| 	if err != nil { | ||||
| 		return container, fmt.Errorf("failed to get container extensions: %v", err) | ||||
| 	} | ||||
| 	ext, ok := exts[containerMetadataExtension] | ||||
| 	if !ok { | ||||
| 		return container, fmt.Errorf("metadata extension %q not found", containerMetadataExtension) | ||||
| 	} | ||||
| 	data, err := typeurl.UnmarshalAny(&ext) | ||||
| 	if err != nil { | ||||
| 		return container, fmt.Errorf("failed to unmarshal metadata extension %q: %v", ext, err) | ||||
| 	} | ||||
| 	meta := data.(*containerstore.Metadata) | ||||
|  | ||||
| 	// Load status from checkpoint. | ||||
| 	status, err := containerstore.LoadStatus(containerDir, id) | ||||
| 	if err != nil { | ||||
| 		logrus.WithError(err).Warnf("Failed to load container status for %q", id) | ||||
| 		status = unknownContainerStatus() | ||||
| 	} | ||||
|  | ||||
| 	// Load up-to-date status from containerd. | ||||
| 	var containerIO *cio.ContainerIO | ||||
| 	t, err := cntr.Task(ctx, func(fifos *containerdio.FIFOSet) (containerdio.IO, error) { | ||||
| 		stdoutWC, stderrWC, err := createContainerLoggers(meta.LogPath, meta.Config.GetTty()) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		containerIO, err = cio.NewContainerIO(id, | ||||
| 			cio.WithFIFOs(fifos), | ||||
| 			cio.WithOutput("log", stdoutWC, stderrWC), | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		containerIO.Pipe() | ||||
| 		return containerIO, nil | ||||
| 	}) | ||||
| 	if err != nil && !errdefs.IsNotFound(err) { | ||||
| 		return container, fmt.Errorf("failed to load task: %v", err) | ||||
| 	} | ||||
| 	var s containerd.Status | ||||
| 	var notFound bool | ||||
| 	if errdefs.IsNotFound(err) { | ||||
| 		// Task is not found. | ||||
| 		notFound = true | ||||
| 	} else { | ||||
| 		// Task is found. Get task status. | ||||
| 		s, err = t.Status(ctx) | ||||
| 		if err != nil { | ||||
| 			// It's still possible that task is deleted during this window. | ||||
| 			if !errdefs.IsNotFound(err) { | ||||
| 				return container, fmt.Errorf("failed to get task status: %v", err) | ||||
| 			} | ||||
| 			notFound = true | ||||
| 		} | ||||
| 	} | ||||
| 	if notFound { | ||||
| 		// Task is not created or has been deleted, use the checkpointed status | ||||
| 		// to generate container status. | ||||
| 		switch status.State() { | ||||
| 		case runtime.ContainerState_CONTAINER_CREATED: | ||||
| 			// NOTE: Another possibility is that we've tried to start the container, but | ||||
| 			// cri-containerd got restarted just during that. In that case, we still | ||||
| 			// treat the container as `CREATED`. | ||||
| 			containerIO, err = cio.NewContainerIO(id, | ||||
| 				cio.WithNewFIFOs(containerDir, meta.Config.GetTty(), meta.Config.GetStdin()), | ||||
| 			) | ||||
| 			if err != nil { | ||||
| 				return container, fmt.Errorf("failed to create container io: %v", err) | ||||
| 			} | ||||
| 		case runtime.ContainerState_CONTAINER_RUNNING: | ||||
| 			// Container was in running state, but its task has been deleted, | ||||
| 			// set unknown exited state. Container io is not needed in this case. | ||||
| 			status.FinishedAt = time.Now().UnixNano() | ||||
| 			status.ExitCode = unknownExitCode | ||||
| 			status.Reason = unknownExitReason | ||||
| 		default: | ||||
| 			// Container is in exited/unknown state, return the status as it is. | ||||
| 		} | ||||
| 	} else { | ||||
| 		// Task status is found. Update container status based on the up-to-date task status. | ||||
| 		switch s.Status { | ||||
| 		case containerd.Created: | ||||
| 			// Task has been created, but not started yet. This could only happen if cri-containerd | ||||
| 			// gets restarted during container start. | ||||
| 			// Container must be in `CREATED` state. | ||||
| 			if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) { | ||||
| 				return container, fmt.Errorf("failed to delete task: %v", err) | ||||
| 			} | ||||
| 			if status.State() != runtime.ContainerState_CONTAINER_CREATED { | ||||
| 				return container, fmt.Errorf("unexpected container state for created task: %q", status.State()) | ||||
| 			} | ||||
| 		case containerd.Running: | ||||
| 			// Task is running. Container must be in `RUNNING` state, based on our assuption that | ||||
| 			// "task should not be started when cri-containerd is down". | ||||
| 			switch status.State() { | ||||
| 			case runtime.ContainerState_CONTAINER_EXITED: | ||||
| 				return container, fmt.Errorf("unexpected container state for running task: %q", status.State()) | ||||
| 			case runtime.ContainerState_CONTAINER_RUNNING: | ||||
| 			default: | ||||
| 				// This may happen if cri-containerd gets restarted after task is started, but | ||||
| 				// before status is checkpointed. | ||||
| 				status.StartedAt = time.Now().UnixNano() | ||||
| 				status.Pid = t.Pid() | ||||
| 			} | ||||
| 		case containerd.Stopped: | ||||
| 			// Task is stopped. Updata status and delete the task. | ||||
| 			if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) { | ||||
| 				return container, fmt.Errorf("failed to delete task: %v", err) | ||||
| 			} | ||||
| 			status.FinishedAt = s.ExitTime.UnixNano() | ||||
| 			status.ExitCode = int32(s.ExitStatus) | ||||
| 		default: | ||||
| 			return container, fmt.Errorf("unexpected task status %q", s.Status) | ||||
| 		} | ||||
| 	} | ||||
| 	opts := []containerstore.Opts{ | ||||
| 		containerstore.WithStatus(status, containerDir), | ||||
| 		containerstore.WithContainer(cntr), | ||||
| 	} | ||||
| 	if containerIO != nil { | ||||
| 		opts = append(opts, containerstore.WithContainerIO(containerIO)) | ||||
| 	} | ||||
| 	return containerstore.NewContainer(*meta, opts...) | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	// unknownExitCode is the exit code when exit reason is unknown. | ||||
| 	unknownExitCode = 255 | ||||
| 	// unknownExitReason is the exit reason when exit reason is unknown. | ||||
| 	unknownExitReason = "Unknown" | ||||
| ) | ||||
|  | ||||
| // unknownContainerStatus returns the default container status when its status is unknown. | ||||
| func unknownContainerStatus() containerstore.Status { | ||||
| 	return containerstore.Status{ | ||||
| 		CreatedAt:  time.Now().UnixNano(), | ||||
| 		StartedAt:  time.Now().UnixNano(), | ||||
| 		FinishedAt: time.Now().UnixNano(), | ||||
| 		ExitCode:   unknownExitCode, | ||||
| 		Reason:     unknownExitReason, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // loadSandbox loads sandbox from containerd. | ||||
| func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.Sandbox, error) { | ||||
| 	var sandbox sandboxstore.Sandbox | ||||
| 	// Load sandbox metadata. | ||||
| 	exts, err := cntr.Extensions(ctx) | ||||
| 	if err != nil { | ||||
| 		return sandbox, fmt.Errorf("failed to get sandbox container extensions: %v", err) | ||||
| 	} | ||||
| 	ext, ok := exts[sandboxMetadataExtension] | ||||
| 	if !ok { | ||||
| 		return sandbox, fmt.Errorf("metadata extension %q not found", sandboxMetadataExtension) | ||||
| 	} | ||||
| 	data, err := typeurl.UnmarshalAny(&ext) | ||||
| 	if err != nil { | ||||
| 		return sandbox, fmt.Errorf("failed to unmarshal metadata extension %q: %v", ext, err) | ||||
| 	} | ||||
| 	meta := data.(*sandboxstore.Metadata) | ||||
|  | ||||
| 	// Load sandbox created timestamp. | ||||
| 	info, err := cntr.Info(ctx) | ||||
| 	if err != nil { | ||||
| 		return sandbox, fmt.Errorf("failed to get sandbox container info: %v", err) | ||||
| 	} | ||||
| 	createdAt := info.CreatedAt | ||||
|  | ||||
| 	// Load sandbox status. | ||||
| 	t, err := cntr.Task(ctx, nil) | ||||
| 	if err != nil && !errdefs.IsNotFound(err) { | ||||
| 		return sandbox, fmt.Errorf("failed to load task: %v", err) | ||||
| 	} | ||||
| 	var s containerd.Status | ||||
| 	var notFound bool | ||||
| 	if errdefs.IsNotFound(err) { | ||||
| 		// Task is not found. | ||||
| 		notFound = true | ||||
| 	} else { | ||||
| 		// Task is found. Get task status. | ||||
| 		s, err = t.Status(ctx) | ||||
| 		if err != nil { | ||||
| 			// It's still possible that task is deleted during this window. | ||||
| 			if !errdefs.IsNotFound(err) { | ||||
| 				return sandbox, fmt.Errorf("failed to get task status: %v", err) | ||||
| 			} | ||||
| 			notFound = true | ||||
| 		} | ||||
| 	} | ||||
| 	var state sandboxstore.State | ||||
| 	var pid uint32 | ||||
| 	if notFound { | ||||
| 		// Task does not exist, set sandbox state as NOTREADY. | ||||
| 		state = sandboxstore.StateNotReady | ||||
| 	} else { | ||||
| 		if s.Status == containerd.Running { | ||||
| 			// Task is running, set sandbox state as READY. | ||||
| 			state = sandboxstore.StateReady | ||||
| 			pid = t.Pid() | ||||
| 		} else { | ||||
| 			// Task is not running. Delete the task and set sandbox state as NOTREADY. | ||||
| 			if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) { | ||||
| 				return sandbox, fmt.Errorf("failed to delete task: %v", err) | ||||
| 			} | ||||
| 			state = sandboxstore.StateNotReady | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	sandbox = sandboxstore.NewSandbox( | ||||
| 		*meta, | ||||
| 		sandboxstore.Status{ | ||||
| 			Pid:       pid, | ||||
| 			CreatedAt: createdAt, | ||||
| 			State:     state, | ||||
| 		}, | ||||
| 	) | ||||
| 	sandbox.Container = cntr | ||||
|  | ||||
| 	// Load network namespace. | ||||
| 	if meta.Config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE { | ||||
| 		// Don't need to load netns for host network sandbox. | ||||
| 		return sandbox, nil | ||||
| 	} | ||||
| 	netNS, err := sandboxstore.LoadNetNS(meta.NetNSPath) | ||||
| 	if err != nil { | ||||
| 		if err != sandboxstore.ErrClosedNetNS { | ||||
| 			return sandbox, fmt.Errorf("failed to load netns %q: %v", meta.NetNSPath, err) | ||||
| 		} | ||||
| 		netNS = nil | ||||
| 	} | ||||
| 	sandbox.NetNS = netNS | ||||
|  | ||||
| 	// It doesn't matter whether task is running or not. If it is running, sandbox | ||||
| 	// status will be `READY`; if it is not running, sandbox status will be `NOT_READY`, | ||||
| 	// kubelet will stop the sandbox which will properly cleanup everything. | ||||
| 	return sandbox, nil | ||||
| } | ||||
|  | ||||
| // loadImages loads images from containerd. | ||||
| // TODO(random-liu): Check whether image is unpacked, because containerd put image reference | ||||
| // into store before image is unpacked. | ||||
| func loadImages(ctx context.Context, cImages []containerd.Image, | ||||
| 	snapshotter string) ([]imagestore.Image, error) { | ||||
| 	// Group images by image id. | ||||
| 	imageMap := make(map[string][]containerd.Image) | ||||
| 	for _, i := range cImages { | ||||
| 		desc, err := i.Config(ctx) | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Warnf("Failed to get image config for %q", i.Name()) | ||||
| 			continue | ||||
| 		} | ||||
| 		id := desc.Digest.String() | ||||
| 		imageMap[id] = append(imageMap[id], i) | ||||
| 	} | ||||
| 	var images []imagestore.Image | ||||
| 	for id, imgs := range imageMap { | ||||
| 		// imgs len must be > 0, or else the entry will not be created in | ||||
| 		// previous loop. | ||||
| 		i := imgs[0] | ||||
| 		ok, _, _, _, err := containerdimages.Check(ctx, i.ContentStore(), i.Target(), platforms.Default()) | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to check image content readiness for %q", i.Name()) | ||||
| 			continue | ||||
| 		} | ||||
| 		if !ok { | ||||
| 			logrus.Warnf("The image content readiness for %q is not ok", i.Name()) | ||||
| 			continue | ||||
| 		} | ||||
| 		// Checking existence of top-level snapshot for each image being recovered. | ||||
| 		unpacked, err := i.IsUnpacked(ctx, snapshotter) | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Warnf("Failed to Check whether image is unpacked for image %s", i.Name()) | ||||
| 			continue | ||||
| 		} | ||||
| 		if !unpacked { | ||||
| 			logrus.Warnf("The image %s is not unpacked.", i.Name()) | ||||
| 			// TODO(random-liu): Consider whether we should try unpack here. | ||||
| 		} | ||||
|  | ||||
| 		info, err := getImageInfo(ctx, i) | ||||
| 		if err != nil { | ||||
| 			logrus.WithError(err).Warnf("Failed to get image info for %q", i.Name()) | ||||
| 			continue | ||||
| 		} | ||||
| 		image := imagestore.Image{ | ||||
| 			ID:        id, | ||||
| 			ChainID:   info.chainID.String(), | ||||
| 			Size:      info.size, | ||||
| 			ImageSpec: info.imagespec, | ||||
| 			Image:     i, | ||||
| 		} | ||||
| 		// Recover repo digests and repo tags. | ||||
| 		for _, i := range imgs { | ||||
| 			name := i.Name() | ||||
| 			r, err := reference.ParseAnyReference(name) | ||||
| 			if err != nil { | ||||
| 				logrus.WithError(err).Warnf("Failed to parse image reference %q", name) | ||||
| 				continue | ||||
| 			} | ||||
| 			if _, ok := r.(reference.Canonical); ok { | ||||
| 				image.RepoDigests = append(image.RepoDigests, name) | ||||
| 			} else if _, ok := r.(reference.Tagged); ok { | ||||
| 				image.RepoTags = append(image.RepoTags, name) | ||||
| 			} else if _, ok := r.(reference.Digested); ok { | ||||
| 				// This is an image id. | ||||
| 				continue | ||||
| 			} else { | ||||
| 				logrus.Warnf("Invalid image reference %q", name) | ||||
| 			} | ||||
| 		} | ||||
| 		images = append(images, image) | ||||
| 	} | ||||
| 	return images, nil | ||||
| } | ||||
|  | ||||
| func cleanupOrphanedSandboxDirs(cntrs []containerd.Container, sandboxesRoot string) error { | ||||
| 	// Cleanup orphaned sandbox directories. | ||||
| 	dirs, err := ioutil.ReadDir(sandboxesRoot) | ||||
| 	if err != nil && !os.IsNotExist(err) { | ||||
| 		return fmt.Errorf("failed to read pod sandboxes directory %q: %v", sandboxesRoot, err) | ||||
| 	} | ||||
| 	cntrsMap := make(map[string]containerd.Container) | ||||
| 	for _, cntr := range cntrs { | ||||
| 		cntrsMap[cntr.ID()] = cntr | ||||
| 	} | ||||
| 	for _, d := range dirs { | ||||
| 		if !d.IsDir() { | ||||
| 			logrus.Warnf("Invalid file %q found in pod sandboxes directory", d.Name()) | ||||
| 			continue | ||||
| 		} | ||||
| 		if _, ok := cntrsMap[d.Name()]; ok { | ||||
| 			// Do not remove sandbox directory if corresponding container is found. | ||||
| 			continue | ||||
| 		} | ||||
| 		sandboxDir := filepath.Join(sandboxesRoot, d.Name()) | ||||
| 		if err := system.EnsureRemoveAll(sandboxDir); err != nil { | ||||
| 			logrus.WithError(err).Warnf("Failed to remove pod sandbox directory %q", sandboxDir) | ||||
| 		} else { | ||||
| 			logrus.Debugf("Cleanup orphaned pod sandbox directory %q", sandboxDir) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func cleanupOrphanedContainerDirs(cntrs []containerd.Container, containersRoot string) error { | ||||
| 	// Cleanup orphaned container directories. | ||||
| 	dirs, err := ioutil.ReadDir(containersRoot) | ||||
| 	if err != nil && !os.IsNotExist(err) { | ||||
| 		return fmt.Errorf("failed to read containers directory %q: %v", containersRoot, err) | ||||
| 	} | ||||
| 	cntrsMap := make(map[string]containerd.Container) | ||||
| 	for _, cntr := range cntrs { | ||||
| 		cntrsMap[cntr.ID()] = cntr | ||||
| 	} | ||||
| 	for _, d := range dirs { | ||||
| 		if !d.IsDir() { | ||||
| 			logrus.Warnf("Invalid file %q found in containers directory", d.Name()) | ||||
| 			continue | ||||
| 		} | ||||
| 		if _, ok := cntrsMap[d.Name()]; ok { | ||||
| 			// Do not remove container directory if corresponding container is found. | ||||
| 			continue | ||||
| 		} | ||||
| 		containerDir := filepath.Join(containersRoot, d.Name()) | ||||
| 		if err := system.EnsureRemoveAll(containerDir); err != nil { | ||||
| 			logrus.WithError(err).Warnf("Failed to remove container directory %q", containerDir) | ||||
| 		} else { | ||||
| 			logrus.Debugf("Cleanup orphaned container directory %q", containerDir) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										100
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // ListPodSandbox returns a list of Sandbox. | ||||
| func (c *criContainerdService) ListPodSandbox(ctx context.Context, r *runtime.ListPodSandboxRequest) (*runtime.ListPodSandboxResponse, error) { | ||||
| 	// List all sandboxes from store. | ||||
| 	sandboxesInStore := c.sandboxStore.List() | ||||
| 	var sandboxes []*runtime.PodSandbox | ||||
| 	for _, sandboxInStore := range sandboxesInStore { | ||||
| 		sandboxes = append(sandboxes, toCRISandbox( | ||||
| 			sandboxInStore.Metadata, | ||||
| 			sandboxInStore.Status.Get(), | ||||
| 		)) | ||||
| 	} | ||||
|  | ||||
| 	sandboxes = c.filterCRISandboxes(sandboxes, r.GetFilter()) | ||||
| 	return &runtime.ListPodSandboxResponse{Items: sandboxes}, nil | ||||
| } | ||||
|  | ||||
| // toCRISandbox converts sandbox metadata into CRI pod sandbox. | ||||
| func toCRISandbox(meta sandboxstore.Metadata, status sandboxstore.Status) *runtime.PodSandbox { | ||||
| 	// Set sandbox state to NOTREADY by default. | ||||
| 	state := runtime.PodSandboxState_SANDBOX_NOTREADY | ||||
| 	if status.State == sandboxstore.StateReady { | ||||
| 		state = runtime.PodSandboxState_SANDBOX_READY | ||||
| 	} | ||||
| 	return &runtime.PodSandbox{ | ||||
| 		Id:          meta.ID, | ||||
| 		Metadata:    meta.Config.GetMetadata(), | ||||
| 		State:       state, | ||||
| 		CreatedAt:   status.CreatedAt.UnixNano(), | ||||
| 		Labels:      meta.Config.GetLabels(), | ||||
| 		Annotations: meta.Config.GetAnnotations(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) normalizePodSandboxFilter(filter *runtime.PodSandboxFilter) { | ||||
| 	if sb, err := c.sandboxStore.Get(filter.GetId()); err == nil { | ||||
| 		filter.Id = sb.ID | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // filterCRISandboxes filters CRISandboxes. | ||||
| func (c *criContainerdService) filterCRISandboxes(sandboxes []*runtime.PodSandbox, filter *runtime.PodSandboxFilter) []*runtime.PodSandbox { | ||||
| 	if filter == nil { | ||||
| 		return sandboxes | ||||
| 	} | ||||
|  | ||||
| 	c.normalizePodSandboxFilter(filter) | ||||
| 	filtered := []*runtime.PodSandbox{} | ||||
| 	for _, s := range sandboxes { | ||||
| 		// Filter by id | ||||
| 		if filter.GetId() != "" && filter.GetId() != s.Id { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Filter by state | ||||
| 		if filter.GetState() != nil && filter.GetState().GetState() != s.State { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Filter by label | ||||
| 		if filter.GetLabelSelector() != nil { | ||||
| 			match := true | ||||
| 			for k, v := range filter.GetLabelSelector() { | ||||
| 				got, ok := s.Labels[k] | ||||
| 				if !ok || got != v { | ||||
| 					match = false | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			if !match { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		filtered = append(filtered, s) | ||||
| 	} | ||||
|  | ||||
| 	return filtered | ||||
| } | ||||
							
								
								
									
										114
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_portforward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_portforward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os/exec" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address. | ||||
| func (c *criContainerdService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (retRes *runtime.PortForwardResponse, retErr error) { | ||||
| 	// TODO(random-liu): Run a socat container inside the sandbox to do portforward. | ||||
| 	sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to find sandbox %q: %v", r.GetPodSandboxId(), err) | ||||
| 	} | ||||
| 	if sandbox.Status.Get().State != sandboxstore.StateReady { | ||||
| 		return nil, errors.New("sandbox container is not running") | ||||
| 	} | ||||
| 	// TODO(random-liu): Verify that ports are exposed. | ||||
| 	return c.streamServer.GetPortForward(r) | ||||
| } | ||||
|  | ||||
| // portForward requires `nsenter` and `socat` on the node, it uses `nsenter` to enter the | ||||
| // sandbox namespace, and run `socat` inside the namespace to forward stream for a specific | ||||
| // port. The `socat` command keeps running until it exits or client disconnect. | ||||
| func (c *criContainerdService) portForward(id string, port int32, stream io.ReadWriteCloser) error { | ||||
| 	s, err := c.sandboxStore.Get(id) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to find sandbox %q in store: %v", id, err) | ||||
| 	} | ||||
| 	t, err := s.Container.Task(context.Background(), nil) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to get sandbox container task: %v", err) | ||||
| 	} | ||||
| 	pid := t.Pid() | ||||
|  | ||||
| 	socat, err := exec.LookPath("socat") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to find socat: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Check following links for meaning of the options: | ||||
| 	// * socat: https://linux.die.net/man/1/socat | ||||
| 	// * nsenter: http://man7.org/linux/man-pages/man1/nsenter.1.html | ||||
| 	args := []string{"-t", fmt.Sprintf("%d", pid), "-n", socat, | ||||
| 		"-", fmt.Sprintf("TCP4:localhost:%d", port)} | ||||
|  | ||||
| 	nsenter, err := exec.LookPath("nsenter") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to find nsenter: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	logrus.Infof("Executing port forwarding command: %s %s", nsenter, strings.Join(args, " ")) | ||||
|  | ||||
| 	cmd := exec.Command(nsenter, args...) | ||||
| 	cmd.Stdout = stream | ||||
|  | ||||
| 	stderr := new(bytes.Buffer) | ||||
| 	cmd.Stderr = stderr | ||||
|  | ||||
| 	// If we use Stdin, command.Run() won't return until the goroutine that's copying | ||||
| 	// from stream finishes. Unfortunately, if you have a client like telnet connected | ||||
| 	// via port forwarding, as long as the user's telnet client is connected to the user's | ||||
| 	// local listener that port forwarding sets up, the telnet session never exits. This | ||||
| 	// means that even if socat has finished running, command.Run() won't ever return | ||||
| 	// (because the client still has the connection and stream open). | ||||
| 	// | ||||
| 	// The work around is to use StdinPipe(), as Wait() (called by Run()) closes the pipe | ||||
| 	// when the command (socat) exits. | ||||
| 	in, err := cmd.StdinPipe() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to create stdin pipe: %v", err) | ||||
| 	} | ||||
| 	go func() { | ||||
| 		if _, err := io.Copy(in, stream); err != nil { | ||||
| 			logrus.WithError(err).Errorf("Failed to copy port forward input for %q port %d", id, port) | ||||
| 		} | ||||
| 		in.Close() | ||||
| 		logrus.Debugf("Finish copy port forward input for %q port %d: %v", id, port) | ||||
| 	}() | ||||
|  | ||||
| 	if err := cmd.Run(); err != nil { | ||||
| 		return fmt.Errorf("nsenter command returns error: %v, stderr: %q", err, stderr.String()) | ||||
| 	} | ||||
|  | ||||
| 	logrus.Infof("Finish port forwarding for %q port %d", id, port) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										102
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_remove.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_remove.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/docker/docker/pkg/system" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/log" | ||||
| 	"github.com/containerd/cri-containerd/pkg/store" | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // RemovePodSandbox removes the sandbox. If there are running containers in the | ||||
| // sandbox, they should be forcibly removed. | ||||
| func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodSandboxRequest) (*runtime.RemovePodSandboxResponse, error) { | ||||
| 	sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId()) | ||||
| 	if err != nil { | ||||
| 		if err != store.ErrNotExist { | ||||
| 			return nil, fmt.Errorf("an error occurred when try to find sandbox %q: %v", | ||||
| 				r.GetPodSandboxId(), err) | ||||
| 		} | ||||
| 		// Do not return error if the id doesn't exist. | ||||
| 		log.Tracef("RemovePodSandbox called for sandbox %q that does not exist", | ||||
| 			r.GetPodSandboxId()) | ||||
| 		return &runtime.RemovePodSandboxResponse{}, nil | ||||
| 	} | ||||
| 	// Use the full sandbox id. | ||||
| 	id := sandbox.ID | ||||
|  | ||||
| 	// Return error if sandbox container is still running. | ||||
| 	if sandbox.Status.Get().State == sandboxstore.StateReady { | ||||
| 		return nil, fmt.Errorf("sandbox container %q is not fully stopped", id) | ||||
| 	} | ||||
|  | ||||
| 	// Return error if sandbox network namespace is not closed yet. | ||||
| 	if sandbox.NetNS != nil && !sandbox.NetNS.Closed() { | ||||
| 		return nil, fmt.Errorf("sandbox network namespace %q is not fully closed", sandbox.NetNS.GetPath()) | ||||
| 	} | ||||
|  | ||||
| 	// Remove all containers inside the sandbox. | ||||
| 	// NOTE(random-liu): container could still be created after this point, Kubelet should | ||||
| 	// not rely on this behavior. | ||||
| 	// TODO(random-liu): Introduce an intermediate state to avoid container creation after | ||||
| 	// this point. | ||||
| 	cntrs := c.containerStore.List() | ||||
| 	for _, cntr := range cntrs { | ||||
| 		if cntr.SandboxID != id { | ||||
| 			continue | ||||
| 		} | ||||
| 		_, err = c.RemoveContainer(ctx, &runtime.RemoveContainerRequest{ContainerId: cntr.ID}) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to remove container %q: %v", cntr.ID, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Cleanup the sandbox root directory. | ||||
| 	sandboxRootDir := getSandboxRootDir(c.config.RootDir, id) | ||||
| 	if err := system.EnsureRemoveAll(sandboxRootDir); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to remove sandbox root directory %q: %v", | ||||
| 			sandboxRootDir, err) | ||||
| 	} | ||||
|  | ||||
| 	// Delete sandbox container. | ||||
| 	if err := sandbox.Container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil { | ||||
| 		if !errdefs.IsNotFound(err) { | ||||
| 			return nil, fmt.Errorf("failed to delete sandbox container %q: %v", id, err) | ||||
| 		} | ||||
| 		log.Tracef("Remove called for sandbox container %q that does not exist", id) | ||||
| 	} | ||||
|  | ||||
| 	// Remove sandbox from sandbox store. Note that once the sandbox is successfully | ||||
| 	// deleted: | ||||
| 	// 1) ListPodSandbox will not include this sandbox. | ||||
| 	// 2) PodSandboxStatus and StopPodSandbox will return error. | ||||
| 	// 3) On-going operations which have held the reference will not be affected. | ||||
| 	c.sandboxStore.Delete(id) | ||||
|  | ||||
| 	// Release the sandbox name reserved for the sandbox. | ||||
| 	c.sandboxNameIndex.ReleaseByKey(id) | ||||
|  | ||||
| 	return &runtime.RemovePodSandboxResponse{}, nil | ||||
| } | ||||
							
								
								
									
										511
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_run.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										511
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_run.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,511 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	containerdio "github.com/containerd/containerd/cio" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/linux/runctypes" | ||||
| 	"github.com/containerd/containerd/oci" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/cri-o/ocicni/pkg/ocicni" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	runtimespec "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"golang.org/x/sys/unix" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/annotations" | ||||
| 	customopts "github.com/containerd/cri-containerd/pkg/containerd/opts" | ||||
| 	"github.com/containerd/cri-containerd/pkg/log" | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| 	"github.com/containerd/cri-containerd/pkg/util" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	typeurl.Register(&sandboxstore.Metadata{}, | ||||
| 		"github.com/containerd/cri-containerd/pkg/store/sandbox", "Metadata") | ||||
| } | ||||
|  | ||||
| // RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure | ||||
| // the sandbox is in ready state. | ||||
| func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) { | ||||
| 	config := r.GetConfig() | ||||
|  | ||||
| 	// Generate unique id and name for the sandbox and reserve the name. | ||||
| 	id := util.GenerateID() | ||||
| 	name := makeSandboxName(config.GetMetadata()) | ||||
| 	logrus.Debugf("Generated id %q for sandbox %q", id, name) | ||||
| 	// Reserve the sandbox name to avoid concurrent `RunPodSandbox` request starting the | ||||
| 	// same sandbox. | ||||
| 	if err := c.sandboxNameIndex.Reserve(name, id); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to reserve sandbox name %q: %v", name, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		// Release the name if the function returns with an error. | ||||
| 		if retErr != nil { | ||||
| 			c.sandboxNameIndex.ReleaseByName(name) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Create initial internal sandbox object. | ||||
| 	sandbox := sandboxstore.NewSandbox( | ||||
| 		sandboxstore.Metadata{ | ||||
| 			ID:     id, | ||||
| 			Name:   name, | ||||
| 			Config: config, | ||||
| 		}, | ||||
| 		sandboxstore.Status{ | ||||
| 			State: sandboxstore.StateUnknown, | ||||
| 		}, | ||||
| 	) | ||||
|  | ||||
| 	// Ensure sandbox container image snapshot. | ||||
| 	image, err := c.ensureImageExists(ctx, c.config.SandboxImage) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get sandbox image %q: %v", c.config.SandboxImage, err) | ||||
| 	} | ||||
| 	securityContext := config.GetLinux().GetSecurityContext() | ||||
| 	//Create Network Namespace if it is not in host network | ||||
| 	hostNet := securityContext.GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE | ||||
| 	if !hostNet { | ||||
| 		// If it is not in host network namespace then create a namespace and set the sandbox | ||||
| 		// handle. NetNSPath in sandbox metadata and NetNS is non empty only for non host network | ||||
| 		// namespaces. If the pod is in host network namespace then both are empty and should not | ||||
| 		// be used. | ||||
| 		sandbox.NetNS, err = sandboxstore.NewNetNS() | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to create network namespace for sandbox %q: %v", id, err) | ||||
| 		} | ||||
| 		sandbox.NetNSPath = sandbox.NetNS.GetPath() | ||||
| 		defer func() { | ||||
| 			if retErr != nil { | ||||
| 				if err := sandbox.NetNS.Remove(); err != nil { | ||||
| 					logrus.WithError(err).Errorf("Failed to remove network namespace %s for sandbox %q", sandbox.NetNSPath, id) | ||||
| 				} | ||||
| 				sandbox.NetNSPath = "" | ||||
| 			} | ||||
| 		}() | ||||
| 		if !c.config.EnableIPv6DAD { | ||||
| 			// It's a known issue that IPv6 DAD increases sandbox start latency by several seconds. | ||||
| 			// Disable it when it's not enabled to avoid the latency. | ||||
| 			// See: | ||||
| 			// * https://github.com/kubernetes/kubernetes/issues/54651 | ||||
| 			// * https://www.agwa.name/blog/post/beware_the_ipv6_dad_race_condition | ||||
| 			if err := disableNetNSDAD(sandbox.NetNSPath); err != nil { | ||||
| 				return nil, fmt.Errorf("failed to disable DAD for sandbox %q: %v", id, err) | ||||
| 			} | ||||
| 		} | ||||
| 		// Setup network for sandbox. | ||||
| 		podNetwork := ocicni.PodNetwork{ | ||||
| 			Name:         config.GetMetadata().GetName(), | ||||
| 			Namespace:    config.GetMetadata().GetNamespace(), | ||||
| 			ID:           id, | ||||
| 			NetNS:        sandbox.NetNSPath, | ||||
| 			PortMappings: toCNIPortMappings(config.GetPortMappings()), | ||||
| 		} | ||||
| 		if _, err = c.netPlugin.SetUpPod(podNetwork); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to setup network for sandbox %q: %v", id, err) | ||||
| 		} | ||||
| 		defer func() { | ||||
| 			if retErr != nil { | ||||
| 				// Teardown network if an error is returned. | ||||
| 				if err := c.netPlugin.TearDownPod(podNetwork); err != nil { | ||||
| 					logrus.WithError(err).Errorf("Failed to destroy network for sandbox %q", id) | ||||
| 				} | ||||
| 			} | ||||
| 		}() | ||||
| 		ip, err := c.netPlugin.GetPodNetworkStatus(podNetwork) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to get network status for sandbox %q: %v", id, err) | ||||
| 		} | ||||
| 		// Certain VM based solutions like clear containers (Issue containerd/cri-containerd#524) | ||||
| 		//  rely on the assumption that CRI shim will not be querying the network namespace to check the | ||||
| 		// network states such as IP. | ||||
| 		// In furture runtime implementation should avoid relying on CRI shim implementation details. | ||||
| 		// In this case however caching the IP will add a subtle performance enhancement by avoiding | ||||
| 		// calls to network namespace of the pod to query the IP of the veth interface on every | ||||
| 		// SandboxStatus request. | ||||
| 		sandbox.IP = ip | ||||
| 	} | ||||
|  | ||||
| 	// Create sandbox container. | ||||
| 	spec, err := c.generateSandboxContainerSpec(id, config, &image.ImageSpec.Config, sandbox.NetNSPath) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to generate sandbox container spec: %v", err) | ||||
| 	} | ||||
| 	logrus.Debugf("Sandbox container spec: %+v", spec) | ||||
|  | ||||
| 	var specOpts []oci.SpecOpts | ||||
| 	if uid := securityContext.GetRunAsUser(); uid != nil { | ||||
| 		specOpts = append(specOpts, oci.WithUserID(uint32(uid.GetValue()))) | ||||
| 	} | ||||
|  | ||||
| 	seccompSpecOpts, err := generateSeccompSpecOpts( | ||||
| 		securityContext.GetSeccompProfilePath(), | ||||
| 		securityContext.GetPrivileged(), | ||||
| 		c.seccompEnabled) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to generate seccomp spec opts: %v", err) | ||||
| 	} | ||||
| 	if seccompSpecOpts != nil { | ||||
| 		specOpts = append(specOpts, seccompSpecOpts) | ||||
| 	} | ||||
|  | ||||
| 	sandboxLabels := buildLabels(config.Labels, containerKindSandbox) | ||||
|  | ||||
| 	opts := []containerd.NewContainerOpts{ | ||||
| 		containerd.WithSnapshotter(c.config.ContainerdConfig.Snapshotter), | ||||
| 		customopts.WithNewSnapshot(id, image.Image), | ||||
| 		containerd.WithSpec(spec, specOpts...), | ||||
| 		containerd.WithContainerLabels(sandboxLabels), | ||||
| 		containerd.WithContainerExtension(sandboxMetadataExtension, &sandbox.Metadata), | ||||
| 		containerd.WithRuntime( | ||||
| 			c.config.ContainerdConfig.Runtime, | ||||
| 			&runctypes.RuncOptions{ | ||||
| 				Runtime:       c.config.ContainerdConfig.RuntimeEngine, | ||||
| 				RuntimeRoot:   c.config.ContainerdConfig.RuntimeRoot, | ||||
| 				SystemdCgroup: c.config.SystemdCgroup})} // TODO (mikebrow): add CriuPath when we add support for pause | ||||
|  | ||||
| 	container, err := c.client.NewContainer(ctx, id, opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create containerd container: %v", err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			if err := container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to delete containerd container %q", id) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Create sandbox container root directory. | ||||
| 	sandboxRootDir := getSandboxRootDir(c.config.RootDir, id) | ||||
| 	if err := c.os.MkdirAll(sandboxRootDir, 0755); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create sandbox root directory %q: %v", | ||||
| 			sandboxRootDir, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			// Cleanup the sandbox root directory. | ||||
| 			if err := c.os.RemoveAll(sandboxRootDir); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to remove sandbox root directory %q", | ||||
| 					sandboxRootDir) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Setup sandbox /dev/shm, /etc/hosts and /etc/resolv.conf. | ||||
| 	if err = c.setupSandboxFiles(sandboxRootDir, config); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to setup sandbox files: %v", err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if retErr != nil { | ||||
| 			if err = c.unmountSandboxFiles(sandboxRootDir, config); err != nil { | ||||
| 				logrus.WithError(err).Errorf("Failed to unmount sandbox files in %q", | ||||
| 					sandboxRootDir) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// Update sandbox created timestamp. | ||||
| 	info, err := container.Info(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get sandbox container info: %v", err) | ||||
| 	} | ||||
| 	if err := sandbox.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) { | ||||
| 		status.CreatedAt = info.CreatedAt | ||||
| 		return status, nil | ||||
| 	}); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to update sandbox created timestamp: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Add sandbox into sandbox store in UNKNOWN state. | ||||
| 	sandbox.Container = container | ||||
| 	if err := c.sandboxStore.Add(sandbox); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to add sandbox %+v into store: %v", sandbox, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		// Delete sandbox from sandbox store if there is an error. | ||||
| 		if retErr != nil { | ||||
| 			c.sandboxStore.Delete(id) | ||||
| 		} | ||||
| 	}() | ||||
| 	// NOTE(random-liu): Sandbox state only stay in UNKNOWN state after this point | ||||
| 	// and before the end of this function. | ||||
| 	// * If `Update` succeeds, sandbox state will become READY in one transaction. | ||||
| 	// * If `Update` fails, sandbox will be removed from the store in the defer above. | ||||
| 	// * If cri-containerd stops at any point before `Update` finishes, because sandbox | ||||
| 	// state is not checkpointed, it will be recovered from corresponding containerd task | ||||
| 	// status during restart: | ||||
| 	//   * If the task is running, sandbox state will be READY, | ||||
| 	//   * Or else, sandbox state will be NOTREADY. | ||||
| 	// | ||||
| 	// In any case, sandbox will leave UNKNOWN state, so it's safe to ignore sandbox | ||||
| 	// in UNKNOWN state in other functions. | ||||
|  | ||||
| 	// Start sandbox container in one transaction to avoid race condition with | ||||
| 	// event monitor. | ||||
| 	if err := sandbox.Status.Update(func(status sandboxstore.Status) (_ sandboxstore.Status, retErr error) { | ||||
| 		// NOTE(random-liu): We should not change the sandbox state to NOTREADY | ||||
| 		// if `Update` fails. | ||||
| 		// | ||||
| 		// If `Update` fails, the sandbox will be cleaned up by all the defers | ||||
| 		// above. We should not let user see this sandbox, or else they will | ||||
| 		// see the sandbox disappear after the defer clean up, which may confuse | ||||
| 		// them. | ||||
| 		// | ||||
| 		// Given so, we should keep the sandbox in UNKNOWN state if `Update` fails, | ||||
| 		// and ignore sandbox in UNKNOWN state in all the inspection functions. | ||||
|  | ||||
| 		// Create sandbox task in containerd. | ||||
| 		log.Tracef("Create sandbox container (id=%q, name=%q).", | ||||
| 			id, name) | ||||
| 		// We don't need stdio for sandbox container. | ||||
| 		task, err := container.NewTask(ctx, containerdio.NullIO) | ||||
| 		if err != nil { | ||||
| 			return status, fmt.Errorf("failed to create containerd task: %v", err) | ||||
| 		} | ||||
| 		defer func() { | ||||
| 			if retErr != nil { | ||||
| 				// Cleanup the sandbox container if an error is returned. | ||||
| 				// It's possible that task is deleted by event monitor. | ||||
| 				if _, err := task.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) { | ||||
| 					logrus.WithError(err).Errorf("Failed to delete sandbox container %q", id) | ||||
| 				} | ||||
| 			} | ||||
| 		}() | ||||
|  | ||||
| 		if err := task.Start(ctx); err != nil { | ||||
| 			return status, fmt.Errorf("failed to start sandbox container task %q: %v", | ||||
| 				id, err) | ||||
| 		} | ||||
|  | ||||
| 		// Set the pod sandbox as ready after successfully start sandbox container. | ||||
| 		status.Pid = task.Pid() | ||||
| 		status.State = sandboxstore.StateReady | ||||
| 		return status, nil | ||||
| 	}); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to start sandbox container: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.RunPodSandboxResponse{PodSandboxId: id}, nil | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) generateSandboxContainerSpec(id string, config *runtime.PodSandboxConfig, | ||||
| 	imageConfig *imagespec.ImageConfig, nsPath string) (*runtimespec.Spec, error) { | ||||
| 	// Creates a spec Generator with the default spec. | ||||
| 	// TODO(random-liu): [P1] Compare the default settings with docker and containerd default. | ||||
| 	spec, err := defaultRuntimeSpec(id) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	g := newSpecGenerator(spec) | ||||
|  | ||||
| 	// Apply default config from image config. | ||||
| 	if err := addImageEnvs(&g, imageConfig.Env); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if imageConfig.WorkingDir != "" { | ||||
| 		g.SetProcessCwd(imageConfig.WorkingDir) | ||||
| 	} | ||||
|  | ||||
| 	if len(imageConfig.Entrypoint) == 0 { | ||||
| 		// Pause image must have entrypoint. | ||||
| 		return nil, fmt.Errorf("invalid empty entrypoint in image config %+v", imageConfig) | ||||
| 	} | ||||
| 	// Set process commands. | ||||
| 	g.SetProcessArgs(append(imageConfig.Entrypoint, imageConfig.Cmd...)) | ||||
|  | ||||
| 	// Set relative root path. | ||||
| 	g.SetRootPath(relativeRootfsPath) | ||||
|  | ||||
| 	// Make root of sandbox container read-only. | ||||
| 	g.SetRootReadonly(true) | ||||
|  | ||||
| 	// Set hostname. | ||||
| 	g.SetHostname(config.GetHostname()) | ||||
|  | ||||
| 	// TODO(random-liu): [P2] Consider whether to add labels and annotations to the container. | ||||
|  | ||||
| 	// Set cgroups parent. | ||||
| 	if config.GetLinux().GetCgroupParent() != "" { | ||||
| 		cgroupsPath := getCgroupsPath(config.GetLinux().GetCgroupParent(), id, | ||||
| 			c.config.SystemdCgroup) | ||||
| 		g.SetLinuxCgroupsPath(cgroupsPath) | ||||
| 	} | ||||
| 	// When cgroup parent is not set, containerd-shim will create container in a child cgroup | ||||
| 	// of the cgroup itself is in. | ||||
| 	// TODO(random-liu): [P2] Set default cgroup path if cgroup parent is not specified. | ||||
|  | ||||
| 	// Set namespace options. | ||||
| 	securityContext := config.GetLinux().GetSecurityContext() | ||||
| 	nsOptions := securityContext.GetNamespaceOptions() | ||||
| 	if nsOptions.GetNetwork() == runtime.NamespaceMode_NODE { | ||||
| 		g.RemoveLinuxNamespace(string(runtimespec.NetworkNamespace)) // nolint: errcheck | ||||
| 	} else { | ||||
| 		//TODO(Abhi): May be move this to containerd spec opts (WithLinuxSpaceOption) | ||||
| 		g.AddOrReplaceLinuxNamespace(string(runtimespec.NetworkNamespace), nsPath) // nolint: errcheck | ||||
| 	} | ||||
| 	if nsOptions.GetPid() == runtime.NamespaceMode_NODE { | ||||
| 		g.RemoveLinuxNamespace(string(runtimespec.PIDNamespace)) // nolint: errcheck | ||||
| 	} | ||||
| 	if nsOptions.GetIpc() == runtime.NamespaceMode_NODE { | ||||
| 		g.RemoveLinuxNamespace(string(runtimespec.IPCNamespace)) // nolint: errcheck | ||||
| 	} | ||||
|  | ||||
| 	selinuxOpt := securityContext.GetSelinuxOptions() | ||||
| 	processLabel, mountLabel, err := initSelinuxOpts(selinuxOpt) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to init selinux options %+v: %v", securityContext.GetSelinuxOptions(), err) | ||||
| 	} | ||||
| 	g.SetProcessSelinuxLabel(processLabel) | ||||
| 	g.SetLinuxMountLabel(mountLabel) | ||||
|  | ||||
| 	supplementalGroups := securityContext.GetSupplementalGroups() | ||||
| 	for _, group := range supplementalGroups { | ||||
| 		g.AddProcessAdditionalGid(uint32(group)) | ||||
| 	} | ||||
|  | ||||
| 	// Add sysctls | ||||
| 	sysctls := config.GetLinux().GetSysctls() | ||||
| 	for key, value := range sysctls { | ||||
| 		g.AddLinuxSysctl(key, value) | ||||
| 	} | ||||
|  | ||||
| 	// Note: LinuxSandboxSecurityContext does not currently provide an apparmor profile | ||||
|  | ||||
| 	g.SetLinuxResourcesCPUShares(uint64(defaultSandboxCPUshares)) | ||||
| 	g.SetProcessOOMScoreAdj(int(defaultSandboxOOMAdj)) | ||||
|  | ||||
| 	g.AddAnnotation(annotations.ContainerType, annotations.ContainerTypeSandbox) | ||||
| 	g.AddAnnotation(annotations.SandboxID, id) | ||||
|  | ||||
| 	return g.Spec(), nil | ||||
| } | ||||
|  | ||||
| // setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts | ||||
| // and /etc/resolv.conf. | ||||
| func (c *criContainerdService) setupSandboxFiles(rootDir string, config *runtime.PodSandboxConfig) error { | ||||
| 	// TODO(random-liu): Consider whether we should maintain /etc/hosts and /etc/resolv.conf in kubelet. | ||||
| 	sandboxEtcHosts := getSandboxHosts(rootDir) | ||||
| 	if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0644); err != nil { | ||||
| 		return fmt.Errorf("failed to generate sandbox hosts file %q: %v", sandboxEtcHosts, err) | ||||
| 	} | ||||
|  | ||||
| 	// Set DNS options. Maintain a resolv.conf for the sandbox. | ||||
| 	var err error | ||||
| 	resolvContent := "" | ||||
| 	if dnsConfig := config.GetDnsConfig(); dnsConfig != nil { | ||||
| 		resolvContent, err = parseDNSOptions(dnsConfig.Servers, dnsConfig.Searches, dnsConfig.Options) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to parse sandbox DNSConfig %+v: %v", dnsConfig, err) | ||||
| 		} | ||||
| 	} | ||||
| 	resolvPath := getResolvPath(rootDir) | ||||
| 	if resolvContent == "" { | ||||
| 		// copy host's resolv.conf to resolvPath | ||||
| 		err = c.os.CopyFile(resolvConfPath, resolvPath, 0644) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to copy host's resolv.conf to %q: %v", resolvPath, err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = c.os.WriteFile(resolvPath, []byte(resolvContent), 0644) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to write resolv content to %q: %v", resolvPath, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Setup sandbox /dev/shm. | ||||
| 	if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetIpc() == runtime.NamespaceMode_NODE { | ||||
| 		if _, err := c.os.Stat(devShm); err != nil { | ||||
| 			return fmt.Errorf("host %q is not available for host ipc: %v", devShm, err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		sandboxDevShm := getSandboxDevShm(rootDir) | ||||
| 		if err := c.os.MkdirAll(sandboxDevShm, 0700); err != nil { | ||||
| 			return fmt.Errorf("failed to create sandbox shm: %v", err) | ||||
| 		} | ||||
| 		shmproperty := fmt.Sprintf("mode=1777,size=%d", defaultShmSize) | ||||
| 		if err := c.os.Mount("shm", sandboxDevShm, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), shmproperty); err != nil { | ||||
| 			return fmt.Errorf("failed to mount sandbox shm: %v", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // parseDNSOptions parse DNS options into resolv.conf format content, | ||||
| // if none option is specified, will return empty with no error. | ||||
| func parseDNSOptions(servers, searches, options []string) (string, error) { | ||||
| 	resolvContent := "" | ||||
|  | ||||
| 	if len(searches) > maxDNSSearches { | ||||
| 		return "", fmt.Errorf("DNSOption.Searches has more than 6 domains") | ||||
| 	} | ||||
|  | ||||
| 	if len(searches) > 0 { | ||||
| 		resolvContent += fmt.Sprintf("search %s\n", strings.Join(searches, " ")) | ||||
| 	} | ||||
|  | ||||
| 	if len(servers) > 0 { | ||||
| 		resolvContent += fmt.Sprintf("nameserver %s\n", strings.Join(servers, "\nnameserver ")) | ||||
| 	} | ||||
|  | ||||
| 	if len(options) > 0 { | ||||
| 		resolvContent += fmt.Sprintf("options %s\n", strings.Join(options, " ")) | ||||
| 	} | ||||
|  | ||||
| 	return resolvContent, nil | ||||
| } | ||||
|  | ||||
| // unmountSandboxFiles unmount some sandbox files, we rely on the removal of sandbox root directory to | ||||
| // remove these files. Unmount should *NOT* return error when: | ||||
| //  1) The mount point is already unmounted. | ||||
| //  2) The mount point doesn't exist. | ||||
| func (c *criContainerdService) unmountSandboxFiles(rootDir string, config *runtime.PodSandboxConfig) error { | ||||
| 	if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetIpc() != runtime.NamespaceMode_NODE { | ||||
| 		if err := c.os.Unmount(getSandboxDevShm(rootDir), unix.MNT_DETACH); err != nil && !os.IsNotExist(err) { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // toCNIPortMappings converts CRI port mappings to CNI. | ||||
| func toCNIPortMappings(criPortMappings []*runtime.PortMapping) []ocicni.PortMapping { | ||||
| 	var portMappings []ocicni.PortMapping | ||||
| 	for _, mapping := range criPortMappings { | ||||
| 		if mapping.HostPort <= 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		portMappings = append(portMappings, ocicni.PortMapping{ | ||||
| 			HostPort:      mapping.HostPort, | ||||
| 			ContainerPort: mapping.ContainerPort, | ||||
| 			Protocol:      strings.ToLower(mapping.Protocol.String()), | ||||
| 			HostIP:        mapping.HostIp, | ||||
| 		}) | ||||
| 	} | ||||
| 	return portMappings | ||||
| } | ||||
							
								
								
									
										173
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	runtimespec "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // PodSandboxStatus returns the status of the PodSandbox. | ||||
| func (c *criContainerdService) PodSandboxStatus(ctx context.Context, r *runtime.PodSandboxStatusRequest) (*runtime.PodSandboxStatusResponse, error) { | ||||
| 	sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("an error occurred when try to find sandbox: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	ip := c.getIP(sandbox) | ||||
| 	status := toCRISandboxStatus(sandbox.Metadata, sandbox.Status.Get(), ip) | ||||
| 	if !r.GetVerbose() { | ||||
| 		return &runtime.PodSandboxStatusResponse{Status: status}, nil | ||||
| 	} | ||||
|  | ||||
| 	// Generate verbose information. | ||||
| 	info, err := toCRISandboxInfo(ctx, sandbox) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get verbose sandbox container info: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return &runtime.PodSandboxStatusResponse{ | ||||
| 		Status: status, | ||||
| 		Info:   info, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (c *criContainerdService) getIP(sandbox sandboxstore.Sandbox) string { | ||||
| 	config := sandbox.Config | ||||
|  | ||||
| 	if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE { | ||||
| 		// For sandboxes using the node network we are not | ||||
| 		// responsible for reporting the IP. | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	// The network namespace has been closed. | ||||
| 	if sandbox.NetNS == nil || sandbox.NetNS.Closed() { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return sandbox.IP | ||||
| } | ||||
|  | ||||
| // toCRISandboxStatus converts sandbox metadata into CRI pod sandbox status. | ||||
| func toCRISandboxStatus(meta sandboxstore.Metadata, status sandboxstore.Status, ip string) *runtime.PodSandboxStatus { | ||||
| 	// Set sandbox state to NOTREADY by default. | ||||
| 	state := runtime.PodSandboxState_SANDBOX_NOTREADY | ||||
| 	if status.State == sandboxstore.StateReady { | ||||
| 		state = runtime.PodSandboxState_SANDBOX_READY | ||||
| 	} | ||||
| 	nsOpts := meta.Config.GetLinux().GetSecurityContext().GetNamespaceOptions() | ||||
| 	return &runtime.PodSandboxStatus{ | ||||
| 		Id:        meta.ID, | ||||
| 		Metadata:  meta.Config.GetMetadata(), | ||||
| 		State:     state, | ||||
| 		CreatedAt: status.CreatedAt.UnixNano(), | ||||
| 		Network:   &runtime.PodSandboxNetworkStatus{Ip: ip}, | ||||
| 		Linux: &runtime.LinuxPodSandboxStatus{ | ||||
| 			Namespaces: &runtime.Namespace{ | ||||
| 				Options: &runtime.NamespaceOption{ | ||||
| 					Network: nsOpts.GetNetwork(), | ||||
| 					Pid:     nsOpts.GetPid(), | ||||
| 					Ipc:     nsOpts.GetIpc(), | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Labels:      meta.Config.GetLabels(), | ||||
| 		Annotations: meta.Config.GetAnnotations(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TODO (mikebrow): discuss predefining constants structures for some or all of these field names in CRI | ||||
| type sandboxInfo struct { | ||||
| 	Pid         uint32                    `json:"pid"` | ||||
| 	Status      string                    `json:"processStatus"` | ||||
| 	NetNSClosed bool                      `json:"netNamespaceClosed"` | ||||
| 	Image       string                    `json:"image"` | ||||
| 	SnapshotKey string                    `json:"snapshotKey"` | ||||
| 	Snapshotter string                    `json:"snapshotter"` | ||||
| 	Config      *runtime.PodSandboxConfig `json:"config"` | ||||
| 	RuntimeSpec *runtimespec.Spec         `json:"runtimeSpec"` | ||||
| } | ||||
|  | ||||
| // toCRISandboxInfo converts internal container object information to CRI sandbox status response info map. | ||||
| func toCRISandboxInfo(ctx context.Context, sandbox sandboxstore.Sandbox) (map[string]string, error) { | ||||
| 	container := sandbox.Container | ||||
| 	task, err := container.Task(ctx, nil) | ||||
| 	if err != nil && !errdefs.IsNotFound(err) { | ||||
| 		return nil, fmt.Errorf("failed to get sandbox container task: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	var processStatus containerd.ProcessStatus | ||||
| 	if task != nil { | ||||
| 		taskStatus, err := task.Status(ctx) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to get task status: %v", err) | ||||
| 		} | ||||
|  | ||||
| 		processStatus = taskStatus.Status | ||||
| 	} | ||||
|  | ||||
| 	si := &sandboxInfo{ | ||||
| 		Pid:    sandbox.Status.Get().Pid, | ||||
| 		Status: string(processStatus), | ||||
| 		Config: sandbox.Config, | ||||
| 	} | ||||
|  | ||||
| 	if si.Status == "" { | ||||
| 		// If processStatus is empty, it means that the task is deleted. Apply "deleted" | ||||
| 		// status which does not exist in containerd. | ||||
| 		si.Status = "deleted" | ||||
| 	} | ||||
|  | ||||
| 	if sandbox.NetNSPath != "" { | ||||
| 		// Add network closed information if sandbox is not using host network. | ||||
| 		si.NetNSClosed = (sandbox.NetNS == nil || sandbox.NetNS.Closed()) | ||||
| 	} | ||||
|  | ||||
| 	spec, err := container.Spec(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get sandbox container runtime spec: %v", err) | ||||
| 	} | ||||
| 	si.RuntimeSpec = spec | ||||
|  | ||||
| 	ctrInfo, err := container.Info(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get sandbox container info: %v", err) | ||||
| 	} | ||||
| 	// Do not use config.SandboxImage because the configuration might | ||||
| 	// be changed during restart. It may not reflect the actual image | ||||
| 	// used by the sandbox container. | ||||
| 	si.Image = ctrInfo.Image | ||||
| 	si.SnapshotKey = ctrInfo.SnapshotKey | ||||
| 	si.Snapshotter = ctrInfo.Snapshotter | ||||
|  | ||||
| 	infoBytes, err := json.Marshal(si) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to marshal info %v: %v", si, err) | ||||
| 	} | ||||
| 	return map[string]string{ | ||||
| 		"info": string(infoBytes), | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										136
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_stop.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_stop.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/cri-o/ocicni/pkg/ocicni" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // StopPodSandbox stops the sandbox. If there are any running containers in the | ||||
| // sandbox, they should be forcibly terminated. | ||||
| func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (*runtime.StopPodSandboxResponse, error) { | ||||
| 	sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("an error occurred when try to find sandbox %q: %v", | ||||
| 			r.GetPodSandboxId(), err) | ||||
| 	} | ||||
| 	// Use the full sandbox id. | ||||
| 	id := sandbox.ID | ||||
|  | ||||
| 	// Stop all containers inside the sandbox. This terminates the container forcibly, | ||||
| 	// and container may still be so production should not rely on this behavior. | ||||
| 	// TODO(random-liu): Delete the sandbox container before this after permanent network namespace | ||||
| 	// is introduced, so that no container will be started after that. | ||||
| 	containers := c.containerStore.List() | ||||
| 	for _, container := range containers { | ||||
| 		if container.SandboxID != id { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Forcibly stop the container. Do not use `StopContainer`, because it introduces a race | ||||
| 		// if a container is removed after list. | ||||
| 		if err = c.stopContainer(ctx, container, 0); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to stop container %q: %v", container.ID, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Teardown network for sandbox. | ||||
| 	if sandbox.NetNSPath != "" && sandbox.NetNS != nil { | ||||
| 		if _, err := os.Stat(sandbox.NetNSPath); err != nil { | ||||
| 			if !os.IsNotExist(err) { | ||||
| 				return nil, fmt.Errorf("failed to stat network namespace path %s :%v", sandbox.NetNSPath, err) | ||||
| 			} | ||||
| 		} else { | ||||
| 			if teardownErr := c.netPlugin.TearDownPod(ocicni.PodNetwork{ | ||||
| 				Name:         sandbox.Config.GetMetadata().GetName(), | ||||
| 				Namespace:    sandbox.Config.GetMetadata().GetNamespace(), | ||||
| 				ID:           id, | ||||
| 				NetNS:        sandbox.NetNSPath, | ||||
| 				PortMappings: toCNIPortMappings(sandbox.Config.GetPortMappings()), | ||||
| 			}); teardownErr != nil { | ||||
| 				return nil, fmt.Errorf("failed to destroy network for sandbox %q: %v", id, teardownErr) | ||||
| 			} | ||||
| 		} | ||||
| 		/*TODO:It is still possible that cri-containerd crashes after we teardown the network, but before we remove the network namespace. | ||||
| 		In that case, we'll not be able to remove the sandbox anymore. The chance is slim, but we should be aware of that. | ||||
| 		In the future, once TearDownPod is idempotent, this will be fixed.*/ | ||||
|  | ||||
| 		//Close the sandbox network namespace if it was created | ||||
| 		if err = sandbox.NetNS.Remove(); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to remove network namespace for sandbox %q:  %v", id, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	logrus.Infof("TearDown network for sandbox %q successfully", id) | ||||
|  | ||||
| 	sandboxRoot := getSandboxRootDir(c.config.RootDir, id) | ||||
| 	if err := c.unmountSandboxFiles(sandboxRoot, sandbox.Config); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to unmount sandbox files in %q: %v", sandboxRoot, err) | ||||
| 	} | ||||
|  | ||||
| 	// Only stop sandbox container when it's running. | ||||
| 	if sandbox.Status.Get().State == sandboxstore.StateReady { | ||||
| 		if err := c.stopSandboxContainer(ctx, sandbox); err != nil { | ||||
| 			return nil, fmt.Errorf("failed to stop sandbox container %q: %v", id, err) | ||||
| 		} | ||||
| 	} | ||||
| 	return &runtime.StopPodSandboxResponse{}, nil | ||||
| } | ||||
|  | ||||
| // stopSandboxContainer kills and deletes sandbox container. | ||||
| func (c *criContainerdService) stopSandboxContainer(ctx context.Context, sandbox sandboxstore.Sandbox) error { | ||||
| 	container := sandbox.Container | ||||
| 	task, err := container.Task(ctx, nil) | ||||
| 	if err != nil { | ||||
| 		if errdefs.IsNotFound(err) { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return fmt.Errorf("failed to get sandbox container: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Delete the sandbox container from containerd. | ||||
| 	_, err = task.Delete(ctx, containerd.WithProcessKill) | ||||
| 	if err != nil && !errdefs.IsNotFound(err) { | ||||
| 		return fmt.Errorf("failed to delete sandbox container: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return c.waitSandboxStop(ctx, sandbox, killContainerTimeout) | ||||
| } | ||||
|  | ||||
| // waitSandboxStop waits for sandbox to be stopped until timeout exceeds or context is cancelled. | ||||
| func (c *criContainerdService) waitSandboxStop(ctx context.Context, sandbox sandboxstore.Sandbox, timeout time.Duration) error { | ||||
| 	timeoutTimer := time.NewTimer(timeout) | ||||
| 	defer timeoutTimer.Stop() | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return fmt.Errorf("wait sandbox container %q is cancelled", sandbox.ID) | ||||
| 	case <-timeoutTimer.C: | ||||
| 		return fmt.Errorf("wait sandbox container %q stop timeout", sandbox.ID) | ||||
| 	case <-sandbox.Stopped(): | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										291
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/service.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/service.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,291 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/plugin" | ||||
| 	"github.com/cri-o/ocicni/pkg/ocicni" | ||||
| 	runcapparmor "github.com/opencontainers/runc/libcontainer/apparmor" | ||||
| 	runcseccomp "github.com/opencontainers/runc/libcontainer/seccomp" | ||||
| 	"github.com/opencontainers/selinux/go-selinux" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"google.golang.org/grpc" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/server/streaming" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/cmd/cri-containerd/options" | ||||
| 	api "github.com/containerd/cri-containerd/pkg/api/v1" | ||||
| 	"github.com/containerd/cri-containerd/pkg/atomic" | ||||
| 	osinterface "github.com/containerd/cri-containerd/pkg/os" | ||||
| 	"github.com/containerd/cri-containerd/pkg/registrar" | ||||
| 	containerstore "github.com/containerd/cri-containerd/pkg/store/container" | ||||
| 	imagestore "github.com/containerd/cri-containerd/pkg/store/image" | ||||
| 	sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" | ||||
| 	snapshotstore "github.com/containerd/cri-containerd/pkg/store/snapshot" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// k8sContainerdNamespace is the namespace we use to connect containerd. | ||||
| 	k8sContainerdNamespace = "k8s.io" | ||||
| 	// unixProtocol is the network protocol of unix socket. | ||||
| 	unixProtocol = "unix" | ||||
| ) | ||||
|  | ||||
| // grpcServices are all the grpc services provided by cri containerd. | ||||
| type grpcServices interface { | ||||
| 	runtime.RuntimeServiceServer | ||||
| 	runtime.ImageServiceServer | ||||
| 	api.CRIContainerdServiceServer | ||||
| } | ||||
|  | ||||
| // CRIContainerdService is the interface implement CRI remote service server. | ||||
| type CRIContainerdService interface { | ||||
| 	Run(bool) error | ||||
| 	// io.Closer is used by containerd to gracefully stop cri service. | ||||
| 	io.Closer | ||||
| 	plugin.Service | ||||
| 	grpcServices | ||||
| } | ||||
|  | ||||
| // criContainerdService implements CRIContainerdService. | ||||
| type criContainerdService struct { | ||||
| 	// config contains all configurations. | ||||
| 	config options.Config | ||||
| 	// imageFSPath is the path to image filesystem. | ||||
| 	imageFSPath string | ||||
| 	// apparmorEnabled indicates whether apparmor is enabled. | ||||
| 	apparmorEnabled bool | ||||
| 	// seccompEnabled indicates whether seccomp is enabled. | ||||
| 	seccompEnabled bool | ||||
| 	// server is the grpc server. | ||||
| 	server *grpc.Server | ||||
| 	// os is an interface for all required os operations. | ||||
| 	os osinterface.OS | ||||
| 	// sandboxStore stores all resources associated with sandboxes. | ||||
| 	sandboxStore *sandboxstore.Store | ||||
| 	// sandboxNameIndex stores all sandbox names and make sure each name | ||||
| 	// is unique. | ||||
| 	sandboxNameIndex *registrar.Registrar | ||||
| 	// containerStore stores all resources associated with containers. | ||||
| 	containerStore *containerstore.Store | ||||
| 	// containerNameIndex stores all container names and make sure each | ||||
| 	// name is unique. | ||||
| 	containerNameIndex *registrar.Registrar | ||||
| 	// imageStore stores all resources associated with images. | ||||
| 	imageStore *imagestore.Store | ||||
| 	// snapshotStore stores information of all snapshots. | ||||
| 	snapshotStore *snapshotstore.Store | ||||
| 	// netPlugin is used to setup and teardown network when run/stop pod sandbox. | ||||
| 	netPlugin ocicni.CNIPlugin | ||||
| 	// client is an instance of the containerd client | ||||
| 	client *containerd.Client | ||||
| 	// streamServer is the streaming server serves container streaming request. | ||||
| 	streamServer streaming.Server | ||||
| 	// eventMonitor is the monitor monitors containerd events. | ||||
| 	eventMonitor *eventMonitor | ||||
| 	// initialized indicates whether the server is initialized. All GRPC services | ||||
| 	// should return error before the server is initialized. | ||||
| 	initialized atomic.Bool | ||||
| } | ||||
|  | ||||
| // NewCRIContainerdService returns a new instance of CRIContainerdService | ||||
| func NewCRIContainerdService(config options.Config) (CRIContainerdService, error) { | ||||
| 	var err error | ||||
| 	c := &criContainerdService{ | ||||
| 		config:             config, | ||||
| 		apparmorEnabled:    runcapparmor.IsEnabled(), | ||||
| 		seccompEnabled:     runcseccomp.IsEnabled(), | ||||
| 		os:                 osinterface.RealOS{}, | ||||
| 		sandboxStore:       sandboxstore.NewStore(), | ||||
| 		containerStore:     containerstore.NewStore(), | ||||
| 		imageStore:         imagestore.NewStore(), | ||||
| 		snapshotStore:      snapshotstore.NewStore(), | ||||
| 		sandboxNameIndex:   registrar.NewRegistrar(), | ||||
| 		containerNameIndex: registrar.NewRegistrar(), | ||||
| 		initialized:        atomic.NewBool(false), | ||||
| 	} | ||||
|  | ||||
| 	if c.config.EnableSelinux { | ||||
| 		if !selinux.GetEnabled() { | ||||
| 			logrus.Warn("Selinux is not supported") | ||||
| 		} | ||||
| 	} else { | ||||
| 		selinux.SetDisabled() | ||||
| 	} | ||||
|  | ||||
| 	c.imageFSPath = imageFSPath(config.ContainerdRootDir, config.ContainerdConfig.Snapshotter) | ||||
| 	logrus.Infof("Get image filesystem path %q", c.imageFSPath) | ||||
|  | ||||
| 	c.netPlugin, err = ocicni.InitCNI(config.NetworkPluginConfDir, config.NetworkPluginBinDir) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to initialize cni plugin: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// prepare streaming server | ||||
| 	c.streamServer, err = newStreamServer(c, config.StreamServerAddress, config.StreamServerPort) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create stream server: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	c.eventMonitor = newEventMonitor(c.containerStore, c.sandboxStore) | ||||
|  | ||||
| 	// To avoid race condition between `Run` and `Stop`, still create grpc server | ||||
| 	// although we may not use it. It's just a small in-memory data structure. | ||||
| 	// TODO(random-liu): Get rid of the grpc server when completely switch | ||||
| 	// to plugin mode. | ||||
| 	c.server = grpc.NewServer() | ||||
|  | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| // Register registers all required services onto a specific grpc server. | ||||
| // This is used by containerd cri plugin. | ||||
| func (c *criContainerdService) Register(s *grpc.Server) error { | ||||
| 	instrumented := newInstrumentedService(c) | ||||
| 	runtime.RegisterRuntimeServiceServer(s, instrumented) | ||||
| 	runtime.RegisterImageServiceServer(s, instrumented) | ||||
| 	api.RegisterCRIContainerdServiceServer(s, instrumented) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Run starts the cri-containerd service. startGRPC specifies | ||||
| // whether to start grpc server in this function. | ||||
| // TODO(random-liu): Remove `startRPC=true` case when we no longer support cri-containerd | ||||
| // standalone mode. | ||||
| func (c *criContainerdService) Run(startGRPC bool) error { | ||||
| 	logrus.Info("Start cri-containerd service") | ||||
|  | ||||
| 	// Connect containerd service here, to get rid of the containerd dependency | ||||
| 	// in `NewCRIContainerdService`. This is required for plugin mode bootstrapping. | ||||
| 	logrus.Info("Connect containerd service") | ||||
| 	client, err := containerd.New(c.config.ContainerdEndpoint, containerd.WithDefaultNamespace(k8sContainerdNamespace)) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to initialize containerd client with endpoint %q: %v", | ||||
| 			c.config.ContainerdEndpoint, err) | ||||
| 	} | ||||
| 	c.client = client | ||||
|  | ||||
| 	logrus.Info("Start subscribing containerd event") | ||||
| 	c.eventMonitor.subscribe(c.client) | ||||
|  | ||||
| 	logrus.Infof("Start recovering state") | ||||
| 	if err := c.recover(context.Background()); err != nil { | ||||
| 		return fmt.Errorf("failed to recover state: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Start event handler. | ||||
| 	logrus.Info("Start event monitor") | ||||
| 	eventMonitorCloseCh, err := c.eventMonitor.start() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to start event monitor: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Start snapshot stats syncer, it doesn't need to be stopped. | ||||
| 	logrus.Info("Start snapshots syncer") | ||||
| 	snapshotsSyncer := newSnapshotsSyncer( | ||||
| 		c.snapshotStore, | ||||
| 		c.client.SnapshotService(c.config.ContainerdConfig.Snapshotter), | ||||
| 		time.Duration(c.config.StatsCollectPeriod)*time.Second, | ||||
| 	) | ||||
| 	snapshotsSyncer.start() | ||||
|  | ||||
| 	// Start streaming server. | ||||
| 	logrus.Info("Start streaming server") | ||||
| 	streamServerCloseCh := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		if err := c.streamServer.Start(true); err != nil { | ||||
| 			logrus.WithError(err).Error("Failed to start streaming server") | ||||
| 		} | ||||
| 		close(streamServerCloseCh) | ||||
| 	}() | ||||
|  | ||||
| 	// Set the server as initialized. GRPC services could start serving traffic. | ||||
| 	c.initialized.Set() | ||||
|  | ||||
| 	grpcServerCloseCh := make(chan struct{}) | ||||
| 	if startGRPC { | ||||
| 		// Create the grpc server and register runtime and image services. | ||||
| 		c.Register(c.server) // nolint: errcheck | ||||
| 		// Start grpc server. | ||||
| 		// Unlink to cleanup the previous socket file. | ||||
| 		logrus.Info("Start grpc server") | ||||
| 		err := syscall.Unlink(c.config.SocketPath) | ||||
| 		if err != nil && !os.IsNotExist(err) { | ||||
| 			return fmt.Errorf("failed to unlink socket file %q: %v", c.config.SocketPath, err) | ||||
| 		} | ||||
| 		l, err := net.Listen(unixProtocol, c.config.SocketPath) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to listen on %q: %v", c.config.SocketPath, err) | ||||
| 		} | ||||
| 		go func() { | ||||
| 			if err := c.server.Serve(l); err != nil { | ||||
| 				logrus.WithError(err).Error("Failed to serve grpc request") | ||||
| 			} | ||||
| 			close(grpcServerCloseCh) | ||||
| 		}() | ||||
| 	} | ||||
| 	// Keep grpcServerCloseCh open if grpc server is not started. | ||||
|  | ||||
| 	// Stop the whole cri-containerd service if any of the critical service exits. | ||||
| 	select { | ||||
| 	case <-eventMonitorCloseCh: | ||||
| 	case <-streamServerCloseCh: | ||||
| 	case <-grpcServerCloseCh: | ||||
| 	} | ||||
| 	if err := c.Close(); err != nil { | ||||
| 		return fmt.Errorf("failed to stop cri service: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	<-eventMonitorCloseCh | ||||
| 	logrus.Info("Event monitor stopped") | ||||
| 	<-streamServerCloseCh | ||||
| 	logrus.Info("Stream server stopped") | ||||
| 	if startGRPC { | ||||
| 		// Only wait for grpc server close channel when grpc server is started. | ||||
| 		<-grpcServerCloseCh | ||||
| 		logrus.Info("GRPC server stopped") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Stop stops the cri-containerd service. | ||||
| func (c *criContainerdService) Close() error { | ||||
| 	logrus.Info("Stop cri-containerd service") | ||||
| 	// TODO(random-liu): Make event monitor stop synchronous. | ||||
| 	c.eventMonitor.stop() | ||||
| 	if err := c.streamServer.Stop(); err != nil { | ||||
| 		return fmt.Errorf("failed to stop stream server: %v", err) | ||||
| 	} | ||||
| 	c.server.Stop() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // imageFSPath returns containerd image filesystem path. | ||||
| // Note that if containerd changes directory layout, we also needs to change this. | ||||
| func imageFSPath(rootDir, snapshotter string) string { | ||||
| 	return filepath.Join(rootDir, fmt.Sprintf("%s.%s", plugin.SnapshotPlugin, snapshotter)) | ||||
| } | ||||
							
								
								
									
										118
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/snapshots.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/snapshots.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	snapshot "github.com/containerd/containerd/snapshots" | ||||
| 	"github.com/sirupsen/logrus" | ||||
|  | ||||
| 	snapshotstore "github.com/containerd/cri-containerd/pkg/store/snapshot" | ||||
| ) | ||||
|  | ||||
| // snapshotsSyncer syncs snapshot stats periodically. imagefs info and container stats | ||||
| // should both use cached result here. | ||||
| // TODO(random-liu): Benchmark with high workload. We may need a statsSyncer instead if | ||||
| // benchmark result shows that container cpu/memory stats also need to be cached. | ||||
| type snapshotsSyncer struct { | ||||
| 	store       *snapshotstore.Store | ||||
| 	snapshotter snapshot.Snapshotter | ||||
| 	syncPeriod  time.Duration | ||||
| } | ||||
|  | ||||
| // newSnapshotsSyncer creates a snapshot syncer. | ||||
| func newSnapshotsSyncer(store *snapshotstore.Store, snapshotter snapshot.Snapshotter, | ||||
| 	period time.Duration) *snapshotsSyncer { | ||||
| 	return &snapshotsSyncer{ | ||||
| 		store:       store, | ||||
| 		snapshotter: snapshotter, | ||||
| 		syncPeriod:  period, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // start starts the snapshots syncer. No stop function is needed because | ||||
| // the syncer doesn't update any persistent states, it's fine to let it | ||||
| // exit with the process. | ||||
| func (s *snapshotsSyncer) start() { | ||||
| 	tick := time.NewTicker(s.syncPeriod) | ||||
| 	go func() { | ||||
| 		defer tick.Stop() | ||||
| 		// TODO(random-liu): This is expensive. We should do benchmark to | ||||
| 		// check the resource usage and optimize this. | ||||
| 		for { | ||||
| 			if err := s.sync(); err != nil { | ||||
| 				logrus.WithError(err).Error("Failed to sync snapshot stats") | ||||
| 			} | ||||
| 			<-tick.C | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
|  | ||||
| // sync updates all snapshots stats. | ||||
| func (s *snapshotsSyncer) sync() error { | ||||
| 	start := time.Now().UnixNano() | ||||
| 	var snapshots []snapshot.Info | ||||
| 	// Do not call `Usage` directly in collect function, because | ||||
| 	// `Usage` takes time, we don't want `Walk` to hold read lock | ||||
| 	// of snapshot metadata store for too long time. | ||||
| 	// TODO(random-liu): Set timeout for the following 2 contexts. | ||||
| 	if err := s.snapshotter.Walk(context.Background(), func(ctx context.Context, info snapshot.Info) error { | ||||
| 		snapshots = append(snapshots, info) | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		return fmt.Errorf("walk all snapshots failed: %v", err) | ||||
| 	} | ||||
| 	for _, info := range snapshots { | ||||
| 		sn, err := s.store.Get(info.Name) | ||||
| 		if err == nil { | ||||
| 			// Only update timestamp for non-active snapshot. | ||||
| 			if sn.Kind == info.Kind && sn.Kind != snapshot.KindActive { | ||||
| 				sn.Timestamp = time.Now().UnixNano() | ||||
| 				s.store.Add(sn) | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		// Get newest stats if the snapshot is new or active. | ||||
| 		sn = snapshotstore.Snapshot{ | ||||
| 			Key:       info.Name, | ||||
| 			Kind:      info.Kind, | ||||
| 			Timestamp: time.Now().UnixNano(), | ||||
| 		} | ||||
| 		usage, err := s.snapshotter.Usage(context.Background(), info.Name) | ||||
| 		if err != nil { | ||||
| 			if !errdefs.IsNotFound(err) { | ||||
| 				logrus.WithError(err).Errorf("Failed to get usage for snapshot %q", info.Name) | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		sn.Size = uint64(usage.Size) | ||||
| 		sn.Inodes = uint64(usage.Inodes) | ||||
| 		s.store.Add(sn) | ||||
| 	} | ||||
| 	for _, sn := range s.store.List() { | ||||
| 		if sn.Timestamp >= start { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Delete the snapshot stats if it's not updated this time. | ||||
| 		s.store.Delete(sn.Key) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										81
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	goruntime "runtime" | ||||
|  | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// runtimeNotReadyReason is the reason reported when runtime is not ready. | ||||
| 	runtimeNotReadyReason = "ContainerdNotReady" | ||||
| 	// networkNotReadyReason is the reason reported when network is not ready. | ||||
| 	networkNotReadyReason = "NetworkPluginNotReady" | ||||
| ) | ||||
|  | ||||
| // Status returns the status of the runtime. | ||||
| func (c *criContainerdService) Status(ctx context.Context, r *runtime.StatusRequest) (*runtime.StatusResponse, error) { | ||||
| 	runtimeCondition := &runtime.RuntimeCondition{ | ||||
| 		Type:   runtime.RuntimeReady, | ||||
| 		Status: true, | ||||
| 	} | ||||
| 	serving, err := c.client.IsServing(ctx) | ||||
| 	if err != nil || !serving { | ||||
| 		runtimeCondition.Status = false | ||||
| 		runtimeCondition.Reason = runtimeNotReadyReason | ||||
| 		if err != nil { | ||||
| 			runtimeCondition.Message = fmt.Sprintf("Containerd healthcheck returns error: %v", err) | ||||
| 		} else { | ||||
| 			runtimeCondition.Message = "Containerd grpc server is not serving" | ||||
| 		} | ||||
| 	} | ||||
| 	networkCondition := &runtime.RuntimeCondition{ | ||||
| 		Type:   runtime.NetworkReady, | ||||
| 		Status: true, | ||||
| 	} | ||||
| 	if err := c.netPlugin.Status(); err != nil { | ||||
| 		networkCondition.Status = false | ||||
| 		networkCondition.Reason = networkNotReadyReason | ||||
| 		networkCondition.Message = fmt.Sprintf("Network plugin returns error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	resp := &runtime.StatusResponse{ | ||||
| 		Status: &runtime.RuntimeStatus{Conditions: []*runtime.RuntimeCondition{ | ||||
| 			runtimeCondition, | ||||
| 			networkCondition, | ||||
| 		}}, | ||||
| 	} | ||||
| 	if r.Verbose { | ||||
| 		configByt, err := json.Marshal(c.config) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		resp.Info = make(map[string]string) | ||||
| 		resp.Info["config"] = string(configByt) | ||||
| 		versionByt, err := json.Marshal(goruntime.Version()) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		resp.Info["golang"] = string(versionByt) | ||||
| 	} | ||||
| 	return resp, nil | ||||
| } | ||||
							
								
								
									
										113
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/streaming.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/streaming.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"net" | ||||
|  | ||||
| 	"golang.org/x/net/context" | ||||
| 	k8snet "k8s.io/apimachinery/pkg/util/net" | ||||
| 	"k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/client-go/tools/remotecommand" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/server/streaming" | ||||
| 	"k8s.io/utils/exec" | ||||
| ) | ||||
|  | ||||
| func newStreamServer(c *criContainerdService, addr, port string) (streaming.Server, error) { | ||||
| 	if addr == "" { | ||||
| 		a, err := k8snet.ChooseBindAddress(nil) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to get stream server address: %v", err) | ||||
| 		} | ||||
| 		addr = a.String() | ||||
| 	} | ||||
| 	config := streaming.DefaultConfig | ||||
| 	config.Addr = net.JoinHostPort(addr, port) | ||||
| 	runtime := newStreamRuntime(c) | ||||
| 	return streaming.NewServer(config, runtime) | ||||
| } | ||||
|  | ||||
| type streamRuntime struct { | ||||
| 	c *criContainerdService | ||||
| } | ||||
|  | ||||
| func newStreamRuntime(c *criContainerdService) streaming.Runtime { | ||||
| 	return &streamRuntime{c: c} | ||||
| } | ||||
|  | ||||
| // Exec executes a command inside the container. exec.ExitError is returned if the command | ||||
| // returns non-zero exit code. | ||||
| func (s *streamRuntime) Exec(containerID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, | ||||
| 	tty bool, resize <-chan remotecommand.TerminalSize) error { | ||||
| 	exitCode, err := s.c.execInContainer(context.Background(), containerID, execOptions{ | ||||
| 		cmd:    cmd, | ||||
| 		stdin:  stdin, | ||||
| 		stdout: stdout, | ||||
| 		stderr: stderr, | ||||
| 		tty:    tty, | ||||
| 		resize: resize, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to exec in container: %v", err) | ||||
| 	} | ||||
| 	if *exitCode == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &exec.CodeExitError{ | ||||
| 		Err:  fmt.Errorf("error executing command %v, exit code %d", cmd, *exitCode), | ||||
| 		Code: int(*exitCode), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *streamRuntime) Attach(containerID string, in io.Reader, out, err io.WriteCloser, tty bool, | ||||
| 	resize <-chan remotecommand.TerminalSize) error { | ||||
| 	return s.c.attachContainer(context.Background(), containerID, in, out, err, tty, resize) | ||||
| } | ||||
|  | ||||
| func (s *streamRuntime) PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error { | ||||
| 	if port <= 0 || port > math.MaxUint16 { | ||||
| 		return fmt.Errorf("invalid port %d", port) | ||||
| 	} | ||||
| 	return s.c.portForward(podSandboxID, port, stream) | ||||
| } | ||||
|  | ||||
| // handleResizing spawns a goroutine that processes the resize channel, calling resizeFunc for each | ||||
| // remotecommand.TerminalSize received from the channel. The resize channel must be closed elsewhere to stop the | ||||
| // goroutine. | ||||
| func handleResizing(resize <-chan remotecommand.TerminalSize, resizeFunc func(size remotecommand.TerminalSize)) { | ||||
| 	if resize == nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	go func() { | ||||
| 		defer runtime.HandleCrash() | ||||
|  | ||||
| 		for { | ||||
| 			size, ok := <-resize | ||||
| 			if !ok { | ||||
| 				return | ||||
| 			} | ||||
| 			if size.Height < 1 || size.Width < 1 { | ||||
| 				continue | ||||
| 			} | ||||
| 			resizeFunc(size) | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
							
								
								
									
										29
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/update_runtime_config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/update_runtime_config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"golang.org/x/net/context" | ||||
|  | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // UpdateRuntimeConfig updates the runtime config. Currently only handles podCIDR updates. | ||||
| // TODO(random-liu): Figure out how to handle pod cidr in cri-containerd. | ||||
| func (c *criContainerdService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateRuntimeConfigRequest) (*runtime.UpdateRuntimeConfigResponse, error) { | ||||
| 	return &runtime.UpdateRuntimeConfigResponse{}, nil | ||||
| } | ||||
							
								
								
									
										48
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/containerd/cri-containerd/pkg/server/version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 server | ||||
|  | ||||
| import ( | ||||
| 	"golang.org/x/net/context" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/version" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// For now, containerd and runc are bundled with cri-containerd, cri-containerd | ||||
| 	// version is more important to us. | ||||
| 	// TODO(random-liu): Figure out how to package cri-containerd and containerd, | ||||
| 	// and how to version it. We still prefer calling the container runtime "containerd", | ||||
| 	// but we care both the cri-containerd version and containerd version. | ||||
| 	containerName        = "cri-containerd" | ||||
| 	containerdAPIVersion = "0.0.0" | ||||
| 	// kubeAPIVersion is the api version of kubernetes. | ||||
| 	kubeAPIVersion = "0.1.0" | ||||
| ) | ||||
|  | ||||
| // Version returns the runtime name, runtime version and runtime API version. | ||||
| // TODO(random-liu): Return containerd version since we are going to merge 2 daemons. | ||||
| func (c *criContainerdService) Version(ctx context.Context, r *runtime.VersionRequest) (*runtime.VersionResponse, error) { | ||||
| 	return &runtime.VersionResponse{ | ||||
| 		Version:        kubeAPIVersion, | ||||
| 		RuntimeName:    containerName, | ||||
| 		RuntimeVersion: version.CRIContainerdVersion, | ||||
| 		// Containerd doesn't have an api version now. | ||||
| 		RuntimeApiVersion: containerdAPIVersion, | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										169
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/container/container.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/container/container.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 container | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/docker/docker/pkg/truncindex" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
|  | ||||
| 	cio "github.com/containerd/cri-containerd/pkg/server/io" | ||||
| 	"github.com/containerd/cri-containerd/pkg/store" | ||||
| ) | ||||
|  | ||||
| // Container contains all resources associated with the container. All methods to | ||||
| // mutate the internal state are thread-safe. | ||||
| type Container struct { | ||||
| 	// Metadata is the metadata of the container, it is **immutable** after created. | ||||
| 	Metadata | ||||
| 	// Status stores the status of the container. | ||||
| 	Status StatusStorage | ||||
| 	// Container is the containerd container client. | ||||
| 	Container containerd.Container | ||||
| 	// Container IO | ||||
| 	IO *cio.ContainerIO | ||||
| 	// StopCh is used to propagate the stop information of the container. | ||||
| 	store.StopCh | ||||
| } | ||||
|  | ||||
| // Opts sets specific information to newly created Container. | ||||
| type Opts func(*Container) error | ||||
|  | ||||
| // WithContainer adds the containerd Container to the internal data store. | ||||
| func WithContainer(cntr containerd.Container) Opts { | ||||
| 	return func(c *Container) error { | ||||
| 		c.Container = cntr | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithContainerIO adds IO into the container. | ||||
| func WithContainerIO(io *cio.ContainerIO) Opts { | ||||
| 	return func(c *Container) error { | ||||
| 		c.IO = io | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithStatus adds status to the container. | ||||
| func WithStatus(status Status, root string) Opts { | ||||
| 	return func(c *Container) error { | ||||
| 		s, err := StoreStatus(root, c.ID, status) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		c.Status = s | ||||
| 		if s.Get().State() == runtime.ContainerState_CONTAINER_EXITED { | ||||
| 			c.Stop() | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewContainer creates an internally used container type. | ||||
| func NewContainer(metadata Metadata, opts ...Opts) (Container, error) { | ||||
| 	c := Container{ | ||||
| 		Metadata: metadata, | ||||
| 		StopCh:   store.StopCh(make(chan struct{})), | ||||
| 	} | ||||
| 	for _, o := range opts { | ||||
| 		if err := o(&c); err != nil { | ||||
| 			return Container{}, err | ||||
| 		} | ||||
| 	} | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| // Delete deletes checkpoint for the container. | ||||
| func (c *Container) Delete() error { | ||||
| 	return c.Status.Delete() | ||||
| } | ||||
|  | ||||
| // Store stores all Containers. | ||||
| type Store struct { | ||||
| 	lock       sync.RWMutex | ||||
| 	containers map[string]Container | ||||
| 	idIndex    *truncindex.TruncIndex | ||||
| } | ||||
|  | ||||
| // NewStore creates a container store. | ||||
| func NewStore() *Store { | ||||
| 	return &Store{ | ||||
| 		containers: make(map[string]Container), | ||||
| 		idIndex:    truncindex.NewTruncIndex([]string{}), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Add a container into the store. Returns store.ErrAlreadyExist if the | ||||
| // container already exists. | ||||
| func (s *Store) Add(c Container) error { | ||||
| 	s.lock.Lock() | ||||
| 	defer s.lock.Unlock() | ||||
| 	if _, ok := s.containers[c.ID]; ok { | ||||
| 		return store.ErrAlreadyExist | ||||
| 	} | ||||
| 	if err := s.idIndex.Add(c.ID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.containers[c.ID] = c | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Get returns the container with specified id. Returns store.ErrNotExist | ||||
| // if the container doesn't exist. | ||||
| func (s *Store) Get(id string) (Container, error) { | ||||
| 	s.lock.RLock() | ||||
| 	defer s.lock.RUnlock() | ||||
| 	id, err := s.idIndex.Get(id) | ||||
| 	if err != nil { | ||||
| 		if err == truncindex.ErrNotExist { | ||||
| 			err = store.ErrNotExist | ||||
| 		} | ||||
| 		return Container{}, err | ||||
| 	} | ||||
| 	if c, ok := s.containers[id]; ok { | ||||
| 		return c, nil | ||||
| 	} | ||||
| 	return Container{}, store.ErrNotExist | ||||
| } | ||||
|  | ||||
| // List lists all containers. | ||||
| func (s *Store) List() []Container { | ||||
| 	s.lock.RLock() | ||||
| 	defer s.lock.RUnlock() | ||||
| 	var containers []Container | ||||
| 	for _, c := range s.containers { | ||||
| 		containers = append(containers, c) | ||||
| 	} | ||||
| 	return containers | ||||
| } | ||||
|  | ||||
| // Delete deletes the container from store with specified id. | ||||
| func (s *Store) Delete(id string) { | ||||
| 	s.lock.Lock() | ||||
| 	defer s.lock.Unlock() | ||||
| 	id, err := s.idIndex.Get(id) | ||||
| 	if err != nil { | ||||
| 		// Note: The idIndex.Delete and delete doesn't handle truncated index. | ||||
| 		// So we need to return if there are error. | ||||
| 		return | ||||
| 	} | ||||
| 	s.idIndex.Delete(id) // nolint: errcheck | ||||
| 	delete(s.containers, id) | ||||
| } | ||||
							
								
								
									
										62
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/container/fake_status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/container/fake_status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 container | ||||
|  | ||||
| import "sync" | ||||
|  | ||||
| // WithFakeStatus adds fake status to the container. | ||||
| func WithFakeStatus(status Status) Opts { | ||||
| 	return func(c *Container) error { | ||||
| 		c.Status = &fakeStatusStorage{status: status} | ||||
| 		if status.FinishedAt != 0 { | ||||
| 			// Fake the TaskExit event | ||||
| 			c.Stop() | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // fakeStatusStorage is a fake status storage for testing. | ||||
| type fakeStatusStorage struct { | ||||
| 	sync.RWMutex | ||||
| 	status Status | ||||
| } | ||||
|  | ||||
| func (f *fakeStatusStorage) Get() Status { | ||||
| 	f.RLock() | ||||
| 	defer f.RUnlock() | ||||
| 	return f.status | ||||
| } | ||||
|  | ||||
| func (f *fakeStatusStorage) UpdateSync(u UpdateFunc) error { | ||||
| 	return f.Update(u) | ||||
| } | ||||
|  | ||||
| func (f *fakeStatusStorage) Update(u UpdateFunc) error { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	newStatus, err := u(f.status) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	f.status = newStatus | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (f *fakeStatusStorage) Delete() error { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										84
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/container/metadata.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/container/metadata.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 container | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // NOTE(random-liu): | ||||
| // 1) Metadata is immutable after created. | ||||
| // 2) Metadata is checkpointed as containerd container label. | ||||
|  | ||||
| // metadataVersion  is current version of container metadata. | ||||
| const metadataVersion = "v1" // nolint | ||||
|  | ||||
| // versionedMetadata is the internal versioned container metadata. | ||||
| // nolint | ||||
| type versionedMetadata struct { | ||||
| 	// Version indicates the version of the versioned container metadata. | ||||
| 	Version string | ||||
| 	// Metadata's type is metadataInternal. If not there will be a recursive call in MarshalJSON. | ||||
| 	Metadata metadataInternal | ||||
| } | ||||
|  | ||||
| // metadataInternal is for internal use. | ||||
| type metadataInternal Metadata | ||||
|  | ||||
| // Metadata is the unversioned container metadata. | ||||
| type Metadata struct { | ||||
| 	// ID is the container id. | ||||
| 	ID string | ||||
| 	// Name is the container name. | ||||
| 	Name string | ||||
| 	// SandboxID is the sandbox id the container belongs to. | ||||
| 	SandboxID string | ||||
| 	// Config is the CRI container config. | ||||
| 	// NOTE(random-liu): Resource limits are updatable, the source | ||||
| 	// of truth for resource limits are in containerd. | ||||
| 	Config *runtime.ContainerConfig | ||||
| 	// ImageRef is the reference of image used by the container. | ||||
| 	ImageRef string | ||||
| 	// LogPath is the container log path. | ||||
| 	LogPath string | ||||
| } | ||||
|  | ||||
| // MarshalJSON encodes Metadata into bytes in json format. | ||||
| func (c *Metadata) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(&versionedMetadata{ | ||||
| 		Version:  metadataVersion, | ||||
| 		Metadata: metadataInternal(*c), | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes Metadata from bytes. | ||||
| func (c *Metadata) UnmarshalJSON(data []byte) error { | ||||
| 	versioned := &versionedMetadata{} | ||||
| 	if err := json.Unmarshal(data, versioned); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// Handle old version after upgrade. | ||||
| 	switch versioned.Version { | ||||
| 	case metadataVersion: | ||||
| 		*c = Metadata(versioned.Metadata) | ||||
| 		return nil | ||||
| 	} | ||||
| 	return fmt.Errorf("unsupported version: %q", versioned.Version) | ||||
| } | ||||
							
								
								
									
										206
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/container/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/container/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 container | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/docker/docker/pkg/ioutils" | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // statusVersion is current version of container status. | ||||
| const statusVersion = "v1" // nolint | ||||
|  | ||||
| // versionedStatus is the internal used versioned container status. | ||||
| // nolint | ||||
| type versionedStatus struct { | ||||
| 	// Version indicates the version of the versioned container status. | ||||
| 	Version string | ||||
| 	Status | ||||
| } | ||||
|  | ||||
| // Status is the status of a container. | ||||
| type Status struct { | ||||
| 	// Pid is the init process id of the container. | ||||
| 	Pid uint32 | ||||
| 	// CreatedAt is the created timestamp. | ||||
| 	CreatedAt int64 | ||||
| 	// StartedAt is the started timestamp. | ||||
| 	StartedAt int64 | ||||
| 	// FinishedAt is the finished timestamp. | ||||
| 	FinishedAt int64 | ||||
| 	// ExitCode is the container exit code. | ||||
| 	ExitCode int32 | ||||
| 	// CamelCase string explaining why container is in its current state. | ||||
| 	Reason string | ||||
| 	// Human-readable message indicating details about why container is in its | ||||
| 	// current state. | ||||
| 	Message string | ||||
| 	// Removing indicates that the container is in removing state. | ||||
| 	// This field doesn't need to be checkpointed. | ||||
| 	Removing bool `json:"-"` | ||||
| } | ||||
|  | ||||
| // State returns current state of the container based on the container status. | ||||
| func (s Status) State() runtime.ContainerState { | ||||
| 	if s.FinishedAt != 0 { | ||||
| 		return runtime.ContainerState_CONTAINER_EXITED | ||||
| 	} | ||||
| 	if s.StartedAt != 0 { | ||||
| 		return runtime.ContainerState_CONTAINER_RUNNING | ||||
| 	} | ||||
| 	if s.CreatedAt != 0 { | ||||
| 		return runtime.ContainerState_CONTAINER_CREATED | ||||
| 	} | ||||
| 	return runtime.ContainerState_CONTAINER_UNKNOWN | ||||
| } | ||||
|  | ||||
| // encode encodes Status into bytes in json format. | ||||
| func (s *Status) encode() ([]byte, error) { | ||||
| 	return json.Marshal(&versionedStatus{ | ||||
| 		Version: statusVersion, | ||||
| 		Status:  *s, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // decode decodes Status from bytes. | ||||
| func (s *Status) decode(data []byte) error { | ||||
| 	versioned := &versionedStatus{} | ||||
| 	if err := json.Unmarshal(data, versioned); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// Handle old version after upgrade. | ||||
| 	switch versioned.Version { | ||||
| 	case statusVersion: | ||||
| 		*s = versioned.Status | ||||
| 		return nil | ||||
| 	} | ||||
| 	return fmt.Errorf("unsupported version") | ||||
| } | ||||
|  | ||||
| // UpdateFunc is function used to update the container status. If there | ||||
| // is an error, the update will be rolled back. | ||||
| type UpdateFunc func(Status) (Status, error) | ||||
|  | ||||
| // StatusStorage manages the container status with a storage backend. | ||||
| type StatusStorage interface { | ||||
| 	// Get a container status. | ||||
| 	Get() Status | ||||
| 	// UpdateSync updates the container status and the on disk checkpoint. | ||||
| 	// Note that the update MUST be applied in one transaction. | ||||
| 	UpdateSync(UpdateFunc) error | ||||
| 	// Update the container status. Note that the update MUST be applied | ||||
| 	// in one transaction. | ||||
| 	Update(UpdateFunc) error | ||||
| 	// Delete the container status. | ||||
| 	// Note: | ||||
| 	// * Delete should be idempotent. | ||||
| 	// * The status must be deleted in one trasaction. | ||||
| 	Delete() error | ||||
| } | ||||
|  | ||||
| // StoreStatus creates the storage containing the passed in container status with the | ||||
| // specified id. | ||||
| // The status MUST be created in one transaction. | ||||
| func StoreStatus(root, id string, status Status) (StatusStorage, error) { | ||||
| 	data, err := status.encode() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to encode status: %v", err) | ||||
| 	} | ||||
| 	path := filepath.Join(root, "status") | ||||
| 	if err := ioutils.AtomicWriteFile(path, data, 0600); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to checkpoint status to %q: %v", path, err) | ||||
| 	} | ||||
| 	return &statusStorage{ | ||||
| 		path:   path, | ||||
| 		status: status, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // LoadStatus loads container status from checkpoint. There shouldn't be threads | ||||
| // writing to the file during loading. | ||||
| func LoadStatus(root, id string) (Status, error) { | ||||
| 	path := filepath.Join(root, "status") | ||||
| 	data, err := ioutil.ReadFile(path) | ||||
| 	if err != nil { | ||||
| 		return Status{}, fmt.Errorf("failed to read status from %q: %v", path, err) | ||||
| 	} | ||||
| 	var status Status | ||||
| 	if err := status.decode(data); err != nil { | ||||
| 		return Status{}, fmt.Errorf("failed to decode status %q: %v", data, err) | ||||
| 	} | ||||
| 	return status, nil | ||||
| } | ||||
|  | ||||
| type statusStorage struct { | ||||
| 	sync.RWMutex | ||||
| 	path   string | ||||
| 	status Status | ||||
| } | ||||
|  | ||||
| // Get a copy of container status. | ||||
| func (s *statusStorage) Get() Status { | ||||
| 	s.RLock() | ||||
| 	defer s.RUnlock() | ||||
| 	return s.status | ||||
| } | ||||
|  | ||||
| // UpdateSync updates the container status and the on disk checkpoint. | ||||
| func (s *statusStorage) UpdateSync(u UpdateFunc) error { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
| 	newStatus, err := u(s.status) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	data, err := newStatus.encode() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to encode status: %v", err) | ||||
| 	} | ||||
| 	if err := ioutils.AtomicWriteFile(s.path, data, 0600); err != nil { | ||||
| 		return fmt.Errorf("failed to checkpoint status to %q: %v", s.path, err) | ||||
| 	} | ||||
| 	s.status = newStatus | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Update the container status. | ||||
| func (s *statusStorage) Update(u UpdateFunc) error { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
| 	newStatus, err := u(s.status) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.status = newStatus | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Delete deletes the container status from disk atomically. | ||||
| func (s *statusStorage) Delete() error { | ||||
| 	temp := filepath.Dir(s.path) + ".del-" + filepath.Base(s.path) | ||||
| 	if err := os.Rename(s.path, temp); err != nil && !os.IsNotExist(err) { | ||||
| 		return err | ||||
| 	} | ||||
| 	return os.RemoveAll(temp) | ||||
| } | ||||
							
								
								
									
										27
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 store | ||||
|  | ||||
| import "errors" | ||||
|  | ||||
| var ( | ||||
| 	// ErrAlreadyExist is the error returned when data added in the store | ||||
| 	// already exists. | ||||
| 	ErrAlreadyExist = errors.New("already exists") | ||||
| 	// ErrNotExist is the error returned when data is not in the store. | ||||
| 	ErrNotExist = errors.New("does not exist") | ||||
| ) | ||||
							
								
								
									
										147
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/image/image.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/image/image.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 image | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/docker/distribution/digestset" | ||||
| 	godigest "github.com/opencontainers/go-digest" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/store" | ||||
| ) | ||||
|  | ||||
| // Image contains all resources associated with the image. All fields | ||||
| // MUST not be mutated directly after created. | ||||
| type Image struct { | ||||
| 	// Id of the image. Normally the digest of image config. | ||||
| 	ID string | ||||
| 	// Other names by which this image is known. | ||||
| 	RepoTags []string | ||||
| 	// Digests by which this image is known. | ||||
| 	RepoDigests []string | ||||
| 	// ChainID is the chainID of the image. | ||||
| 	ChainID string | ||||
| 	// Size is the compressed size of the image. | ||||
| 	Size int64 | ||||
| 	// ImageSpec is the oci image structure which describes basic information about the image. | ||||
| 	ImageSpec imagespec.Image | ||||
| 	// Containerd image reference | ||||
| 	Image containerd.Image | ||||
| } | ||||
|  | ||||
| // Store stores all images. | ||||
| type Store struct { | ||||
| 	lock      sync.RWMutex | ||||
| 	images    map[string]Image | ||||
| 	digestSet *digestset.Set | ||||
| } | ||||
|  | ||||
| // NewStore creates an image store. | ||||
| func NewStore() *Store { | ||||
| 	return &Store{ | ||||
| 		images:    make(map[string]Image), | ||||
| 		digestSet: digestset.NewSet(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Add an image into the store. | ||||
| func (s *Store) Add(img Image) error { | ||||
| 	s.lock.Lock() | ||||
| 	defer s.lock.Unlock() | ||||
| 	if _, err := s.digestSet.Lookup(img.ID); err != nil { | ||||
| 		if err != digestset.ErrDigestNotFound { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err := s.digestSet.Add(godigest.Digest(img.ID)); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	i, ok := s.images[img.ID] | ||||
| 	if !ok { | ||||
| 		// If the image doesn't exist, add it. | ||||
| 		s.images[img.ID] = img | ||||
| 		return nil | ||||
| 	} | ||||
| 	// Or else, merge the repo tags/digests. | ||||
| 	i.RepoTags = mergeStringSlices(i.RepoTags, img.RepoTags) | ||||
| 	i.RepoDigests = mergeStringSlices(i.RepoDigests, img.RepoDigests) | ||||
| 	s.images[img.ID] = i | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Get returns the image with specified id. Returns store.ErrNotExist if the | ||||
| // image doesn't exist. | ||||
| func (s *Store) Get(id string) (Image, error) { | ||||
| 	s.lock.RLock() | ||||
| 	defer s.lock.RUnlock() | ||||
| 	digest, err := s.digestSet.Lookup(id) | ||||
| 	if err != nil { | ||||
| 		if err == digestset.ErrDigestNotFound { | ||||
| 			err = store.ErrNotExist | ||||
| 		} | ||||
| 		return Image{}, err | ||||
| 	} | ||||
| 	if i, ok := s.images[digest.String()]; ok { | ||||
| 		return i, nil | ||||
| 	} | ||||
| 	return Image{}, store.ErrNotExist | ||||
| } | ||||
|  | ||||
| // List lists all images. | ||||
| func (s *Store) List() []Image { | ||||
| 	s.lock.RLock() | ||||
| 	defer s.lock.RUnlock() | ||||
| 	var images []Image | ||||
| 	for _, i := range s.images { | ||||
| 		images = append(images, i) | ||||
| 	} | ||||
| 	return images | ||||
| } | ||||
|  | ||||
| // Delete deletes the image with specified id. | ||||
| func (s *Store) Delete(id string) { | ||||
| 	s.lock.Lock() | ||||
| 	defer s.lock.Unlock() | ||||
| 	digest, err := s.digestSet.Lookup(id) | ||||
| 	if err != nil { | ||||
| 		// Note: The idIndex.Delete and delete doesn't handle truncated index. | ||||
| 		// So we need to return if there are error. | ||||
| 		return | ||||
| 	} | ||||
| 	s.digestSet.Remove(digest) // nolint: errcheck | ||||
| 	delete(s.images, digest.String()) | ||||
| } | ||||
|  | ||||
| // mergeStringSlices merges 2 string slices into one and remove duplicated elements. | ||||
| func mergeStringSlices(a []string, b []string) []string { | ||||
| 	set := map[string]struct{}{} | ||||
| 	for _, s := range a { | ||||
| 		set[s] = struct{}{} | ||||
| 	} | ||||
| 	for _, s := range b { | ||||
| 		set[s] = struct{}{} | ||||
| 	} | ||||
| 	var ss []string | ||||
| 	for s := range set { | ||||
| 		ss = append(ss, s) | ||||
| 	} | ||||
| 	return ss | ||||
| } | ||||
							
								
								
									
										80
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/sandbox/metadata.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/sandbox/metadata.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 sandbox | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | ||||
| 	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" | ||||
| ) | ||||
|  | ||||
| // NOTE(random-liu): | ||||
| // 1) Metadata is immutable after created. | ||||
| // 2) Metadata is checkpointed as containerd container label. | ||||
|  | ||||
| // metadataVersion is current version of sandbox metadata. | ||||
| const metadataVersion = "v1" // nolint | ||||
|  | ||||
| // versionedMetadata is the internal versioned sandbox metadata. | ||||
| // nolint | ||||
| type versionedMetadata struct { | ||||
| 	// Version indicates the version of the versioned sandbox metadata. | ||||
| 	Version string | ||||
| 	// Metadata's type is metadataInternal. If not there will be a recursive call in MarshalJSON. | ||||
| 	Metadata metadataInternal | ||||
| } | ||||
|  | ||||
| // metadataInternal is for internal use. | ||||
| type metadataInternal Metadata | ||||
|  | ||||
| // Metadata is the unversioned sandbox metadata. | ||||
| type Metadata struct { | ||||
| 	// ID is the sandbox id. | ||||
| 	ID string | ||||
| 	// Name is the sandbox name. | ||||
| 	Name string | ||||
| 	// Config is the CRI sandbox config. | ||||
| 	Config *runtime.PodSandboxConfig | ||||
| 	// NetNSPath is the network namespace used by the sandbox. | ||||
| 	NetNSPath string | ||||
| 	// IP of Pod if it is attached to non host network | ||||
| 	IP string | ||||
| } | ||||
|  | ||||
| // MarshalJSON encodes Metadata into bytes in json format. | ||||
| func (c *Metadata) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(&versionedMetadata{ | ||||
| 		Version:  metadataVersion, | ||||
| 		Metadata: metadataInternal(*c), | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes Metadata from bytes. | ||||
| func (c *Metadata) UnmarshalJSON(data []byte) error { | ||||
| 	versioned := &versionedMetadata{} | ||||
| 	if err := json.Unmarshal(data, versioned); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// Handle old version after upgrade. | ||||
| 	switch versioned.Version { | ||||
| 	case metadataVersion: | ||||
| 		*c = Metadata(versioned.Metadata) | ||||
| 		return nil | ||||
| 	} | ||||
| 	return fmt.Errorf("unsupported version: %q", versioned.Version) | ||||
| } | ||||
							
								
								
									
										127
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/sandbox/netns.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/sandbox/netns.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 sandbox | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"sync" | ||||
|  | ||||
| 	cnins "github.com/containernetworking/plugins/pkg/ns" | ||||
| 	"github.com/docker/docker/pkg/mount" | ||||
| 	"github.com/docker/docker/pkg/symlink" | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // ErrClosedNetNS is the error returned when network namespace is closed. | ||||
| var ErrClosedNetNS = errors.New("network namespace is closed") | ||||
|  | ||||
| // NetNS holds network namespace for sandbox | ||||
| type NetNS struct { | ||||
| 	sync.Mutex | ||||
| 	ns       cnins.NetNS | ||||
| 	closed   bool | ||||
| 	restored bool | ||||
| } | ||||
|  | ||||
| // NewNetNS creates a network namespace for the sandbox | ||||
| func NewNetNS() (*NetNS, error) { | ||||
| 	netns, err := cnins.NewNS() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to setup network namespace %v", err) | ||||
| 	} | ||||
| 	n := new(NetNS) | ||||
| 	n.ns = netns | ||||
| 	return n, nil | ||||
| } | ||||
|  | ||||
| // LoadNetNS loads existing network namespace. It returns ErrClosedNetNS | ||||
| // if the network namespace has already been closed. | ||||
| func LoadNetNS(path string) (*NetNS, error) { | ||||
| 	ns, err := cnins.GetNS(path) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(cnins.NSPathNotExistErr); ok { | ||||
| 			return nil, ErrClosedNetNS | ||||
| 		} | ||||
| 		if _, ok := err.(cnins.NSPathNotNSErr); ok { | ||||
| 			// Do best effort cleanup. | ||||
| 			os.RemoveAll(path) // nolint: errcheck | ||||
| 			return nil, ErrClosedNetNS | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("failed to load network namespace %v", err) | ||||
| 	} | ||||
| 	return &NetNS{ns: ns, restored: true}, nil | ||||
| } | ||||
|  | ||||
| // Remove removes network namepace if it exists and not closed. Remove is idempotent, | ||||
| // meaning it might be invoked multiple times and provides consistent result. | ||||
| func (n *NetNS) Remove() error { | ||||
| 	n.Lock() | ||||
| 	defer n.Unlock() | ||||
| 	if !n.closed { | ||||
| 		err := n.ns.Close() | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to close network namespace: %v", err) | ||||
| 		} | ||||
| 		n.closed = true | ||||
| 	} | ||||
| 	if n.restored { | ||||
| 		path := n.ns.Path() | ||||
| 		// TODO(random-liu): Add util function for unmount. | ||||
| 		// Check netns existence. | ||||
| 		if _, err := os.Stat(path); err != nil { | ||||
| 			if os.IsNotExist(err) { | ||||
| 				return nil | ||||
| 			} | ||||
| 			return fmt.Errorf("failed to stat netns: %v", err) | ||||
| 		} | ||||
| 		path, err := symlink.FollowSymlinkInScope(path, "/") | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to follow symlink: %v", err) | ||||
| 		} | ||||
| 		mounted, err := mount.Mounted(path) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to check netns mounted: %v", err) | ||||
| 		} | ||||
| 		if mounted { | ||||
| 			err := unix.Unmount(path, unix.MNT_DETACH) | ||||
| 			if err != nil && !os.IsNotExist(err) { | ||||
| 				return fmt.Errorf("failed to umount netns: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 		if err := os.RemoveAll(path); err != nil { | ||||
| 			return fmt.Errorf("failed to remove netns: %v", err) | ||||
| 		} | ||||
| 		n.restored = false | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Closed checks whether the network namespace has been closed. | ||||
| func (n *NetNS) Closed() bool { | ||||
| 	n.Lock() | ||||
| 	defer n.Unlock() | ||||
| 	return n.closed && !n.restored | ||||
| } | ||||
|  | ||||
| // GetPath returns network namespace path for sandbox container | ||||
| func (n *NetNS) GetPath() string { | ||||
| 	n.Lock() | ||||
| 	defer n.Unlock() | ||||
| 	return n.ns.Path() | ||||
| } | ||||
							
								
								
									
										143
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/sandbox/sandbox.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/sandbox/sandbox.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 sandbox | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/docker/docker/pkg/truncindex" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/store" | ||||
| ) | ||||
|  | ||||
| // Sandbox contains all resources associated with the sandbox. All methods to | ||||
| // mutate the internal state are thread safe. | ||||
| type Sandbox struct { | ||||
| 	// Metadata is the metadata of the sandbox, it is immutable after created. | ||||
| 	Metadata | ||||
| 	// Status stores the status of the sandbox. | ||||
| 	Status StatusStorage | ||||
| 	// Container is the containerd sandbox container client | ||||
| 	Container containerd.Container | ||||
| 	// CNI network namespace client | ||||
| 	NetNS *NetNS | ||||
| 	// StopCh is used to propagate the stop information of the sandbox. | ||||
| 	store.StopCh | ||||
| } | ||||
|  | ||||
| // NewSandbox creates an internally used sandbox type. This functions reminds | ||||
| // the caller that a sandbox must have a status. | ||||
| func NewSandbox(metadata Metadata, status Status) Sandbox { | ||||
| 	s := Sandbox{ | ||||
| 		Metadata: metadata, | ||||
| 		Status:   StoreStatus(status), | ||||
| 		StopCh:   store.StopCh(make(chan struct{})), | ||||
| 	} | ||||
| 	if status.State == StateNotReady { | ||||
| 		s.Stop() | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // Store stores all sandboxes. | ||||
| type Store struct { | ||||
| 	lock      sync.RWMutex | ||||
| 	sandboxes map[string]Sandbox | ||||
| 	idIndex   *truncindex.TruncIndex | ||||
| } | ||||
|  | ||||
| // NewStore creates a sandbox store. | ||||
| func NewStore() *Store { | ||||
| 	return &Store{ | ||||
| 		sandboxes: make(map[string]Sandbox), | ||||
| 		idIndex:   truncindex.NewTruncIndex([]string{}), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Add a sandbox into the store. | ||||
| func (s *Store) Add(sb Sandbox) error { | ||||
| 	s.lock.Lock() | ||||
| 	defer s.lock.Unlock() | ||||
| 	if _, ok := s.sandboxes[sb.ID]; ok { | ||||
| 		return store.ErrAlreadyExist | ||||
| 	} | ||||
| 	if err := s.idIndex.Add(sb.ID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.sandboxes[sb.ID] = sb | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Get returns the sandbox with specified id. Returns store.ErrNotExist | ||||
| // if the sandbox doesn't exist. | ||||
| func (s *Store) Get(id string) (Sandbox, error) { | ||||
| 	sb, err := s.GetAll(id) | ||||
| 	if err != nil { | ||||
| 		return sb, err | ||||
| 	} | ||||
| 	if sb.Status.Get().State == StateUnknown { | ||||
| 		return Sandbox{}, store.ErrNotExist | ||||
| 	} | ||||
| 	return sb, nil | ||||
| } | ||||
|  | ||||
| // GetAll returns the sandbox with specified id, including sandbox in unknown | ||||
| // state. Returns store.ErrNotExist if the sandbox doesn't exist. | ||||
| func (s *Store) GetAll(id string) (Sandbox, error) { | ||||
| 	s.lock.RLock() | ||||
| 	defer s.lock.RUnlock() | ||||
| 	id, err := s.idIndex.Get(id) | ||||
| 	if err != nil { | ||||
| 		if err == truncindex.ErrNotExist { | ||||
| 			err = store.ErrNotExist | ||||
| 		} | ||||
| 		return Sandbox{}, err | ||||
| 	} | ||||
| 	if sb, ok := s.sandboxes[id]; ok { | ||||
| 		return sb, nil | ||||
| 	} | ||||
| 	return Sandbox{}, store.ErrNotExist | ||||
| } | ||||
|  | ||||
| // List lists all sandboxes. | ||||
| func (s *Store) List() []Sandbox { | ||||
| 	s.lock.RLock() | ||||
| 	defer s.lock.RUnlock() | ||||
| 	var sandboxes []Sandbox | ||||
| 	for _, sb := range s.sandboxes { | ||||
| 		if sb.Status.Get().State == StateUnknown { | ||||
| 			continue | ||||
| 		} | ||||
| 		sandboxes = append(sandboxes, sb) | ||||
| 	} | ||||
| 	return sandboxes | ||||
| } | ||||
|  | ||||
| // Delete deletes the sandbox with specified id. | ||||
| func (s *Store) Delete(id string) { | ||||
| 	s.lock.Lock() | ||||
| 	defer s.lock.Unlock() | ||||
| 	id, err := s.idIndex.Get(id) | ||||
| 	if err != nil { | ||||
| 		// Note: The idIndex.Delete and delete doesn't handle truncated index. | ||||
| 		// So we need to return if there are error. | ||||
| 		return | ||||
| 	} | ||||
| 	s.idIndex.Delete(id) // nolint: errcheck | ||||
| 	delete(s.sandboxes, id) | ||||
| } | ||||
							
								
								
									
										100
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/sandbox/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/sandbox/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| /* | ||||
| Copyright 2018 The Containerd 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 sandbox | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // State is the sandbox state we use in cri-containerd. | ||||
| // It has unknown state defined. | ||||
| type State uint32 | ||||
|  | ||||
| const ( | ||||
| 	// StateUnknown is unknown state of sandbox. Sandbox | ||||
| 	// is in unknown state before its corresponding sandbox container | ||||
| 	// is created. Sandbox in unknown state should be ignored by most | ||||
| 	// functions, unless the caller needs to update sandbox state. | ||||
| 	StateUnknown State = iota | ||||
| 	// StateReady is ready state, it means sandbox container | ||||
| 	// is running. | ||||
| 	StateReady | ||||
| 	// StateNotReady is notready state, it ONLY means sandbox | ||||
| 	// container is not running. | ||||
| 	// StopPodSandbox should still be called for NOTREADY sandbox to | ||||
| 	// cleanup resources other than sandbox container, e.g. network namespace. | ||||
| 	// This is an assumption made in CRI. | ||||
| 	StateNotReady | ||||
| ) | ||||
|  | ||||
| // Status is the status of a sandbox. | ||||
| type Status struct { | ||||
| 	// Pid is the init process id of the sandbox container. | ||||
| 	Pid uint32 | ||||
| 	// CreatedAt is the created timestamp. | ||||
| 	CreatedAt time.Time | ||||
| 	// State is the state of the sandbox. | ||||
| 	State State | ||||
| } | ||||
|  | ||||
| // UpdateFunc is function used to update the sandbox status. If there | ||||
| // is an error, the update will be rolled back. | ||||
| type UpdateFunc func(Status) (Status, error) | ||||
|  | ||||
| // StatusStorage manages the sandbox status. | ||||
| // The status storage for sandbox is different from container status storage, | ||||
| // because we don't checkpoint sandbox status. If we need checkpoint in the | ||||
| // future, we should combine this with container status storage. | ||||
| type StatusStorage interface { | ||||
| 	// Get a sandbox status. | ||||
| 	Get() Status | ||||
| 	// Update the sandbox status. Note that the update MUST be applied | ||||
| 	// in one transaction. | ||||
| 	Update(UpdateFunc) error | ||||
| } | ||||
|  | ||||
| // StoreStatus creates the storage containing the passed in sandbox status with the | ||||
| // specified id. | ||||
| // The status MUST be created in one transaction. | ||||
| func StoreStatus(status Status) StatusStorage { | ||||
| 	return &statusStorage{status: status} | ||||
| } | ||||
|  | ||||
| type statusStorage struct { | ||||
| 	sync.RWMutex | ||||
| 	status Status | ||||
| } | ||||
|  | ||||
| // Get a copy of sandbox status. | ||||
| func (s *statusStorage) Get() Status { | ||||
| 	s.RLock() | ||||
| 	defer s.RUnlock() | ||||
| 	return s.status | ||||
| } | ||||
|  | ||||
| // Update the sandbox status. | ||||
| func (s *statusStorage) Update(u UpdateFunc) error { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
| 	newStatus, err := u(s.status) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.status = newStatus | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										87
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/snapshot/snapshot.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/snapshot/snapshot.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 snapshot | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	snapshot "github.com/containerd/containerd/snapshots" | ||||
|  | ||||
| 	"github.com/containerd/cri-containerd/pkg/store" | ||||
| ) | ||||
|  | ||||
| // Snapshot contains the information about the snapshot. | ||||
| type Snapshot struct { | ||||
| 	// Key is the key of the snapshot | ||||
| 	Key string | ||||
| 	// Kind is the kind of the snapshot (active, commited, view) | ||||
| 	Kind snapshot.Kind | ||||
| 	// Size is the size of the snapshot in bytes. | ||||
| 	Size uint64 | ||||
| 	// Inodes is the number of inodes used by the snapshot | ||||
| 	Inodes uint64 | ||||
| 	// Timestamp is latest update time (in nanoseconds) of the snapshot | ||||
| 	// information. | ||||
| 	Timestamp int64 | ||||
| } | ||||
|  | ||||
| // Store stores all snapshots. | ||||
| type Store struct { | ||||
| 	lock      sync.RWMutex | ||||
| 	snapshots map[string]Snapshot | ||||
| } | ||||
|  | ||||
| // NewStore creates a snapshot store. | ||||
| func NewStore() *Store { | ||||
| 	return &Store{snapshots: make(map[string]Snapshot)} | ||||
| } | ||||
|  | ||||
| // Add a snapshot into the store. | ||||
| func (s *Store) Add(snapshot Snapshot) { | ||||
| 	s.lock.Lock() | ||||
| 	defer s.lock.Unlock() | ||||
| 	s.snapshots[snapshot.Key] = snapshot | ||||
| } | ||||
|  | ||||
| // Get returns the snapshot with specified key. Returns store.ErrNotExist if the | ||||
| // snapshot doesn't exist. | ||||
| func (s *Store) Get(key string) (Snapshot, error) { | ||||
| 	s.lock.RLock() | ||||
| 	defer s.lock.RUnlock() | ||||
| 	if sn, ok := s.snapshots[key]; ok { | ||||
| 		return sn, nil | ||||
| 	} | ||||
| 	return Snapshot{}, store.ErrNotExist | ||||
| } | ||||
|  | ||||
| // List lists all snapshots. | ||||
| func (s *Store) List() []Snapshot { | ||||
| 	s.lock.RLock() | ||||
| 	defer s.lock.RUnlock() | ||||
| 	var snapshots []Snapshot | ||||
| 	for _, sn := range s.snapshots { | ||||
| 		snapshots = append(snapshots, sn) | ||||
| 	} | ||||
| 	return snapshots | ||||
| } | ||||
|  | ||||
| // Delete deletes the snapshot with specified key. | ||||
| func (s *Store) Delete(key string) { | ||||
| 	s.lock.Lock() | ||||
| 	defer s.lock.Unlock() | ||||
| 	delete(s.snapshots, key) | ||||
| } | ||||
							
								
								
									
										30
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/containerd/cri-containerd/pkg/store/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 store | ||||
|  | ||||
| // StopCh is used to propagate the stop information of a container. | ||||
| type StopCh chan struct{} | ||||
|  | ||||
| // Stop close stopCh of the container. | ||||
| func (ch StopCh) Stop() { | ||||
| 	close(ch) | ||||
| } | ||||
|  | ||||
| // Stopped return the stopCh of the container as a readonly channel. | ||||
| func (ch StopCh) Stopped() <-chan struct{} { | ||||
| 	return ch | ||||
| } | ||||
							
								
								
									
										41
									
								
								vendor/github.com/containerd/cri-containerd/pkg/util/deep_copy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/containerd/cri-containerd/pkg/util/deep_copy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 util | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // DeepCopy makes a deep copy from src into dst. | ||||
| func DeepCopy(dst interface{}, src interface{}) error { | ||||
| 	if dst == nil { | ||||
| 		return fmt.Errorf("dst cannot be nil") | ||||
| 	} | ||||
| 	if src == nil { | ||||
| 		return fmt.Errorf("src cannot be nil") | ||||
| 	} | ||||
| 	bytes, err := json.Marshal(src) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to marshal src: %s", err) | ||||
| 	} | ||||
| 	err = json.Unmarshal(bytes, dst) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to unmarshal into dst: %s", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										24
									
								
								vendor/github.com/containerd/cri-containerd/pkg/util/id.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/containerd/cri-containerd/pkg/util/id.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 util | ||||
|  | ||||
| import "github.com/docker/docker/pkg/stringid" | ||||
|  | ||||
| // GenerateID generates a random unique id. | ||||
| func GenerateID() string { | ||||
| 	return stringid.GenerateNonCryptoID() | ||||
| } | ||||
							
								
								
									
										50
									
								
								vendor/github.com/containerd/cri-containerd/pkg/util/image.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/containerd/cri-containerd/pkg/util/image.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 util | ||||
|  | ||||
| import ( | ||||
| 	"github.com/docker/distribution/reference" | ||||
| ) | ||||
|  | ||||
| // NormalizeImageRef normalizes the image reference following the docker convention. This is added | ||||
| // mainly for backward compatibility. | ||||
| // The reference returned can only be either tagged or digested. For reference contains both tag | ||||
| // and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@ | ||||
| // sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as | ||||
| // docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa. | ||||
| func NormalizeImageRef(ref string) (reference.Named, error) { | ||||
| 	named, err := reference.ParseNormalizedNamed(ref) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if _, ok := named.(reference.NamedTagged); ok { | ||||
| 		if canonical, ok := named.(reference.Canonical); ok { | ||||
| 			// The reference is both tagged and digested, only | ||||
| 			// return digested. | ||||
| 			newNamed, err := reference.WithName(canonical.Name()) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			newCanonical, err := reference.WithDigest(newNamed, canonical.Digest()) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			return newCanonical, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return reference.TagNameOnly(named), nil | ||||
| } | ||||
							
								
								
									
										43
									
								
								vendor/github.com/containerd/cri-containerd/pkg/util/strings.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/containerd/cri-containerd/pkg/util/strings.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 util | ||||
|  | ||||
| import "strings" | ||||
|  | ||||
| // InStringSlice checks whether a string is inside a string slice. | ||||
| // Comparison is case insensitive. | ||||
| func InStringSlice(ss []string, str string) bool { | ||||
| 	for _, s := range ss { | ||||
| 		if strings.ToLower(s) == strings.ToLower(str) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // SubtractStringSlice subtracts string from string slice. | ||||
| // Comparison is case insensitive. | ||||
| func SubtractStringSlice(ss []string, str string) []string { | ||||
| 	var res []string | ||||
| 	for _, s := range ss { | ||||
| 		if strings.ToLower(s) == strings.ToLower(str) { | ||||
| 			continue | ||||
| 		} | ||||
| 		res = append(res, s) | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
							
								
								
									
										43
									
								
								vendor/github.com/containerd/cri-containerd/pkg/version/version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/containerd/cri-containerd/pkg/version/version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes 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 ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/blang/semver" | ||||
| ) | ||||
|  | ||||
| // CRIContainerdVersion is the version of the cri-containerd. | ||||
| var CRIContainerdVersion = "UNKNOWN" | ||||
|  | ||||
| func validateSemver(sv string) error { | ||||
| 	_, err := semver.Parse(sv) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("couldn't parse cri-containerd version %q: %v", sv, err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // PrintVersion outputs the release version of cri-containerd | ||||
| func PrintVersion() { | ||||
| 	err := validateSemver(CRIContainerdVersion) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 	fmt.Println(CRIContainerdVersion) | ||||
| } | ||||
							
								
								
									
										62
									
								
								vendor/github.com/containerd/cri-containerd/vendor.conf
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/containerd/cri-containerd/vendor.conf
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| github.com/blang/semver v3.1.0 | ||||
| github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 | ||||
| github.com/containerd/cgroups c0710c92e8b3a44681d1321dcfd1360fc5c6c089 | ||||
| github.com/containerd/containerd ee6ffdd91e8fd9ec2b9684fd385e81a16c40e99c | ||||
| github.com/containerd/continuity 1a794c0014a7b786879312d1dab309ba96ead8bc | ||||
| github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 | ||||
| github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 | ||||
| github.com/containernetworking/cni v0.6.0 | ||||
| github.com/containernetworking/plugins v0.6.0 | ||||
| github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 | ||||
| github.com/cri-o/ocicni 9b451e26eb7c694d564991fbf44f77d0afb9b03c | ||||
| github.com/davecgh/go-spew v1.1.0 | ||||
| github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 | ||||
| github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 | ||||
| github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 | ||||
| github.com/docker/go-units v0.3.1 | ||||
| github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 | ||||
| github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46 | ||||
| github.com/fsnotify/fsnotify 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1 | ||||
| github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee | ||||
| github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f | ||||
| github.com/gogo/protobuf v0.5 | ||||
| github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed | ||||
| github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9 | ||||
| github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c | ||||
| github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 | ||||
| github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f | ||||
| github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 | ||||
| github.com/json-iterator/go 1.0.4 | ||||
| github.com/Microsoft/go-winio v0.4.5 | ||||
| github.com/Microsoft/hcsshim v0.6.7 | ||||
| github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448 | ||||
| github.com/opencontainers/image-spec v1.0.1 | ||||
| github.com/opencontainers/runc a618ab5a0186905949ee463dbb762c3d23e12a80 | ||||
| github.com/opencontainers/runtime-spec v1.0.1 | ||||
| github.com/opencontainers/runtime-tools 6073aff4ac61897f75895123f7e24135204a404d | ||||
| github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206 | ||||
| github.com/pkg/errors v0.8.0 | ||||
| github.com/pmezard/go-difflib v1.0.0 | ||||
| github.com/renstrom/dedent 020d11c3b9c0c7a3c2efcc8e5cf5b9ef7bcea21f | ||||
| github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 | ||||
| github.com/sirupsen/logrus v1.0.0 | ||||
| github.com/spf13/cobra v0.0.1 | ||||
| github.com/spf13/pflag v1.0.0 | ||||
| github.com/stretchr/testify v1.1.4 | ||||
| github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 | ||||
| github.com/tchap/go-patricia 5ad6cdb7538b0097d5598c7e57f0a24072adf7dc | ||||
| golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 | ||||
| golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c | ||||
| golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys | ||||
| golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 | ||||
| golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 | ||||
| google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 | ||||
| google.golang.org/grpc v1.7.4 | ||||
| gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 | ||||
| gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 | ||||
| k8s.io/api a1d6dce6736a6c75929bb75111e89077e35a5856 | ||||
| k8s.io/apimachinery 8259d997cf059cd83dc47e5f8074b7a7d7967c09 | ||||
| k8s.io/apiserver 8e45eac9dff86447a5c2effe6a3d2cba70121ebf | ||||
| k8s.io/client-go 33bd23f75b6de861994706a322b0afab824b2171 | ||||
| k8s.io/kubernetes 05944b1d2ca7f60b09762a330425108f48f6b603 | ||||
| k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e | ||||
							
								
								
									
										202
									
								
								vendor/github.com/containernetworking/cni/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/containernetworking/cni/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
|                                  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. | ||||
|  | ||||
							
								
								
									
										191
									
								
								vendor/github.com/containernetworking/cni/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								vendor/github.com/containernetworking/cni/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| [](https://travis-ci.org/containernetworking/cni) | ||||
| [](https://coveralls.io/github/containernetworking/cni?branch=master) | ||||
| [](https://cryptic-tundra-43194.herokuapp.com/) | ||||
|  | ||||
|  | ||||
|  | ||||
| --- | ||||
|  | ||||
| # Community Sync Meeting | ||||
|  | ||||
| There is a community sync meeting for users and developers every 1-2 months. The next meeting will help on a Google Hangout and the link is in the [agenda](https://docs.google.com/document/d/10ECyT2mBGewsJUcmYmS8QNo1AcNgy2ZIe2xS7lShYhE/edit?usp=sharing) (Notes from previous meeting are also in this doc). The next meeting will be held on *Wednesday, June 21th* at *3:00pm UTC* [Add to Calendar]https://www.worldtimebuddy.com/?qm=1&lid=100,5,2643743,5391959&h=100&date=2017-6-21&sln=15-16). | ||||
|  | ||||
| --- | ||||
|  | ||||
| # CNI - the Container Network Interface | ||||
|  | ||||
| ## What is CNI? | ||||
|  | ||||
| CNI (_Container Network Interface_), a [Cloud Native Computing Foundation](https://cncf.io) project, consists of a specification and libraries for writing plugins to configure network interfaces in Linux containers, along with a number of supported plugins. | ||||
| CNI concerns itself only with network connectivity of containers and removing allocated resources when the container is deleted. | ||||
| Because of this focus, CNI has a wide range of support and the specification is simple to implement. | ||||
|  | ||||
| As well as the [specification](SPEC.md), this repository contains the Go source code of a [library for integrating CNI into applications](libcni) and an [example command-line tool](cnitool) for executing CNI plugins.  A [separate repository contains reference plugins](https://github.com/containernetworking/plugins) and a template for making new plugins. | ||||
|  | ||||
| The template code makes it straight-forward to create a CNI plugin for an existing container networking project. | ||||
| CNI also makes a good framework for creating a new container networking project from scratch. | ||||
|  | ||||
| ## Why develop CNI? | ||||
|  | ||||
| Application containers on Linux are a rapidly evolving area, and within this area networking is not well addressed as it is highly environment-specific. | ||||
| We believe that many container runtimes and orchestrators will seek to solve the same problem of making the network layer pluggable. | ||||
|  | ||||
| To avoid duplication, we think it is prudent to define a common interface between the network plugins and container execution: hence we put forward this specification, along with libraries for Go and a set of plugins. | ||||
|  | ||||
| ## Who is using CNI? | ||||
| ### Container runtimes | ||||
| - [rkt - container engine](https://coreos.com/blog/rkt-cni-networking.html) | ||||
| - [Kurma - container runtime](http://kurma.io/) | ||||
| - [Kubernetes - a system to simplify container operations](http://kubernetes.io/docs/admin/network-plugins/) | ||||
| - [OpenShift - Kubernetes with additional enterprise features](https://github.com/openshift/origin/blob/master/docs/openshift_networking_requirements.md) | ||||
| - [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/cf-networking-release) | ||||
| - [Mesos - a distributed systems kernel](https://github.com/apache/mesos/blob/master/docs/cni.md) | ||||
|  | ||||
| ### 3rd party plugins | ||||
| - [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico-cni) | ||||
| - [Weave - a multi-host Docker network](https://github.com/weaveworks/weave) | ||||
| - [Contiv Networking - policy networking for various use cases](https://github.com/contiv/netplugin) | ||||
| - [SR-IOV](https://github.com/hustcat/sriov-cni) | ||||
| - [Cilium - BPF & XDP for containers](https://github.com/cilium/cilium) | ||||
| - [Infoblox - enterprise IP address management for containers](https://github.com/infobloxopen/cni-infoblox) | ||||
| - [Multus - a Multi plugin](https://github.com/Intel-Corp/multus-cni) | ||||
| - [Romana - Layer 3 CNI plugin supporting network policy for Kubernetes](https://github.com/romana/kube) | ||||
| - [CNI-Genie - generic CNI network plugin](https://github.com/Huawei-PaaS/CNI-Genie) | ||||
| - [Nuage CNI - Nuage Networks SDN plugin for network policy kubernetes support ](https://github.com/nuagenetworks/nuage-cni) | ||||
| - [Silk - a CNI plugin designed for Cloud Foundry](https://github.com/cloudfoundry-incubator/silk) | ||||
| - [Linen - a CNI plugin designed for overlay networks with Open vSwitch and fit in SDN/OpenFlow network environment](https://github.com/John-Lin/linen-cni) | ||||
|  | ||||
| The CNI team also maintains some [core plugins in a separate repository](https://github.com/containernetworking/plugins). | ||||
|  | ||||
|  | ||||
| ## Contributing to CNI | ||||
|  | ||||
| We welcome contributions, including [bug reports](https://github.com/containernetworking/cni/issues), and code and documentation improvements. | ||||
| If you intend to contribute to code or documentation, please read [CONTRIBUTING.md](CONTRIBUTING.md). Also see the [contact section](#contact) in this README. | ||||
|  | ||||
| ## How do I use CNI? | ||||
|  | ||||
| ### Requirements | ||||
|  | ||||
| The CNI spec is language agnostic.  To use the Go language libraries in this repository, you'll need a recent version of Go.  Our [automated tests](https://travis-ci.org/containernetworking/cni/builds) cover Go versions 1.7 and 1.8. | ||||
|  | ||||
| ### Reference Plugins | ||||
|  | ||||
| The CNI project maintains a set of [reference plugins](https://github.com/containernetworking/plugins) that implement the CNI specification. | ||||
| NOTE: the reference plugins used to live in this repository but have been split out into a [separate repository](https://github.com/containernetworking/plugins) as of May 2017. | ||||
|  | ||||
| ### Running the plugins | ||||
|  | ||||
| After building and installing the [reference plugins](https://github.com/containernetworking/plugins), you can use the `priv-net-run.sh` and `docker-run.sh` scripts in the `scripts/` directory to exercise the plugins. | ||||
|  | ||||
| **note - priv-net-run.sh depends on `jq`** | ||||
|  | ||||
| Start out by creating a netconf file to describe a network: | ||||
|  | ||||
| ```bash | ||||
| $ mkdir -p /etc/cni/net.d | ||||
| $ cat >/etc/cni/net.d/10-mynet.conf <<EOF | ||||
| { | ||||
| 	"cniVersion": "0.2.0", | ||||
| 	"name": "mynet", | ||||
| 	"type": "bridge", | ||||
| 	"bridge": "cni0", | ||||
| 	"isGateway": true, | ||||
| 	"ipMasq": true, | ||||
| 	"ipam": { | ||||
| 		"type": "host-local", | ||||
| 		"subnet": "10.22.0.0/16", | ||||
| 		"routes": [ | ||||
| 			{ "dst": "0.0.0.0/0" } | ||||
| 		] | ||||
| 	} | ||||
| } | ||||
| EOF | ||||
| $ cat >/etc/cni/net.d/99-loopback.conf <<EOF | ||||
| { | ||||
| 	"cniVersion": "0.2.0", | ||||
| 	"type": "loopback" | ||||
| } | ||||
| EOF | ||||
| ``` | ||||
|  | ||||
| The directory `/etc/cni/net.d` is the default location in which the scripts will look for net configurations. | ||||
|  | ||||
| Next, build the plugins: | ||||
|  | ||||
| ```bash | ||||
| $ cd $GOPATH/src/github.com/containernetworking/plugins | ||||
| $ ./build.sh | ||||
| ``` | ||||
|  | ||||
| Finally, execute a command (`ifconfig` in this example) in a private network namespace that has joined the `mynet` network: | ||||
|  | ||||
| ```bash | ||||
| $ CNI_PATH=$GOPATH/src/github.com/containernetworking/plugins/bin | ||||
| $ cd $GOPATH/src/github.com/containernetworking/cni/scripts | ||||
| $ sudo CNI_PATH=$CNI_PATH ./priv-net-run.sh ifconfig | ||||
| eth0      Link encap:Ethernet  HWaddr f2:c2:6f:54:b8:2b   | ||||
|           inet addr:10.22.0.2  Bcast:0.0.0.0  Mask:255.255.0.0 | ||||
|           inet6 addr: fe80::f0c2:6fff:fe54:b82b/64 Scope:Link | ||||
|           UP BROADCAST MULTICAST  MTU:1500  Metric:1 | ||||
|           RX packets:1 errors:0 dropped:0 overruns:0 frame:0 | ||||
|           TX packets:0 errors:0 dropped:1 overruns:0 carrier:0 | ||||
|           collisions:0 txqueuelen:0 | ||||
|           RX bytes:90 (90.0 B)  TX bytes:0 (0.0 B) | ||||
|  | ||||
| lo        Link encap:Local Loopback   | ||||
|           inet addr:127.0.0.1  Mask:255.0.0.0 | ||||
|           inet6 addr: ::1/128 Scope:Host | ||||
|           UP LOOPBACK RUNNING  MTU:65536  Metric:1 | ||||
|           RX packets:0 errors:0 dropped:0 overruns:0 frame:0 | ||||
|           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 | ||||
|           collisions:0 txqueuelen:0 | ||||
|           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B) | ||||
| ``` | ||||
|  | ||||
| The environment variable `CNI_PATH` tells the scripts and library where to look for plugin executables. | ||||
|  | ||||
| ## Running a Docker container with network namespace set up by CNI plugins | ||||
|  | ||||
| Use the instructions in the previous section to define a netconf and build the plugins. | ||||
| Next, docker-run.sh script wraps `docker run`, to execute the plugins prior to entering the container: | ||||
|  | ||||
| ```bash | ||||
| $ CNI_PATH=$GOPATH/src/github.com/containernetworking/plugins/bin | ||||
| $ cd $GOPATH/src/github.com/containernetworking/cni/scripts | ||||
| $ sudo CNI_PATH=$CNI_PATH ./docker-run.sh --rm busybox:latest ifconfig | ||||
| eth0      Link encap:Ethernet  HWaddr fa:60:70:aa:07:d1   | ||||
|           inet addr:10.22.0.2  Bcast:0.0.0.0  Mask:255.255.0.0 | ||||
|           inet6 addr: fe80::f860:70ff:feaa:7d1/64 Scope:Link | ||||
|           UP BROADCAST MULTICAST  MTU:1500  Metric:1 | ||||
|           RX packets:1 errors:0 dropped:0 overruns:0 frame:0 | ||||
|           TX packets:0 errors:0 dropped:1 overruns:0 carrier:0 | ||||
|           collisions:0 txqueuelen:0 | ||||
|           RX bytes:90 (90.0 B)  TX bytes:0 (0.0 B) | ||||
|  | ||||
| lo        Link encap:Local Loopback   | ||||
|           inet addr:127.0.0.1  Mask:255.0.0.0 | ||||
|           inet6 addr: ::1/128 Scope:Host | ||||
|           UP LOOPBACK RUNNING  MTU:65536  Metric:1 | ||||
|           RX packets:0 errors:0 dropped:0 overruns:0 frame:0 | ||||
|           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 | ||||
|           collisions:0 txqueuelen:0 | ||||
|           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B) | ||||
| ``` | ||||
|  | ||||
| ## What might CNI do in the future? | ||||
|  | ||||
| CNI currently covers a wide range of needs for network configuration due to it simple model and API. | ||||
| However, in the future CNI might want to branch out into other directions: | ||||
|  | ||||
| - Dynamic updates to existing network configuration | ||||
| - Dynamic policies for network bandwidth and firewall rules | ||||
|  | ||||
| If these topics of are interest, please contact the team via the mailing list or IRC and find some like-minded people in the community to put a proposal together. | ||||
|  | ||||
| ## Contact | ||||
|  | ||||
| For any questions about CNI, please reach out on the mailing list: | ||||
| - Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev) | ||||
| - IRC: #[containernetworking](irc://irc.freenode.org:6667/#containernetworking) channel on freenode.org | ||||
| - Slack: [containernetworking.slack.com](https://cryptic-tundra-43194.herokuapp.com) | ||||
							
								
								
									
										219
									
								
								vendor/github.com/containernetworking/cni/libcni/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								vendor/github.com/containernetworking/cni/libcni/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | ||||
| // 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 ( | ||||
| 	"os" | ||||
| 	"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 | ||||
| 	// A dictionary of capability-specific data passed by the runtime | ||||
| 	// to plugins as top-level keys in the 'runtimeConfig' dictionary | ||||
| 	// of the plugin's stdin data.  libcni will ensure that only keys | ||||
| 	// in this map which match the capabilities of the plugin are passed | ||||
| 	// to the plugin | ||||
| 	CapabilityArgs map[string]interface{} | ||||
| } | ||||
|  | ||||
| type NetworkConfig struct { | ||||
| 	Network *types.NetConf | ||||
| 	Bytes   []byte | ||||
| } | ||||
|  | ||||
| type NetworkConfigList struct { | ||||
| 	Name       string | ||||
| 	CNIVersion string | ||||
| 	Plugins    []*NetworkConfig | ||||
| 	Bytes      []byte | ||||
| } | ||||
|  | ||||
| type CNI interface { | ||||
| 	AddNetworkList(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) | ||||
| 	DelNetworkList(net *NetworkConfigList, rt *RuntimeConf) error | ||||
|  | ||||
| 	AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) | ||||
| 	DelNetwork(net *NetworkConfig, rt *RuntimeConf) error | ||||
| } | ||||
|  | ||||
| type CNIConfig struct { | ||||
| 	Path []string | ||||
| } | ||||
|  | ||||
| // CNIConfig implements the CNI interface | ||||
| var _ CNI = &CNIConfig{} | ||||
|  | ||||
| func buildOneConfig(list *NetworkConfigList, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) { | ||||
| 	var err error | ||||
|  | ||||
| 	inject := map[string]interface{}{ | ||||
| 		"name":       list.Name, | ||||
| 		"cniVersion": list.CNIVersion, | ||||
| 	} | ||||
| 	// Add previous plugin result | ||||
| 	if prevResult != nil { | ||||
| 		inject["prevResult"] = prevResult | ||||
| 	} | ||||
|  | ||||
| 	// Ensure every config uses the same name and version | ||||
| 	orig, err = InjectConf(orig, inject) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return injectRuntimeConfig(orig, rt) | ||||
| } | ||||
|  | ||||
| // This function takes a libcni RuntimeConf structure and injects values into | ||||
| // a "runtimeConfig" dictionary in the CNI network configuration JSON that | ||||
| // will be passed to the plugin on stdin. | ||||
| // | ||||
| // Only "capabilities arguments" passed by the runtime are currently injected. | ||||
| // These capabilities arguments are filtered through the plugin's advertised | ||||
| // capabilities from its config JSON, and any keys in the CapabilityArgs | ||||
| // matching plugin capabilities are added to the "runtimeConfig" dictionary | ||||
| // sent to the plugin via JSON on stdin.  For exmaple, if the plugin's | ||||
| // capabilities include "portMappings", and the CapabilityArgs map includes a | ||||
| // "portMappings" key, that key and its value are added to the "runtimeConfig" | ||||
| // dictionary to be passed to the plugin's stdin. | ||||
| func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig, error) { | ||||
| 	var err error | ||||
|  | ||||
| 	rc := make(map[string]interface{}) | ||||
| 	for capability, supported := range orig.Network.Capabilities { | ||||
| 		if !supported { | ||||
| 			continue | ||||
| 		} | ||||
| 		if data, ok := rt.CapabilityArgs[capability]; ok { | ||||
| 			rc[capability] = data | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(rc) > 0 { | ||||
| 		orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc}) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return orig, nil | ||||
| } | ||||
|  | ||||
| // AddNetworkList executes a sequence of plugins with the ADD command | ||||
| func (c *CNIConfig) AddNetworkList(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) { | ||||
| 	var prevResult types.Result | ||||
| 	for _, net := range list.Plugins { | ||||
| 		pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		newConf, err := buildOneConfig(list, net, prevResult, rt) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		prevResult, err = invoke.ExecPluginWithResult(pluginPath, newConf.Bytes, c.args("ADD", rt)) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return prevResult, nil | ||||
| } | ||||
|  | ||||
| // DelNetworkList executes a sequence of plugins with the DEL command | ||||
| func (c *CNIConfig) DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) error { | ||||
| 	for i := len(list.Plugins) - 1; i >= 0; i-- { | ||||
| 		net := list.Plugins[i] | ||||
|  | ||||
| 		pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		newConf, err := buildOneConfig(list, net, nil, rt) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if err := invoke.ExecPluginWithoutResult(pluginPath, newConf.Bytes, c.args("DEL", rt)); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| 	} | ||||
|  | ||||
| 	net, err = injectRuntimeConfig(net, rt) | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	net, err = injectRuntimeConfig(net, rt) | ||||
| 	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, string(os.PathListSeparator)), | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										256
									
								
								vendor/github.com/containernetworking/cni/libcni/conf.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								vendor/github.com/containernetworking/cni/libcni/conf.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | ||||
| // 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" | ||||
| ) | ||||
|  | ||||
| type NotFoundError struct { | ||||
| 	Dir  string | ||||
| 	Name string | ||||
| } | ||||
|  | ||||
| func (e NotFoundError) Error() string { | ||||
| 	return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir) | ||||
| } | ||||
|  | ||||
| type NoConfigsFoundError struct { | ||||
| 	Dir string | ||||
| } | ||||
|  | ||||
| func (e NoConfigsFoundError) Error() string { | ||||
| 	return fmt.Sprintf(`no net configurations found in %s`, e.Dir) | ||||
| } | ||||
|  | ||||
| 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 ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) { | ||||
| 	rawList := make(map[string]interface{}) | ||||
| 	if err := json.Unmarshal(bytes, &rawList); err != nil { | ||||
| 		return nil, fmt.Errorf("error parsing configuration list: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	rawName, ok := rawList["name"] | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("error parsing configuration list: no name") | ||||
| 	} | ||||
| 	name, ok := rawName.(string) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("error parsing configuration list: invalid name type %T", rawName) | ||||
| 	} | ||||
|  | ||||
| 	var cniVersion string | ||||
| 	rawVersion, ok := rawList["cniVersion"] | ||||
| 	if ok { | ||||
| 		cniVersion, ok = rawVersion.(string) | ||||
| 		if !ok { | ||||
| 			return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion type %T", rawVersion) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	list := &NetworkConfigList{ | ||||
| 		Name:       name, | ||||
| 		CNIVersion: cniVersion, | ||||
| 		Bytes:      bytes, | ||||
| 	} | ||||
|  | ||||
| 	var plugins []interface{} | ||||
| 	plug, ok := rawList["plugins"] | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("error parsing configuration list: no 'plugins' key") | ||||
| 	} | ||||
| 	plugins, ok = plug.([]interface{}) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug) | ||||
| 	} | ||||
| 	if len(plugins) == 0 { | ||||
| 		return nil, fmt.Errorf("error parsing configuration list: no plugins in list") | ||||
| 	} | ||||
|  | ||||
| 	for i, conf := range plugins { | ||||
| 		newBytes, err := json.Marshal(conf) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("Failed to marshal plugin config %d: %v", i, err) | ||||
| 		} | ||||
| 		netConf, err := ConfFromBytes(newBytes) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("Failed to parse plugin config %d: %v", i, err) | ||||
| 		} | ||||
| 		list.Plugins = append(list.Plugins, netConf) | ||||
| 	} | ||||
|  | ||||
| 	return list, nil | ||||
| } | ||||
|  | ||||
| func ConfListFromFile(filename string) (*NetworkConfigList, error) { | ||||
| 	bytes, err := ioutil.ReadFile(filename) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error reading %s: %s", filename, err) | ||||
| 	} | ||||
| 	return ConfListFromBytes(bytes) | ||||
| } | ||||
|  | ||||
| func ConfFiles(dir string, extensions []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 | ||||
| 		} | ||||
| 		fileExt := filepath.Ext(f.Name()) | ||||
| 		for _, ext := range extensions { | ||||
| 			if fileExt == ext { | ||||
| 				confFiles = append(confFiles, filepath.Join(dir, f.Name())) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return confFiles, nil | ||||
| } | ||||
|  | ||||
| func LoadConf(dir, name string) (*NetworkConfig, error) { | ||||
| 	files, err := ConfFiles(dir, []string{".conf", ".json"}) | ||||
| 	switch { | ||||
| 	case err != nil: | ||||
| 		return nil, err | ||||
| 	case len(files) == 0: | ||||
| 		return nil, NoConfigsFoundError{Dir: dir} | ||||
| 	} | ||||
| 	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, NotFoundError{dir, name} | ||||
| } | ||||
|  | ||||
| func LoadConfList(dir, name string) (*NetworkConfigList, error) { | ||||
| 	files, err := ConfFiles(dir, []string{".conflist"}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	sort.Strings(files) | ||||
|  | ||||
| 	for _, confFile := range files { | ||||
| 		conf, err := ConfListFromFile(confFile) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if conf.Name == name { | ||||
| 			return conf, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Try and load a network configuration file (instead of list) | ||||
| 	// from the same name, then upconvert. | ||||
| 	singleConf, err := LoadConf(dir, name) | ||||
| 	if err != nil { | ||||
| 		// A little extra logic so the error makes sense | ||||
| 		if _, ok := err.(NoConfigsFoundError); len(files) != 0 && ok { | ||||
| 			// Config lists found but no config files found | ||||
| 			return nil, NotFoundError{dir, name} | ||||
| 		} | ||||
|  | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return ConfListFromConf(singleConf) | ||||
| } | ||||
|  | ||||
| func InjectConf(original *NetworkConfig, newValues map[string]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) | ||||
| 	} | ||||
|  | ||||
| 	for key, value := range newValues { | ||||
| 		if key == "" { | ||||
| 			return nil, fmt.Errorf("keys cannot be empty") | ||||
| 		} | ||||
|  | ||||
| 		if value == nil { | ||||
| 			return nil, fmt.Errorf("key '%s' value must not be nil", key) | ||||
| 		} | ||||
|  | ||||
| 		config[key] = value | ||||
| 	} | ||||
|  | ||||
| 	newBytes, err := json.Marshal(config) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return ConfFromBytes(newBytes) | ||||
| } | ||||
|  | ||||
| // ConfListFromConf "upconverts" a network config in to a NetworkConfigList, | ||||
| // with the single network as the only entry in the list. | ||||
| func ConfListFromConf(original *NetworkConfig) (*NetworkConfigList, error) { | ||||
| 	// Re-deserialize the config's json, then make a raw map configlist. | ||||
| 	// This may seem a bit strange, but it's to make the Bytes fields | ||||
| 	// actually make sense. Otherwise, the generated json is littered with | ||||
| 	// golang default values. | ||||
|  | ||||
| 	rawConfig := make(map[string]interface{}) | ||||
| 	if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	rawConfigList := map[string]interface{}{ | ||||
| 		"name":       original.Network.Name, | ||||
| 		"cniVersion": original.Network.CNIVersion, | ||||
| 		"plugins":    []interface{}{rawConfig}, | ||||
| 	} | ||||
|  | ||||
| 	b, err := json.Marshal(rawConfigList) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return ConfListFromBytes(b) | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Derek McGowan
					Derek McGowan