Use local version of cri packages
Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
201
vendor/github.com/containerd/cri/LICENSE
generated
vendored
201
vendor/github.com/containerd/cri/LICENSE
generated
vendored
@@ -1,201 +0,0 @@
|
||||
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.
|
||||
189
vendor/github.com/containerd/cri/README.md
generated
vendored
189
vendor/github.com/containerd/cri/README.md
generated
vendored
@@ -1,189 +0,0 @@
|
||||
# cri
|
||||
<p align="center">
|
||||
<img src="https://kubernetes.io/images/favicon.png" width="50" height="50">
|
||||
<img src="https://containerd.io/img/logos/icon/black/containerd-icon-black.png" width="50" >
|
||||
</p>
|
||||
|
||||
*Note: The standalone `cri-containerd` binary is end-of-life. `cri-containerd` is
|
||||
transitioning from a standalone binary that talks to containerd to a plugin within
|
||||
containerd. This github branch is for the `cri` plugin. See
|
||||
[standalone-cri-containerd branch](https://github.com/containerd/cri/tree/standalone-cri-containerd)
|
||||
for information about the standalone version of `cri-containerd`.*
|
||||
|
||||
*Note: You need to [drain your node](https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/) before upgrading from standalone `cri-containerd` to containerd with `cri` plugin.*
|
||||
|
||||
[](https://travis-ci.org/containerd/cri)
|
||||
[](https://goreportcard.com/report/github.com/containerd/cri)
|
||||
|
||||
`cri` is a [containerd](https://containerd.io/) plugin implementation of Kubernetes [container runtime interface (CRI)](https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1alpha2/api.proto).
|
||||
|
||||
With it, you could run Kubernetes using containerd as the container runtime.
|
||||

|
||||
## Current Status
|
||||
`cri` is a native plugin of containerd 1.1 and above. It is built into containerd and enabled by default.
|
||||
|
||||
`cri` is in GA:
|
||||
* It is feature complete.
|
||||
* It (the GA version) works with Kubernetes 1.10 and above.
|
||||
* It has passed all [CRI validation tests](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/cri-validation.md).
|
||||
* It has passed all [node e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/e2e-node-tests.md).
|
||||
* It has passed all [e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-testing/e2e-tests.md).
|
||||
|
||||
See [test dashboard](https://k8s-testgrid.appspot.com/sig-node-containerd)
|
||||
## Support Metrics
|
||||
| CRI-Containerd Version | Containerd Version | Kubernetes Version | CRI Version |
|
||||
|:----------------------:|:------------------:|:------------------:|:-----------:|
|
||||
| v1.0.0-alpha.x | | 1.7, 1.8 | v1alpha1 |
|
||||
| v1.0.0-beta.x | | 1.9 | v1alpha1 |
|
||||
| End-Of-Life | v1.1 (End-Of-Life) | 1.10+ | v1alpha2 |
|
||||
| | v1.2 (Extended) | 1.10+ | v1alpha2 |
|
||||
| | v1.3 | 1.12+ | v1alpha2 |
|
||||
| | v1.4 | 1.19+ (rc) | v1alpha2 |
|
||||
|
||||
**Note:** The support table above specifies the Kubernetes Version that was supported at time of release of the containerd - cri integration.
|
||||
|
||||
The following is the current support table for containerd CRI integration taking into account that Kubernetes only supports n-3 minor release versions.
|
||||
|
||||
| Containerd Version | Kubernetes Version | CRI Version |
|
||||
|:------------------:|:------------------:|:-----------:|
|
||||
| v1.2 | 1.15+ | v1alpha2 |
|
||||
| v1.3 | 1.15+ | v1alpha2 |
|
||||
| v1.4 | 1.19+ (rc) | v1alpha2 |
|
||||
|
||||
## 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 the `cri` plugin has the following dependencies:
|
||||
* [containerd](https://github.com/containerd/containerd)
|
||||
* [runc](https://github.com/opencontainers/runc)
|
||||
* [CNI](https://github.com/containernetworking/cni)
|
||||
|
||||
See [versions](./vendor.conf) of these dependencies `cri` is tested with.
|
||||
|
||||
As containerd and runc move to their respective general availability releases,
|
||||
we will do our best to rebase/retest `cri` with these releases on a
|
||||
weekly/monthly basis. Similarly, given that `cri` 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` to the latest releases of these
|
||||
specifications as appropriate.
|
||||
### Install Dependencies
|
||||
1. Install development libraries:
|
||||
* **libseccomp development library.** Required by `cri` 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.
|
||||
* **btrfs development library.** Required by containerd btrfs support. `btrfs-tools`(Ubuntu, Debian) / `btrfs-progs-devel`(Fedora, CentOS, RHEL)
|
||||
2. Install **`pkg-config`** (required for linking with `libseccomp`).
|
||||
3. Install and setup a Go 1.13.15 development environment.
|
||||
4. Make a local clone of this repository.
|
||||
5. Install binary dependencies by running the following command from your cloned `cri/` 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`
|
||||
To build and install a version of containerd with the `cri` plugin, enter the
|
||||
following commands from your `cri` project directory:
|
||||
```bash
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
*NOTE: The version of containerd built and installed from the `Makefile` is only for
|
||||
testing purposes. The version tag carries the suffix "-TEST".*
|
||||
#### Build Tags
|
||||
`cri` supports optional build tags for compiling support of various features.
|
||||
To add build tags to the make option the `BUILD_TAGS` variable must be set.
|
||||
|
||||
```bash
|
||||
make BUILD_TAGS='seccomp apparmor selinux'
|
||||
```
|
||||
|
||||
| Build Tag | Feature | Dependency |
|
||||
|-----------|------------------------------------|---------------------------------|
|
||||
| seccomp | syscall filtering | libseccomp development library |
|
||||
| selinux | selinux process and mount labeling | <none> |
|
||||
| apparmor | apparmor profile support | <none> |
|
||||
### Validate Your `cri` Setup
|
||||
A Kubernetes incubator project called [cri-tools](https://github.com/kubernetes-sigs/cri-tools)
|
||||
includes programs for exercising CRI implementations such as the `cri` plugin.
|
||||
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/sig-node/cri-validation.md).
|
||||
|
||||
Run the CRI Validation test to validate your installation of `containerd` with `cri` built in:
|
||||
```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` in a local cluster:
|
||||
|
||||
1. Start the version of `containerd` with `cri` plugin that you built and installed
|
||||
above as root in a first terminal:
|
||||
```bash
|
||||
sudo containerd
|
||||
```
|
||||
2. From the Kubernetes project directory startup a local cluster using `containerd`:
|
||||
```bash
|
||||
CONTAINER_RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT='unix:///run/containerd/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.
|
||||
## Configurations
|
||||
See [here](./docs/config.md) for information about how to configure cri plugins
|
||||
and [here](https://github.com/containerd/containerd/blob/master/docs/man/containerd-config.8.md)
|
||||
for information about how to configure containerd
|
||||
## Documentation
|
||||
See [here](./docs) for additional documentation.
|
||||
## Communication
|
||||
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 catch us in the `#containerd` and `#containerd-dev` slack
|
||||
channels on Cloud Native Computing Foundation's (CNCF) slack -
|
||||
`cloud-native.slack.com`. Everyone is welcome to join and chat.
|
||||
[Get Invite to CNCF slack.](https://slack.cncf.io)
|
||||
|
||||
## 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/.
|
||||
|
||||
## Project details
|
||||
cri is a containerd sub-project. 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).
|
||||
As a containerd sub-project, you will find the:
|
||||
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
|
||||
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
|
||||
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
|
||||
|
||||
information in our [`containerd/project`](https://github.com/containerd/project) repository.
|
||||
192
vendor/github.com/containerd/cri/cri.go
generated
vendored
192
vendor/github.com/containerd/cri/cri.go
generated
vendored
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"flag"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/api/services/containers/v1"
|
||||
"github.com/containerd/containerd/api/services/diff/v1"
|
||||
"github.com/containerd/containerd/api/services/images/v1"
|
||||
introspectionapi "github.com/containerd/containerd/api/services/introspection/v1"
|
||||
"github.com/containerd/containerd/api/services/namespaces/v1"
|
||||
"github.com/containerd/containerd/api/services/tasks/v1"
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/leases"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/services"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
criconfig "github.com/containerd/cri/pkg/config"
|
||||
"github.com/containerd/cri/pkg/constants"
|
||||
criplatforms "github.com/containerd/cri/pkg/containerd/platforms"
|
||||
"github.com/containerd/cri/pkg/server"
|
||||
)
|
||||
|
||||
// TODO(random-liu): Use github.com/pkg/errors for our errors.
|
||||
// Register CRI service plugin
|
||||
func init() {
|
||||
config := criconfig.DefaultConfig()
|
||||
plugin.Register(&plugin.Registration{
|
||||
Type: plugin.GRPCPlugin,
|
||||
ID: "cri",
|
||||
Config: &config,
|
||||
Requires: []plugin.Type{
|
||||
plugin.ServicePlugin,
|
||||
},
|
||||
InitFn: initCRIService,
|
||||
})
|
||||
}
|
||||
|
||||
func initCRIService(ic *plugin.InitContext) (interface{}, error) {
|
||||
ic.Meta.Platforms = []imagespec.Platform{platforms.DefaultSpec()}
|
||||
ic.Meta.Exports = map[string]string{"CRIVersion": constants.CRIVersion}
|
||||
ctx := ic.Context
|
||||
pluginConfig := ic.Config.(*criconfig.PluginConfig)
|
||||
if err := criconfig.ValidatePluginConfig(ctx, pluginConfig); err != nil {
|
||||
return nil, errors.Wrap(err, "invalid plugin config")
|
||||
}
|
||||
|
||||
c := criconfig.Config{
|
||||
PluginConfig: *pluginConfig,
|
||||
ContainerdRootDir: filepath.Dir(ic.Root),
|
||||
ContainerdEndpoint: ic.Address,
|
||||
RootDir: ic.Root,
|
||||
StateDir: ic.State,
|
||||
}
|
||||
log.G(ctx).Infof("Start cri plugin with config %+v", c)
|
||||
|
||||
if err := setGLogLevel(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set glog level")
|
||||
}
|
||||
|
||||
servicesOpts, err := getServicesOpts(ic)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get services")
|
||||
}
|
||||
|
||||
log.G(ctx).Info("Connect containerd service")
|
||||
client, err := containerd.New(
|
||||
"",
|
||||
containerd.WithDefaultNamespace(constants.K8sContainerdNamespace),
|
||||
containerd.WithDefaultPlatform(criplatforms.Default()),
|
||||
containerd.WithServices(servicesOpts...),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create containerd client")
|
||||
}
|
||||
|
||||
s, err := server.NewCRIService(c, client)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create CRI service")
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := s.Run(); 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
|
||||
}
|
||||
|
||||
// getServicesOpts get service options from plugin context.
|
||||
func getServicesOpts(ic *plugin.InitContext) ([]containerd.ServicesOpt, error) {
|
||||
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get service plugin")
|
||||
}
|
||||
|
||||
opts := []containerd.ServicesOpt{
|
||||
containerd.WithEventService(ic.Events),
|
||||
}
|
||||
for s, fn := range map[string]func(interface{}) containerd.ServicesOpt{
|
||||
services.ContentService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithContentStore(s.(content.Store))
|
||||
},
|
||||
services.ImagesService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithImageService(s.(images.ImagesClient))
|
||||
},
|
||||
services.SnapshotsService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithSnapshotters(s.(map[string]snapshots.Snapshotter))
|
||||
},
|
||||
services.ContainersService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithContainerService(s.(containers.ContainersClient))
|
||||
},
|
||||
services.TasksService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithTaskService(s.(tasks.TasksClient))
|
||||
},
|
||||
services.DiffService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithDiffService(s.(diff.DiffClient))
|
||||
},
|
||||
services.NamespacesService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithNamespaceService(s.(namespaces.NamespacesClient))
|
||||
},
|
||||
services.LeasesService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithLeasesService(s.(leases.Manager))
|
||||
},
|
||||
services.IntrospectionService: func(s interface{}) containerd.ServicesOpt {
|
||||
return containerd.WithIntrospectionService(s.(introspectionapi.IntrospectionClient))
|
||||
},
|
||||
} {
|
||||
p := plugins[s]
|
||||
if p == nil {
|
||||
return nil, errors.Errorf("service %q not found", s)
|
||||
}
|
||||
i, err := p.Instance()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get instance of service %q", s)
|
||||
}
|
||||
if i == nil {
|
||||
return nil, errors.Errorf("instance of service %q not found", s)
|
||||
}
|
||||
opts = append(opts, fn(i))
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// Set glog level.
|
||||
func setGLogLevel() error {
|
||||
l := logrus.GetLevel()
|
||||
fs := flag.NewFlagSet("klog", flag.PanicOnError)
|
||||
klog.InitFlags(fs)
|
||||
if err := fs.Set("logtostderr", "true"); err != nil {
|
||||
return err
|
||||
}
|
||||
switch l {
|
||||
case logrus.TraceLevel:
|
||||
return fs.Set("v", "5")
|
||||
case logrus.DebugLevel:
|
||||
return fs.Set("v", "4")
|
||||
case logrus.InfoLevel:
|
||||
return fs.Set("v", "2")
|
||||
// glog doesn't support following filters. Defaults to v=0.
|
||||
case logrus.WarnLevel:
|
||||
case logrus.ErrorLevel:
|
||||
case logrus.FatalLevel:
|
||||
case logrus.PanicLevel:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
50
vendor/github.com/containerd/cri/pkg/annotations/annotations.go
generated
vendored
50
vendor/github.com/containerd/cri/pkg/annotations/annotations.go
generated
vendored
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
Copyright 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"
|
||||
|
||||
// SandboxLogDir is the pod log directory annotation.
|
||||
// If the sandbox needs to generate any log, it will put it into this directory.
|
||||
// Kubelet will be responsible for:
|
||||
// 1) Monitoring the disk usage of the log, and including it as part of the pod
|
||||
// ephemeral storage usage.
|
||||
// 2) Cleaning up the logs when the pod is deleted.
|
||||
// NOTE: Kubelet is not responsible for rotating the logs.
|
||||
SandboxLogDir = "io.kubernetes.cri.sandbox-log-directory"
|
||||
|
||||
// UntrustedWorkload is the sandbox annotation for untrusted workload. Untrusted
|
||||
// workload can only run on dedicated runtime for untrusted workload.
|
||||
UntrustedWorkload = "io.kubernetes.cri.untrusted-workload"
|
||||
|
||||
// containerName is the name of the container in the pod
|
||||
ContainerName = "io.kubernetes.cri.container-name"
|
||||
)
|
||||
394
vendor/github.com/containerd/cri/pkg/api/runtimeoptions/v1/api.pb.go
generated
vendored
394
vendor/github.com/containerd/cri/pkg/api/runtimeoptions/v1/api.pb.go
generated
vendored
@@ -1,394 +0,0 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: api.proto
|
||||
|
||||
/*
|
||||
Package cri_runtimeoptions_v1 is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
api.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Options
|
||||
*/
|
||||
package cri_runtimeoptions_v1
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "github.com/gogo/protobuf/gogoproto"
|
||||
|
||||
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 Options struct {
|
||||
// TypeUrl specifies the type of the content inside the config file.
|
||||
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
|
||||
// ConfigPath specifies the filesystem location of the config file
|
||||
// used by the runtime.
|
||||
ConfigPath string `protobuf:"bytes,2,opt,name=config_path,json=configPath,proto3" json:"config_path,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Options) Reset() { *m = Options{} }
|
||||
func (*Options) ProtoMessage() {}
|
||||
func (*Options) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{0} }
|
||||
|
||||
func (m *Options) GetTypeUrl() string {
|
||||
if m != nil {
|
||||
return m.TypeUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Options) GetConfigPath() string {
|
||||
if m != nil {
|
||||
return m.ConfigPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Options)(nil), "cri.runtimeoptions.v1.Options")
|
||||
}
|
||||
func (m *Options) 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 *Options) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.TypeUrl) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintApi(dAtA, i, uint64(len(m.TypeUrl)))
|
||||
i += copy(dAtA[i:], m.TypeUrl)
|
||||
}
|
||||
if len(m.ConfigPath) > 0 {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintApi(dAtA, i, uint64(len(m.ConfigPath)))
|
||||
i += copy(dAtA[i:], m.ConfigPath)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
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 *Options) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.TypeUrl)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovApi(uint64(l))
|
||||
}
|
||||
l = len(m.ConfigPath)
|
||||
if l > 0 {
|
||||
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 *Options) String() string {
|
||||
if this == nil {
|
||||
return "nil"
|
||||
}
|
||||
s := strings.Join([]string{`&Options{`,
|
||||
`TypeUrl:` + fmt.Sprintf("%v", this.TypeUrl) + `,`,
|
||||
`ConfigPath:` + fmt.Sprintf("%v", this.ConfigPath) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
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 *Options) 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: Options: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Options: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", 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.TypeUrl = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ConfigPath", 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.ConfigPath = 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{
|
||||
// 183 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, 0x12, 0x4d, 0x2e, 0xca, 0xd4, 0x2b, 0x2a, 0xcd, 0x2b, 0xc9,
|
||||
0xcc, 0x4d, 0xcd, 0x2f, 0x28, 0xc9, 0xcc, 0xcf, 0x2b, 0xd6, 0x2b, 0x33, 0x94, 0xd2, 0x4d, 0xcf,
|
||||
0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0x4f, 0xcf, 0xd7, 0x07, 0xab,
|
||||
0x4e, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, 0x62, 0x8a, 0x92, 0x2b, 0x17, 0xbb, 0x3f,
|
||||
0x44, 0xb3, 0x90, 0x24, 0x17, 0x47, 0x49, 0x65, 0x41, 0x6a, 0x7c, 0x69, 0x51, 0x8e, 0x04, 0xa3,
|
||||
0x02, 0xa3, 0x06, 0x67, 0x10, 0x3b, 0x88, 0x1f, 0x5a, 0x94, 0x23, 0x24, 0xcf, 0xc5, 0x9d, 0x9c,
|
||||
0x9f, 0x97, 0x96, 0x99, 0x1e, 0x5f, 0x90, 0x58, 0x92, 0x21, 0xc1, 0x04, 0x96, 0xe5, 0x82, 0x08,
|
||||
0x05, 0x24, 0x96, 0x64, 0x38, 0xc9, 0x9c, 0x78, 0x28, 0xc7, 0x78, 0xe3, 0xa1, 0x1c, 0x43, 0xc3,
|
||||
0x23, 0x39, 0xc6, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71,
|
||||
0xc2, 0x63, 0x39, 0x86, 0x24, 0x36, 0xb0, 0x5d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x07,
|
||||
0x00, 0xf2, 0x18, 0xbe, 0x00, 0x00, 0x00,
|
||||
}
|
||||
22
vendor/github.com/containerd/cri/pkg/api/runtimeoptions/v1/api.proto
generated
vendored
22
vendor/github.com/containerd/cri/pkg/api/runtimeoptions/v1/api.proto
generated
vendored
@@ -1,22 +0,0 @@
|
||||
// To regenerate api.pb.go run `make proto`
|
||||
syntax = "proto3";
|
||||
|
||||
package cri.runtimeoptions.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;
|
||||
|
||||
message Options {
|
||||
// TypeUrl specifies the type of the content inside the config file.
|
||||
string type_url = 1;
|
||||
// ConfigPath specifies the filesystem location of the config file
|
||||
// used by the runtime.
|
||||
string config_path = 2;
|
||||
}
|
||||
54
vendor/github.com/containerd/cri/pkg/atomic/atomic_boolean.go
generated
vendored
54
vendor/github.com/containerd/cri/pkg/atomic/atomic_boolean.go
generated
vendored
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
Copyright 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
|
||||
}
|
||||
369
vendor/github.com/containerd/cri/pkg/config/config.go
generated
vendored
369
vendor/github.com/containerd/cri/pkg/config/config.go
generated
vendored
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
Copyright 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 config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Runtime struct to contain the type(ID), engine, and root variables for a default runtime
|
||||
// and a runtime for untrusted worload.
|
||||
type Runtime struct {
|
||||
// Type is the runtime type to use in containerd e.g. io.containerd.runtime.v1.linux
|
||||
Type string `toml:"runtime_type" json:"runtimeType"`
|
||||
// Engine is the name of the runtime engine used by containerd.
|
||||
// This only works for runtime type "io.containerd.runtime.v1.linux".
|
||||
// DEPRECATED: use Options instead. Remove when shim v1 is deprecated.
|
||||
Engine string `toml:"runtime_engine" json:"runtimeEngine"`
|
||||
// PodAnnotations is a list of pod annotations passed to both pod sandbox as well as
|
||||
// container OCI annotations.
|
||||
PodAnnotations []string `toml:"pod_annotations" json:"PodAnnotations"`
|
||||
// ContainerAnnotations is a list of container annotations passed through to the OCI config of the containers.
|
||||
// Container annotations in CRI are usually generated by other Kubernetes node components (i.e., not users).
|
||||
// Currently, only device plugins populate the annotations.
|
||||
ContainerAnnotations []string `toml:"container_annotations" json:"ContainerAnnotations"`
|
||||
// Root is the directory used by containerd for runtime state.
|
||||
// DEPRECATED: use Options instead. Remove when shim v1 is deprecated.
|
||||
// This only works for runtime type "io.containerd.runtime.v1.linux".
|
||||
Root string `toml:"runtime_root" json:"runtimeRoot"`
|
||||
// Options are config options for the runtime. If options is loaded
|
||||
// from toml config, it will be toml.Primitive.
|
||||
Options *toml.Primitive `toml:"options" json:"options"`
|
||||
// PrivilegedWithoutHostDevices overloads the default behaviour for adding host devices to the
|
||||
// runtime spec when the container is privileged. Defaults to false.
|
||||
PrivilegedWithoutHostDevices bool `toml:"privileged_without_host_devices" json:"privileged_without_host_devices"`
|
||||
// BaseRuntimeSpec is a json file with OCI spec to use as base spec that all container's will be created from.
|
||||
BaseRuntimeSpec string `toml:"base_runtime_spec" json:"baseRuntimeSpec"`
|
||||
}
|
||||
|
||||
// ContainerdConfig contains toml config related to containerd
|
||||
type ContainerdConfig struct {
|
||||
// Snapshotter is the snapshotter used by containerd.
|
||||
Snapshotter string `toml:"snapshotter" json:"snapshotter"`
|
||||
// DefaultRuntimeName is the default runtime name to use from the runtimes table.
|
||||
DefaultRuntimeName string `toml:"default_runtime_name" json:"defaultRuntimeName"`
|
||||
// DefaultRuntime is the default runtime to use in containerd.
|
||||
// This runtime is used when no runtime handler (or the empty string) is provided.
|
||||
// DEPRECATED: use DefaultRuntimeName instead. Remove in containerd 1.4.
|
||||
DefaultRuntime Runtime `toml:"default_runtime" json:"defaultRuntime"`
|
||||
// UntrustedWorkloadRuntime is a runtime to run untrusted workloads on it.
|
||||
// DEPRECATED: use `untrusted` runtime in Runtimes instead. Remove in containerd 1.4.
|
||||
UntrustedWorkloadRuntime Runtime `toml:"untrusted_workload_runtime" json:"untrustedWorkloadRuntime"`
|
||||
// Runtimes is a map from CRI RuntimeHandler strings, which specify types of runtime
|
||||
// configurations, to the matching configurations.
|
||||
Runtimes map[string]Runtime `toml:"runtimes" json:"runtimes"`
|
||||
// NoPivot disables pivot-root (linux only), required when running a container in a RamDisk with runc
|
||||
// This only works for runtime type "io.containerd.runtime.v1.linux".
|
||||
NoPivot bool `toml:"no_pivot" json:"noPivot"`
|
||||
|
||||
// DisableSnapshotAnnotations disables to pass additional annotations (image
|
||||
// related information) to snapshotters. These annotations are required by
|
||||
// stargz snapshotter (https://github.com/containerd/stargz-snapshotter).
|
||||
DisableSnapshotAnnotations bool `toml:"disable_snapshot_annotations" json:"disableSnapshotAnnotations"`
|
||||
|
||||
// DiscardUnpackedLayers is a boolean flag to specify whether to allow GC to
|
||||
// remove layers from the content store after successfully unpacking these
|
||||
// layers to the snapshotter.
|
||||
DiscardUnpackedLayers bool `toml:"discard_unpacked_layers" json:"discardUnpackedLayers"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
// NetworkPluginConfDir is the directory in which the admin places a CNI conf.
|
||||
NetworkPluginConfDir string `toml:"conf_dir" json:"confDir"`
|
||||
// NetworkPluginMaxConfNum is the max number of plugin config files that will
|
||||
// be loaded from the cni config directory by go-cni. Set the value to 0 to
|
||||
// load all config files (no arbitrary limit). The legacy default value is 1.
|
||||
NetworkPluginMaxConfNum int `toml:"max_conf_num" json:"maxConfNum"`
|
||||
// NetworkPluginConfTemplate is the file path of golang template used to generate
|
||||
// cni config.
|
||||
// When it is set, containerd will get cidr(s) from kubelet to replace {{.PodCIDR}},
|
||||
// {{.PodCIDRRanges}} or {{.Routes}} in the template, and write the config into
|
||||
// NetworkPluginConfDir.
|
||||
// Ideally the cni config should be placed by system admin or cni daemon like calico,
|
||||
// weaveworks etc. However, there are still users using kubenet
|
||||
// (https://kubernetes.io/docs/concepts/cluster-administration/network-plugins/#kubenet)
|
||||
// today, who don't have a cni daemonset in production. NetworkPluginConfTemplate is
|
||||
// a temporary backward-compatible solution for them.
|
||||
// TODO(random-liu): Deprecate this option when kubenet is deprecated.
|
||||
NetworkPluginConfTemplate string `toml:"conf_template" json:"confTemplate"`
|
||||
}
|
||||
|
||||
// Mirror contains the config related to the registry mirror
|
||||
type Mirror struct {
|
||||
// Endpoints are endpoints for a namespace. CRI plugin will try the endpoints
|
||||
// one by one until a working one is found. The endpoint must be a valid url
|
||||
// with host specified.
|
||||
// The scheme, host and path from the endpoint URL will be used.
|
||||
Endpoints []string `toml:"endpoint" json:"endpoint"`
|
||||
}
|
||||
|
||||
// AuthConfig contains the config related to authentication to a specific registry
|
||||
type AuthConfig struct {
|
||||
// Username is the username to login the registry.
|
||||
Username string `toml:"username" json:"username"`
|
||||
// Password is the password to login the registry.
|
||||
Password string `toml:"password" json:"password"`
|
||||
// Auth is a base64 encoded string from the concatenation of the username,
|
||||
// a colon, and the password.
|
||||
Auth string `toml:"auth" json:"auth"`
|
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `toml:"identitytoken" json:"identitytoken"`
|
||||
}
|
||||
|
||||
// TLSConfig contains the CA/Cert/Key used for a registry
|
||||
type TLSConfig struct {
|
||||
InsecureSkipVerify bool `toml:"insecure_skip_verify" json:"insecure_skip_verify"`
|
||||
CAFile string `toml:"ca_file" json:"caFile"`
|
||||
CertFile string `toml:"cert_file" json:"certFile"`
|
||||
KeyFile string `toml:"key_file" json:"keyFile"`
|
||||
}
|
||||
|
||||
// Registry is registry settings configured
|
||||
type Registry struct {
|
||||
// Mirrors are namespace to mirror mapping for all namespaces.
|
||||
Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors"`
|
||||
// Configs are configs for each registry.
|
||||
// The key is the domain name or IP of the registry.
|
||||
Configs map[string]RegistryConfig `toml:"configs" json:"configs"`
|
||||
|
||||
// Auths are registry endpoint to auth config mapping. The registry endpoint must
|
||||
// be a valid url with host specified.
|
||||
// DEPRECATED: Use Configs instead. Remove in containerd 1.4.
|
||||
Auths map[string]AuthConfig `toml:"auths" json:"auths"`
|
||||
// Headers adds additional HTTP headers that get sent to all registries
|
||||
Headers map[string][]string `toml:"headers" json:"headers"`
|
||||
}
|
||||
|
||||
// RegistryConfig contains configuration used to communicate with the registry.
|
||||
type RegistryConfig struct {
|
||||
// Auth contains information to authenticate to the registry.
|
||||
Auth *AuthConfig `toml:"auth" json:"auth"`
|
||||
// TLS is a pair of CA/Cert/Key which then are used when creating the transport
|
||||
// that communicates with the registry.
|
||||
TLS *TLSConfig `toml:"tls" json:"tls"`
|
||||
}
|
||||
|
||||
// ImageDecryption contains configuration to handling decryption of encrypted container images.
|
||||
type ImageDecryption struct {
|
||||
// KeyModel specifies the trust model of where keys should reside.
|
||||
//
|
||||
// Details of field usage can be found in:
|
||||
// https://github.com/containerd/cri/tree/master/docs/config.md
|
||||
//
|
||||
// Details of key models can be found in:
|
||||
// https://github.com/containerd/cri/tree/master/docs/decryption.md
|
||||
KeyModel string `toml:"key_model" json:"keyModel"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
// CniConfig contains config related to cni
|
||||
CniConfig `toml:"cni" json:"cni"`
|
||||
// Registry contains config related to the registry
|
||||
Registry Registry `toml:"registry" json:"registry"`
|
||||
// ImageDecryption contains config related to handling decryption of encrypted container images
|
||||
ImageDecryption `toml:"image_decryption" json:"imageDecryption"`
|
||||
// DisableTCPService disables serving CRI on the TCP server.
|
||||
DisableTCPService bool `toml:"disable_tcp_service" json:"disableTCPService"`
|
||||
// StreamServerAddress is the ip address streaming server is listening on.
|
||||
StreamServerAddress string `toml:"stream_server_address" json:"streamServerAddress"`
|
||||
// StreamServerPort is the port streaming server is listening on.
|
||||
StreamServerPort string `toml:"stream_server_port" json:"streamServerPort"`
|
||||
// StreamIdleTimeout is the maximum time a streaming connection
|
||||
// can be idle before the connection is automatically closed.
|
||||
// The string is in the golang duration format, see:
|
||||
// https://golang.org/pkg/time/#ParseDuration
|
||||
StreamIdleTimeout string `toml:"stream_idle_timeout" json:"streamIdleTimeout"`
|
||||
// EnableSelinux indicates to enable the selinux support.
|
||||
EnableSelinux bool `toml:"enable_selinux" json:"enableSelinux"`
|
||||
// SelinuxCategoryRange allows the upper bound on the category range to be set.
|
||||
// If not specified or set to 0, defaults to 1024 from the selinux package.
|
||||
SelinuxCategoryRange int `toml:"selinux_category_range" json:"selinuxCategoryRange"`
|
||||
// SandboxImage is the image used by sandbox container.
|
||||
SandboxImage string `toml:"sandbox_image" json:"sandboxImage"`
|
||||
// StatsCollectPeriod is the period (in seconds) of snapshots stats collection.
|
||||
StatsCollectPeriod int `toml:"stats_collect_period" json:"statsCollectPeriod"`
|
||||
// SystemdCgroup enables systemd cgroup support.
|
||||
// This only works for runtime type "io.containerd.runtime.v1.linux".
|
||||
// DEPRECATED: config runc runtime handler instead. Remove when shim v1 is deprecated.
|
||||
SystemdCgroup bool `toml:"systemd_cgroup" json:"systemdCgroup"`
|
||||
// EnableTLSStreaming indicates to enable the TLS streaming support.
|
||||
EnableTLSStreaming bool `toml:"enable_tls_streaming" json:"enableTLSStreaming"`
|
||||
// X509KeyPairStreaming is a x509 key pair used for TLS streaming
|
||||
X509KeyPairStreaming `toml:"x509_key_pair_streaming" json:"x509KeyPairStreaming"`
|
||||
// MaxContainerLogLineSize is the maximum log line size in bytes for a container.
|
||||
// Log line longer than the limit will be split into multiple lines. Non-positive
|
||||
// value means no limit.
|
||||
MaxContainerLogLineSize int `toml:"max_container_log_line_size" json:"maxContainerLogSize"`
|
||||
// DisableCgroup indicates to disable the cgroup support.
|
||||
// This is useful when the containerd does not have permission to access cgroup.
|
||||
DisableCgroup bool `toml:"disable_cgroup" json:"disableCgroup"`
|
||||
// DisableApparmor indicates to disable the apparmor support.
|
||||
// This is useful when the containerd does not have permission to access Apparmor.
|
||||
DisableApparmor bool `toml:"disable_apparmor" json:"disableApparmor"`
|
||||
// RestrictOOMScoreAdj indicates to limit the lower bound of OOMScoreAdj to the containerd's
|
||||
// current OOMScoreADj.
|
||||
// This is useful when the containerd does not have permission to decrease OOMScoreAdj.
|
||||
RestrictOOMScoreAdj bool `toml:"restrict_oom_score_adj" json:"restrictOOMScoreAdj"`
|
||||
// MaxConcurrentDownloads restricts the number of concurrent downloads for each image.
|
||||
MaxConcurrentDownloads int `toml:"max_concurrent_downloads" json:"maxConcurrentDownloads"`
|
||||
// DisableProcMount disables Kubernetes ProcMount support. This MUST be set to `true`
|
||||
// when using containerd with Kubernetes <=1.11.
|
||||
DisableProcMount bool `toml:"disable_proc_mount" json:"disableProcMount"`
|
||||
// UnsetSeccompProfile is the profile containerd/cri will use If the provided seccomp profile is
|
||||
// unset (`""`) for a container (default is `unconfined`)
|
||||
UnsetSeccompProfile string `toml:"unset_seccomp_profile" json:"unsetSeccompProfile"`
|
||||
// TolerateMissingHugetlbController if set to false will error out on create/update
|
||||
// container requests with huge page limits if the cgroup controller for hugepages is not present.
|
||||
// This helps with supporting Kubernetes <=1.18 out of the box. (default is `true`)
|
||||
TolerateMissingHugetlbController bool `toml:"tolerate_missing_hugetlb_controller" json:"tolerateMissingHugetlbController"`
|
||||
// DisableHugetlbController indicates to silently disable the hugetlb controller, even when it is
|
||||
// present in /sys/fs/cgroup/cgroup.controllers.
|
||||
// This helps with running rootless mode + cgroup v2 + systemd but without hugetlb delegation.
|
||||
DisableHugetlbController bool `toml:"disable_hugetlb_controller" json:"disableHugetlbController"`
|
||||
// IgnoreImageDefinedVolumes ignores volumes defined by the image. Useful for better resource
|
||||
// isolation, security and early detection of issues in the mount configuration when using
|
||||
// ReadOnlyRootFilesystem since containers won't silently mount a temporary volume.
|
||||
IgnoreImageDefinedVolumes bool `toml:"ignore_image_defined_volumes" json:"ignoreImageDefinedVolumes"`
|
||||
}
|
||||
|
||||
// X509KeyPairStreaming contains the x509 configuration for streaming
|
||||
type X509KeyPairStreaming struct {
|
||||
// TLSCertFile is the path to a certificate file
|
||||
TLSCertFile string `toml:"tls_cert_file" json:"tlsCertFile"`
|
||||
// TLSKeyFile is the path to a private key file
|
||||
TLSKeyFile string `toml:"tls_key_file" json:"tlsKeyFile"`
|
||||
}
|
||||
|
||||
// Config contains all configurations for cri server.
|
||||
type Config struct {
|
||||
// PluginConfig is the config for CRI plugin.
|
||||
PluginConfig
|
||||
// ContainerdRootDir is the root directory path for containerd.
|
||||
ContainerdRootDir string `json:"containerdRootDir"`
|
||||
// ContainerdEndpoint is the containerd endpoint path.
|
||||
ContainerdEndpoint string `json:"containerdEndpoint"`
|
||||
// RootDir is the root directory path for managing cri plugin files
|
||||
// (metadata checkpoint etc.)
|
||||
RootDir string `json:"rootDir"`
|
||||
// StateDir is the root directory path for managing volatile pod/container data
|
||||
StateDir string `json:"stateDir"`
|
||||
}
|
||||
|
||||
const (
|
||||
// RuntimeUntrusted is the implicit runtime defined for ContainerdConfig.UntrustedWorkloadRuntime
|
||||
RuntimeUntrusted = "untrusted"
|
||||
// RuntimeDefault is the implicit runtime defined for ContainerdConfig.DefaultRuntime
|
||||
RuntimeDefault = "default"
|
||||
// KeyModelNode is the key model where key for encrypted images reside
|
||||
// on the worker nodes
|
||||
KeyModelNode = "node"
|
||||
)
|
||||
|
||||
// ValidatePluginConfig validates the given plugin configuration.
|
||||
func ValidatePluginConfig(ctx context.Context, c *PluginConfig) error {
|
||||
if c.ContainerdConfig.Runtimes == nil {
|
||||
c.ContainerdConfig.Runtimes = make(map[string]Runtime)
|
||||
}
|
||||
|
||||
// Validation for deprecated untrusted_workload_runtime.
|
||||
if c.ContainerdConfig.UntrustedWorkloadRuntime.Type != "" {
|
||||
log.G(ctx).Warning("`untrusted_workload_runtime` is deprecated, please use `untrusted` runtime in `runtimes` instead")
|
||||
if _, ok := c.ContainerdConfig.Runtimes[RuntimeUntrusted]; ok {
|
||||
return errors.Errorf("conflicting definitions: configuration includes both `untrusted_workload_runtime` and `runtimes[%q]`", RuntimeUntrusted)
|
||||
}
|
||||
c.ContainerdConfig.Runtimes[RuntimeUntrusted] = c.ContainerdConfig.UntrustedWorkloadRuntime
|
||||
}
|
||||
|
||||
// Validation for deprecated default_runtime field.
|
||||
if c.ContainerdConfig.DefaultRuntime.Type != "" {
|
||||
log.G(ctx).Warning("`default_runtime` is deprecated, please use `default_runtime_name` to reference the default configuration you have defined in `runtimes`")
|
||||
c.ContainerdConfig.DefaultRuntimeName = RuntimeDefault
|
||||
c.ContainerdConfig.Runtimes[RuntimeDefault] = c.ContainerdConfig.DefaultRuntime
|
||||
}
|
||||
|
||||
// Validation for default_runtime_name
|
||||
if c.ContainerdConfig.DefaultRuntimeName == "" {
|
||||
return errors.New("`default_runtime_name` is empty")
|
||||
}
|
||||
if _, ok := c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName]; !ok {
|
||||
return errors.New("no corresponding runtime configured in `runtimes` for `default_runtime_name`")
|
||||
}
|
||||
|
||||
// Validation for deprecated runtime options.
|
||||
if c.SystemdCgroup {
|
||||
if c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName].Type != plugin.RuntimeLinuxV1 {
|
||||
return errors.Errorf("`systemd_cgroup` only works for runtime %s", plugin.RuntimeLinuxV1)
|
||||
}
|
||||
log.G(ctx).Warning("`systemd_cgroup` is deprecated, please use runtime `options` instead")
|
||||
}
|
||||
if c.NoPivot {
|
||||
if c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName].Type != plugin.RuntimeLinuxV1 {
|
||||
return errors.Errorf("`no_pivot` only works for runtime %s", plugin.RuntimeLinuxV1)
|
||||
}
|
||||
// NoPivot can't be deprecated yet, because there is no alternative config option
|
||||
// for `io.containerd.runtime.v1.linux`.
|
||||
}
|
||||
for _, r := range c.ContainerdConfig.Runtimes {
|
||||
if r.Engine != "" {
|
||||
if r.Type != plugin.RuntimeLinuxV1 {
|
||||
return errors.Errorf("`runtime_engine` only works for runtime %s", plugin.RuntimeLinuxV1)
|
||||
}
|
||||
log.G(ctx).Warning("`runtime_engine` is deprecated, please use runtime `options` instead")
|
||||
}
|
||||
if r.Root != "" {
|
||||
if r.Type != plugin.RuntimeLinuxV1 {
|
||||
return errors.Errorf("`runtime_root` only works for runtime %s", plugin.RuntimeLinuxV1)
|
||||
}
|
||||
log.G(ctx).Warning("`runtime_root` is deprecated, please use runtime `options` instead")
|
||||
}
|
||||
}
|
||||
|
||||
// Validation for deprecated auths options and mapping it to configs.
|
||||
if len(c.Registry.Auths) != 0 {
|
||||
if c.Registry.Configs == nil {
|
||||
c.Registry.Configs = make(map[string]RegistryConfig)
|
||||
}
|
||||
for endpoint, auth := range c.Registry.Auths {
|
||||
config := c.Registry.Configs[endpoint]
|
||||
config.Auth = &auth
|
||||
c.Registry.Configs[endpoint] = config
|
||||
}
|
||||
log.G(ctx).Warning("`auths` is deprecated, please use registry`configs` instead")
|
||||
}
|
||||
|
||||
// Validation for stream_idle_timeout
|
||||
if c.StreamIdleTimeout != "" {
|
||||
if _, err := time.ParseDuration(c.StreamIdleTimeout); err != nil {
|
||||
return errors.Wrap(err, "invalid stream idle timeout")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
75
vendor/github.com/containerd/cri/pkg/config/config_unix.go
generated
vendored
75
vendor/github.com/containerd/cri/pkg/config/config_unix.go
generated
vendored
@@ -1,75 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 config
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/cri/pkg/streaming"
|
||||
)
|
||||
|
||||
// DefaultConfig returns default configurations of cri plugin.
|
||||
func DefaultConfig() PluginConfig {
|
||||
return PluginConfig{
|
||||
CniConfig: CniConfig{
|
||||
NetworkPluginBinDir: "/opt/cni/bin",
|
||||
NetworkPluginConfDir: "/etc/cni/net.d",
|
||||
NetworkPluginMaxConfNum: 1, // only one CNI plugin config file will be loaded
|
||||
NetworkPluginConfTemplate: "",
|
||||
},
|
||||
ContainerdConfig: ContainerdConfig{
|
||||
Snapshotter: containerd.DefaultSnapshotter,
|
||||
DefaultRuntimeName: "runc",
|
||||
NoPivot: false,
|
||||
Runtimes: map[string]Runtime{
|
||||
"runc": {
|
||||
Type: "io.containerd.runc.v2",
|
||||
Options: new(toml.Primitive),
|
||||
},
|
||||
},
|
||||
},
|
||||
DisableTCPService: true,
|
||||
StreamServerAddress: "127.0.0.1",
|
||||
StreamServerPort: "0",
|
||||
StreamIdleTimeout: streaming.DefaultConfig.StreamIdleTimeout.String(), // 4 hour
|
||||
EnableSelinux: false,
|
||||
SelinuxCategoryRange: 1024,
|
||||
EnableTLSStreaming: false,
|
||||
X509KeyPairStreaming: X509KeyPairStreaming{
|
||||
TLSKeyFile: "",
|
||||
TLSCertFile: "",
|
||||
},
|
||||
SandboxImage: "k8s.gcr.io/pause:3.2",
|
||||
StatsCollectPeriod: 10,
|
||||
SystemdCgroup: false,
|
||||
MaxContainerLogLineSize: 16 * 1024,
|
||||
Registry: Registry{
|
||||
Mirrors: map[string]Mirror{
|
||||
"docker.io": {
|
||||
Endpoints: []string{"https://registry-1.docker.io"},
|
||||
},
|
||||
},
|
||||
},
|
||||
MaxConcurrentDownloads: 3,
|
||||
DisableProcMount: false,
|
||||
TolerateMissingHugetlbController: true,
|
||||
DisableHugetlbController: true,
|
||||
IgnoreImageDefinedVolumes: false,
|
||||
}
|
||||
}
|
||||
71
vendor/github.com/containerd/cri/pkg/config/config_windows.go
generated
vendored
71
vendor/github.com/containerd/cri/pkg/config/config_windows.go
generated
vendored
@@ -1,71 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/cri/pkg/streaming"
|
||||
)
|
||||
|
||||
// DefaultConfig returns default configurations of cri plugin.
|
||||
func DefaultConfig() PluginConfig {
|
||||
return PluginConfig{
|
||||
CniConfig: CniConfig{
|
||||
NetworkPluginBinDir: filepath.Join(os.Getenv("ProgramFiles"), "containerd", "cni", "bin"),
|
||||
NetworkPluginConfDir: filepath.Join(os.Getenv("ProgramFiles"), "containerd", "cni", "conf"),
|
||||
NetworkPluginMaxConfNum: 1,
|
||||
NetworkPluginConfTemplate: "",
|
||||
},
|
||||
ContainerdConfig: ContainerdConfig{
|
||||
Snapshotter: containerd.DefaultSnapshotter,
|
||||
DefaultRuntimeName: "runhcs-wcow-process",
|
||||
NoPivot: false,
|
||||
Runtimes: map[string]Runtime{
|
||||
"runhcs-wcow-process": {
|
||||
Type: "io.containerd.runhcs.v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
DisableTCPService: true,
|
||||
StreamServerAddress: "127.0.0.1",
|
||||
StreamServerPort: "0",
|
||||
StreamIdleTimeout: streaming.DefaultConfig.StreamIdleTimeout.String(), // 4 hour
|
||||
EnableTLSStreaming: false,
|
||||
X509KeyPairStreaming: X509KeyPairStreaming{
|
||||
TLSKeyFile: "",
|
||||
TLSCertFile: "",
|
||||
},
|
||||
SandboxImage: "mcr.microsoft.com/oss/kubernetes/pause:1.4.0",
|
||||
StatsCollectPeriod: 10,
|
||||
MaxContainerLogLineSize: 16 * 1024,
|
||||
Registry: Registry{
|
||||
Mirrors: map[string]Mirror{
|
||||
"docker.io": {
|
||||
Endpoints: []string{"https://registry-1.docker.io"},
|
||||
},
|
||||
},
|
||||
},
|
||||
MaxConcurrentDownloads: 3,
|
||||
IgnoreImageDefinedVolumes: false,
|
||||
// TODO(windows): Add platform specific config, so that most common defaults can be shared.
|
||||
}
|
||||
}
|
||||
26
vendor/github.com/containerd/cri/pkg/constants/constants.go
generated
vendored
26
vendor/github.com/containerd/cri/pkg/constants/constants.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
Copyright 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 constants
|
||||
|
||||
// TODO(random-liu): Merge annotations package into this package.
|
||||
|
||||
const (
|
||||
// K8sContainerdNamespace is the namespace we use to connect containerd.
|
||||
K8sContainerdNamespace = "k8s.io"
|
||||
// CRIVersion is the CRI version supported by the CRI plugin.
|
||||
CRIVersion = "v1alpha2"
|
||||
)
|
||||
118
vendor/github.com/containerd/cri/pkg/containerd/opts/container.go
generated
vendored
118
vendor/github.com/containerd/cri/pkg/containerd/opts/container.go
generated
vendored
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
Copyright 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 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/log"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/continuity/fs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 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.
|
||||
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 {
|
||||
log.G(ctx).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 {
|
||||
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)
|
||||
}
|
||||
113
vendor/github.com/containerd/cri/pkg/containerd/opts/spec.go
generated
vendored
113
vendor/github.com/containerd/cri/pkg/containerd/opts/spec.go
generated
vendored
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
Copyright 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 opts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/containers"
|
||||
"github.com/containerd/containerd/oci"
|
||||
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// DefaultSandboxCPUshares is default cpu shares for sandbox container.
|
||||
// TODO(windows): Revisit cpu shares for windows (https://github.com/containerd/cri/issues/1297)
|
||||
const DefaultSandboxCPUshares = 2
|
||||
|
||||
// WithRelativeRoot sets the root for the container
|
||||
func WithRelativeRoot(root string) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) (err error) {
|
||||
if s.Root == nil {
|
||||
s.Root = &runtimespec.Root{}
|
||||
}
|
||||
s.Root.Path = root
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutRoot sets the root to nil for the container.
|
||||
func WithoutRoot(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
s.Root = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithProcessArgs sets the process args on the spec based on the image and runtime config
|
||||
func WithProcessArgs(config *runtime.ContainerConfig, image *imagespec.ImageConfig) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) (err 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{}, image.Cmd...)
|
||||
}
|
||||
if command == nil {
|
||||
command = append([]string{}, image.Entrypoint...)
|
||||
}
|
||||
}
|
||||
if len(command) == 0 && len(args) == 0 {
|
||||
return errors.New("no command specified")
|
||||
}
|
||||
return oci.WithProcessArgs(append(command, args...)...)(ctx, client, c, s)
|
||||
}
|
||||
}
|
||||
|
||||
// mounts defines how to sort runtime.Mount.
|
||||
// This is the same with the Docker implementation:
|
||||
// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26
|
||||
type orderedMounts []*runtime.Mount
|
||||
|
||||
// Len returns the number of mounts. Used in sorting.
|
||||
func (m orderedMounts) Len() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
// Less returns true if the number of parts (a/b/c would be 3 parts) in the
|
||||
// mount indexed by parameter 1 is less than that of the mount indexed by
|
||||
// parameter 2. Used in sorting.
|
||||
func (m orderedMounts) Less(i, j int) bool {
|
||||
return m.parts(i) < m.parts(j)
|
||||
}
|
||||
|
||||
// Swap swaps two items in an array of mounts. Used in sorting
|
||||
func (m orderedMounts) Swap(i, j int) {
|
||||
m[i], m[j] = m[j], m[i]
|
||||
}
|
||||
|
||||
// parts returns the number of parts in the destination of a mount. Used in sorting.
|
||||
func (m orderedMounts) parts(i int) int {
|
||||
return strings.Count(filepath.Clean(m[i].ContainerPath), string(os.PathSeparator))
|
||||
}
|
||||
|
||||
// WithAnnotation sets the provided annotation
|
||||
func WithAnnotation(k, v string) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Annotations == nil {
|
||||
s.Annotations = make(map[string]string)
|
||||
}
|
||||
s.Annotations[k] = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
721
vendor/github.com/containerd/cri/pkg/containerd/opts/spec_unix.go
generated
vendored
721
vendor/github.com/containerd/cri/pkg/containerd/opts/spec_unix.go
generated
vendored
@@ -1,721 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 opts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/containers"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/opencontainers/runc/libcontainer/devices"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
osinterface "github.com/containerd/cri/pkg/os"
|
||||
"github.com/containerd/cri/pkg/util"
|
||||
)
|
||||
|
||||
// WithAdditionalGIDs adds any additional groups listed for a particular user in the
|
||||
// /etc/groups file of the image's root filesystem to the OCI spec's additionalGids array.
|
||||
func WithAdditionalGIDs(userstr string) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) (err error) {
|
||||
if s.Process == nil {
|
||||
s.Process = &runtimespec.Process{}
|
||||
}
|
||||
gids := s.Process.User.AdditionalGids
|
||||
if err := oci.WithAdditionalGIDs(userstr)(ctx, client, c, s); err != nil {
|
||||
return err
|
||||
}
|
||||
// Merge existing gids and new gids.
|
||||
s.Process.User.AdditionalGids = mergeGids(s.Process.User.AdditionalGids, gids)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func mergeGids(gids1, gids2 []uint32) []uint32 {
|
||||
gidsMap := make(map[uint32]struct{})
|
||||
for _, gid1 := range gids1 {
|
||||
gidsMap[gid1] = struct{}{}
|
||||
}
|
||||
for _, gid2 := range gids2 {
|
||||
gidsMap[gid2] = struct{}{}
|
||||
}
|
||||
var gids []uint32
|
||||
for gid := range gidsMap {
|
||||
gids = append(gids, gid)
|
||||
}
|
||||
sort.Slice(gids, func(i, j int) bool { return gids[i] < gids[j] })
|
||||
return gids
|
||||
}
|
||||
|
||||
// WithoutRunMount removes the `/run` inside the spec
|
||||
func WithoutRunMount(_ context.Context, _ oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
var (
|
||||
mounts []runtimespec.Mount
|
||||
current = s.Mounts
|
||||
)
|
||||
for _, m := range current {
|
||||
if filepath.Clean(m.Destination) == "/run" {
|
||||
continue
|
||||
}
|
||||
mounts = append(mounts, m)
|
||||
}
|
||||
s.Mounts = mounts
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithoutDefaultSecuritySettings removes the default security settings generated on a spec
|
||||
func WithoutDefaultSecuritySettings(_ context.Context, _ oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Process == nil {
|
||||
s.Process = &runtimespec.Process{}
|
||||
}
|
||||
// Make sure no default seccomp/apparmor is specified
|
||||
s.Process.ApparmorProfile = ""
|
||||
if s.Linux != nil {
|
||||
s.Linux.Seccomp = nil
|
||||
}
|
||||
// Remove default rlimits (See issue #515)
|
||||
s.Process.Rlimits = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithMounts sorts and adds runtime and CRI mounts to the spec
|
||||
func WithMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*runtime.Mount, mountLabel string) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, _ *containers.Container, s *runtimespec.Spec) (err error) {
|
||||
// mergeMounts merge CRI mounts with extra mounts. If a mount destination
|
||||
// is mounted by both a CRI mount and an extra mount, the CRI mount will
|
||||
// be kept.
|
||||
var (
|
||||
criMounts = config.GetMounts()
|
||||
mounts = append([]*runtime.Mount{}, criMounts...)
|
||||
)
|
||||
// Copy all mounts from extra mounts, except for mounts overridden by CRI.
|
||||
for _, e := range extra {
|
||||
found := false
|
||||
for _, c := range criMounts {
|
||||
if filepath.Clean(e.ContainerPath) == filepath.Clean(c.ContainerPath) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
mounts = append(mounts, e)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort mounts in number of parts. This ensures that high level mounts don't
|
||||
// shadow other mounts.
|
||||
sort.Sort(orderedMounts(mounts))
|
||||
|
||||
// Mount cgroup into the container as readonly, which inherits docker's behavior.
|
||||
s.Mounts = append(s.Mounts, runtimespec.Mount{
|
||||
Source: "cgroup",
|
||||
Destination: "/sys/fs/cgroup",
|
||||
Type: "cgroup",
|
||||
Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
|
||||
})
|
||||
|
||||
// Copy all mounts from default mounts, except for
|
||||
// - mounts overridden by supplied mount;
|
||||
// - all mounts under /dev if a supplied /dev is present.
|
||||
mountSet := make(map[string]struct{})
|
||||
for _, m := range mounts {
|
||||
mountSet[filepath.Clean(m.ContainerPath)] = struct{}{}
|
||||
}
|
||||
|
||||
defaultMounts := s.Mounts
|
||||
s.Mounts = nil
|
||||
|
||||
for _, m := range defaultMounts {
|
||||
dst := filepath.Clean(m.Destination)
|
||||
if _, ok := mountSet[dst]; ok {
|
||||
// filter out mount overridden by a supplied mount
|
||||
continue
|
||||
}
|
||||
if _, mountDev := mountSet["/dev"]; mountDev && strings.HasPrefix(dst, "/dev/") {
|
||||
// filter out everything under /dev if /dev is a supplied mount
|
||||
continue
|
||||
}
|
||||
s.Mounts = append(s.Mounts, m)
|
||||
}
|
||||
|
||||
for _, mount := range mounts {
|
||||
var (
|
||||
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 := osi.Stat(src); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return errors.Wrapf(err, "failed to stat %q", src)
|
||||
}
|
||||
if err := osi.MkdirAll(src, 0755); err != nil {
|
||||
return errors.Wrapf(err, "failed to mkdir %q", src)
|
||||
}
|
||||
}
|
||||
// TODO(random-liu): Add cri-containerd integration test or cri validation test
|
||||
// for this.
|
||||
src, err := osi.ResolveSymbolicLink(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to resolve symlink %q", src)
|
||||
}
|
||||
if s.Linux == nil {
|
||||
s.Linux = &runtimespec.Linux{}
|
||||
}
|
||||
options := []string{"rbind"}
|
||||
switch mount.GetPropagation() {
|
||||
case runtime.MountPropagation_PROPAGATION_PRIVATE:
|
||||
options = append(options, "rprivate")
|
||||
// Since default root propagation in runc is rprivate ignore
|
||||
// setting the root propagation
|
||||
case runtime.MountPropagation_PROPAGATION_BIDIRECTIONAL:
|
||||
if err := ensureShared(src, osi.(osinterface.UNIX).LookupMount); err != nil {
|
||||
return err
|
||||
}
|
||||
options = append(options, "rshared")
|
||||
s.Linux.RootfsPropagation = "rshared"
|
||||
case runtime.MountPropagation_PROPAGATION_HOST_TO_CONTAINER:
|
||||
if err := ensureSharedOrSlave(src, osi.(osinterface.UNIX).LookupMount); err != nil {
|
||||
return err
|
||||
}
|
||||
options = append(options, "rslave")
|
||||
if s.Linux.RootfsPropagation != "rshared" &&
|
||||
s.Linux.RootfsPropagation != "rslave" {
|
||||
s.Linux.RootfsPropagation = "rslave"
|
||||
}
|
||||
default:
|
||||
log.G(ctx).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, false); err != nil && err != unix.ENOTSUP {
|
||||
return errors.Wrapf(err, "relabel %q with %q failed", src, mountLabel)
|
||||
}
|
||||
}
|
||||
s.Mounts = append(s.Mounts, runtimespec.Mount{
|
||||
Source: src,
|
||||
Destination: dst,
|
||||
Type: "bind",
|
||||
Options: options,
|
||||
})
|
||||
}
|
||||
return 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 errors.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 errors.Errorf("path %q is mounted on %q but it is not a shared or slave mount", path, mountInfo.Mountpoint)
|
||||
}
|
||||
|
||||
func addDevice(s *runtimespec.Spec, rd runtimespec.LinuxDevice) {
|
||||
for i, dev := range s.Linux.Devices {
|
||||
if dev.Path == rd.Path {
|
||||
s.Linux.Devices[i] = rd
|
||||
return
|
||||
}
|
||||
}
|
||||
s.Linux.Devices = append(s.Linux.Devices, rd)
|
||||
}
|
||||
|
||||
// WithDevices sets the provided devices onto the container spec
|
||||
func WithDevices(osi osinterface.OS, config *runtime.ContainerConfig) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) (err error) {
|
||||
if s.Linux == nil {
|
||||
s.Linux = &runtimespec.Linux{}
|
||||
}
|
||||
if s.Linux.Resources == nil {
|
||||
s.Linux.Resources = &runtimespec.LinuxResources{}
|
||||
}
|
||||
for _, device := range config.GetDevices() {
|
||||
path, err := osi.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,
|
||||
}
|
||||
|
||||
addDevice(s, rd)
|
||||
|
||||
s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, runtimespec.LinuxDeviceCgroup{
|
||||
Allow: true,
|
||||
Type: string(dev.Type),
|
||||
Major: &dev.Major,
|
||||
Minor: &dev.Minor,
|
||||
Access: string(dev.Permissions),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithCapabilities sets the provided capabilties from the security context
|
||||
func WithCapabilities(sc *runtime.LinuxContainerSecurityContext) oci.SpecOpts {
|
||||
capabilities := sc.GetCapabilities()
|
||||
if capabilities == nil {
|
||||
return nullOpt
|
||||
}
|
||||
|
||||
var opts []oci.SpecOpts
|
||||
// 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") {
|
||||
opts = append(opts, oci.WithAllCapabilities)
|
||||
}
|
||||
if util.InStringSlice(capabilities.GetDropCapabilities(), "ALL") {
|
||||
opts = append(opts, oci.WithCapabilities(nil))
|
||||
}
|
||||
|
||||
var caps []string
|
||||
for _, c := range capabilities.GetAddCapabilities() {
|
||||
if strings.ToUpper(c) == "ALL" {
|
||||
continue
|
||||
}
|
||||
// Capabilities in CRI doesn't have `CAP_` prefix, so add it.
|
||||
caps = append(caps, "CAP_"+strings.ToUpper(c))
|
||||
}
|
||||
opts = append(opts, oci.WithAddedCapabilities(caps))
|
||||
|
||||
caps = []string{}
|
||||
for _, c := range capabilities.GetDropCapabilities() {
|
||||
if strings.ToUpper(c) == "ALL" {
|
||||
continue
|
||||
}
|
||||
caps = append(caps, "CAP_"+strings.ToUpper(c))
|
||||
}
|
||||
opts = append(opts, oci.WithDroppedCapabilities(caps))
|
||||
return oci.Compose(opts...)
|
||||
}
|
||||
|
||||
// WithoutAmbientCaps removes the ambient caps from the spec
|
||||
func WithoutAmbientCaps(_ context.Context, _ oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Process == nil {
|
||||
s.Process = &runtimespec.Process{}
|
||||
}
|
||||
if s.Process.Capabilities == nil {
|
||||
s.Process.Capabilities = &runtimespec.LinuxCapabilities{}
|
||||
}
|
||||
s.Process.Capabilities.Ambient = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithDisabledCgroups clears the Cgroups Path from the spec
|
||||
func WithDisabledCgroups(_ context.Context, _ oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Linux == nil {
|
||||
s.Linux = &runtimespec.Linux{}
|
||||
}
|
||||
s.Linux.CgroupsPath = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithSelinuxLabels sets the mount and process labels
|
||||
func WithSelinuxLabels(process, mount string) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) (err error) {
|
||||
if s.Linux == nil {
|
||||
s.Linux = &runtimespec.Linux{}
|
||||
}
|
||||
if s.Process == nil {
|
||||
s.Process = &runtimespec.Process{}
|
||||
}
|
||||
s.Linux.MountLabel = mount
|
||||
s.Process.SelinuxLabel = process
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithResources sets the provided resource restrictions
|
||||
func WithResources(resources *runtime.LinuxContainerResources, tolerateMissingHugetlbController, disableHugetlbController bool) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) (err error) {
|
||||
if resources == nil {
|
||||
return nil
|
||||
}
|
||||
if s.Linux == nil {
|
||||
s.Linux = &runtimespec.Linux{}
|
||||
}
|
||||
if s.Linux.Resources == nil {
|
||||
s.Linux.Resources = &runtimespec.LinuxResources{}
|
||||
}
|
||||
if s.Linux.Resources.CPU == nil {
|
||||
s.Linux.Resources.CPU = &runtimespec.LinuxCPU{}
|
||||
}
|
||||
if s.Linux.Resources.Memory == nil {
|
||||
s.Linux.Resources.Memory = &runtimespec.LinuxMemory{}
|
||||
}
|
||||
var (
|
||||
p = uint64(resources.GetCpuPeriod())
|
||||
q = resources.GetCpuQuota()
|
||||
shares = uint64(resources.GetCpuShares())
|
||||
limit = resources.GetMemoryLimitInBytes()
|
||||
hugepages = resources.GetHugepageLimits()
|
||||
)
|
||||
|
||||
if p != 0 {
|
||||
s.Linux.Resources.CPU.Period = &p
|
||||
}
|
||||
if q != 0 {
|
||||
s.Linux.Resources.CPU.Quota = &q
|
||||
}
|
||||
if shares != 0 {
|
||||
s.Linux.Resources.CPU.Shares = &shares
|
||||
}
|
||||
if cpus := resources.GetCpusetCpus(); cpus != "" {
|
||||
s.Linux.Resources.CPU.Cpus = cpus
|
||||
}
|
||||
if mems := resources.GetCpusetMems(); mems != "" {
|
||||
s.Linux.Resources.CPU.Mems = resources.GetCpusetMems()
|
||||
}
|
||||
if limit != 0 {
|
||||
s.Linux.Resources.Memory.Limit = &limit
|
||||
}
|
||||
if !disableHugetlbController {
|
||||
if isHugetlbControllerPresent() {
|
||||
for _, limit := range hugepages {
|
||||
s.Linux.Resources.HugepageLimits = append(s.Linux.Resources.HugepageLimits, runtimespec.LinuxHugepageLimit{
|
||||
Pagesize: limit.PageSize,
|
||||
Limit: limit.Limit,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if !tolerateMissingHugetlbController {
|
||||
return errors.Errorf("huge pages limits are specified but hugetlb cgroup controller is missing. " +
|
||||
"Please set tolerate_missing_hugetlb_controller to `true` to ignore this error")
|
||||
}
|
||||
logrus.Warn("hugetlb cgroup controller is absent. skipping huge pages limits")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
supportsHugetlbOnce sync.Once
|
||||
supportsHugetlb bool
|
||||
)
|
||||
|
||||
func isHugetlbControllerPresent() bool {
|
||||
supportsHugetlbOnce.Do(func() {
|
||||
supportsHugetlb = false
|
||||
if IsCgroup2UnifiedMode() {
|
||||
supportsHugetlb, _ = cgroupv2HasHugetlb()
|
||||
} else {
|
||||
supportsHugetlb, _ = cgroupv1HasHugetlb()
|
||||
}
|
||||
})
|
||||
return supportsHugetlb
|
||||
}
|
||||
|
||||
var (
|
||||
_cgroupv1HasHugetlbOnce sync.Once
|
||||
_cgroupv1HasHugetlb bool
|
||||
_cgroupv1HasHugetlbErr error
|
||||
_cgroupv2HasHugetlbOnce sync.Once
|
||||
_cgroupv2HasHugetlb bool
|
||||
_cgroupv2HasHugetlbErr error
|
||||
isUnifiedOnce sync.Once
|
||||
isUnified bool
|
||||
)
|
||||
|
||||
// cgroupv1HasHugetlb returns whether the hugetlb controller is present on
|
||||
// cgroup v1.
|
||||
func cgroupv1HasHugetlb() (bool, error) {
|
||||
_cgroupv1HasHugetlbOnce.Do(func() {
|
||||
if _, err := ioutil.ReadDir("/sys/fs/cgroup/hugetlb"); err != nil {
|
||||
_cgroupv1HasHugetlbErr = errors.Wrap(err, "readdir /sys/fs/cgroup/hugetlb")
|
||||
_cgroupv1HasHugetlb = false
|
||||
} else {
|
||||
_cgroupv1HasHugetlbErr = nil
|
||||
_cgroupv1HasHugetlb = true
|
||||
}
|
||||
})
|
||||
return _cgroupv1HasHugetlb, _cgroupv1HasHugetlbErr
|
||||
}
|
||||
|
||||
// cgroupv2HasHugetlb returns whether the hugetlb controller is present on
|
||||
// cgroup v2.
|
||||
func cgroupv2HasHugetlb() (bool, error) {
|
||||
_cgroupv2HasHugetlbOnce.Do(func() {
|
||||
controllers, err := ioutil.ReadFile("/sys/fs/cgroup/cgroup.controllers")
|
||||
if err != nil {
|
||||
_cgroupv2HasHugetlbErr = errors.Wrap(err, "read /sys/fs/cgroup/cgroup.controllers")
|
||||
return
|
||||
}
|
||||
_cgroupv2HasHugetlb = strings.Contains(string(controllers), "hugetlb")
|
||||
})
|
||||
return _cgroupv2HasHugetlb, _cgroupv2HasHugetlbErr
|
||||
}
|
||||
|
||||
// IsCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode.
|
||||
func IsCgroup2UnifiedMode() bool {
|
||||
isUnifiedOnce.Do(func() {
|
||||
var st syscall.Statfs_t
|
||||
if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil {
|
||||
panic("cannot statfs cgroup root")
|
||||
}
|
||||
isUnified = st.Type == unix.CGROUP2_SUPER_MAGIC
|
||||
})
|
||||
return isUnified
|
||||
}
|
||||
|
||||
// WithOOMScoreAdj sets the oom score
|
||||
func WithOOMScoreAdj(config *runtime.ContainerConfig, restrict bool) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Process == nil {
|
||||
s.Process = &runtimespec.Process{}
|
||||
}
|
||||
|
||||
resources := config.GetLinux().GetResources()
|
||||
if resources == nil {
|
||||
return nil
|
||||
}
|
||||
adj := int(resources.GetOomScoreAdj())
|
||||
if restrict {
|
||||
var err error
|
||||
adj, err = restrictOOMScoreAdj(adj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.Process.OOMScoreAdj = &adj
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSysctls sets the provided sysctls onto the spec
|
||||
func WithSysctls(sysctls map[string]string) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Linux == nil {
|
||||
s.Linux = &runtimespec.Linux{}
|
||||
}
|
||||
if s.Linux.Sysctl == nil {
|
||||
s.Linux.Sysctl = make(map[string]string)
|
||||
}
|
||||
for k, v := range sysctls {
|
||||
s.Linux.Sysctl[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithPodOOMScoreAdj sets the oom score for the pod sandbox
|
||||
func WithPodOOMScoreAdj(adj int, restrict bool) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Process == nil {
|
||||
s.Process = &runtimespec.Process{}
|
||||
}
|
||||
if restrict {
|
||||
var err error
|
||||
adj, err = restrictOOMScoreAdj(adj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.Process.OOMScoreAdj = &adj
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSupplementalGroups sets the supplemental groups for the process
|
||||
func WithSupplementalGroups(groups []int64) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Process == nil {
|
||||
s.Process = &runtimespec.Process{}
|
||||
}
|
||||
var guids []uint32
|
||||
for _, g := range groups {
|
||||
guids = append(guids, uint32(g))
|
||||
}
|
||||
s.Process.User.AdditionalGids = mergeGids(s.Process.User.AdditionalGids, guids)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithPodNamespaces sets the pod namespaces for the container
|
||||
func WithPodNamespaces(config *runtime.LinuxContainerSecurityContext, pid uint32) oci.SpecOpts {
|
||||
namespaces := config.GetNamespaceOptions()
|
||||
|
||||
opts := []oci.SpecOpts{
|
||||
oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.NetworkNamespace, Path: GetNetworkNamespace(pid)}),
|
||||
oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.IPCNamespace, Path: GetIPCNamespace(pid)}),
|
||||
oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.UTSNamespace, Path: GetUTSNamespace(pid)}),
|
||||
}
|
||||
if namespaces.GetPid() != runtime.NamespaceMode_CONTAINER {
|
||||
opts = append(opts, oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.PIDNamespace, Path: GetPIDNamespace(pid)}))
|
||||
}
|
||||
return oci.Compose(opts...)
|
||||
}
|
||||
|
||||
// WithDefaultSandboxShares sets the default sandbox CPU shares
|
||||
func WithDefaultSandboxShares(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Linux == nil {
|
||||
s.Linux = &runtimespec.Linux{}
|
||||
}
|
||||
if s.Linux.Resources == nil {
|
||||
s.Linux.Resources = &runtimespec.LinuxResources{}
|
||||
}
|
||||
if s.Linux.Resources.CPU == nil {
|
||||
s.Linux.Resources.CPU = &runtimespec.LinuxCPU{}
|
||||
}
|
||||
i := uint64(DefaultSandboxCPUshares)
|
||||
s.Linux.Resources.CPU.Shares = &i
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithoutNamespace removes the provided namespace
|
||||
func WithoutNamespace(t runtimespec.LinuxNamespaceType) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Linux == nil {
|
||||
return nil
|
||||
}
|
||||
var namespaces []runtimespec.LinuxNamespace
|
||||
for i, ns := range s.Linux.Namespaces {
|
||||
if ns.Type != t {
|
||||
namespaces = append(namespaces, s.Linux.Namespaces[i])
|
||||
}
|
||||
}
|
||||
s.Linux.Namespaces = namespaces
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func nullOpt(_ context.Context, _ oci.Client, _ *containers.Container, _ *runtimespec.Spec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCurrentOOMScoreAdj() (int, error) {
|
||||
b, err := ioutil.ReadFile("/proc/self/oom_score_adj")
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get the daemon oom_score_adj")
|
||||
}
|
||||
s := strings.TrimSpace(string(b))
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get the daemon oom_score_adj")
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func restrictOOMScoreAdj(preferredOOMScoreAdj int) (int, error) {
|
||||
currentOOMScoreAdj, err := getCurrentOOMScoreAdj()
|
||||
if err != nil {
|
||||
return preferredOOMScoreAdj, err
|
||||
}
|
||||
if preferredOOMScoreAdj < currentOOMScoreAdj {
|
||||
return currentOOMScoreAdj, nil
|
||||
}
|
||||
return preferredOOMScoreAdj, nil
|
||||
}
|
||||
|
||||
const (
|
||||
// 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"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
224
vendor/github.com/containerd/cri/pkg/containerd/opts/spec_windows.go
generated
vendored
224
vendor/github.com/containerd/cri/pkg/containerd/opts/spec_windows.go
generated
vendored
@@ -1,224 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 opts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/containers"
|
||||
"github.com/containerd/containerd/oci"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
osinterface "github.com/containerd/cri/pkg/os"
|
||||
)
|
||||
|
||||
// WithWindowsNetworkNamespace sets windows network namespace for container.
|
||||
// TODO(windows): Move this into container/containerd.
|
||||
func WithWindowsNetworkNamespace(path string) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Windows == nil {
|
||||
s.Windows = &runtimespec.Windows{}
|
||||
}
|
||||
if s.Windows.Network == nil {
|
||||
s.Windows.Network = &runtimespec.WindowsNetwork{}
|
||||
}
|
||||
s.Windows.Network.NetworkNamespace = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// namedPipePath returns true if the given path is to a named pipe.
|
||||
func namedPipePath(p string) bool {
|
||||
return strings.HasPrefix(p, `\\.\pipe\`)
|
||||
}
|
||||
|
||||
// cleanMount returns a cleaned version of the mount path. The input is returned
|
||||
// as-is if it is a named pipe path.
|
||||
func cleanMount(p string) string {
|
||||
if namedPipePath(p) {
|
||||
return p
|
||||
}
|
||||
return filepath.Clean(p)
|
||||
}
|
||||
|
||||
// WithWindowsMounts sorts and adds runtime and CRI mounts to the spec for
|
||||
// windows container.
|
||||
func WithWindowsMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*runtime.Mount) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, _ *containers.Container, s *runtimespec.Spec) error {
|
||||
// mergeMounts merge CRI mounts with extra mounts. If a mount destination
|
||||
// is mounted by both a CRI mount and an extra mount, the CRI mount will
|
||||
// be kept.
|
||||
var (
|
||||
criMounts = config.GetMounts()
|
||||
mounts = append([]*runtime.Mount{}, criMounts...)
|
||||
)
|
||||
// Copy all mounts from extra mounts, except for mounts overridden by CRI.
|
||||
for _, e := range extra {
|
||||
found := false
|
||||
for _, c := range criMounts {
|
||||
if cleanMount(e.ContainerPath) == cleanMount(c.ContainerPath) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
mounts = append(mounts, e)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort mounts in number of parts. This ensures that high level mounts don't
|
||||
// shadow other mounts.
|
||||
sort.Sort(orderedMounts(mounts))
|
||||
|
||||
// Copy all mounts from default mounts, except for
|
||||
// mounts overridden by supplied mount;
|
||||
mountSet := make(map[string]struct{})
|
||||
for _, m := range mounts {
|
||||
mountSet[cleanMount(m.ContainerPath)] = struct{}{}
|
||||
}
|
||||
|
||||
defaultMounts := s.Mounts
|
||||
s.Mounts = nil
|
||||
|
||||
for _, m := range defaultMounts {
|
||||
dst := cleanMount(m.Destination)
|
||||
if _, ok := mountSet[dst]; ok {
|
||||
// filter out mount overridden by a supplied mount
|
||||
continue
|
||||
}
|
||||
s.Mounts = append(s.Mounts, m)
|
||||
}
|
||||
|
||||
for _, mount := range mounts {
|
||||
var (
|
||||
dst = mount.GetContainerPath()
|
||||
src = mount.GetHostPath()
|
||||
)
|
||||
// In the case of a named pipe mount on Windows, don't stat the file
|
||||
// or do other operations that open it, as that could interfere with
|
||||
// the listening process. filepath.Clean also breaks named pipe
|
||||
// paths, so don't use it.
|
||||
if !namedPipePath(src) {
|
||||
if _, err := osi.Stat(src); err != nil {
|
||||
// If the source doesn't exist, return an error instead
|
||||
// of creating the source. This aligns with Docker's
|
||||
// behavior on windows.
|
||||
return errors.Wrapf(err, "failed to stat %q", src)
|
||||
}
|
||||
var err error
|
||||
src, err = osi.ResolveSymbolicLink(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to resolve symlink %q", src)
|
||||
}
|
||||
// hcsshim requires clean path, especially '/' -> '\'.
|
||||
src = filepath.Clean(src)
|
||||
dst = filepath.Clean(dst)
|
||||
}
|
||||
|
||||
var options []string
|
||||
// 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")
|
||||
}
|
||||
s.Mounts = append(s.Mounts, runtimespec.Mount{
|
||||
Source: src,
|
||||
Destination: dst,
|
||||
Options: options,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithWindowsResources sets the provided resource restrictions for windows.
|
||||
func WithWindowsResources(resources *runtime.WindowsContainerResources) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if resources == nil {
|
||||
return nil
|
||||
}
|
||||
if s.Windows == nil {
|
||||
s.Windows = &runtimespec.Windows{}
|
||||
}
|
||||
if s.Windows.Resources == nil {
|
||||
s.Windows.Resources = &runtimespec.WindowsResources{}
|
||||
}
|
||||
if s.Windows.Resources.CPU == nil {
|
||||
s.Windows.Resources.CPU = &runtimespec.WindowsCPUResources{}
|
||||
}
|
||||
if s.Windows.Resources.Memory == nil {
|
||||
s.Windows.Resources.Memory = &runtimespec.WindowsMemoryResources{}
|
||||
}
|
||||
|
||||
var (
|
||||
count = uint64(resources.GetCpuCount())
|
||||
shares = uint16(resources.GetCpuShares())
|
||||
max = uint16(resources.GetCpuMaximum())
|
||||
limit = uint64(resources.GetMemoryLimitInBytes())
|
||||
)
|
||||
if count != 0 {
|
||||
s.Windows.Resources.CPU.Count = &count
|
||||
}
|
||||
if shares != 0 {
|
||||
s.Windows.Resources.CPU.Shares = &shares
|
||||
}
|
||||
if max != 0 {
|
||||
s.Windows.Resources.CPU.Maximum = &max
|
||||
}
|
||||
if limit != 0 {
|
||||
s.Windows.Resources.Memory.Limit = &limit
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithWindowsDefaultSandboxShares sets the default sandbox CPU shares
|
||||
func WithWindowsDefaultSandboxShares(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Windows == nil {
|
||||
s.Windows = &runtimespec.Windows{}
|
||||
}
|
||||
if s.Windows.Resources == nil {
|
||||
s.Windows.Resources = &runtimespec.WindowsResources{}
|
||||
}
|
||||
if s.Windows.Resources.CPU == nil {
|
||||
s.Windows.Resources.CPU = &runtimespec.WindowsCPUResources{}
|
||||
}
|
||||
i := uint16(DefaultSandboxCPUshares)
|
||||
s.Windows.Resources.CPU.Shares = &i
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithWindowsCredentialSpec assigns `credentialSpec` to the
|
||||
// `runtime.Spec.Windows.CredentialSpec` field.
|
||||
func WithWindowsCredentialSpec(credentialSpec string) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, c *containers.Container, s *runtimespec.Spec) error {
|
||||
if s.Windows == nil {
|
||||
s.Windows = &runtimespec.Windows{}
|
||||
}
|
||||
s.Windows.CredentialSpec = credentialSpec
|
||||
return nil
|
||||
}
|
||||
}
|
||||
38
vendor/github.com/containerd/cri/pkg/containerd/opts/task.go
generated
vendored
38
vendor/github.com/containerd/cri/pkg/containerd/opts/task.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
Copyright 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 opts
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/runtime/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.
|
||||
28
vendor/github.com/containerd/cri/pkg/containerd/platforms/default_unix.go
generated
vendored
28
vendor/github.com/containerd/cri/pkg/containerd/platforms/default_unix.go
generated
vendored
@@ -1,28 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 platforms
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/platforms"
|
||||
)
|
||||
|
||||
// Default returns the current platform's default platform specification.
|
||||
func Default() platforms.MatchComparer {
|
||||
return platforms.Default()
|
||||
}
|
||||
77
vendor/github.com/containerd/cri/pkg/containerd/platforms/default_windows.go
generated
vendored
77
vendor/github.com/containerd/cri/pkg/containerd/platforms/default_windows.go
generated
vendored
@@ -1,77 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 platforms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type matchComparer struct {
|
||||
defaults platforms.Matcher
|
||||
osVersionPrefix string
|
||||
}
|
||||
|
||||
// Match matches platform with the same windows major, minor
|
||||
// and build version.
|
||||
func (m matchComparer) Match(p imagespec.Platform) bool {
|
||||
if m.defaults.Match(p) {
|
||||
// TODO(windows): Figure out whether OSVersion is deprecated.
|
||||
return strings.HasPrefix(p.OSVersion, m.osVersionPrefix)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Less sorts matched platforms in front of other platforms.
|
||||
// For matched platforms, it puts platforms with larger revision
|
||||
// number in front.
|
||||
func (m matchComparer) Less(p1, p2 imagespec.Platform) bool {
|
||||
m1, m2 := m.Match(p1), m.Match(p2)
|
||||
if m1 && m2 {
|
||||
r1, r2 := revision(p1.OSVersion), revision(p2.OSVersion)
|
||||
return r1 > r2
|
||||
}
|
||||
return m1 && !m2
|
||||
}
|
||||
|
||||
func revision(v string) int {
|
||||
parts := strings.Split(v, ".")
|
||||
if len(parts) < 4 {
|
||||
return 0
|
||||
}
|
||||
r, err := strconv.Atoi(parts[3])
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Default returns the current platform's default platform specification.
|
||||
func Default() platforms.MatchComparer {
|
||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
||||
return matchComparer{
|
||||
defaults: platforms.Only(platforms.DefaultSpec()),
|
||||
osVersionPrefix: fmt.Sprintf("%d.%d.%d", major, minor, build),
|
||||
}
|
||||
}
|
||||
46
vendor/github.com/containerd/cri/pkg/containerd/util/util.go
generated
vendored
46
vendor/github.com/containerd/cri/pkg/containerd/util/util.go
generated
vendored
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
Copyright 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 util
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/containerd/cri/pkg/constants"
|
||||
)
|
||||
|
||||
// deferCleanupTimeout is the default timeout for containerd cleanup operations
|
||||
// in defer.
|
||||
const deferCleanupTimeout = 1 * time.Minute
|
||||
|
||||
// DeferContext returns a context for containerd cleanup operations in defer.
|
||||
// A default timeout is applied to avoid cleanup operation pending forever.
|
||||
func DeferContext() (context.Context, context.CancelFunc) {
|
||||
return context.WithTimeout(NamespacedContext(), deferCleanupTimeout)
|
||||
}
|
||||
|
||||
// NamespacedContext returns a context with kubernetes namespace set.
|
||||
func NamespacedContext() context.Context {
|
||||
return WithNamespace(context.Background())
|
||||
}
|
||||
|
||||
// WithNamespace adds kubernetes namespace to the context.
|
||||
func WithNamespace(ctx context.Context) context.Context {
|
||||
return namespaces.WithNamespace(ctx, constants.K8sContainerdNamespace)
|
||||
}
|
||||
57
vendor/github.com/containerd/cri/pkg/ioutil/read_closer.go
generated
vendored
57
vendor/github.com/containerd/cri/pkg/ioutil/read_closer.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 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 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
|
||||
}
|
||||
102
vendor/github.com/containerd/cri/pkg/ioutil/write_closer.go
generated
vendored
102
vendor/github.com/containerd/cri/pkg/ioutil/write_closer.go
generated
vendored
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 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 ioutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// serialWriteCloser wraps a write closer and makes sure all writes
|
||||
// are done in serial.
|
||||
// Parallel write won't intersect with each other. Use case:
|
||||
// 1) Pipe: Write content longer than PIPE_BUF.
|
||||
// See http://man7.org/linux/man-pages/man7/pipe.7.html
|
||||
// 2) <3.14 Linux Kernel: write is not atomic
|
||||
// See http://man7.org/linux/man-pages/man2/write.2.html
|
||||
type serialWriteCloser struct {
|
||||
mu sync.Mutex
|
||||
wc io.WriteCloser
|
||||
}
|
||||
|
||||
// NewSerialWriteCloser creates a SerialWriteCloser from a write closer.
|
||||
func NewSerialWriteCloser(wc io.WriteCloser) io.WriteCloser {
|
||||
return &serialWriteCloser{wc: wc}
|
||||
}
|
||||
|
||||
// Write writes a group of byte arrays in order atomically.
|
||||
func (s *serialWriteCloser) Write(data []byte) (int, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.wc.Write(data)
|
||||
}
|
||||
|
||||
// Close closes the write closer.
|
||||
func (s *serialWriteCloser) Close() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.wc.Close()
|
||||
}
|
||||
105
vendor/github.com/containerd/cri/pkg/ioutil/writer_group.go
generated
vendored
105
vendor/github.com/containerd/cri/pkg/ioutil/writer_group.go
generated
vendored
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
Copyright 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 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. The writer will be closed
|
||||
// if the writer group is closed.
|
||||
func (g *WriterGroup) Add(key string, w io.WriteCloser) {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
if g.closed {
|
||||
w.Close()
|
||||
return
|
||||
}
|
||||
g.writers[key] = w
|
||||
}
|
||||
|
||||
// Get gets a writer from the group, returns nil if the writer
|
||||
// doesn't exist.
|
||||
func (g *WriterGroup) Get(key string) io.WriteCloser {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
return g.writers[key]
|
||||
}
|
||||
|
||||
// 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 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
|
||||
}
|
||||
222
vendor/github.com/containerd/cri/pkg/netns/netns_unix.go
generated
vendored
222
vendor/github.com/containerd/cri/pkg/netns/netns_unix.go
generated
vendored
@@ -1,222 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
// Copyright 2018 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 netns
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
cnins "github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
osinterface "github.com/containerd/cri/pkg/os"
|
||||
)
|
||||
|
||||
const nsRunDir = "/var/run/netns"
|
||||
|
||||
// Some of the following functions are migrated from
|
||||
// https://github.com/containernetworking/plugins/blob/master/pkg/testutils/netns_linux.go
|
||||
|
||||
// newNS creates a new persistent (bind-mounted) network namespace and returns the
|
||||
// path to the network namespace.
|
||||
func newNS() (nsPath string, err error) {
|
||||
b := make([]byte, 16)
|
||||
if _, err := rand.Reader.Read(b); err != nil {
|
||||
return "", errors.Wrap(err, "failed to generate random netns name")
|
||||
}
|
||||
|
||||
// Create the directory for mounting network namespaces
|
||||
// This needs to be a shared mountpoint in case it is mounted in to
|
||||
// other namespaces (containers)
|
||||
if err := os.MkdirAll(nsRunDir, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// create an empty file at the mount point
|
||||
nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
nsPath = path.Join(nsRunDir, nsName)
|
||||
mountPointFd, err := os.Create(nsPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mountPointFd.Close()
|
||||
|
||||
defer func() {
|
||||
// Ensure the mount point is cleaned up on errors
|
||||
if err != nil {
|
||||
os.RemoveAll(nsPath) // nolint: errcheck
|
||||
}
|
||||
}()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
// do namespace work in a dedicated goroutine, so that we can safely
|
||||
// Lock/Unlock OSThread without upsetting the lock/unlock state of
|
||||
// the caller of this function
|
||||
go (func() {
|
||||
defer wg.Done()
|
||||
runtime.LockOSThread()
|
||||
// Don't unlock. By not unlocking, golang will kill the OS thread when the
|
||||
// goroutine is done (for go1.10+)
|
||||
|
||||
var origNS cnins.NetNS
|
||||
origNS, err = cnins.GetNS(getCurrentThreadNetNSPath())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer origNS.Close()
|
||||
|
||||
// create a new netns on the current thread
|
||||
err = unix.Unshare(unix.CLONE_NEWNET)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Put this thread back to the orig ns, since it might get reused (pre go1.10)
|
||||
defer origNS.Set() // nolint: errcheck
|
||||
|
||||
// bind mount the netns from the current thread (from /proc) onto the
|
||||
// mount point. This causes the namespace to persist, even when there
|
||||
// are no threads in the ns.
|
||||
err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to bind mount ns at %s", nsPath)
|
||||
}
|
||||
})()
|
||||
wg.Wait()
|
||||
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to create namespace")
|
||||
}
|
||||
|
||||
return nsPath, nil
|
||||
}
|
||||
|
||||
// unmountNS unmounts the NS held by the netns object. unmountNS is idempotent.
|
||||
func unmountNS(path string) error {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrap(err, "failed to stat netns")
|
||||
}
|
||||
path, err := symlink.FollowSymlinkInScope(path, "/")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to follow symlink")
|
||||
}
|
||||
if err := osinterface.Unmount(path); err != nil && !os.IsNotExist(err) {
|
||||
return errors.Wrap(err, "failed to umount netns")
|
||||
}
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return errors.Wrap(err, "failed to remove netns")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCurrentThreadNetNSPath copied from pkg/ns
|
||||
func getCurrentThreadNetNSPath() string {
|
||||
// /proc/self/ns/net returns the namespace of the main thread, not
|
||||
// of whatever thread this goroutine is running on. Make sure we
|
||||
// use the thread's net namespace since the thread is switching around
|
||||
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
|
||||
}
|
||||
|
||||
// NetNS holds network namespace.
|
||||
type NetNS struct {
|
||||
path string
|
||||
}
|
||||
|
||||
// NewNetNS creates a network namespace.
|
||||
func NewNetNS() (*NetNS, error) {
|
||||
path, err := newNS()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to setup netns")
|
||||
}
|
||||
return &NetNS{path: path}, nil
|
||||
}
|
||||
|
||||
// LoadNetNS loads existing network namespace.
|
||||
func LoadNetNS(path string) *NetNS {
|
||||
return &NetNS{path: path}
|
||||
}
|
||||
|
||||
// Remove removes network namepace. Remove is idempotent, meaning it might
|
||||
// be invoked multiple times and provides consistent result.
|
||||
func (n *NetNS) Remove() error {
|
||||
return unmountNS(n.path)
|
||||
}
|
||||
|
||||
// Closed checks whether the network namespace has been closed.
|
||||
func (n *NetNS) Closed() (bool, error) {
|
||||
ns, err := cnins.GetNS(n.path)
|
||||
if err != nil {
|
||||
if _, ok := err.(cnins.NSPathNotExistErr); ok {
|
||||
// The network namespace has already been removed.
|
||||
return true, nil
|
||||
}
|
||||
if _, ok := err.(cnins.NSPathNotNSErr); ok {
|
||||
// The network namespace is not mounted, remove it.
|
||||
if err := os.RemoveAll(n.path); err != nil {
|
||||
return false, errors.Wrap(err, "remove netns")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "get netns fd")
|
||||
}
|
||||
if err := ns.Close(); err != nil {
|
||||
return false, errors.Wrap(err, "close netns fd")
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// GetPath returns network namespace path for sandbox container
|
||||
func (n *NetNS) GetPath() string {
|
||||
return n.path
|
||||
}
|
||||
|
||||
// Do runs a function in the network namespace.
|
||||
func (n *NetNS) Do(f func(cnins.NetNS) error) error {
|
||||
ns, err := cnins.GetNS(n.path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get netns fd")
|
||||
}
|
||||
defer ns.Close() // nolint: errcheck
|
||||
return ns.Do(f)
|
||||
}
|
||||
78
vendor/github.com/containerd/cri/pkg/netns/netns_windows.go
generated
vendored
78
vendor/github.com/containerd/cri/pkg/netns/netns_windows.go
generated
vendored
@@ -1,78 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 netns
|
||||
|
||||
import "github.com/Microsoft/hcsshim/hcn"
|
||||
|
||||
// NetNS holds network namespace for sandbox
|
||||
type NetNS struct {
|
||||
path string
|
||||
}
|
||||
|
||||
// NewNetNS creates a network namespace for the sandbox
|
||||
func NewNetNS() (*NetNS, error) {
|
||||
temp := hcn.HostComputeNamespace{}
|
||||
hcnNamespace, err := temp.Create()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NetNS{path: hcnNamespace.Id}, nil
|
||||
}
|
||||
|
||||
// LoadNetNS loads existing network namespace.
|
||||
func LoadNetNS(path string) *NetNS {
|
||||
return &NetNS{path: path}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
hcnNamespace, err := hcn.GetNamespaceByID(n.path)
|
||||
if err != nil {
|
||||
if hcn.IsNotFoundError(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = hcnNamespace.Delete()
|
||||
if err == nil || hcn.IsNotFoundError(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Closed checks whether the network namespace has been closed.
|
||||
func (n *NetNS) Closed() (bool, error) {
|
||||
_, err := hcn.GetNamespaceByID(n.path)
|
||||
if err == nil {
|
||||
return false, nil
|
||||
}
|
||||
if hcn.IsNotFoundError(err) {
|
||||
return true, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
// GetPath returns network namespace path for sandbox container
|
||||
func (n *NetNS) GetPath() string {
|
||||
return n.path
|
||||
}
|
||||
|
||||
// NOTE: Do function is not supported.
|
||||
102
vendor/github.com/containerd/cri/pkg/os/os.go
generated
vendored
102
vendor/github.com/containerd/cri/pkg/os/os.go
generated
vendored
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 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 os
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
)
|
||||
|
||||
// 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
|
||||
Stat(name string) (os.FileInfo, error)
|
||||
ResolveSymbolicLink(name string) (string, error)
|
||||
FollowSymlinkInScope(path, scope string) (string, error)
|
||||
CopyFile(src, dest string, perm os.FileMode) error
|
||||
WriteFile(filename string, data []byte, perm os.FileMode) error
|
||||
Hostname() (string, error)
|
||||
}
|
||||
|
||||
// RealOS is used to dispatch the real system level operations.
|
||||
type RealOS struct{}
|
||||
|
||||
// MkdirAll 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// FollowSymlinkInScope will call symlink.FollowSymlinkInScope.
|
||||
func (RealOS) FollowSymlinkInScope(path, scope string) (string, error) {
|
||||
return symlink.FollowSymlinkInScope(path, scope)
|
||||
}
|
||||
|
||||
// CopyFile will copy 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)
|
||||
}
|
||||
|
||||
// Hostname will call os.Hostname to get the hostname of the host.
|
||||
func (RealOS) Hostname() (string, error) {
|
||||
return os.Hostname()
|
||||
}
|
||||
59
vendor/github.com/containerd/cri/pkg/os/os_unix.go
generated
vendored
59
vendor/github.com/containerd/cri/pkg/os/os_unix.go
generated
vendored
@@ -1,59 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 os
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/mount"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// UNIX collects unix system level operations that need to be
|
||||
// mocked out during tests.
|
||||
type UNIX interface {
|
||||
Mount(source string, target string, fstype string, flags uintptr, data string) error
|
||||
Unmount(target string) error
|
||||
LookupMount(path string) (mount.Info, error)
|
||||
}
|
||||
|
||||
// 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 Unmount to unmount the file.
|
||||
func (RealOS) Unmount(target string) error {
|
||||
return Unmount(target)
|
||||
}
|
||||
|
||||
// LookupMount gets mount info of a given path.
|
||||
func (RealOS) LookupMount(path string) (mount.Info, error) {
|
||||
return mount.Lookup(path)
|
||||
}
|
||||
|
||||
// Unmount unmounts the target. It does not return an error in case the target is not mounted.
|
||||
// In case the target does not exist, the appropriate error is returned.
|
||||
func Unmount(target string) error {
|
||||
err := unix.Unmount(target, unix.MNT_DETACH)
|
||||
if err == unix.EINVAL {
|
||||
// ignore "not mounted" error
|
||||
err = nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
102
vendor/github.com/containerd/cri/pkg/registrar/registrar.go
generated
vendored
102
vendor/github.com/containerd/cri/pkg/registrar/registrar.go
generated
vendored
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 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 registrar
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 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 errors.Errorf("invalid name %q or key %q", name, key)
|
||||
}
|
||||
|
||||
if k, exists := r.nameToKey[name]; exists {
|
||||
if k != key {
|
||||
return errors.Errorf("name %q is reserved for %q", name, k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if n, exists := r.keyToName[key]; exists {
|
||||
if n != name {
|
||||
return errors.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)
|
||||
}
|
||||
88
vendor/github.com/containerd/cri/pkg/seccomp/seccomp_linux.go
generated
vendored
88
vendor/github.com/containerd/cri/pkg/seccomp/seccomp_linux.go
generated
vendored
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright The runc 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 seccomp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsEnabled returns if the kernel has been configured to support seccomp.
|
||||
// From https://github.com/opencontainers/runc/blob/v1.0.0-rc91/libcontainer/seccomp/seccomp_linux.go#L86-L102
|
||||
func IsEnabled() bool {
|
||||
// Try to read from /proc/self/status for kernels > 3.8
|
||||
s, err := parseStatusFile("/proc/self/status")
|
||||
if err != nil {
|
||||
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
||||
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
||||
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
||||
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
_, ok := s["Seccomp"]
|
||||
return ok
|
||||
}
|
||||
|
||||
// parseStatusFile is from https://github.com/opencontainers/runc/blob/v1.0.0-rc91/libcontainer/seccomp/seccomp_linux.go#L243-L268
|
||||
func parseStatusFile(path string) (map[string]string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
status := make(map[string]string)
|
||||
|
||||
for s.Scan() {
|
||||
text := s.Text()
|
||||
parts := strings.Split(text, ":")
|
||||
|
||||
if len(parts) <= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
status[parts[0]] = parts[1]
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
23
vendor/github.com/containerd/cri/pkg/seccomp/seccomp_unsupported.go
generated
vendored
23
vendor/github.com/containerd/cri/pkg/seccomp/seccomp_unsupported.go
generated
vendored
@@ -1,23 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 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 seccomp
|
||||
|
||||
func IsEnabled() bool {
|
||||
return false
|
||||
}
|
||||
34
vendor/github.com/containerd/cri/pkg/server/bandwidth/doc.go
generated
vendored
34
vendor/github.com/containerd/cri/pkg/server/bandwidth/doc.go
generated
vendored
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2015 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 bandwidth provides utilities for bandwidth shaping
|
||||
package bandwidth
|
||||
72
vendor/github.com/containerd/cri/pkg/server/bandwidth/fake_shaper.go
generated
vendored
72
vendor/github.com/containerd/cri/pkg/server/bandwidth/fake_shaper.go
generated
vendored
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2015 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 bandwidth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
// FakeShaper provides an implementation of the bandwith.Shaper.
|
||||
// Beware this is implementation has no features besides Reset and GetCIDRs.
|
||||
type FakeShaper struct {
|
||||
CIDRs []string
|
||||
ResetCIDRs []string
|
||||
}
|
||||
|
||||
// Limit is not implemented
|
||||
func (f *FakeShaper) Limit(cidr string, egress, ingress *resource.Quantity) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// Reset appends a particular CIDR to the set of ResetCIDRs being managed by this shaper
|
||||
func (f *FakeShaper) Reset(cidr string) error {
|
||||
f.ResetCIDRs = append(f.ResetCIDRs, cidr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReconcileInterface is not implemented
|
||||
func (f *FakeShaper) ReconcileInterface() error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// ReconcileCIDR is not implemented
|
||||
func (f *FakeShaper) ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// GetCIDRs returns the set of CIDRs that are being managed by this shaper
|
||||
func (f *FakeShaper) GetCIDRs() ([]string, error) {
|
||||
return f.CIDRs, nil
|
||||
}
|
||||
56
vendor/github.com/containerd/cri/pkg/server/bandwidth/interfaces.go
generated
vendored
56
vendor/github.com/containerd/cri/pkg/server/bandwidth/interfaces.go
generated
vendored
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2015 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 bandwidth
|
||||
|
||||
import "k8s.io/apimachinery/pkg/api/resource"
|
||||
|
||||
// Shaper is designed so that the shaper structs created
|
||||
// satisfy the Shaper interface.
|
||||
type Shaper interface {
|
||||
// Limit the bandwidth for a particular CIDR on a particular interface
|
||||
// * ingress and egress are in bits/second
|
||||
// * cidr is expected to be a valid network CIDR (e.g. '1.2.3.4/32' or '10.20.0.1/16')
|
||||
// 'egress' bandwidth limit applies to all packets on the interface whose source matches 'cidr'
|
||||
// 'ingress' bandwidth limit applies to all packets on the interface whose destination matches 'cidr'
|
||||
// Limits are aggregate limits for the CIDR, not per IP address. CIDRs must be unique, but can be overlapping, traffic
|
||||
// that matches multiple CIDRs counts against all limits.
|
||||
Limit(cidr string, egress, ingress *resource.Quantity) error
|
||||
// Remove a bandwidth limit for a particular CIDR on a particular network interface
|
||||
Reset(cidr string) error
|
||||
// Reconcile the interface managed by this shaper with the state on the ground.
|
||||
ReconcileInterface() error
|
||||
// Reconcile a CIDR managed by this shaper with the state on the ground
|
||||
ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error
|
||||
// GetCIDRs returns the set of CIDRs that are being managed by this shaper
|
||||
GetCIDRs() ([]string, error)
|
||||
}
|
||||
361
vendor/github.com/containerd/cri/pkg/server/bandwidth/linux.go
generated
vendored
361
vendor/github.com/containerd/cri/pkg/server/bandwidth/linux.go
generated
vendored
@@ -1,361 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2015 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 bandwidth
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/utils/exec"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
classShowMatcher = regexp.MustCompile(`class htb (1:\d+)`)
|
||||
classAndHandleMatcher = regexp.MustCompile(`filter parent 1:.*fh (\d+::\d+).*flowid (\d+:\d+)`)
|
||||
)
|
||||
|
||||
// tcShaper provides an implementation of the Shaper interface on Linux using the 'tc' tool.
|
||||
// In general, using this requires that the caller posses the NET_CAP_ADMIN capability, though if you
|
||||
// do this within an container, it only requires the NS_CAPABLE capability for manipulations to that
|
||||
// container's network namespace.
|
||||
// Uses the hierarchical token bucket queuing discipline (htb), this requires Linux 2.4.20 or newer
|
||||
// or a custom kernel with that queuing discipline backported.
|
||||
type tcShaper struct {
|
||||
e exec.Interface
|
||||
iface string
|
||||
}
|
||||
|
||||
// NewTCShaper makes a new tcShaper for the given interface
|
||||
func NewTCShaper(iface string) Shaper {
|
||||
shaper := &tcShaper{
|
||||
e: exec.New(),
|
||||
iface: iface,
|
||||
}
|
||||
return shaper
|
||||
}
|
||||
|
||||
func (t *tcShaper) execAndLog(cmdStr string, args ...string) error {
|
||||
klog.V(6).Infof("Running: %s %s", cmdStr, strings.Join(args, " "))
|
||||
cmd := t.e.Command(cmdStr, args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
klog.V(6).Infof("Output from tc: %s", string(out))
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *tcShaper) nextClassID() (int, error) {
|
||||
data, err := t.e.Command("tc", "class", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(data))
|
||||
classes := sets.String{}
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
// skip empty lines
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
// expected tc line:
|
||||
// class htb 1:1 root prio 0 rate 1000Kbit ceil 1000Kbit burst 1600b cburst 1600b
|
||||
matches := classShowMatcher.FindStringSubmatch(line)
|
||||
if len(matches) != 2 {
|
||||
return -1, fmt.Errorf("unexpected output from tc: %s (%v)", scanner.Text(), matches)
|
||||
}
|
||||
classes.Insert(matches[1])
|
||||
}
|
||||
|
||||
// Make sure it doesn't go forever
|
||||
for nextClass := 1; nextClass < 10000; nextClass++ {
|
||||
if !classes.Has(fmt.Sprintf("1:%d", nextClass)) {
|
||||
return nextClass, nil
|
||||
}
|
||||
}
|
||||
// This should really never happen
|
||||
return -1, fmt.Errorf("exhausted class space, please try again")
|
||||
}
|
||||
|
||||
// Convert a CIDR from text to a hex representation
|
||||
// Strips any masked parts of the IP, so 1.2.3.4/16 becomes hex(1.2.0.0)/ffffffff
|
||||
func hexCIDR(cidr string) (string, error) {
|
||||
ip, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip = ip.Mask(ipnet.Mask)
|
||||
hexIP := hex.EncodeToString([]byte(ip))
|
||||
hexMask := ipnet.Mask.String()
|
||||
return hexIP + "/" + hexMask, nil
|
||||
}
|
||||
|
||||
// Convert a CIDR from hex representation to text, opposite of the above.
|
||||
func asciiCIDR(cidr string) (string, error) {
|
||||
parts := strings.Split(cidr, "/")
|
||||
if len(parts) != 2 {
|
||||
return "", fmt.Errorf("unexpected CIDR format: %s", cidr)
|
||||
}
|
||||
ipData, err := hex.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip := net.IP(ipData)
|
||||
|
||||
maskData, err := hex.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mask := net.IPMask(maskData)
|
||||
size, _ := mask.Size()
|
||||
|
||||
return fmt.Sprintf("%s/%d", ip.String(), size), nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) findCIDRClass(cidr string) (classAndHandleList [][]string, found bool, err error) {
|
||||
data, err := t.e.Command("tc", "filter", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return classAndHandleList, false, err
|
||||
}
|
||||
|
||||
hex, err := hexCIDR(cidr)
|
||||
if err != nil {
|
||||
return classAndHandleList, false, err
|
||||
}
|
||||
spec := fmt.Sprintf("match %s", hex)
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(data))
|
||||
filter := ""
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "filter") {
|
||||
filter = line
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, spec) {
|
||||
// expected tc line:
|
||||
// `filter parent 1: protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1` (old version) or
|
||||
// `filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw` (new version)
|
||||
matches := classAndHandleMatcher.FindStringSubmatch(filter)
|
||||
if len(matches) != 3 {
|
||||
return classAndHandleList, false, fmt.Errorf("unexpected output from tc: %s %d (%v)", filter, len(matches), matches)
|
||||
}
|
||||
resultTmp := []string{matches[2], matches[1]}
|
||||
classAndHandleList = append(classAndHandleList, resultTmp)
|
||||
}
|
||||
}
|
||||
if len(classAndHandleList) > 0 {
|
||||
return classAndHandleList, true, nil
|
||||
}
|
||||
return classAndHandleList, false, nil
|
||||
}
|
||||
|
||||
func makeKBitString(rsrc *resource.Quantity) string {
|
||||
return fmt.Sprintf("%dkbit", (rsrc.Value() / 1000))
|
||||
}
|
||||
|
||||
func (t *tcShaper) makeNewClass(rate string) (int, error) {
|
||||
class, err := t.nextClassID()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if err := t.execAndLog("tc", "class", "add",
|
||||
"dev", t.iface,
|
||||
"parent", "1:",
|
||||
"classid", fmt.Sprintf("1:%d", class),
|
||||
"htb", "rate", rate); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return class, nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) Limit(cidr string, upload, download *resource.Quantity) (err error) {
|
||||
var downloadClass, uploadClass int
|
||||
if download != nil {
|
||||
if downloadClass, err = t.makeNewClass(makeKBitString(download)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.execAndLog("tc", "filter", "add",
|
||||
"dev", t.iface,
|
||||
"protocol", "ip",
|
||||
"parent", "1:0",
|
||||
"prio", "1", "u32",
|
||||
"match", "ip", "dst", cidr,
|
||||
"flowid", fmt.Sprintf("1:%d", downloadClass)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if upload != nil {
|
||||
if uploadClass, err = t.makeNewClass(makeKBitString(upload)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.execAndLog("tc", "filter", "add",
|
||||
"dev", t.iface,
|
||||
"protocol", "ip",
|
||||
"parent", "1:0",
|
||||
"prio", "1", "u32",
|
||||
"match", "ip", "src", cidr,
|
||||
"flowid", fmt.Sprintf("1:%d", uploadClass)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// tests to see if an interface exists, if it does, return true and the status line for the interface
|
||||
// returns false, "", <err> if an error occurs.
|
||||
func (t *tcShaper) interfaceExists() (bool, string, error) {
|
||||
data, err := t.e.Command("tc", "qdisc", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
value := strings.TrimSpace(string(data))
|
||||
if len(value) == 0 {
|
||||
return false, "", nil
|
||||
}
|
||||
// Newer versions of tc and/or the kernel return the following instead of nothing:
|
||||
// qdisc noqueue 0: root refcnt 2
|
||||
fields := strings.Fields(value)
|
||||
if len(fields) > 1 && fields[1] == "noqueue" {
|
||||
return false, "", nil
|
||||
}
|
||||
return true, value, nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) ReconcileCIDR(cidr string, upload, download *resource.Quantity) error {
|
||||
_, found, err := t.findCIDRClass(cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return t.Limit(cidr, upload, download)
|
||||
}
|
||||
// TODO: actually check bandwidth limits here
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) ReconcileInterface() error {
|
||||
exists, output, err := t.interfaceExists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
klog.V(4).Info("Didn't find bandwidth interface, creating")
|
||||
return t.initializeInterface()
|
||||
}
|
||||
fields := strings.Split(output, " ")
|
||||
if len(fields) < 12 || fields[1] != "htb" || fields[2] != "1:" {
|
||||
if err := t.deleteInterface(fields[2]); err != nil {
|
||||
return err
|
||||
}
|
||||
return t.initializeInterface()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) initializeInterface() error {
|
||||
return t.execAndLog("tc", "qdisc", "add", "dev", t.iface, "root", "handle", "1:", "htb", "default", "30")
|
||||
}
|
||||
|
||||
func (t *tcShaper) Reset(cidr string) error {
|
||||
classAndHandle, found, err := t.findCIDRClass(cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("Failed to find cidr: %s on interface: %s", cidr, t.iface)
|
||||
}
|
||||
for i := 0; i < len(classAndHandle); i++ {
|
||||
if err := t.execAndLog("tc", "filter", "del",
|
||||
"dev", t.iface,
|
||||
"parent", "1:",
|
||||
"proto", "ip",
|
||||
"prio", "1",
|
||||
"handle", classAndHandle[i][1], "u32"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.execAndLog("tc", "class", "del",
|
||||
"dev", t.iface,
|
||||
"parent", "1:",
|
||||
"classid", classAndHandle[i][0]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) deleteInterface(class string) error {
|
||||
return t.execAndLog("tc", "qdisc", "delete", "dev", t.iface, "root", "handle", class)
|
||||
}
|
||||
|
||||
func (t *tcShaper) GetCIDRs() ([]string, error) {
|
||||
data, err := t.e.Command("tc", "filter", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := []string{}
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(data))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, "match") {
|
||||
parts := strings.Split(line, " ")
|
||||
// expected tc line:
|
||||
// match <cidr> at <number>
|
||||
if len(parts) != 4 {
|
||||
return nil, fmt.Errorf("unexpected output: %v", parts)
|
||||
}
|
||||
cidr, err := asciiCIDR(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, cidr)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
69
vendor/github.com/containerd/cri/pkg/server/bandwidth/unsupported.go
generated
vendored
69
vendor/github.com/containerd/cri/pkg/server/bandwidth/unsupported.go
generated
vendored
@@ -1,69 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2015 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 bandwidth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
type unsupportedShaper struct {
|
||||
}
|
||||
|
||||
// NewTCShaper makes a new unsupportedShapper for the given interface
|
||||
func NewTCShaper(iface string) Shaper {
|
||||
return &unsupportedShaper{}
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) Limit(cidr string, egress, ingress *resource.Quantity) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) Reset(cidr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) ReconcileInterface() error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) GetCIDRs() ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
82
vendor/github.com/containerd/cri/pkg/server/bandwidth/utils.go
generated
vendored
82
vendor/github.com/containerd/cri/pkg/server/bandwidth/utils.go
generated
vendored
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2015 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 bandwidth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
var minRsrc = resource.MustParse("1k")
|
||||
var maxRsrc = resource.MustParse("1P")
|
||||
|
||||
func validateBandwidthIsReasonable(rsrc *resource.Quantity) error {
|
||||
if rsrc.Value() < minRsrc.Value() {
|
||||
return fmt.Errorf("resource is unreasonably small (< 1kbit)")
|
||||
}
|
||||
if rsrc.Value() > maxRsrc.Value() {
|
||||
return fmt.Errorf("resoruce is unreasonably large (> 1Pbit)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractPodBandwidthResources extracts the ingress and egress from the given pod annotations
|
||||
func ExtractPodBandwidthResources(podAnnotations map[string]string) (ingress, egress *resource.Quantity, err error) {
|
||||
if podAnnotations == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
str, found := podAnnotations["kubernetes.io/ingress-bandwidth"]
|
||||
if found {
|
||||
ingressValue, err := resource.ParseQuantity(str)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ingress = &ingressValue
|
||||
if err := validateBandwidthIsReasonable(ingress); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
str, found = podAnnotations["kubernetes.io/egress-bandwidth"]
|
||||
if found {
|
||||
egressValue, err := resource.ParseQuantity(str)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
egress = &egressValue
|
||||
if err := validateBandwidthIsReasonable(egress); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return ingress, egress, nil
|
||||
}
|
||||
121
vendor/github.com/containerd/cri/pkg/server/cni_conf_syncer.go
generated
vendored
121
vendor/github.com/containerd/cri/pkg/server/cni_conf_syncer.go
generated
vendored
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
cni "github.com/containerd/go-cni"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// cniNetConfSyncer is used to reload cni network conf triggered by fs change
|
||||
// events.
|
||||
type cniNetConfSyncer struct {
|
||||
// only used for lastSyncStatus
|
||||
sync.RWMutex
|
||||
lastSyncStatus error
|
||||
|
||||
watcher *fsnotify.Watcher
|
||||
confDir string
|
||||
netPlugin cni.CNI
|
||||
loadOpts []cni.CNIOpt
|
||||
}
|
||||
|
||||
// newCNINetConfSyncer creates cni network conf syncer.
|
||||
func newCNINetConfSyncer(confDir string, netPlugin cni.CNI, loadOpts []cni.CNIOpt) (*cniNetConfSyncer, error) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create fsnotify watcher")
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(confDir, 0700); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create cni conf dir=%s for watch", confDir)
|
||||
}
|
||||
|
||||
if err := watcher.Add(confDir); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to watch cni conf dir %s", confDir)
|
||||
}
|
||||
|
||||
syncer := &cniNetConfSyncer{
|
||||
watcher: watcher,
|
||||
confDir: confDir,
|
||||
netPlugin: netPlugin,
|
||||
loadOpts: loadOpts,
|
||||
}
|
||||
|
||||
if err := syncer.netPlugin.Load(syncer.loadOpts...); err != nil {
|
||||
logrus.WithError(err).Error("failed to load cni during init, please check CRI plugin status before setting up network for pods")
|
||||
syncer.updateLastStatus(err)
|
||||
}
|
||||
return syncer, nil
|
||||
}
|
||||
|
||||
// syncLoop monitors any fs change events from cni conf dir and tries to reload
|
||||
// cni configuration.
|
||||
func (syncer *cniNetConfSyncer) syncLoop() error {
|
||||
for {
|
||||
select {
|
||||
case event := <-syncer.watcher.Events:
|
||||
// Only reload config when receiving write/rename/remove
|
||||
// events
|
||||
//
|
||||
// TODO(fuweid): Might only reload target cni config
|
||||
// files to prevent no-ops.
|
||||
if event.Op&(fsnotify.Chmod|fsnotify.Create) > 0 {
|
||||
logrus.Debugf("ignore event from cni conf dir: %s", event)
|
||||
continue
|
||||
}
|
||||
logrus.Debugf("receiving change event from cni conf dir: %s", event)
|
||||
|
||||
lerr := syncer.netPlugin.Load(syncer.loadOpts...)
|
||||
if lerr != nil {
|
||||
logrus.WithError(lerr).
|
||||
Errorf("failed to reload cni configuration after receiving fs change event(%s)", event)
|
||||
}
|
||||
syncer.updateLastStatus(lerr)
|
||||
|
||||
case err := <-syncer.watcher.Errors:
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("failed to continue sync cni conf change")
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lastStatus retrieves last sync status.
|
||||
func (syncer *cniNetConfSyncer) lastStatus() error {
|
||||
syncer.RLock()
|
||||
defer syncer.RUnlock()
|
||||
return syncer.lastSyncStatus
|
||||
}
|
||||
|
||||
// updateLastStatus will be called after every single cni load.
|
||||
func (syncer *cniNetConfSyncer) updateLastStatus(err error) {
|
||||
syncer.Lock()
|
||||
defer syncer.Unlock()
|
||||
syncer.lastSyncStatus = err
|
||||
}
|
||||
|
||||
// stop stops watcher in the syncLoop.
|
||||
func (syncer *cniNetConfSyncer) stop() error {
|
||||
return syncer.watcher.Close()
|
||||
}
|
||||
84
vendor/github.com/containerd/cri/pkg/server/container_attach.go
generated
vendored
84
vendor/github.com/containerd/cri/pkg/server/container_attach.go
generated
vendored
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"io"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
cio "github.com/containerd/cri/pkg/server/io"
|
||||
)
|
||||
|
||||
// Attach prepares a streaming endpoint to attach to a running container, and returns the address.
|
||||
func (c *criService) Attach(ctx context.Context, r *runtime.AttachRequest) (*runtime.AttachResponse, error) {
|
||||
cntr, err := c.containerStore.Get(r.GetContainerId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find container in store")
|
||||
}
|
||||
state := cntr.Status.Get().State()
|
||||
if state != runtime.ContainerState_CONTAINER_RUNNING {
|
||||
return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
|
||||
}
|
||||
return c.streamServer.GetAttach(r)
|
||||
}
|
||||
|
||||
func (c *criService) attachContainer(ctx context.Context, id string, stdin io.Reader, stdout, stderr io.WriteCloser,
|
||||
tty bool, resize <-chan remotecommand.TerminalSize) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
// Get container from our container store.
|
||||
cntr, err := c.containerStore.Get(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to find container %q in store", id)
|
||||
}
|
||||
id = cntr.ID
|
||||
|
||||
state := cntr.Status.Get().State()
|
||||
if state != runtime.ContainerState_CONTAINER_RUNNING {
|
||||
return errors.Errorf("container is in %s state", criContainerStateToString(state))
|
||||
}
|
||||
|
||||
task, err := cntr.Container.Task(ctx, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load task")
|
||||
}
|
||||
handleResizing(ctx, resize, func(size remotecommand.TerminalSize) {
|
||||
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
|
||||
log.G(ctx).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.
|
||||
cntr.IO.Attach(opts)
|
||||
return nil
|
||||
}
|
||||
343
vendor/github.com/containerd/cri/pkg/server/container_create.go
generated
vendored
343
vendor/github.com/containerd/cri/pkg/server/container_create.go
generated
vendored
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/containers"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
selinux "github.com/opencontainers/selinux/go-selinux"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
customopts "github.com/containerd/cri/pkg/containerd/opts"
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
cio "github.com/containerd/cri/pkg/server/io"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
"github.com/containerd/cri/pkg/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
typeurl.Register(&containerstore.Metadata{},
|
||||
"github.com/containerd/cri/pkg/store/container", "Metadata")
|
||||
}
|
||||
|
||||
// CreateContainer creates a new container in the given PodSandbox.
|
||||
func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (_ *runtime.CreateContainerResponse, retErr error) {
|
||||
config := r.GetConfig()
|
||||
log.G(ctx).Debugf("Container config %+v", config)
|
||||
sandboxConfig := r.GetSandboxConfig()
|
||||
sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to find sandbox id %q", r.GetPodSandboxId())
|
||||
}
|
||||
sandboxID := sandbox.ID
|
||||
s, err := sandbox.Container.Task(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get sandbox container task")
|
||||
}
|
||||
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()
|
||||
metadata := config.GetMetadata()
|
||||
if metadata == nil {
|
||||
return nil, errors.New("container config must include metadata")
|
||||
}
|
||||
containerName := metadata.Name
|
||||
name := makeContainerName(metadata, sandboxConfig.GetMetadata())
|
||||
log.G(ctx).Debugf("Generated id %q for container %q", id, name)
|
||||
if err = c.containerNameIndex.Reserve(name, id); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to reserve container name %q", name)
|
||||
}
|
||||
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.
|
||||
image, err := c.localResolve(config.GetImage().GetImage())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to resolve image %q", config.GetImage().GetImage())
|
||||
}
|
||||
containerdImage, err := c.toContainerdImage(ctx, image)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get image from containerd %q", image.ID)
|
||||
}
|
||||
|
||||
// Run container using the same runtime with sandbox.
|
||||
sandboxInfo, err := sandbox.Container.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get sandbox %q info", sandboxID)
|
||||
}
|
||||
|
||||
// Create container root directory.
|
||||
containerRootDir := c.getContainerRootDir(id)
|
||||
if err = c.os.MkdirAll(containerRootDir, 0755); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create container root directory %q",
|
||||
containerRootDir)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Cleanup the container root directory.
|
||||
if err = c.os.RemoveAll(containerRootDir); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to remove container root directory %q",
|
||||
containerRootDir)
|
||||
}
|
||||
}
|
||||
}()
|
||||
volatileContainerRootDir := c.getVolatileContainerRootDir(id)
|
||||
if err = c.os.MkdirAll(volatileContainerRootDir, 0755); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create volatile container root directory %q",
|
||||
volatileContainerRootDir)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Cleanup the volatile container root directory.
|
||||
if err = c.os.RemoveAll(volatileContainerRootDir); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to remove volatile container root directory %q",
|
||||
volatileContainerRootDir)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var volumeMounts []*runtime.Mount
|
||||
if !c.config.IgnoreImageDefinedVolumes {
|
||||
// Create container image volumes mounts.
|
||||
volumeMounts = c.volumeMounts(containerRootDir, config.GetMounts(), &image.ImageSpec.Config)
|
||||
} else if len(image.ImageSpec.Config.Volumes) != 0 {
|
||||
log.G(ctx).Debugf("Ignoring volumes defined in image %v because IgnoreImageDefinedVolumes is set", image.ID)
|
||||
}
|
||||
|
||||
// Generate container mounts.
|
||||
mounts := c.containerMounts(sandboxID, config)
|
||||
|
||||
ociRuntime, err := c.getSandboxRuntime(sandboxConfig, sandbox.Metadata.RuntimeHandler)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get sandbox runtime")
|
||||
}
|
||||
log.G(ctx).Debugf("Use OCI runtime %+v for sandbox %q and container %q", ociRuntime, sandboxID, id)
|
||||
|
||||
spec, err := c.containerSpec(id, sandboxID, sandboxPid, sandbox.NetNSPath, containerName, config, sandboxConfig,
|
||||
&image.ImageSpec.Config, append(mounts, volumeMounts...), ociRuntime)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to generate container %q spec", id)
|
||||
}
|
||||
|
||||
meta.ProcessLabel = spec.Process.SelinuxLabel
|
||||
|
||||
// handle any KVM based runtime
|
||||
if err := modifyProcessLabel(ociRuntime.Type, spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.GetLinux().GetSecurityContext().GetPrivileged() {
|
||||
// If privileged don't set the SELinux label but still record it on the container so
|
||||
// the unused MCS label can be release later
|
||||
spec.Process.SelinuxLabel = ""
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
selinux.ReleaseLabel(spec.Process.SelinuxLabel)
|
||||
}
|
||||
}()
|
||||
|
||||
log.G(ctx).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, containerdImage),
|
||||
}
|
||||
if len(volumeMounts) > 0 {
|
||||
mountMap := make(map[string]string)
|
||||
for _, v := range volumeMounts {
|
||||
mountMap[filepath.Clean(v.HostPath)] = v.ContainerPath
|
||||
}
|
||||
opts = append(opts, customopts.WithVolumes(mountMap))
|
||||
}
|
||||
meta.ImageRef = image.ID
|
||||
meta.StopSignal = image.ImageSpec.Config.StopSignal
|
||||
|
||||
// Validate log paths and compose full container log path.
|
||||
if sandboxConfig.GetLogDirectory() != "" && config.GetLogPath() != "" {
|
||||
meta.LogPath = filepath.Join(sandboxConfig.GetLogDirectory(), config.GetLogPath())
|
||||
log.G(ctx).Debugf("Composed container full log path %q using sandbox log dir %q and container log path %q",
|
||||
meta.LogPath, sandboxConfig.GetLogDirectory(), config.GetLogPath())
|
||||
} else {
|
||||
log.G(ctx).Infof("Logging will be disabled due to empty log paths for sandbox (%q) or container (%q)",
|
||||
sandboxConfig.GetLogDirectory(), config.GetLogPath())
|
||||
}
|
||||
|
||||
containerIO, err := cio.NewContainerIO(id,
|
||||
cio.WithNewFIFOs(volatileContainerRootDir, config.GetTty(), config.GetStdin()))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create container io")
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if err := containerIO.Close(); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to close container io %q", id)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
specOpts, err := c.containerSpecOpts(config, &image.ImageSpec.Config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "")
|
||||
}
|
||||
|
||||
containerLabels := buildLabels(config.Labels, containerKindContainer)
|
||||
|
||||
runtimeOptions, err := getRuntimeOptions(sandboxInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get runtime options")
|
||||
}
|
||||
opts = append(opts,
|
||||
containerd.WithSpec(spec, specOpts...),
|
||||
containerd.WithRuntime(sandboxInfo.Runtime.Name, runtimeOptions),
|
||||
containerd.WithContainerLabels(containerLabels),
|
||||
containerd.WithContainerExtension(containerMetadataExtension, &meta))
|
||||
var cntr containerd.Container
|
||||
if cntr, err = c.client.NewContainer(ctx, id, opts...); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create containerd container")
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||
defer deferCancel()
|
||||
if err := cntr.Delete(deferCtx, containerd.WithSnapshotCleanup); err != nil {
|
||||
log.G(ctx).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, errors.Wrapf(err, "failed to create internal container object for %q", id)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Cleanup container checkpoint on error.
|
||||
if err := container.Delete(); err != nil {
|
||||
log.G(ctx).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, errors.Wrapf(err, "failed to add container %q into store", id)
|
||||
}
|
||||
|
||||
return &runtime.CreateContainerResponse{ContainerId: id}, nil
|
||||
}
|
||||
|
||||
// volumeMounts 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 *criService) volumeMounts(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,
|
||||
SelinuxRelabel: true,
|
||||
})
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
||||
// runtimeSpec returns a default runtime spec used in cri-containerd.
|
||||
func (c *criService) runtimeSpec(id string, baseSpecFile string, opts ...oci.SpecOpts) (*runtimespec.Spec, error) {
|
||||
// GenerateSpec needs namespace.
|
||||
ctx := ctrdutil.NamespacedContext()
|
||||
container := &containers.Container{ID: id}
|
||||
|
||||
if baseSpecFile != "" {
|
||||
baseSpec, ok := c.baseOCISpecs[baseSpecFile]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("can't find base OCI spec %q", baseSpecFile)
|
||||
}
|
||||
|
||||
spec := oci.Spec{}
|
||||
if err := util.DeepCopy(&spec, &baseSpec); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to clone OCI spec")
|
||||
}
|
||||
|
||||
// Fix up cgroups path
|
||||
applyOpts := append([]oci.SpecOpts{oci.WithNamespacedCgroup()}, opts...)
|
||||
|
||||
if err := oci.ApplyOpts(ctx, nil, container, &spec, applyOpts...); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to apply OCI options")
|
||||
}
|
||||
|
||||
return &spec, nil
|
||||
}
|
||||
|
||||
spec, err := oci.GenerateSpec(ctx, nil, container, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate spec")
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
460
vendor/github.com/containerd/cri/pkg/server/container_create_unix.go
generated
vendored
460
vendor/github.com/containerd/cri/pkg/server/container_create_unix.go
generated
vendored
@@ -1,460 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/cgroups"
|
||||
"github.com/containerd/containerd/contrib/apparmor"
|
||||
"github.com/containerd/containerd/contrib/seccomp"
|
||||
"github.com/containerd/containerd/oci"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
selinux "github.com/opencontainers/selinux/go-selinux"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/annotations"
|
||||
"github.com/containerd/cri/pkg/config"
|
||||
customopts "github.com/containerd/cri/pkg/containerd/opts"
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
// containerMounts sets up necessary container system file mounts
|
||||
// including /dev/shm, /etc/hosts and /etc/resolv.conf.
|
||||
func (c *criService) containerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
|
||||
var mounts []*runtime.Mount
|
||||
securityContext := config.GetLinux().GetSecurityContext()
|
||||
if !isInCRIMounts(etcHostname, config.GetMounts()) {
|
||||
// /etc/hostname is added since 1.1.6, 1.2.4 and 1.3.
|
||||
// For in-place upgrade, the old sandbox doesn't have the hostname file,
|
||||
// do not mount this in that case.
|
||||
// TODO(random-liu): Remove the check and always mount this when
|
||||
// containerd 1.1 and 1.2 are deprecated.
|
||||
hostpath := c.getSandboxHostname(sandboxID)
|
||||
if _, err := c.os.Stat(hostpath); err == nil {
|
||||
mounts = append(mounts, &runtime.Mount{
|
||||
ContainerPath: etcHostname,
|
||||
HostPath: hostpath,
|
||||
Readonly: securityContext.GetReadonlyRootfs(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if !isInCRIMounts(etcHosts, config.GetMounts()) {
|
||||
mounts = append(mounts, &runtime.Mount{
|
||||
ContainerPath: etcHosts,
|
||||
HostPath: c.getSandboxHosts(sandboxID),
|
||||
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: c.getResolvPath(sandboxID),
|
||||
Readonly: securityContext.GetReadonlyRootfs(),
|
||||
})
|
||||
}
|
||||
|
||||
if !isInCRIMounts(devShm, config.GetMounts()) {
|
||||
sandboxDevShm := c.getSandboxDevShm(sandboxID)
|
||||
if securityContext.GetNamespaceOptions().GetIpc() == runtime.NamespaceMode_NODE {
|
||||
sandboxDevShm = devShm
|
||||
}
|
||||
mounts = append(mounts, &runtime.Mount{
|
||||
ContainerPath: devShm,
|
||||
HostPath: sandboxDevShm,
|
||||
Readonly: false,
|
||||
})
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
||||
func (c *criService) containerSpec(id string, sandboxID string, sandboxPid uint32, netNSPath string, containerName string,
|
||||
config *runtime.ContainerConfig, sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig,
|
||||
extraMounts []*runtime.Mount, ociRuntime config.Runtime) (_ *runtimespec.Spec, retErr error) {
|
||||
|
||||
specOpts := []oci.SpecOpts{
|
||||
customopts.WithoutRunMount,
|
||||
customopts.WithoutDefaultSecuritySettings,
|
||||
customopts.WithRelativeRoot(relativeRootfsPath),
|
||||
customopts.WithProcessArgs(config, imageConfig),
|
||||
oci.WithDefaultPathEnv,
|
||||
// this will be set based on the security context below
|
||||
oci.WithNewPrivileges,
|
||||
}
|
||||
if config.GetWorkingDir() != "" {
|
||||
specOpts = append(specOpts, oci.WithProcessCwd(config.GetWorkingDir()))
|
||||
} else if imageConfig.WorkingDir != "" {
|
||||
specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
|
||||
}
|
||||
|
||||
if config.GetTty() {
|
||||
specOpts = append(specOpts, oci.WithTTY)
|
||||
}
|
||||
|
||||
// Add HOSTNAME env.
|
||||
var (
|
||||
err error
|
||||
hostname = sandboxConfig.GetHostname()
|
||||
)
|
||||
if hostname == "" {
|
||||
if hostname, err = c.os.Hostname(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
specOpts = append(specOpts, oci.WithEnv([]string{hostnameEnv + "=" + hostname}))
|
||||
|
||||
// Apply envs from image config first, so that envs from container config
|
||||
// can override them.
|
||||
env := imageConfig.Env
|
||||
for _, e := range config.GetEnvs() {
|
||||
env = append(env, e.GetKey()+"="+e.GetValue())
|
||||
}
|
||||
specOpts = append(specOpts, oci.WithEnv(env))
|
||||
|
||||
securityContext := config.GetLinux().GetSecurityContext()
|
||||
labelOptions, err := toLabel(securityContext.GetSelinuxOptions())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(labelOptions) == 0 {
|
||||
// Use pod level SELinux config
|
||||
if sandbox, err := c.sandboxStore.Get(sandboxID); err == nil {
|
||||
labelOptions, err = selinux.DupSecOpt(sandbox.ProcessLabel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processLabel, mountLabel, err := label.InitLabels(labelOptions)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to init selinux options %+v", securityContext.GetSelinuxOptions())
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
_ = label.ReleaseLabel(processLabel)
|
||||
}
|
||||
}()
|
||||
|
||||
specOpts = append(specOpts, customopts.WithMounts(c.os, config, extraMounts, mountLabel))
|
||||
|
||||
if !c.config.DisableProcMount {
|
||||
// Apply masked paths if specified.
|
||||
// If the container is privileged, this will be cleared later on.
|
||||
specOpts = append(specOpts, oci.WithMaskedPaths(securityContext.GetMaskedPaths()))
|
||||
|
||||
// Apply readonly paths if specified.
|
||||
// If the container is privileged, this will be cleared later on.
|
||||
specOpts = append(specOpts, oci.WithReadonlyPaths(securityContext.GetReadonlyPaths()))
|
||||
}
|
||||
|
||||
if securityContext.GetPrivileged() {
|
||||
if !sandboxConfig.GetLinux().GetSecurityContext().GetPrivileged() {
|
||||
return nil, errors.New("no privileged container allowed in sandbox")
|
||||
}
|
||||
specOpts = append(specOpts, oci.WithPrivileged)
|
||||
if !ociRuntime.PrivilegedWithoutHostDevices {
|
||||
specOpts = append(specOpts, oci.WithHostDevices, oci.WithAllDevicesAllowed)
|
||||
} else {
|
||||
// add requested devices by the config as host devices are not automatically added
|
||||
specOpts = append(specOpts, customopts.WithDevices(c.os, config), customopts.WithCapabilities(securityContext))
|
||||
}
|
||||
} else { // not privileged
|
||||
specOpts = append(specOpts, customopts.WithDevices(c.os, config), customopts.WithCapabilities(securityContext))
|
||||
}
|
||||
|
||||
// Clear all ambient capabilities. The implication of non-root + caps
|
||||
// is not clearly defined in Kubernetes.
|
||||
// See https://github.com/kubernetes/kubernetes/issues/56374
|
||||
// Keep docker's behavior for now.
|
||||
specOpts = append(specOpts,
|
||||
customopts.WithoutAmbientCaps,
|
||||
customopts.WithSelinuxLabels(processLabel, mountLabel),
|
||||
)
|
||||
|
||||
// TODO: Figure out whether we should set no new privilege for sandbox container by default
|
||||
if securityContext.GetNoNewPrivs() {
|
||||
specOpts = append(specOpts, oci.WithNoNewPrivileges)
|
||||
}
|
||||
// TODO(random-liu): [P1] Set selinux options (privileged or not).
|
||||
if securityContext.GetReadonlyRootfs() {
|
||||
specOpts = append(specOpts, oci.WithRootFSReadonly())
|
||||
}
|
||||
|
||||
if c.config.DisableCgroup {
|
||||
specOpts = append(specOpts, customopts.WithDisabledCgroups)
|
||||
} else {
|
||||
specOpts = append(specOpts, customopts.WithResources(config.GetLinux().GetResources(), c.config.TolerateMissingHugetlbController, c.config.DisableHugetlbController))
|
||||
if sandboxConfig.GetLinux().GetCgroupParent() != "" {
|
||||
cgroupsPath := getCgroupsPath(sandboxConfig.GetLinux().GetCgroupParent(), id)
|
||||
specOpts = append(specOpts, oci.WithCgroup(cgroupsPath))
|
||||
}
|
||||
}
|
||||
|
||||
supplementalGroups := securityContext.GetSupplementalGroups()
|
||||
|
||||
for pKey, pValue := range getPassthroughAnnotations(sandboxConfig.Annotations,
|
||||
ociRuntime.PodAnnotations) {
|
||||
specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
|
||||
}
|
||||
|
||||
for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
|
||||
ociRuntime.ContainerAnnotations) {
|
||||
specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
|
||||
}
|
||||
|
||||
specOpts = append(specOpts,
|
||||
customopts.WithOOMScoreAdj(config, c.config.RestrictOOMScoreAdj),
|
||||
customopts.WithPodNamespaces(securityContext, sandboxPid),
|
||||
customopts.WithSupplementalGroups(supplementalGroups),
|
||||
customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer),
|
||||
customopts.WithAnnotation(annotations.SandboxID, sandboxID),
|
||||
customopts.WithAnnotation(annotations.ContainerName, containerName),
|
||||
)
|
||||
// cgroupns is used for hiding /sys/fs/cgroup from containers.
|
||||
// For compatibility, cgroupns is not used when running in cgroup v1 mode or in privileged.
|
||||
// https://github.com/containers/libpod/issues/4363
|
||||
// https://github.com/kubernetes/enhancements/blob/0e409b47497e398b369c281074485c8de129694f/keps/sig-node/20191118-cgroups-v2.md#cgroup-namespace
|
||||
if cgroups.Mode() == cgroups.Unified && !securityContext.GetPrivileged() {
|
||||
specOpts = append(specOpts, oci.WithLinuxNamespace(
|
||||
runtimespec.LinuxNamespace{
|
||||
Type: runtimespec.CgroupNamespace,
|
||||
}))
|
||||
}
|
||||
return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
|
||||
}
|
||||
|
||||
func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
|
||||
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.
|
||||
userstr, err := generateUserString(
|
||||
securityContext.GetRunAsUsername(),
|
||||
securityContext.GetRunAsUser(),
|
||||
securityContext.GetRunAsGroup())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate user string")
|
||||
}
|
||||
if userstr == "" {
|
||||
// Lastly, since no user override was passed via CRI try to set via OCI
|
||||
// Image
|
||||
userstr = imageConfig.User
|
||||
}
|
||||
if userstr != "" {
|
||||
specOpts = append(specOpts, oci.WithUser(userstr))
|
||||
}
|
||||
|
||||
if securityContext.GetRunAsUsername() != "" {
|
||||
userstr = securityContext.GetRunAsUsername()
|
||||
} else {
|
||||
// Even if RunAsUser is not set, we still call `GetValue` to get uid 0.
|
||||
// Because it is still useful to get additional gids for uid 0.
|
||||
userstr = strconv.FormatInt(securityContext.GetRunAsUser().GetValue(), 10)
|
||||
}
|
||||
specOpts = append(specOpts, customopts.WithAdditionalGIDs(userstr))
|
||||
|
||||
apparmorSpecOpts, err := generateApparmorSpecOpts(
|
||||
securityContext.GetApparmorProfile(),
|
||||
securityContext.GetPrivileged(),
|
||||
c.apparmorEnabled())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate apparmor spec opts")
|
||||
}
|
||||
if apparmorSpecOpts != nil {
|
||||
specOpts = append(specOpts, apparmorSpecOpts)
|
||||
}
|
||||
|
||||
seccompSpecOpts, err := c.generateSeccompSpecOpts(
|
||||
securityContext.GetSeccompProfilePath(),
|
||||
securityContext.GetPrivileged(),
|
||||
c.seccompEnabled())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate seccomp spec opts")
|
||||
}
|
||||
if seccompSpecOpts != nil {
|
||||
specOpts = append(specOpts, seccompSpecOpts)
|
||||
}
|
||||
return specOpts, nil
|
||||
}
|
||||
|
||||
// generateSeccompSpecOpts generates containerd SpecOpts for seccomp.
|
||||
func (c *criService) generateSeccompSpecOpts(seccompProf string, privileged, seccompEnabled bool) (oci.SpecOpts, error) {
|
||||
if privileged {
|
||||
// Do not set seccomp profile when container is privileged
|
||||
return nil, nil
|
||||
}
|
||||
if seccompProf == "" {
|
||||
seccompProf = c.config.UnsetSeccompProfile
|
||||
}
|
||||
// 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, errors.New("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, errors.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, errors.New("apparmor is not supported")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
switch apparmorProf {
|
||||
// Based on kubernetes#51746, default apparmor profile should be applied
|
||||
// for when apparmor is not specified.
|
||||
case runtimeDefault, "":
|
||||
if privileged {
|
||||
// Do not set apparmor profile when container is privileged
|
||||
return nil, nil
|
||||
}
|
||||
// TODO (mikebrow): delete created apparmor default profile
|
||||
return apparmor.WithDefaultProfile(appArmorDefaultProfileName), nil
|
||||
case unconfinedProfile:
|
||||
return nil, nil
|
||||
default:
|
||||
// Require and Trim default profile name prefix
|
||||
if !strings.HasPrefix(apparmorProf, profileNamePrefix) {
|
||||
return nil, errors.Errorf("invalid apparmor profile %q", apparmorProf)
|
||||
}
|
||||
appArmorProfile := strings.TrimPrefix(apparmorProf, profileNamePrefix)
|
||||
if profileExists, err := appArmorProfileExists(appArmorProfile); !profileExists {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate apparmor spec opts")
|
||||
}
|
||||
return nil, errors.Errorf("apparmor profile not found %s", appArmorProfile)
|
||||
}
|
||||
return apparmor.WithProfile(appArmorProfile), nil
|
||||
}
|
||||
}
|
||||
|
||||
// appArmorProfileExists scans apparmor/profiles for the requested profile
|
||||
func appArmorProfileExists(profile string) (bool, error) {
|
||||
if profile == "" {
|
||||
return false, errors.New("nil apparmor profile is not supported")
|
||||
}
|
||||
profiles, err := os.Open("/sys/kernel/security/apparmor/profiles")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer profiles.Close()
|
||||
|
||||
rbuff := bufio.NewReader(profiles)
|
||||
for {
|
||||
line, err := rbuff.ReadString('\n')
|
||||
switch err {
|
||||
case nil:
|
||||
if strings.HasPrefix(line, profile+" (") {
|
||||
return true, nil
|
||||
}
|
||||
case io.EOF:
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generateUserString generates valid user string based on OCI Image Spec
|
||||
// v1.0.0.
|
||||
//
|
||||
// CRI defines that the following combinations are valid:
|
||||
//
|
||||
// (none) -> ""
|
||||
// username -> username
|
||||
// username, uid -> username
|
||||
// username, uid, gid -> username:gid
|
||||
// username, gid -> username:gid
|
||||
// uid -> uid
|
||||
// uid, gid -> uid:gid
|
||||
// gid -> error
|
||||
//
|
||||
// TODO(random-liu): Add group name support in CRI.
|
||||
func generateUserString(username string, uid, gid *runtime.Int64Value) (string, error) {
|
||||
var userstr, groupstr string
|
||||
if uid != nil {
|
||||
userstr = strconv.FormatInt(uid.GetValue(), 10)
|
||||
}
|
||||
if username != "" {
|
||||
userstr = username
|
||||
}
|
||||
if gid != nil {
|
||||
groupstr = strconv.FormatInt(gid.GetValue(), 10)
|
||||
}
|
||||
if userstr == "" {
|
||||
if groupstr != "" {
|
||||
return "", errors.Errorf("user group %q is specified without user", groupstr)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
if groupstr != "" {
|
||||
userstr = userstr + ":" + groupstr
|
||||
}
|
||||
return userstr, nil
|
||||
}
|
||||
117
vendor/github.com/containerd/cri/pkg/server/container_create_windows.go
generated
vendored
117
vendor/github.com/containerd/cri/pkg/server/container_create_windows.go
generated
vendored
@@ -1,117 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/containerd/containerd/oci"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/annotations"
|
||||
"github.com/containerd/cri/pkg/config"
|
||||
customopts "github.com/containerd/cri/pkg/containerd/opts"
|
||||
)
|
||||
|
||||
// No container mounts for windows.
|
||||
func (c *criService) containerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *criService) containerSpec(id string, sandboxID string, sandboxPid uint32, netNSPath string, containerName string,
|
||||
config *runtime.ContainerConfig, sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig,
|
||||
extraMounts []*runtime.Mount, ociRuntime config.Runtime) (*runtimespec.Spec, error) {
|
||||
specOpts := []oci.SpecOpts{
|
||||
customopts.WithProcessArgs(config, imageConfig),
|
||||
}
|
||||
if config.GetWorkingDir() != "" {
|
||||
specOpts = append(specOpts, oci.WithProcessCwd(config.GetWorkingDir()))
|
||||
} else if imageConfig.WorkingDir != "" {
|
||||
specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
|
||||
}
|
||||
|
||||
if config.GetTty() {
|
||||
specOpts = append(specOpts, oci.WithTTY)
|
||||
}
|
||||
|
||||
// Apply envs from image config first, so that envs from container config
|
||||
// can override them.
|
||||
env := imageConfig.Env
|
||||
for _, e := range config.GetEnvs() {
|
||||
env = append(env, e.GetKey()+"="+e.GetValue())
|
||||
}
|
||||
specOpts = append(specOpts, oci.WithEnv(env))
|
||||
|
||||
specOpts = append(specOpts,
|
||||
// Clear the root location since hcsshim expects it.
|
||||
// NOTE: readonly rootfs doesn't work on windows.
|
||||
customopts.WithoutRoot,
|
||||
customopts.WithWindowsNetworkNamespace(netNSPath),
|
||||
oci.WithHostname(sandboxConfig.GetHostname()),
|
||||
)
|
||||
|
||||
specOpts = append(specOpts, customopts.WithWindowsMounts(c.os, config, extraMounts))
|
||||
|
||||
// Start with the image config user and override below if RunAsUsername is not "".
|
||||
username := imageConfig.User
|
||||
|
||||
windowsConfig := config.GetWindows()
|
||||
if windowsConfig != nil {
|
||||
specOpts = append(specOpts, customopts.WithWindowsResources(windowsConfig.GetResources()))
|
||||
securityCtx := windowsConfig.GetSecurityContext()
|
||||
if securityCtx != nil {
|
||||
runAsUser := securityCtx.GetRunAsUsername()
|
||||
if runAsUser != "" {
|
||||
username = runAsUser
|
||||
}
|
||||
cs := securityCtx.GetCredentialSpec()
|
||||
if cs != "" {
|
||||
specOpts = append(specOpts, customopts.WithWindowsCredentialSpec(cs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There really isn't a good Windows way to verify that the username is available in the
|
||||
// image as early as here like there is for Linux. Later on in the stack hcsshim
|
||||
// will handle the behavior of erroring out if the user isn't available in the image
|
||||
// when trying to run the init process.
|
||||
specOpts = append(specOpts, oci.WithUser(username))
|
||||
|
||||
for pKey, pValue := range getPassthroughAnnotations(sandboxConfig.Annotations,
|
||||
ociRuntime.PodAnnotations) {
|
||||
specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
|
||||
}
|
||||
|
||||
for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
|
||||
ociRuntime.ContainerAnnotations) {
|
||||
specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
|
||||
}
|
||||
|
||||
specOpts = append(specOpts,
|
||||
customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer),
|
||||
customopts.WithAnnotation(annotations.SandboxID, sandboxID),
|
||||
customopts.WithAnnotation(annotations.ContainerName, containerName),
|
||||
)
|
||||
return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
|
||||
}
|
||||
|
||||
// No extra spec options needed for windows.
|
||||
func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
|
||||
return nil, nil
|
||||
}
|
||||
36
vendor/github.com/containerd/cri/pkg/server/container_exec.go
generated
vendored
36
vendor/github.com/containerd/cri/pkg/server/container_exec.go
generated
vendored
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// Exec prepares a streaming endpoint to execute a command in the container, and returns the address.
|
||||
func (c *criService) Exec(ctx context.Context, r *runtime.ExecRequest) (*runtime.ExecResponse, error) {
|
||||
cntr, err := c.containerStore.Get(r.GetContainerId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to find container %q in store", r.GetContainerId())
|
||||
}
|
||||
state := cntr.Status.Get().State()
|
||||
if state != runtime.ContainerState_CONTAINER_RUNNING {
|
||||
return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
|
||||
}
|
||||
return c.streamServer.GetExec(r)
|
||||
}
|
||||
211
vendor/github.com/containerd/cri/pkg/server/container_execsync.go
generated
vendored
211
vendor/github.com/containerd/cri/pkg/server/container_execsync.go
generated
vendored
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"bytes"
|
||||
"io"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
containerdio "github.com/containerd/containerd/cio"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
cioutil "github.com/containerd/cri/pkg/ioutil"
|
||||
cio "github.com/containerd/cri/pkg/server/io"
|
||||
"github.com/containerd/cri/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 *criService) 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, errors.Wrap(err, "failed to exec in container")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (c *criService) execInternal(ctx context.Context, container containerd.Container, 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()
|
||||
|
||||
spec, err := container.Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get container spec")
|
||||
}
|
||||
task, err := container.Task(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load task")
|
||||
}
|
||||
pspec := spec.Process
|
||||
|
||||
pspec.Terminal = opts.tty
|
||||
if opts.tty {
|
||||
if err := oci.WithEnv([]string{"TERM=xterm"})(ctx, nil, nil, spec); err != nil {
|
||||
return nil, errors.Wrap(err, "add TERM env var to spec")
|
||||
}
|
||||
}
|
||||
|
||||
pspec.Args = opts.cmd
|
||||
|
||||
if opts.stdout == nil {
|
||||
opts.stdout = cio.NewDiscardLogger()
|
||||
}
|
||||
if opts.stderr == nil {
|
||||
opts.stderr = cio.NewDiscardLogger()
|
||||
}
|
||||
execID := util.GenerateID()
|
||||
log.G(ctx).Debugf("Generated exec id %q for container %q", execID, id)
|
||||
volatileRootDir := c.getVolatileContainerRootDir(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, volatileRootDir, opts.tty, opts.stdin != nil)
|
||||
return execIO, err
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create exec %q", execID)
|
||||
}
|
||||
defer func() {
|
||||
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||
defer deferCancel()
|
||||
if _, err := process.Delete(deferCtx, containerd.WithProcessKill); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id)
|
||||
}
|
||||
}()
|
||||
|
||||
exitCh, err := process.Wait(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to wait for process %q", execID)
|
||||
}
|
||||
if err := process.Start(ctx); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to start exec %q", execID)
|
||||
}
|
||||
|
||||
handleResizing(ctx, opts.resize, func(size remotecommand.TerminalSize) {
|
||||
if err := process.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
|
||||
log.G(ctx).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)
|
||||
},
|
||||
})
|
||||
|
||||
execCtx := ctx
|
||||
if opts.timeout > 0 {
|
||||
var execCtxCancel context.CancelFunc
|
||||
execCtx, execCtxCancel = context.WithTimeout(ctx, opts.timeout)
|
||||
defer execCtxCancel()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-execCtx.Done():
|
||||
// Ignore the not found error because the process may exit itself before killing.
|
||||
if err := process.Kill(ctx, syscall.SIGKILL); err != nil && !errdefs.IsNotFound(err) {
|
||||
return nil, errors.Wrapf(err, "failed to kill exec %q", execID)
|
||||
}
|
||||
// Wait for the process to be killed.
|
||||
exitRes := <-exitCh
|
||||
log.G(ctx).Infof("Timeout received while waiting for exec process kill %q code %d and error %v",
|
||||
execID, exitRes.ExitCode(), exitRes.Error())
|
||||
<-attachDone
|
||||
log.G(ctx).Debugf("Stream pipe for exec process %q done", execID)
|
||||
return nil, errors.Wrapf(execCtx.Err(), "timeout %v exceeded", opts.timeout)
|
||||
case exitRes := <-exitCh:
|
||||
code, _, err := exitRes.Result()
|
||||
log.G(ctx).Infof("Exec process %q exits with exit code %d and error %v", execID, code, err)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed while waiting for exec %q", execID)
|
||||
}
|
||||
<-attachDone
|
||||
log.G(ctx).Debugf("Stream pipe for exec process %q done", execID)
|
||||
return &code, nil
|
||||
}
|
||||
}
|
||||
|
||||
// execInContainer executes a command inside the container synchronously, and
|
||||
// redirects stdio stream properly.
|
||||
// This function only returns when the exec process exits, this means that:
|
||||
// 1) As long as the exec process is running, the goroutine in the cri plugin
|
||||
// will be running and wait for the exit code;
|
||||
// 2) `kubectl exec -it` will hang until the exec process exits, even after io
|
||||
// is detached. This is different from dockershim, which leaves the exec process
|
||||
// running in background after io is detached.
|
||||
// https://github.com/kubernetes/kubernetes/blob/v1.15.0/pkg/kubelet/dockershim/exec.go#L127
|
||||
// For example, if the `kubectl exec -it` process is killed, IO will be closed. In
|
||||
// this case, the CRI plugin will still have a goroutine waiting for the exec process
|
||||
// to exit and log the exit code, but dockershim won't.
|
||||
func (c *criService) execInContainer(ctx context.Context, id string, opts execOptions) (*uint32, error) {
|
||||
// Get container from our container store.
|
||||
cntr, err := c.containerStore.Get(id)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to find container %q in store", id)
|
||||
}
|
||||
id = cntr.ID
|
||||
|
||||
state := cntr.Status.Get().State()
|
||||
if state != runtime.ContainerState_CONTAINER_RUNNING {
|
||||
return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
|
||||
}
|
||||
|
||||
return c.execInternal(ctx, cntr.Container, id, opts)
|
||||
}
|
||||
112
vendor/github.com/containerd/cri/pkg/server/container_list.go
generated
vendored
112
vendor/github.com/containerd/cri/pkg/server/container_list.go
generated
vendored
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
)
|
||||
|
||||
// ListContainers lists all containers matching the filter.
|
||||
func (c *criService) 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 *criService) 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 *criService) filterCRIContainers(containers []*runtime.Container, filter *runtime.ContainerFilter) []*runtime.Container {
|
||||
if filter == nil {
|
||||
return containers
|
||||
}
|
||||
|
||||
// The containerd cri plugin supports short ids so long as there is only one
|
||||
// match. So we do a lookup against the store here if a pod id has been
|
||||
// included in the filter.
|
||||
sb := filter.GetPodSandboxId()
|
||||
if sb != "" {
|
||||
sandbox, err := c.sandboxStore.Get(sb)
|
||||
if err == nil {
|
||||
sb = sandbox.ID
|
||||
}
|
||||
}
|
||||
|
||||
c.normalizeContainerFilter(filter)
|
||||
filtered := []*runtime.Container{}
|
||||
for _, cntr := range containers {
|
||||
if filter.GetId() != "" && filter.GetId() != cntr.Id {
|
||||
continue
|
||||
}
|
||||
if sb != "" && sb != 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
|
||||
}
|
||||
51
vendor/github.com/containerd/cri/pkg/server/container_log_reopen.go
generated
vendored
51
vendor/github.com/containerd/cri/pkg/server/container_log_reopen.go
generated
vendored
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// ReopenContainerLog asks the cri plugin to reopen the stdout/stderr log file for the container.
|
||||
// This is often called after the log file has been rotated.
|
||||
func (c *criService) ReopenContainerLog(ctx context.Context, r *runtime.ReopenContainerLogRequest) (*runtime.ReopenContainerLogResponse, error) {
|
||||
container, err := c.containerStore.Get(r.GetContainerId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
|
||||
}
|
||||
|
||||
if container.Status.Get().State() != runtime.ContainerState_CONTAINER_RUNNING {
|
||||
return nil, errors.New("container is not running")
|
||||
}
|
||||
|
||||
// Create new container logger and replace the existing ones.
|
||||
stdoutWC, stderrWC, err := c.createContainerLoggers(container.LogPath, container.Config.GetTty())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldStdoutWC, oldStderrWC := container.IO.AddOutput("log", stdoutWC, stderrWC)
|
||||
if oldStdoutWC != nil {
|
||||
oldStdoutWC.Close()
|
||||
}
|
||||
if oldStderrWC != nil {
|
||||
oldStderrWC.Close()
|
||||
}
|
||||
return &runtime.ReopenContainerLogResponse{}, nil
|
||||
}
|
||||
135
vendor/github.com/containerd/cri/pkg/server/container_remove.go
generated
vendored
135
vendor/github.com/containerd/cri/pkg/server/container_remove.go
generated
vendored
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/store"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
)
|
||||
|
||||
// RemoveContainer removes the container.
|
||||
func (c *criService) 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, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
|
||||
}
|
||||
// Do not return error if container metadata doesn't exist.
|
||||
log.G(ctx).Tracef("RemoveContainer called for container %q that does not exist", r.GetContainerId())
|
||||
return &runtime.RemoveContainerResponse{}, nil
|
||||
}
|
||||
id := container.ID
|
||||
|
||||
// Forcibly stop the containers if they are in running or unknown state
|
||||
state := container.Status.Get().State()
|
||||
if state == runtime.ContainerState_CONTAINER_RUNNING ||
|
||||
state == runtime.ContainerState_CONTAINER_UNKNOWN {
|
||||
logrus.Infof("Forcibly stopping container %q", id)
|
||||
if err := c.stopContainer(ctx, container, 0); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to forcibly stop container %q", 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, errors.Wrapf(err, "failed to set removing state for container %q", id)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Reset removing if remove failed.
|
||||
if err := resetContainerRemoving(container); err != nil {
|
||||
log.G(ctx).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, errors.Wrapf(err, "failed to delete containerd container %q", id)
|
||||
}
|
||||
log.G(ctx).Tracef("Remove called for containerd container %q that does not exist", id)
|
||||
}
|
||||
|
||||
// Delete container checkpoint.
|
||||
if err := container.Delete(); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to delete container checkpoint for %q", id)
|
||||
}
|
||||
|
||||
containerRootDir := c.getContainerRootDir(id)
|
||||
if err := ensureRemoveAll(ctx, containerRootDir); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to remove container root directory %q",
|
||||
containerRootDir)
|
||||
}
|
||||
volatileContainerRootDir := c.getVolatileContainerRootDir(id)
|
||||
if err := ensureRemoveAll(ctx, volatileContainerRootDir); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to remove volatile container root directory %q",
|
||||
volatileContainerRootDir)
|
||||
}
|
||||
|
||||
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 or unknown.
|
||||
if status.State() == runtime.ContainerState_CONTAINER_RUNNING {
|
||||
return status, errors.New("container is still running, to stop first")
|
||||
}
|
||||
if status.State() == runtime.ContainerState_CONTAINER_UNKNOWN {
|
||||
return status, errors.New("container state is unknown, to stop first")
|
||||
}
|
||||
if status.Starting {
|
||||
return status, errors.New("container is in starting state, can't be removed")
|
||||
}
|
||||
if status.Removing {
|
||||
return status, errors.New("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
|
||||
})
|
||||
}
|
||||
222
vendor/github.com/containerd/cri/pkg/server/container_start.go
generated
vendored
222
vendor/github.com/containerd/cri/pkg/server/container_start.go
generated
vendored
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
containerdio "github.com/containerd/containerd/cio"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/nri"
|
||||
v1 "github.com/containerd/nri/types/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
cioutil "github.com/containerd/cri/pkg/ioutil"
|
||||
cio "github.com/containerd/cri/pkg/server/io"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
// StartContainer starts the container.
|
||||
func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (retRes *runtime.StartContainerResponse, retErr error) {
|
||||
cntr, err := c.containerStore.Get(r.GetContainerId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
|
||||
}
|
||||
|
||||
id := cntr.ID
|
||||
meta := cntr.Metadata
|
||||
container := cntr.Container
|
||||
config := meta.Config
|
||||
|
||||
// Set starting state to prevent other start/remove operations against this container
|
||||
// while it's being started.
|
||||
if err := setContainerStarting(cntr); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to set starting state for container %q", id)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Set container to exited if fail to start.
|
||||
if err := cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
|
||||
status.Pid = 0
|
||||
status.FinishedAt = time.Now().UnixNano()
|
||||
status.ExitCode = errorStartExitCode
|
||||
status.Reason = errorStartReason
|
||||
status.Message = retErr.Error()
|
||||
return status, nil
|
||||
}); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to set start failure state for container %q", id)
|
||||
}
|
||||
}
|
||||
if err := resetContainerStarting(cntr); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to reset starting state for container %q", id)
|
||||
}
|
||||
}()
|
||||
|
||||
// Get sandbox config from sandbox store.
|
||||
sandbox, err := c.sandboxStore.Get(meta.SandboxID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "sandbox %q not found", meta.SandboxID)
|
||||
}
|
||||
sandboxID := meta.SandboxID
|
||||
if sandbox.Status.Get().State != sandboxstore.StateReady {
|
||||
return nil, errors.Errorf("sandbox container %q is not running", sandboxID)
|
||||
}
|
||||
|
||||
ioCreation := func(id string) (_ containerdio.IO, err error) {
|
||||
stdoutWC, stderrWC, err := c.createContainerLoggers(meta.LogPath, config.GetTty())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create container loggers")
|
||||
}
|
||||
cntr.IO.AddOutput("log", stdoutWC, stderrWC)
|
||||
cntr.IO.Pipe()
|
||||
return cntr.IO, nil
|
||||
}
|
||||
|
||||
ctrInfo, err := container.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get container info")
|
||||
}
|
||||
|
||||
taskOpts := c.taskOpts(ctrInfo.Runtime.Name)
|
||||
task, err := container.NewTask(ctx, ioCreation, taskOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create containerd task")
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||
defer deferCancel()
|
||||
// It's possible that task is deleted by event monitor.
|
||||
if _, err := task.Delete(deferCtx, WithNRISandboxDelete(sandboxID), containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to delete containerd task %q", id)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// wait is a long running background request, no timeout needed.
|
||||
exitCh, err := task.Wait(ctrdutil.NamespacedContext())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to wait for containerd task")
|
||||
}
|
||||
nric, err := nri.New()
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Error("unable to create nri client")
|
||||
}
|
||||
if nric != nil {
|
||||
nriSB := &nri.Sandbox{
|
||||
ID: sandboxID,
|
||||
}
|
||||
if _, err := nric.InvokeWithSandbox(ctx, task, v1.Create, nriSB); err != nil {
|
||||
return nil, errors.Wrap(err, "nri invoke")
|
||||
}
|
||||
}
|
||||
|
||||
// Start containerd task.
|
||||
if err := task.Start(ctx); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to start containerd task %q", id)
|
||||
}
|
||||
|
||||
// Update container start timestamp.
|
||||
if err := cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
|
||||
status.Pid = task.Pid()
|
||||
status.StartedAt = time.Now().UnixNano()
|
||||
return status, nil
|
||||
}); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to update container %q state", id)
|
||||
}
|
||||
|
||||
// start the monitor after updating container state, this ensures that
|
||||
// event monitor receives the TaskExit event and update container state
|
||||
// after this.
|
||||
c.eventMonitor.startExitMonitor(context.Background(), id, task.Pid(), exitCh)
|
||||
|
||||
return &runtime.StartContainerResponse{}, nil
|
||||
}
|
||||
|
||||
// setContainerStarting sets the container into starting state. In starting state, the
|
||||
// container will not be removed or started again.
|
||||
func setContainerStarting(container containerstore.Container) error {
|
||||
return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
|
||||
// Return error if container is not in created state.
|
||||
if status.State() != runtime.ContainerState_CONTAINER_CREATED {
|
||||
return status, errors.Errorf("container is in %s state", criContainerStateToString(status.State()))
|
||||
}
|
||||
// Do not start the container when there is a removal in progress.
|
||||
if status.Removing {
|
||||
return status, errors.New("container is in removing state, can't be started")
|
||||
}
|
||||
if status.Starting {
|
||||
return status, errors.New("container is already in starting state")
|
||||
}
|
||||
status.Starting = true
|
||||
return status, nil
|
||||
})
|
||||
}
|
||||
|
||||
// resetContainerStarting resets the container starting state on start failure. So
|
||||
// that we could remove the container later.
|
||||
func resetContainerStarting(container containerstore.Container) error {
|
||||
return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
|
||||
status.Starting = false
|
||||
return status, nil
|
||||
})
|
||||
}
|
||||
|
||||
// createContainerLoggers creates container loggers and return write closer for stdout and stderr.
|
||||
func (c *criService) createContainerLoggers(logPath string, tty bool) (stdout io.WriteCloser, stderr io.WriteCloser, err error) {
|
||||
if logPath != "" {
|
||||
// Only generate container log when log path is specified.
|
||||
f, err := openLogFile(logPath)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to create and open log file")
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
var stdoutCh, stderrCh <-chan struct{}
|
||||
wc := cioutil.NewSerialWriteCloser(f)
|
||||
stdout, stdoutCh = cio.NewCRILogger(logPath, wc, cio.Stdout, c.config.MaxContainerLogLineSize)
|
||||
// Only redirect stderr when there is no tty.
|
||||
if !tty {
|
||||
stderr, stderrCh = cio.NewCRILogger(logPath, wc, cio.Stderr, c.config.MaxContainerLogLineSize)
|
||||
}
|
||||
go func() {
|
||||
if stdoutCh != nil {
|
||||
<-stdoutCh
|
||||
}
|
||||
if stderrCh != nil {
|
||||
<-stderrCh
|
||||
}
|
||||
logrus.Debugf("Finish redirecting log file %q, closing it", logPath)
|
||||
f.Close()
|
||||
}()
|
||||
} else {
|
||||
stdout = cio.NewDiscardLogger()
|
||||
stderr = cio.NewDiscardLogger()
|
||||
}
|
||||
return
|
||||
}
|
||||
47
vendor/github.com/containerd/cri/pkg/server/container_stats.go
generated
vendored
47
vendor/github.com/containerd/cri/pkg/server/container_stats.go
generated
vendored
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
tasks "github.com/containerd/containerd/api/services/tasks/v1"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// ContainerStats returns stats of the container. If the container does not
|
||||
// exist, the call returns an error.
|
||||
func (c *criService) ContainerStats(ctx context.Context, in *runtime.ContainerStatsRequest) (*runtime.ContainerStatsResponse, error) {
|
||||
cntr, err := c.containerStore.Get(in.GetContainerId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find container")
|
||||
}
|
||||
request := &tasks.MetricsRequest{Filters: []string{"id==" + cntr.ID}}
|
||||
resp, err := c.client.TaskService().Metrics(ctx, request)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to fetch metrics for task")
|
||||
}
|
||||
if len(resp.Metrics) != 1 {
|
||||
return nil, errors.Errorf("unexpected metrics response: %+v", resp.Metrics)
|
||||
}
|
||||
|
||||
cs, err := c.containerMetrics(cntr.Metadata, resp.Metrics[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode container metrics")
|
||||
}
|
||||
return &runtime.ContainerStatsResponse{Stats: cs}, nil
|
||||
}
|
||||
116
vendor/github.com/containerd/cri/pkg/server/container_stats_list.go
generated
vendored
116
vendor/github.com/containerd/cri/pkg/server/container_stats_list.go
generated
vendored
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
tasks "github.com/containerd/containerd/api/services/tasks/v1"
|
||||
"github.com/containerd/containerd/api/types"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
)
|
||||
|
||||
// ListContainerStats returns stats of all running containers.
|
||||
func (c *criService) ListContainerStats(
|
||||
ctx context.Context,
|
||||
in *runtime.ListContainerStatsRequest,
|
||||
) (*runtime.ListContainerStatsResponse, error) {
|
||||
request, containers, err := c.buildTaskMetricsRequest(in)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to build metrics request")
|
||||
}
|
||||
resp, err := c.client.TaskService().Metrics(ctx, &request)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to fetch metrics for tasks")
|
||||
}
|
||||
criStats, err := c.toCRIContainerStats(resp.Metrics, containers)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert to cri containerd stats format")
|
||||
}
|
||||
return criStats, nil
|
||||
}
|
||||
|
||||
func (c *criService) 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.containerMetrics(cntr.Metadata, statsMap[cntr.ID])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode container metrics for %q", cntr.ID)
|
||||
}
|
||||
containerStats.Stats = append(containerStats.Stats, cs)
|
||||
}
|
||||
return containerStats, nil
|
||||
}
|
||||
|
||||
func (c *criService) 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 *criService) 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
|
||||
}
|
||||
129
vendor/github.com/containerd/cri/pkg/server/container_stats_list_unix.go
generated
vendored
129
vendor/github.com/containerd/cri/pkg/server/container_stats_list_unix.go
generated
vendored
@@ -1,129 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/containerd/api/types"
|
||||
v1 "github.com/containerd/containerd/metrics/types/v1"
|
||||
v2 "github.com/containerd/containerd/metrics/types/v2"
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
)
|
||||
|
||||
func (c *criService) containerMetrics(
|
||||
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, errors.Wrap(err, "failed to extract container metrics")
|
||||
}
|
||||
switch metrics := s.(type) {
|
||||
case *v1.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: getWorkingSet(metrics.Memory),
|
||||
},
|
||||
}
|
||||
}
|
||||
case *v2.Metrics:
|
||||
if metrics.CPU != nil {
|
||||
cs.Cpu = &runtime.CpuUsage{
|
||||
Timestamp: stats.Timestamp.UnixNano(),
|
||||
UsageCoreNanoSeconds: &runtime.UInt64Value{Value: metrics.CPU.UsageUsec * 1000},
|
||||
}
|
||||
}
|
||||
if metrics.Memory != nil {
|
||||
cs.Memory = &runtime.MemoryUsage{
|
||||
Timestamp: stats.Timestamp.UnixNano(),
|
||||
WorkingSetBytes: &runtime.UInt64Value{
|
||||
Value: getWorkingSetV2(metrics.Memory),
|
||||
},
|
||||
}
|
||||
}
|
||||
default:
|
||||
return &cs, errors.New(fmt.Sprintf("unxpected metrics type: %v", metrics))
|
||||
}
|
||||
}
|
||||
|
||||
return &cs, nil
|
||||
}
|
||||
|
||||
// getWorkingSet calculates workingset memory from cgroup memory stats.
|
||||
// The caller should make sure memory is not nil.
|
||||
// workingset = usage - total_inactive_file
|
||||
func getWorkingSet(memory *v1.MemoryStat) uint64 {
|
||||
if memory.Usage == nil {
|
||||
return 0
|
||||
}
|
||||
var workingSet uint64
|
||||
if memory.TotalInactiveFile < memory.Usage.Usage {
|
||||
workingSet = memory.Usage.Usage - memory.TotalInactiveFile
|
||||
}
|
||||
return workingSet
|
||||
}
|
||||
|
||||
// getWorkingSetV2 calculates workingset memory from cgroupv2 memory stats.
|
||||
// The caller should make sure memory is not nil.
|
||||
// workingset = usage - inactive_file
|
||||
func getWorkingSetV2(memory *v2.MemoryStat) uint64 {
|
||||
var workingSet uint64
|
||||
if memory.InactiveFile < memory.Usage {
|
||||
workingSet = memory.Usage - memory.InactiveFile
|
||||
}
|
||||
return workingSet
|
||||
}
|
||||
84
vendor/github.com/containerd/cri/pkg/server/container_stats_list_windows.go
generated
vendored
84
vendor/github.com/containerd/cri/pkg/server/container_stats_list_windows.go
generated
vendored
@@ -1,84 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
wstats "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
|
||||
"github.com/containerd/containerd/api/types"
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
)
|
||||
|
||||
func (c *criService) containerMetrics(
|
||||
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, errors.Wrap(err, "failed to extract container metrics")
|
||||
}
|
||||
wstats := s.(*wstats.Statistics).GetWindows()
|
||||
if wstats == nil {
|
||||
return nil, errors.New("windows stats is empty")
|
||||
}
|
||||
if wstats.Processor != nil {
|
||||
cs.Cpu = &runtime.CpuUsage{
|
||||
Timestamp: wstats.Timestamp.UnixNano(),
|
||||
UsageCoreNanoSeconds: &runtime.UInt64Value{Value: wstats.Processor.TotalRuntimeNS},
|
||||
}
|
||||
}
|
||||
if wstats.Memory != nil {
|
||||
cs.Memory = &runtime.MemoryUsage{
|
||||
Timestamp: wstats.Timestamp.UnixNano(),
|
||||
WorkingSetBytes: &runtime.UInt64Value{
|
||||
Value: wstats.Memory.MemoryUsagePrivateWorkingSetBytes,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return &cs, nil
|
||||
}
|
||||
173
vendor/github.com/containerd/cri/pkg/server/container_status.go
generated
vendored
173
vendor/github.com/containerd/cri/pkg/server/container_status.go
generated
vendored
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"encoding/json"
|
||||
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/store"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
)
|
||||
|
||||
// ContainerStatus inspects the container and returns the status.
|
||||
func (c *criService) ContainerStatus(ctx context.Context, r *runtime.ContainerStatusRequest) (*runtime.ContainerStatusResponse, error) {
|
||||
container, err := c.containerStore.Get(r.GetContainerId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if err != store.ErrNotExist {
|
||||
return nil, errors.Wrapf(err, "failed to get image %q", imageRef)
|
||||
}
|
||||
} else {
|
||||
repoTags, repoDigests := parseImageReferences(image.References)
|
||||
if len(repoTags) > 0 {
|
||||
// Based on current behavior of dockershim, this field should be
|
||||
// image tag.
|
||||
spec = &runtime.ImageSpec{Image: repoTags[0]}
|
||||
}
|
||||
if len(repoDigests) > 0 {
|
||||
// Based on the CRI definition, this field will be consumed by user.
|
||||
imageRef = repoDigests[0]
|
||||
}
|
||||
}
|
||||
status := toCRIContainerStatus(container, spec, imageRef)
|
||||
if status.GetCreatedAt() == 0 {
|
||||
// CRI doesn't allow CreatedAt == 0.
|
||||
info, err := container.Container.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get CreatedAt in %q state", status.State)
|
||||
}
|
||||
status.CreatedAt = info.CreatedAt.UnixNano()
|
||||
}
|
||||
|
||||
info, err := toCRIContainerInfo(ctx, container, r.GetVerbose())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get verbose container info")
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerInfo is extra information for a container.
|
||||
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"`
|
||||
RuntimeType string `json:"runtimeType"`
|
||||
RuntimeOptions interface{} `json:"runtimeOptions"`
|
||||
Config *runtime.ContainerConfig `json:"config"`
|
||||
RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"`
|
||||
}
|
||||
|
||||
// toCRIContainerInfo converts internal container object information to CRI container status response info map.
|
||||
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,
|
||||
}
|
||||
|
||||
var err error
|
||||
ci.RuntimeSpec, err = container.Container.Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get container runtime spec")
|
||||
}
|
||||
|
||||
ctrInfo, err := container.Container.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get container info")
|
||||
}
|
||||
ci.SnapshotKey = ctrInfo.SnapshotKey
|
||||
ci.Snapshotter = ctrInfo.Snapshotter
|
||||
|
||||
runtimeOptions, err := getRuntimeOptions(ctrInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get runtime options")
|
||||
}
|
||||
ci.RuntimeType = ctrInfo.Runtime.Name
|
||||
ci.RuntimeOptions = runtimeOptions
|
||||
|
||||
infoBytes, err := json.Marshal(ci)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal info %v", ci)
|
||||
}
|
||||
return map[string]string{
|
||||
"info": string(infoBytes),
|
||||
}, nil
|
||||
}
|
||||
186
vendor/github.com/containerd/cri/pkg/server/container_stop.go
generated
vendored
186
vendor/github.com/containerd/cri/pkg/server/container_stop.go
generated
vendored
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
eventtypes "github.com/containerd/containerd/api/events"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
"github.com/containerd/cri/pkg/store"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
)
|
||||
|
||||
// StopContainer stops a running container with a grace period (i.e., timeout).
|
||||
func (c *criService) 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, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
|
||||
}
|
||||
|
||||
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 *criService) 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 &&
|
||||
state != runtime.ContainerState_CONTAINER_UNKNOWN {
|
||||
log.G(ctx).Infof("Container to stop %q must be in running or unknown state, current state %q",
|
||||
id, criContainerStateToString(state))
|
||||
return nil
|
||||
}
|
||||
|
||||
task, err := container.Container.Task(ctx, nil)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "failed to get task for container %q", id)
|
||||
}
|
||||
// Don't return for unknown state, some cleanup needs to be done.
|
||||
if state == runtime.ContainerState_CONTAINER_UNKNOWN {
|
||||
return cleanupUnknownContainer(ctx, id, container)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle unknown state.
|
||||
if state == runtime.ContainerState_CONTAINER_UNKNOWN {
|
||||
// Start an exit handler for containers in unknown state.
|
||||
waitCtx, waitCancel := context.WithCancel(ctrdutil.NamespacedContext())
|
||||
defer waitCancel()
|
||||
exitCh, err := task.Wait(waitCtx)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "failed to wait for task for %q", id)
|
||||
}
|
||||
return cleanupUnknownContainer(ctx, id, container)
|
||||
}
|
||||
|
||||
exitCtx, exitCancel := context.WithCancel(context.Background())
|
||||
stopCh := c.eventMonitor.startExitMonitor(exitCtx, id, task.Pid(), exitCh)
|
||||
defer func() {
|
||||
exitCancel()
|
||||
// This ensures that exit monitor is stopped before
|
||||
// `Wait` is cancelled, so no exit event is generated
|
||||
// because of the `Wait` cancellation.
|
||||
<-stopCh
|
||||
}()
|
||||
}
|
||||
|
||||
// We only need to kill the task. The event handler will Delete the
|
||||
// task from containerd after it handles the Exited event.
|
||||
if timeout > 0 {
|
||||
stopSignal := "SIGTERM"
|
||||
if container.StopSignal != "" {
|
||||
stopSignal = container.StopSignal
|
||||
} else {
|
||||
// The image may have been deleted, and the `StopSignal` field is
|
||||
// just introduced to handle that.
|
||||
// However, for containers created before the `StopSignal` field is
|
||||
// introduced, still try to get the stop signal from the image config.
|
||||
// If the image has been deleted, logging an error and using the
|
||||
// default SIGTERM is still better than returning error and leaving
|
||||
// the container unstoppable. (See issue #990)
|
||||
// TODO(random-liu): Remove this logic when containerd 1.2 is deprecated.
|
||||
image, err := c.imageStore.Get(container.ImageRef)
|
||||
if err != nil {
|
||||
if err != store.ErrNotExist {
|
||||
return errors.Wrapf(err, "failed to get image %q", container.ImageRef)
|
||||
}
|
||||
log.G(ctx).Warningf("Image %q not found, stop container with signal %q", container.ImageRef, stopSignal)
|
||||
} else {
|
||||
if image.ImageSpec.Config.StopSignal != "" {
|
||||
stopSignal = image.ImageSpec.Config.StopSignal
|
||||
}
|
||||
}
|
||||
}
|
||||
sig, err := containerd.ParseSignal(stopSignal)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse stop signal %q", stopSignal)
|
||||
}
|
||||
log.G(ctx).Infof("Stop container %q with signal %v", id, sig)
|
||||
if err = task.Kill(ctx, sig); err != nil && !errdefs.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "failed to stop container %q", id)
|
||||
}
|
||||
|
||||
sigTermCtx, sigTermCtxCancel := context.WithTimeout(ctx, timeout)
|
||||
defer sigTermCtxCancel()
|
||||
err = c.waitContainerStop(sigTermCtx, container)
|
||||
if err == nil {
|
||||
// Container stopped on first signal no need for SIGKILL
|
||||
return nil
|
||||
}
|
||||
// If the parent context was cancelled or exceeded return immediately
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
// sigTermCtx was exceeded. Send SIGKILL
|
||||
log.G(ctx).Debugf("Stop container %q with signal %v timed out", id, sig)
|
||||
}
|
||||
|
||||
log.G(ctx).Infof("Kill container %q", id)
|
||||
if err = task.Kill(ctx, syscall.SIGKILL); err != nil && !errdefs.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "failed to kill container %q", id)
|
||||
}
|
||||
|
||||
// Wait for a fixed timeout until container stop is observed by event monitor.
|
||||
err = c.waitContainerStop(ctx, container)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "an error occurs during waiting for container %q to be killed", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// waitContainerStop waits for container to be stopped until context is
|
||||
// cancelled or the context deadline is exceeded.
|
||||
func (c *criService) waitContainerStop(ctx context.Context, container containerstore.Container) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Wrapf(ctx.Err(), "wait container %q", container.ID)
|
||||
case <-container.Stopped():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupUnknownContainer cleanup stopped container in unknown state.
|
||||
func cleanupUnknownContainer(ctx context.Context, id string, cntr containerstore.Container) error {
|
||||
// Reuse handleContainerExit to do the cleanup.
|
||||
return handleContainerExit(ctx, &eventtypes.TaskExit{
|
||||
ContainerID: id,
|
||||
ID: id,
|
||||
Pid: 0,
|
||||
ExitStatus: unknownExitCode,
|
||||
ExitedAt: time.Now(),
|
||||
}, cntr)
|
||||
}
|
||||
150
vendor/github.com/containerd/cri/pkg/server/container_update_resources_unix.go
generated
vendored
150
vendor/github.com/containerd/cri/pkg/server/container_update_resources_unix.go
generated
vendored
@@ -1,150 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
gocontext "context"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/containers"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/typeurl"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/containerd/opts"
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
"github.com/containerd/cri/pkg/util"
|
||||
)
|
||||
|
||||
// UpdateContainerResources updates ContainerConfig of the container.
|
||||
func (c *criService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (retRes *runtime.UpdateContainerResourcesResponse, retErr error) {
|
||||
container, err := c.containerStore.Get(r.GetContainerId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find container")
|
||||
}
|
||||
// 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, errors.Wrap(err, "failed to update resources")
|
||||
}
|
||||
return &runtime.UpdateContainerResourcesResponse{}, nil
|
||||
}
|
||||
|
||||
func (c *criService) 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 errors.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 errors.Wrap(err, "failed to get container spec")
|
||||
}
|
||||
newSpec, err := updateOCILinuxResource(ctx, oldSpec, resources,
|
||||
c.config.TolerateMissingHugetlbController, c.config.DisableHugetlbController)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to update resource in spec")
|
||||
}
|
||||
|
||||
if err := updateContainerSpec(ctx, cntr.Container, newSpec); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||
defer deferCancel()
|
||||
// Reset spec on error.
|
||||
if err := updateContainerSpec(deferCtx, cntr.Container, oldSpec); err != nil {
|
||||
log.G(ctx).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 errors.Wrap(err, "failed to get task")
|
||||
}
|
||||
// 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 errors.Wrap(err, "failed to update resources")
|
||||
}
|
||||
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 errors.Wrapf(err, "failed to marshal spec %+v", spec)
|
||||
}
|
||||
if err := cntr.Update(ctx, func(ctx gocontext.Context, client *containerd.Client, c *containers.Container) error {
|
||||
c.Spec = any
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "failed to update container spec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateOCILinuxResource updates container resource limit.
|
||||
func updateOCILinuxResource(ctx context.Context, spec *runtimespec.Spec, new *runtime.LinuxContainerResources,
|
||||
tolerateMissingHugetlbController, disableHugetlbController bool) (*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, errors.Wrap(err, "failed to deep copy")
|
||||
}
|
||||
if cloned.Linux == nil {
|
||||
cloned.Linux = &runtimespec.Linux{}
|
||||
}
|
||||
if err := opts.WithResources(new, tolerateMissingHugetlbController, disableHugetlbController)(ctx, nil, nil, &cloned); err != nil {
|
||||
return nil, errors.Wrap(err, "unable to set linux container resources")
|
||||
}
|
||||
return &cloned, nil
|
||||
}
|
||||
31
vendor/github.com/containerd/cri/pkg/server/container_update_resources_windows.go
generated
vendored
31
vendor/github.com/containerd/cri/pkg/server/container_update_resources_windows.go
generated
vendored
@@ -1,31 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// UpdateContainerResources updates ContainerConfig of the container.
|
||||
// TODO(windows): Figure out whether windows support this.
|
||||
func (c *criService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (*runtime.UpdateContainerResourcesResponse, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
461
vendor/github.com/containerd/cri/pkg/server/events.go
generated
vendored
461
vendor/github.com/containerd/cri/pkg/server/events.go
generated
vendored
@@ -1,461 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
eventtypes "github.com/containerd/containerd/api/events"
|
||||
containerdio "github.com/containerd/containerd/cio"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/events"
|
||||
"github.com/containerd/typeurl"
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
|
||||
"github.com/containerd/cri/pkg/constants"
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
"github.com/containerd/cri/pkg/store"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
const (
|
||||
backOffInitDuration = 1 * time.Second
|
||||
backOffMaxDuration = 5 * time.Minute
|
||||
backOffExpireCheckDuration = 1 * time.Second
|
||||
|
||||
// handleEventTimeout is the timeout for handling 1 event. Event monitor
|
||||
// handles events in serial, if one event blocks the event monitor, no
|
||||
// other events can be handled.
|
||||
// Add a timeout for each event handling, events that timeout will be requeued and
|
||||
// handled again in the future.
|
||||
handleEventTimeout = 10 * time.Second
|
||||
|
||||
exitChannelSize = 1024
|
||||
)
|
||||
|
||||
// eventMonitor monitors containerd event and updates internal state correspondingly.
|
||||
// TODO(random-liu): Handle event for each container in a separate goroutine.
|
||||
type eventMonitor struct {
|
||||
c *criService
|
||||
ch <-chan *events.Envelope
|
||||
// exitCh receives container/sandbox exit events from exit monitors.
|
||||
exitCh chan *eventtypes.TaskExit
|
||||
errCh <-chan error
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
backOff *backOff
|
||||
}
|
||||
|
||||
type backOff struct {
|
||||
queuePool map[string]*backOffQueue
|
||||
// tickerMu is mutex used to protect the ticker.
|
||||
tickerMu sync.Mutex
|
||||
ticker *time.Ticker
|
||||
minDuration time.Duration
|
||||
maxDuration time.Duration
|
||||
checkDuration time.Duration
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
type backOffQueue struct {
|
||||
events []interface{}
|
||||
expireTime time.Time
|
||||
duration time.Duration
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
// Create new event monitor. New event monitor will start subscribing containerd event. All events
|
||||
// happen after it should be monitored.
|
||||
func newEventMonitor(c *criService) *eventMonitor {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &eventMonitor{
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
exitCh: make(chan *eventtypes.TaskExit, exitChannelSize),
|
||||
backOff: newBackOff(),
|
||||
}
|
||||
}
|
||||
|
||||
// subscribe starts to subscribe containerd events.
|
||||
func (em *eventMonitor) subscribe(subscriber events.Subscriber) {
|
||||
// note: filters are any match, if you want any match but not in namespace foo
|
||||
// then you have to manually filter namespace foo
|
||||
filters := []string{
|
||||
`topic=="/tasks/oom"`,
|
||||
`topic~="/images/"`,
|
||||
}
|
||||
em.ch, em.errCh = subscriber.Subscribe(em.ctx, filters...)
|
||||
}
|
||||
|
||||
// startExitMonitor starts an exit monitor for a given container/sandbox.
|
||||
func (em *eventMonitor) startExitMonitor(ctx context.Context, id string, pid uint32, exitCh <-chan containerd.ExitStatus) <-chan struct{} {
|
||||
stopCh := make(chan struct{})
|
||||
go func() {
|
||||
defer close(stopCh)
|
||||
select {
|
||||
case exitRes := <-exitCh:
|
||||
exitStatus, exitedAt, err := exitRes.Result()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to get task exit status for %q", id)
|
||||
exitStatus = unknownExitCode
|
||||
exitedAt = time.Now()
|
||||
}
|
||||
em.exitCh <- &eventtypes.TaskExit{
|
||||
ContainerID: id,
|
||||
ID: id,
|
||||
Pid: pid,
|
||||
ExitStatus: exitStatus,
|
||||
ExitedAt: exitedAt,
|
||||
}
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
return stopCh
|
||||
}
|
||||
|
||||
func convertEvent(e *gogotypes.Any) (string, interface{}, error) {
|
||||
id := ""
|
||||
evt, err := typeurl.UnmarshalAny(e)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, "failed to unmarshalany")
|
||||
}
|
||||
|
||||
switch e := evt.(type) {
|
||||
case *eventtypes.TaskOOM:
|
||||
id = e.ContainerID
|
||||
case *eventtypes.ImageCreate:
|
||||
id = e.Name
|
||||
case *eventtypes.ImageUpdate:
|
||||
id = e.Name
|
||||
case *eventtypes.ImageDelete:
|
||||
id = e.Name
|
||||
default:
|
||||
return "", nil, errors.New("unsupported event")
|
||||
}
|
||||
return id, evt, nil
|
||||
}
|
||||
|
||||
// start starts the event monitor which monitors and handles all subscribed events. It returns
|
||||
// an error channel for the caller to wait for stop errors from the event monitor.
|
||||
// start must be called after subscribe.
|
||||
func (em *eventMonitor) start() <-chan error {
|
||||
errCh := make(chan error)
|
||||
if em.ch == nil || em.errCh == nil {
|
||||
panic("event channel is nil")
|
||||
}
|
||||
backOffCheckCh := em.backOff.start()
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
for {
|
||||
select {
|
||||
case e := <-em.exitCh:
|
||||
logrus.Debugf("Received exit event %+v", e)
|
||||
id := e.ID
|
||||
if em.backOff.isInBackOff(id) {
|
||||
logrus.Infof("Events for %q is in backoff, enqueue event %+v", id, e)
|
||||
em.backOff.enBackOff(id, e)
|
||||
break
|
||||
}
|
||||
if err := em.handleEvent(e); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to handle exit event %+v for %s", e, id)
|
||||
em.backOff.enBackOff(id, e)
|
||||
}
|
||||
case e := <-em.ch:
|
||||
logrus.Debugf("Received containerd event timestamp - %v, namespace - %q, topic - %q", e.Timestamp, e.Namespace, e.Topic)
|
||||
if e.Namespace != constants.K8sContainerdNamespace {
|
||||
logrus.Debugf("Ignoring events in namespace - %q", e.Namespace)
|
||||
break
|
||||
}
|
||||
id, evt, err := convertEvent(e.Event)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to convert event %+v", e)
|
||||
break
|
||||
}
|
||||
if em.backOff.isInBackOff(id) {
|
||||
logrus.Infof("Events for %q is in backoff, enqueue event %+v", id, evt)
|
||||
em.backOff.enBackOff(id, evt)
|
||||
break
|
||||
}
|
||||
if err := em.handleEvent(evt); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to handle event %+v for %s", evt, id)
|
||||
em.backOff.enBackOff(id, evt)
|
||||
}
|
||||
case err := <-em.errCh:
|
||||
// Close errCh in defer directly if there is no error.
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to handle event stream")
|
||||
errCh <- err
|
||||
}
|
||||
return
|
||||
case <-backOffCheckCh:
|
||||
ids := em.backOff.getExpiredIDs()
|
||||
for _, id := range ids {
|
||||
queue := em.backOff.deBackOff(id)
|
||||
for i, any := range queue.events {
|
||||
if err := em.handleEvent(any); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to handle backOff event %+v for %s", any, id)
|
||||
em.backOff.reBackOff(id, queue.events[i:], queue.duration)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return errCh
|
||||
}
|
||||
|
||||
// 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.backOff.stop()
|
||||
em.cancel()
|
||||
}
|
||||
|
||||
// handleEvent handles a containerd event.
|
||||
func (em *eventMonitor) handleEvent(any interface{}) error {
|
||||
ctx := ctrdutil.NamespacedContext()
|
||||
ctx, cancel := context.WithTimeout(ctx, handleEventTimeout)
|
||||
defer cancel()
|
||||
|
||||
switch e := any.(type) {
|
||||
case *eventtypes.TaskExit:
|
||||
logrus.Infof("TaskExit event %+v", e)
|
||||
// Use ID instead of ContainerID to rule out TaskExit event for exec.
|
||||
cntr, err := em.c.containerStore.Get(e.ID)
|
||||
if err == nil {
|
||||
if err := handleContainerExit(ctx, e, cntr); err != nil {
|
||||
return errors.Wrap(err, "failed to handle container TaskExit event")
|
||||
}
|
||||
return nil
|
||||
} else if err != store.ErrNotExist {
|
||||
return errors.Wrap(err, "can't find container for TaskExit event")
|
||||
}
|
||||
sb, err := em.c.sandboxStore.Get(e.ID)
|
||||
if err == nil {
|
||||
if err := handleSandboxExit(ctx, e, sb); err != nil {
|
||||
return errors.Wrap(err, "failed to handle sandbox TaskExit event")
|
||||
}
|
||||
return nil
|
||||
} else if err != store.ErrNotExist {
|
||||
return errors.Wrap(err, "can't find sandbox for TaskExit event")
|
||||
}
|
||||
return nil
|
||||
case *eventtypes.TaskOOM:
|
||||
logrus.Infof("TaskOOM event %+v", e)
|
||||
// For TaskOOM, we only care which container it belongs to.
|
||||
cntr, err := em.c.containerStore.Get(e.ContainerID)
|
||||
if err != nil {
|
||||
if err != store.ErrNotExist {
|
||||
return errors.Wrap(err, "can't find container for TaskOOM event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
|
||||
status.Reason = oomExitReason
|
||||
return status, nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to update container status for TaskOOM event")
|
||||
}
|
||||
case *eventtypes.ImageCreate:
|
||||
logrus.Infof("ImageCreate event %+v", e)
|
||||
return em.c.updateImage(ctx, e.Name)
|
||||
case *eventtypes.ImageUpdate:
|
||||
logrus.Infof("ImageUpdate event %+v", e)
|
||||
return em.c.updateImage(ctx, e.Name)
|
||||
case *eventtypes.ImageDelete:
|
||||
logrus.Infof("ImageDelete event %+v", e)
|
||||
return em.c.updateImage(ctx, e.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleContainerExit handles TaskExit event for container.
|
||||
func handleContainerExit(ctx context.Context, e *eventtypes.TaskExit, cntr containerstore.Container) error {
|
||||
// Attach container IO so that `Delete` could cleanup the stream properly.
|
||||
task, err := cntr.Container.Task(ctx,
|
||||
func(*containerdio.FIFOSet) (containerdio.IO, error) {
|
||||
// We can't directly return cntr.IO here, because
|
||||
// even if cntr.IO is nil, the cio.IO interface
|
||||
// is not.
|
||||
// See https://tour.golang.org/methods/12:
|
||||
// Note that an interface value that holds a nil
|
||||
// concrete value is itself non-nil.
|
||||
if cntr.IO != nil {
|
||||
return cntr.IO, nil
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "failed to load task for container")
|
||||
}
|
||||
} else {
|
||||
// TODO(random-liu): [P1] This may block the loop, we may want to spawn a worker
|
||||
if _, err = task.Delete(ctx, WithNRISandboxDelete(cntr.SandboxID), containerd.WithProcessKill); err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to stop container")
|
||||
}
|
||||
// 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)
|
||||
// Unknown state can only transit to EXITED state, so we need
|
||||
// to handle unknown state here.
|
||||
if status.Unknown {
|
||||
logrus.Debugf("Container %q transited from UNKNOWN to EXITED", cntr.ID)
|
||||
status.Unknown = false
|
||||
}
|
||||
return status, nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to update container state")
|
||||
}
|
||||
// Using channel to propagate the information of container stop
|
||||
cntr.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleSandboxExit handles TaskExit event for sandbox.
|
||||
func handleSandboxExit(ctx context.Context, e *eventtypes.TaskExit, sb sandboxstore.Sandbox) error {
|
||||
// No stream attached to sandbox container.
|
||||
task, err := sb.Container.Task(ctx, nil)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to load task for sandbox")
|
||||
}
|
||||
} else {
|
||||
// TODO(random-liu): [P1] This may block the loop, we may want to spawn a worker
|
||||
if _, err = task.Delete(ctx, WithNRISandboxDelete(sb.ID), containerd.WithProcessKill); err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to stop sandbox")
|
||||
}
|
||||
// Move on to make sure container status is updated.
|
||||
}
|
||||
}
|
||||
err = sb.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
|
||||
status.State = sandboxstore.StateNotReady
|
||||
status.Pid = 0
|
||||
return status, nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to update sandbox state")
|
||||
}
|
||||
// Using channel to propagate the information of sandbox stop
|
||||
sb.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func newBackOff() *backOff {
|
||||
return &backOff{
|
||||
queuePool: map[string]*backOffQueue{},
|
||||
minDuration: backOffInitDuration,
|
||||
maxDuration: backOffMaxDuration,
|
||||
checkDuration: backOffExpireCheckDuration,
|
||||
clock: clock.RealClock{},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backOff) getExpiredIDs() []string {
|
||||
var ids []string
|
||||
for id, q := range b.queuePool {
|
||||
if q.isExpire() {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (b *backOff) isInBackOff(key string) bool {
|
||||
if _, ok := b.queuePool[key]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// enBackOff start to backOff and put event to the tail of queue
|
||||
func (b *backOff) enBackOff(key string, evt interface{}) {
|
||||
if queue, ok := b.queuePool[key]; ok {
|
||||
queue.events = append(queue.events, evt)
|
||||
return
|
||||
}
|
||||
b.queuePool[key] = newBackOffQueue([]interface{}{evt}, b.minDuration, b.clock)
|
||||
}
|
||||
|
||||
// enBackOff get out the whole queue
|
||||
func (b *backOff) deBackOff(key string) *backOffQueue {
|
||||
queue := b.queuePool[key]
|
||||
delete(b.queuePool, key)
|
||||
return queue
|
||||
}
|
||||
|
||||
// enBackOff start to backOff again and put events to the queue
|
||||
func (b *backOff) reBackOff(key string, events []interface{}, oldDuration time.Duration) {
|
||||
duration := 2 * oldDuration
|
||||
if duration > b.maxDuration {
|
||||
duration = b.maxDuration
|
||||
}
|
||||
b.queuePool[key] = newBackOffQueue(events, duration, b.clock)
|
||||
}
|
||||
|
||||
func (b *backOff) start() <-chan time.Time {
|
||||
b.tickerMu.Lock()
|
||||
defer b.tickerMu.Unlock()
|
||||
b.ticker = time.NewTicker(b.checkDuration)
|
||||
return b.ticker.C
|
||||
}
|
||||
|
||||
func (b *backOff) stop() {
|
||||
b.tickerMu.Lock()
|
||||
defer b.tickerMu.Unlock()
|
||||
if b.ticker != nil {
|
||||
b.ticker.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func newBackOffQueue(events []interface{}, init time.Duration, c clock.Clock) *backOffQueue {
|
||||
return &backOffQueue{
|
||||
events: events,
|
||||
duration: init,
|
||||
expireTime: c.Now().Add(init),
|
||||
clock: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *backOffQueue) isExpire() bool {
|
||||
// return time.Now >= expireTime
|
||||
return !q.clock.Now().Before(q.expireTime)
|
||||
}
|
||||
390
vendor/github.com/containerd/cri/pkg/server/helpers.go
generated
vendored
390
vendor/github.com/containerd/cri/pkg/server/helpers.go
generated
vendored
@@ -1,390 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
runhcsoptions "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/containers"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/reference/docker"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
"github.com/containerd/typeurl"
|
||||
imagedigest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
runtimeoptions "github.com/containerd/cri/pkg/api/runtimeoptions/v1"
|
||||
criconfig "github.com/containerd/cri/pkg/config"
|
||||
"github.com/containerd/cri/pkg/store"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
imagestore "github.com/containerd/cri/pkg/store/image"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
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.
|
||||
// TODO(windows): Figure out what should be used for windows.
|
||||
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"
|
||||
|
||||
// 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"
|
||||
// Delimiter used to construct container/sandbox names.
|
||||
nameDelimiter = "_"
|
||||
|
||||
// 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"
|
||||
// imageLabelKey is the label key indicating the image is managed by cri plugin.
|
||||
imageLabelKey = criContainerdPrefix + ".image"
|
||||
// imageLabelValue is the label value indicating the image is managed by cri plugin.
|
||||
imageLabelValue = "managed"
|
||||
// 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"
|
||||
|
||||
// defaultIfName is the default network interface for the pods
|
||||
defaultIfName = "eth0"
|
||||
|
||||
// runtimeRunhcsV1 is the runtime type for runhcs.
|
||||
runtimeRunhcsV1 = "io.containerd.runhcs.v1"
|
||||
)
|
||||
|
||||
// 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: pod name
|
||||
s.Namespace, // 2: pod namespace
|
||||
s.Uid, // 3: pod uid
|
||||
fmt.Sprintf("%d", c.Attempt), // 4
|
||||
}, nameDelimiter)
|
||||
}
|
||||
|
||||
// getSandboxRootDir returns the root directory for managing sandbox files,
|
||||
// e.g. hosts files.
|
||||
func (c *criService) getSandboxRootDir(id string) string {
|
||||
return filepath.Join(c.config.RootDir, sandboxesDir, id)
|
||||
}
|
||||
|
||||
// getVolatileSandboxRootDir returns the root directory for managing volatile sandbox files,
|
||||
// e.g. named pipes.
|
||||
func (c *criService) getVolatileSandboxRootDir(id string) string {
|
||||
return filepath.Join(c.config.StateDir, sandboxesDir, id)
|
||||
}
|
||||
|
||||
// getContainerRootDir returns the root directory for managing container files,
|
||||
// e.g. state checkpoint.
|
||||
func (c *criService) getContainerRootDir(id string) string {
|
||||
return filepath.Join(c.config.RootDir, containersDir, id)
|
||||
}
|
||||
|
||||
// getVolatileContainerRootDir returns the root directory for managing volatile container files,
|
||||
// e.g. named pipes.
|
||||
func (c *criService) getVolatileContainerRootDir(id string) string {
|
||||
return filepath.Join(c.config.StateDir, containersDir, id)
|
||||
}
|
||||
|
||||
// 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 docker.Named, digest imagedigest.Digest, schema1 bool) (string, string) {
|
||||
var repoTag, repoDigest string
|
||||
if _, ok := namedRef.(docker.NamedTagged); ok {
|
||||
repoTag = namedRef.String()
|
||||
}
|
||||
if _, ok := namedRef.(docker.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 store.ErrNotExist if the reference doesn't exist.
|
||||
func (c *criService) localResolve(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.
|
||||
// TODO(random-liu): Handle this error better for debugging.
|
||||
normalized, err := docker.ParseDockerRef(ref)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
id, err := c.imageStore.Resolve(normalized.String())
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return id
|
||||
}(refOrID)
|
||||
}
|
||||
|
||||
imageID := getImageID(refOrID)
|
||||
if imageID == "" {
|
||||
// Try to treat ref as imageID
|
||||
imageID = refOrID
|
||||
}
|
||||
return c.imageStore.Get(imageID)
|
||||
}
|
||||
|
||||
// toContainerdImage converts an image object in image store to containerd image handler.
|
||||
func (c *criService) toContainerdImage(ctx context.Context, image imagestore.Image) (containerd.Image, error) {
|
||||
// image should always have at least one reference.
|
||||
if len(image.References) == 0 {
|
||||
return nil, errors.Errorf("invalid image with no reference %q", image.ID)
|
||||
}
|
||||
return c.client.GetImage(ctx, image.References[0])
|
||||
}
|
||||
|
||||
// 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 *criService) ensureImageExists(ctx context.Context, ref string, config *runtime.PodSandboxConfig) (*imagestore.Image, error) {
|
||||
image, err := c.localResolve(ref)
|
||||
if err != nil && err != store.ErrNotExist {
|
||||
return nil, errors.Wrapf(err, "failed to get image %q", ref)
|
||||
}
|
||||
if err == nil {
|
||||
return &image, nil
|
||||
}
|
||||
// Pull image to ensure the image exists
|
||||
resp, err := c.PullImage(ctx, &runtime.PullImageRequest{Image: &runtime.ImageSpec{Image: ref}, SandboxConfig: config})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to pull image %q", ref)
|
||||
}
|
||||
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, errors.Wrapf(err, "failed to get image %q after pulling", imageID)
|
||||
}
|
||||
return &newImage, nil
|
||||
}
|
||||
|
||||
// isInCRIMounts checks whether a destination is in CRI mount list.
|
||||
func isInCRIMounts(dst string, mounts []*runtime.Mount) bool {
|
||||
for _, m := range mounts {
|
||||
if filepath.Clean(m.ContainerPath) == filepath.Clean(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
|
||||
}
|
||||
|
||||
// toRuntimeAuthConfig converts cri plugin auth config to runtime auth config.
|
||||
func toRuntimeAuthConfig(a criconfig.AuthConfig) *runtime.AuthConfig {
|
||||
return &runtime.AuthConfig{
|
||||
Username: a.Username,
|
||||
Password: a.Password,
|
||||
Auth: a.Auth,
|
||||
IdentityToken: a.IdentityToken,
|
||||
}
|
||||
}
|
||||
|
||||
// parseImageReferences parses a list of arbitrary image references and returns
|
||||
// the repotags and repodigests
|
||||
func parseImageReferences(refs []string) ([]string, []string) {
|
||||
var tags, digests []string
|
||||
for _, ref := range refs {
|
||||
parsed, err := docker.ParseAnyReference(ref)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if _, ok := parsed.(docker.Canonical); ok {
|
||||
digests = append(digests, parsed.String())
|
||||
} else if _, ok := parsed.(docker.Tagged); ok {
|
||||
tags = append(tags, parsed.String())
|
||||
}
|
||||
}
|
||||
return tags, digests
|
||||
}
|
||||
|
||||
// generateRuntimeOptions generates runtime options from cri plugin config.
|
||||
func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{}, error) {
|
||||
if r.Options == nil {
|
||||
if r.Type != plugin.RuntimeLinuxV1 {
|
||||
return nil, nil
|
||||
}
|
||||
// This is a legacy config, generate runctypes.RuncOptions.
|
||||
return &runctypes.RuncOptions{
|
||||
Runtime: r.Engine,
|
||||
RuntimeRoot: r.Root,
|
||||
SystemdCgroup: c.SystemdCgroup,
|
||||
}, nil
|
||||
}
|
||||
options := getRuntimeOptionsType(r.Type)
|
||||
if err := toml.PrimitiveDecode(*r.Options, options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
// getRuntimeOptionsType gets empty runtime options by the runtime type name.
|
||||
func getRuntimeOptionsType(t string) interface{} {
|
||||
switch t {
|
||||
case plugin.RuntimeRuncV1:
|
||||
fallthrough
|
||||
case plugin.RuntimeRuncV2:
|
||||
return &runcoptions.Options{}
|
||||
case plugin.RuntimeLinuxV1:
|
||||
return &runctypes.RuncOptions{}
|
||||
case runtimeRunhcsV1:
|
||||
return &runhcsoptions.Options{}
|
||||
default:
|
||||
return &runtimeoptions.Options{}
|
||||
}
|
||||
}
|
||||
|
||||
// getRuntimeOptions get runtime options from container metadata.
|
||||
func getRuntimeOptions(c containers.Container) (interface{}, error) {
|
||||
if c.Runtime.Options == nil {
|
||||
return nil, nil
|
||||
}
|
||||
opts, err := typeurl.UnmarshalAny(c.Runtime.Options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
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: 0,
|
||||
StartedAt: 0,
|
||||
FinishedAt: 0,
|
||||
ExitCode: unknownExitCode,
|
||||
Reason: unknownExitReason,
|
||||
Unknown: true,
|
||||
}
|
||||
}
|
||||
|
||||
// unknownSandboxStatus returns the default sandbox status when its status is unknown.
|
||||
func unknownSandboxStatus() sandboxstore.Status {
|
||||
return sandboxstore.Status{
|
||||
State: sandboxstore.StateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
// getPassthroughAnnotations filters requested pod annotations by comparing
|
||||
// against permitted annotations for the given runtime.
|
||||
func getPassthroughAnnotations(podAnnotations map[string]string,
|
||||
runtimePodAnnotations []string) (passthroughAnnotations map[string]string) {
|
||||
passthroughAnnotations = make(map[string]string)
|
||||
|
||||
for podAnnotationKey, podAnnotationValue := range podAnnotations {
|
||||
for _, pattern := range runtimePodAnnotations {
|
||||
// Use path.Match instead of filepath.Match here.
|
||||
// filepath.Match treated `\\` as path separator
|
||||
// on windows, which is not what we want.
|
||||
if ok, _ := path.Match(pattern, podAnnotationKey); ok {
|
||||
passthroughAnnotations[podAnnotationKey] = podAnnotationValue
|
||||
}
|
||||
}
|
||||
}
|
||||
return passthroughAnnotations
|
||||
}
|
||||
292
vendor/github.com/containerd/cri/pkg/server/helpers_unix.go
generated
vendored
292
vendor/github.com/containerd/cri/pkg/server/helpers_unix.go
generated
vendored
@@ -1,292 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/cri/pkg/seccomp"
|
||||
"github.com/containerd/cri/pkg/seutil"
|
||||
runcapparmor "github.com/opencontainers/runc/libcontainer/apparmor"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultSandboxOOMAdj is default omm adj for sandbox container. (kubernetes#47938).
|
||||
defaultSandboxOOMAdj = -998
|
||||
// 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"
|
||||
// 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
|
||||
// devShm is the default path of /dev/shm.
|
||||
devShm = "/dev/shm"
|
||||
// etcHosts is the default path of /etc/hosts file.
|
||||
etcHosts = "/etc/hosts"
|
||||
// etcHostname is the default path of /etc/hostname file.
|
||||
etcHostname = "/etc/hostname"
|
||||
// resolvConfPath is the abs path of resolv.conf on host or container.
|
||||
resolvConfPath = "/etc/resolv.conf"
|
||||
// hostnameEnv is the key for HOSTNAME env.
|
||||
hostnameEnv = "HOSTNAME"
|
||||
)
|
||||
|
||||
// getCgroupsPath generates container cgroups path.
|
||||
func getCgroupsPath(cgroupsParent, id string) string {
|
||||
base := path.Base(cgroupsParent)
|
||||
if strings.HasSuffix(base, ".slice") {
|
||||
// For a.slice/b.slice/c.slice, base is c.slice.
|
||||
// runc systemd cgroup path format is "slice:prefix:name".
|
||||
return strings.Join([]string{base, "cri-containerd", id}, ":")
|
||||
}
|
||||
return filepath.Join(cgroupsParent, id)
|
||||
}
|
||||
|
||||
// getSandboxHostname returns the hostname file path inside the sandbox root directory.
|
||||
func (c *criService) getSandboxHostname(id string) string {
|
||||
return filepath.Join(c.getSandboxRootDir(id), "hostname")
|
||||
}
|
||||
|
||||
// getSandboxHosts returns the hosts file path inside the sandbox root directory.
|
||||
func (c *criService) getSandboxHosts(id string) string {
|
||||
return filepath.Join(c.getSandboxRootDir(id), "hosts")
|
||||
}
|
||||
|
||||
// getResolvPath returns resolv.conf filepath for specified sandbox.
|
||||
func (c *criService) getResolvPath(id string) string {
|
||||
return filepath.Join(c.getSandboxRootDir(id), "resolv.conf")
|
||||
}
|
||||
|
||||
// getSandboxDevShm returns the shm file path inside the sandbox root directory.
|
||||
func (c *criService) getSandboxDevShm(id string) string {
|
||||
return filepath.Join(c.getVolatileSandboxRootDir(id), "shm")
|
||||
}
|
||||
|
||||
func toLabel(selinuxOptions *runtime.SELinuxOption) ([]string, error) {
|
||||
var labels []string
|
||||
|
||||
if selinuxOptions == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := checkSelinuxLevel(selinuxOptions.Level); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if selinuxOptions.User != "" {
|
||||
labels = append(labels, "user:"+selinuxOptions.User)
|
||||
}
|
||||
if selinuxOptions.Role != "" {
|
||||
labels = append(labels, "role:"+selinuxOptions.Role)
|
||||
}
|
||||
if selinuxOptions.Type != "" {
|
||||
labels = append(labels, "type:"+selinuxOptions.Type)
|
||||
}
|
||||
if selinuxOptions.Level != "" {
|
||||
labels = append(labels, "level:"+selinuxOptions.Level)
|
||||
}
|
||||
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
func initLabelsFromOpt(selinuxOpts *runtime.SELinuxOption) (string, string, error) {
|
||||
labels, err := toLabel(selinuxOpts)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return label.InitLabels(labels)
|
||||
}
|
||||
|
||||
func checkSelinuxLevel(level string) error {
|
||||
if len(level) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
matched, err := regexp.MatchString(`^s\d(-s\d)??(:c\d{1,4}(\.c\d{1,4})?(,c\d{1,4}(\.c\d{1,4})?)*)?$`, level)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "the format of 'level' %q is not correct", level)
|
||||
}
|
||||
if !matched {
|
||||
return fmt.Errorf("the format of 'level' %q is not correct", level)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *criService) apparmorEnabled() bool {
|
||||
return runcapparmor.IsEnabled() && !c.config.DisableApparmor
|
||||
}
|
||||
|
||||
func (c *criService) seccompEnabled() bool {
|
||||
return seccomp.IsEnabled()
|
||||
}
|
||||
|
||||
// openLogFile opens/creates a container log file.
|
||||
func openLogFile(path string) (*os.File, error) {
|
||||
return os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0640)
|
||||
}
|
||||
|
||||
// unmountRecursive unmounts the target and all mounts underneath, starting with
|
||||
// the deepest mount first.
|
||||
func unmountRecursive(ctx context.Context, target string) error {
|
||||
mounts, err := mount.Self()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var toUnmount []string
|
||||
for _, m := range mounts {
|
||||
p, err := filepath.Rel(target, m.Mountpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.HasPrefix(p, "..") {
|
||||
toUnmount = append(toUnmount, m.Mountpoint)
|
||||
}
|
||||
}
|
||||
|
||||
// Make the deepest mount be first
|
||||
sort.Slice(toUnmount, func(i, j int) bool {
|
||||
return len(toUnmount[i]) > len(toUnmount[j])
|
||||
})
|
||||
|
||||
for i, mountPath := range toUnmount {
|
||||
if err := mount.UnmountAll(mountPath, unix.MNT_DETACH); err != nil {
|
||||
if i == len(toUnmount)-1 { // last mount
|
||||
return err
|
||||
}
|
||||
// This is some submount, we can ignore this error for now, the final unmount will fail if this is a real problem
|
||||
log.G(ctx).WithError(err).Debugf("failed to unmount submount %s", mountPath)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
|
||||
// often be remedied.
|
||||
// Only use `ensureRemoveAll` if you really want to make every effort to remove
|
||||
// a directory.
|
||||
//
|
||||
// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
|
||||
// can be a race between reading directory entries and then actually attempting
|
||||
// to remove everything in the directory.
|
||||
// These types of errors do not need to be returned since it's ok for the dir to
|
||||
// be gone we can just retry the remove operation.
|
||||
//
|
||||
// This should not return a `os.ErrNotExist` kind of error under any circumstances
|
||||
func ensureRemoveAll(ctx context.Context, dir string) error {
|
||||
notExistErr := make(map[string]bool)
|
||||
|
||||
// track retries
|
||||
exitOnErr := make(map[string]int)
|
||||
maxRetry := 50
|
||||
|
||||
// Attempt to unmount anything beneath this dir first.
|
||||
if err := unmountRecursive(ctx, dir); err != nil {
|
||||
log.G(ctx).WithError(err).Debugf("failed to do initial unmount of %s", dir)
|
||||
}
|
||||
|
||||
for {
|
||||
err := os.RemoveAll(dir)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pe, ok := err.(*os.PathError)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
if notExistErr[pe.Path] {
|
||||
return err
|
||||
}
|
||||
notExistErr[pe.Path] = true
|
||||
|
||||
// There is a race where some subdir can be removed but after the
|
||||
// parent dir entries have been read.
|
||||
// So the path could be from `os.Remove(subdir)`
|
||||
// If the reported non-existent path is not the passed in `dir` we
|
||||
// should just retry, but otherwise return with no error.
|
||||
if pe.Path == dir {
|
||||
return nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if pe.Err != syscall.EBUSY {
|
||||
return err
|
||||
}
|
||||
if e := mount.Unmount(pe.Path, unix.MNT_DETACH); e != nil {
|
||||
return errors.Wrapf(e, "error while removing %s", dir)
|
||||
}
|
||||
|
||||
if exitOnErr[pe.Path] == maxRetry {
|
||||
return err
|
||||
}
|
||||
exitOnErr[pe.Path]++
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
var vmbasedRuntimes = []string{
|
||||
"io.containerd.kata",
|
||||
}
|
||||
|
||||
func isVMBasedRuntime(runtimeType string) bool {
|
||||
for _, rt := range vmbasedRuntimes {
|
||||
if strings.Contains(runtimeType, rt) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func modifyProcessLabel(runtimeType string, spec *specs.Spec) error {
|
||||
if !isVMBasedRuntime(runtimeType) {
|
||||
return nil
|
||||
}
|
||||
l, err := getKVMLabel(spec.Process.SelinuxLabel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get selinux kvm label")
|
||||
}
|
||||
spec.Process.SelinuxLabel = l
|
||||
return nil
|
||||
}
|
||||
|
||||
func getKVMLabel(l string) (string, error) {
|
||||
if !seutil.HasType("container_kvm_t") {
|
||||
return "", nil
|
||||
}
|
||||
return seutil.ChangeToKVM(l)
|
||||
}
|
||||
225
vendor/github.com/containerd/cri/pkg/server/helpers_windows.go
generated
vendored
225
vendor/github.com/containerd/cri/pkg/server/helpers_windows.go
generated
vendored
@@ -1,225 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// openLogFile opens/creates a container log file.
|
||||
// It specifies `FILE_SHARE_DELETE` option to make sure
|
||||
// log files can be rotated by kubelet.
|
||||
// TODO(windows): Use golang support after 1.14. (https://github.com/golang/go/issues/32088)
|
||||
func openLogFile(path string) (*os.File, error) {
|
||||
path = fixLongPath(path)
|
||||
if len(path) == 0 {
|
||||
return nil, syscall.ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
createmode := uint32(syscall.OPEN_ALWAYS)
|
||||
access := uint32(syscall.FILE_APPEND_DATA)
|
||||
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
|
||||
h, err := syscall.CreateFile(pathp, access, sharemode, nil, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(h), path), nil
|
||||
}
|
||||
|
||||
// Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// fixLongPath returns the extended-length (\\?\-prefixed) form of
|
||||
// path when needed, in order to avoid the default 260 character file
|
||||
// path limit imposed by Windows. If path is not easily converted to
|
||||
// the extended-length form (for example, if path is a relative path
|
||||
// or contains .. elements), or is short enough, fixLongPath returns
|
||||
// path unmodified.
|
||||
//
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
|
||||
//
|
||||
// This is copied from https://golang.org/src/path/filepath/path_windows.go.
|
||||
func fixLongPath(path string) string {
|
||||
// Do nothing (and don't allocate) if the path is "short".
|
||||
// Empirically (at least on the Windows Server 2013 builder),
|
||||
// the kernel is arbitrarily okay with < 248 bytes. That
|
||||
// matches what the docs above say:
|
||||
// "When using an API to create a directory, the specified
|
||||
// path cannot be so long that you cannot append an 8.3 file
|
||||
// name (that is, the directory name cannot exceed MAX_PATH
|
||||
// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
|
||||
//
|
||||
// The MSDN docs appear to say that a normal path that is 248 bytes long
|
||||
// will work; empirically the path must be less then 248 bytes long.
|
||||
if len(path) < 248 {
|
||||
// Don't fix. (This is how Go 1.7 and earlier worked,
|
||||
// not automatically generating the \\?\ form)
|
||||
return path
|
||||
}
|
||||
|
||||
// The extended form begins with \\?\, as in
|
||||
// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
|
||||
// The extended form disables evaluation of . and .. path
|
||||
// elements and disables the interpretation of / as equivalent
|
||||
// to \. The conversion here rewrites / to \ and elides
|
||||
// . elements as well as trailing or duplicate separators. For
|
||||
// simplicity it avoids the conversion entirely for relative
|
||||
// paths or paths containing .. elements. For now,
|
||||
// \\server\share paths are not converted to
|
||||
// \\?\UNC\server\share paths because the rules for doing so
|
||||
// are less well-specified.
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
// Don't canonicalize UNC paths.
|
||||
return path
|
||||
}
|
||||
if !filepath.IsAbs(path) {
|
||||
// Relative path
|
||||
return path
|
||||
}
|
||||
|
||||
const prefix = `\\?`
|
||||
|
||||
pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
|
||||
copy(pathbuf, prefix)
|
||||
n := len(path)
|
||||
r, w := 0, len(prefix)
|
||||
for r < n {
|
||||
switch {
|
||||
case os.IsPathSeparator(path[r]):
|
||||
// empty block
|
||||
r++
|
||||
case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
|
||||
// /./
|
||||
r++
|
||||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
|
||||
// /../ is currently unhandled
|
||||
return path
|
||||
default:
|
||||
pathbuf[w] = '\\'
|
||||
w++
|
||||
for ; r < n && !os.IsPathSeparator(path[r]); r++ {
|
||||
pathbuf[w] = path[r]
|
||||
w++
|
||||
}
|
||||
}
|
||||
}
|
||||
// A drive's root directory needs a trailing \
|
||||
if w == len(`\\?\c:`) {
|
||||
pathbuf[w] = '\\'
|
||||
w++
|
||||
}
|
||||
return string(pathbuf[:w])
|
||||
}
|
||||
|
||||
// ensureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
|
||||
// often be remedied.
|
||||
// Only use `ensureRemoveAll` if you really want to make every effort to remove
|
||||
// a directory.
|
||||
//
|
||||
// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
|
||||
// can be a race between reading directory entries and then actually attempting
|
||||
// to remove everything in the directory.
|
||||
// These types of errors do not need to be returned since it's ok for the dir to
|
||||
// be gone we can just retry the remove operation.
|
||||
//
|
||||
// This should not return a `os.ErrNotExist` kind of error under any circumstances
|
||||
func ensureRemoveAll(_ context.Context, dir string) error {
|
||||
notExistErr := make(map[string]bool)
|
||||
|
||||
// track retries
|
||||
exitOnErr := make(map[string]int)
|
||||
maxRetry := 50
|
||||
|
||||
for {
|
||||
err := os.RemoveAll(dir)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pe, ok := err.(*os.PathError)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
if notExistErr[pe.Path] {
|
||||
return err
|
||||
}
|
||||
notExistErr[pe.Path] = true
|
||||
|
||||
// There is a race where some subdir can be removed but after the
|
||||
// parent dir entries have been read.
|
||||
// So the path could be from `os.Remove(subdir)`
|
||||
// If the reported non-existent path is not the passed in `dir` we
|
||||
// should just retry, but otherwise return with no error.
|
||||
if pe.Path == dir {
|
||||
return nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if pe.Err != syscall.EBUSY {
|
||||
return err
|
||||
}
|
||||
|
||||
if exitOnErr[pe.Path] == maxRetry {
|
||||
return err
|
||||
}
|
||||
exitOnErr[pe.Path]++
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func modifyProcessLabel(runtimeType string, spec *specs.Spec) error {
|
||||
return nil
|
||||
}
|
||||
38
vendor/github.com/containerd/cri/pkg/server/image_list.go
generated
vendored
38
vendor/github.com/containerd/cri/pkg/server/image_list.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// ListImages lists existing images.
|
||||
// TODO(random-liu): Add image list filters after CRI defines this more clear, and kubelet
|
||||
// actually needs it.
|
||||
func (c *criService) 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
|
||||
}
|
||||
519
vendor/github.com/containerd/cri/pkg/server/image_pull.go
generated
vendored
519
vendor/github.com/containerd/cri/pkg/server/image_pull.go
generated
vendored
@@ -1,519 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
containerdimages "github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/labels"
|
||||
"github.com/containerd/containerd/log"
|
||||
distribution "github.com/containerd/containerd/reference/docker"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
"github.com/containerd/imgcrypt"
|
||||
"github.com/containerd/imgcrypt/images/encryption"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
criconfig "github.com/containerd/cri/pkg/config"
|
||||
)
|
||||
|
||||
// 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 can'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 occurs during the pulling, should we remove the entry from metadata
|
||||
// store? Or should we leave it there until next startup (resource leakage)?
|
||||
//
|
||||
// 3) The cri plugin 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 the cri plugin,
|
||||
// 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 *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (*runtime.PullImageResponse, error) {
|
||||
imageRef := r.GetImage().GetImage()
|
||||
namedRef, err := distribution.ParseDockerRef(imageRef)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse image reference %q", imageRef)
|
||||
}
|
||||
ref := namedRef.String()
|
||||
if ref != imageRef {
|
||||
log.G(ctx).Debugf("PullImage using normalized image ref: %q", ref)
|
||||
}
|
||||
var (
|
||||
resolver = docker.NewResolver(docker.ResolverOptions{
|
||||
Headers: c.config.Registry.Headers,
|
||||
Hosts: c.registryHosts(r.GetAuth()),
|
||||
})
|
||||
isSchema1 bool
|
||||
imageHandler containerdimages.HandlerFunc = func(_ context.Context,
|
||||
desc imagespec.Descriptor) ([]imagespec.Descriptor, error) {
|
||||
if desc.MediaType == containerdimages.MediaTypeDockerSchema1Manifest {
|
||||
isSchema1 = true
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
)
|
||||
|
||||
pullOpts := []containerd.RemoteOpt{
|
||||
containerd.WithSchema1Conversion,
|
||||
containerd.WithResolver(resolver),
|
||||
containerd.WithPullSnapshotter(c.config.ContainerdConfig.Snapshotter),
|
||||
containerd.WithPullUnpack,
|
||||
containerd.WithPullLabel(imageLabelKey, imageLabelValue),
|
||||
containerd.WithMaxConcurrentDownloads(c.config.MaxConcurrentDownloads),
|
||||
containerd.WithImageHandler(imageHandler),
|
||||
}
|
||||
|
||||
pullOpts = append(pullOpts, c.encryptedImagesPullOpts()...)
|
||||
if !c.config.ContainerdConfig.DisableSnapshotAnnotations {
|
||||
pullOpts = append(pullOpts,
|
||||
containerd.WithImageHandlerWrapper(appendInfoHandlerWrapper(ref)))
|
||||
}
|
||||
|
||||
if c.config.ContainerdConfig.DiscardUnpackedLayers {
|
||||
// Allows GC to clean layers up from the content store after unpacking
|
||||
pullOpts = append(pullOpts,
|
||||
containerd.WithChildLabelMap(containerdimages.ChildGCLabelsFilterLayers))
|
||||
}
|
||||
|
||||
image, err := c.client.Pull(ctx, ref, pullOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to pull and unpack image %q", ref)
|
||||
}
|
||||
|
||||
configDesc, err := image.Config(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get image config descriptor")
|
||||
}
|
||||
imageID := configDesc.Digest.String()
|
||||
|
||||
repoDigest, repoTag := getRepoDigestAndTag(namedRef, image.Target().Digest, isSchema1)
|
||||
for _, r := range []string{imageID, repoTag, repoDigest} {
|
||||
if r == "" {
|
||||
continue
|
||||
}
|
||||
if err := c.createImageReference(ctx, r, image.Target()); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create image reference %q", r)
|
||||
}
|
||||
// Update image store to reflect the newest state in containerd.
|
||||
// No need to use `updateImage`, because the image reference must
|
||||
// have been managed by the cri plugin.
|
||||
if err := c.imageStore.Update(ctx, r); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to update image store %q", r)
|
||||
}
|
||||
}
|
||||
|
||||
log.G(ctx).Debugf("Pulled image %q with image id %q, repo tag %q, repo digest %q", imageRef, imageID,
|
||||
repoTag, repoDigest)
|
||||
// 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: imageID}, nil
|
||||
}
|
||||
|
||||
// ParseAuth parses AuthConfig and returns username and password/secret required by containerd.
|
||||
func ParseAuth(auth *runtime.AuthConfig, host string) (string, string, error) {
|
||||
if auth == nil {
|
||||
return "", "", nil
|
||||
}
|
||||
if auth.ServerAddress != "" {
|
||||
// Do not return the auth info when server address doesn't match.
|
||||
u, err := url.Parse(auth.ServerAddress)
|
||||
if err != nil {
|
||||
return "", "", errors.Wrap(err, "parse server address")
|
||||
}
|
||||
if host != u.Host {
|
||||
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 "", "", errors.Errorf("invalid decoded auth: %q", decoded)
|
||||
}
|
||||
user, passwd := fields[0], fields[1]
|
||||
return user, strings.Trim(passwd, "\x00"), nil
|
||||
}
|
||||
// TODO(random-liu): Support RegistryToken.
|
||||
// An empty auth config is valid for anonymous registry
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
// 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 *criService) createImageReference(ctx context.Context, name string, desc imagespec.Descriptor) error {
|
||||
img := containerdimages.Image{
|
||||
Name: name,
|
||||
Target: desc,
|
||||
// Add a label to indicate that the image is managed by the cri plugin.
|
||||
Labels: map[string]string{imageLabelKey: imageLabelValue},
|
||||
}
|
||||
// TODO(random-liu): Figure out which is the more performant sequence create then update or
|
||||
// update then create.
|
||||
oldImg, err := c.client.ImageService().Create(ctx, img)
|
||||
if err == nil || !errdefs.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
if oldImg.Target.Digest == img.Target.Digest && oldImg.Labels[imageLabelKey] == imageLabelValue {
|
||||
return nil
|
||||
}
|
||||
_, err = c.client.ImageService().Update(ctx, img, "target", "labels")
|
||||
return err
|
||||
}
|
||||
|
||||
// updateImage updates image store to reflect the newest state of an image reference
|
||||
// in containerd. If the reference is not managed by the cri plugin, the function also
|
||||
// generates necessary metadata for the image and make it managed.
|
||||
func (c *criService) updateImage(ctx context.Context, r string) error {
|
||||
img, err := c.client.GetImage(ctx, r)
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "get image by reference")
|
||||
}
|
||||
if err == nil && img.Labels()[imageLabelKey] != imageLabelValue {
|
||||
// Make sure the image has the image id as its unique
|
||||
// identifier that references the image in its lifetime.
|
||||
configDesc, err := img.Config(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get image id")
|
||||
}
|
||||
id := configDesc.Digest.String()
|
||||
if err := c.createImageReference(ctx, id, img.Target()); err != nil {
|
||||
return errors.Wrapf(err, "create image id reference %q", id)
|
||||
}
|
||||
if err := c.imageStore.Update(ctx, id); err != nil {
|
||||
return errors.Wrapf(err, "update image store for %q", id)
|
||||
}
|
||||
// The image id is ready, add the label to mark the image as managed.
|
||||
if err := c.createImageReference(ctx, r, img.Target()); err != nil {
|
||||
return errors.Wrap(err, "create managed label")
|
||||
}
|
||||
}
|
||||
// If the image is not found, we should continue updating the cache,
|
||||
// so that the image can be removed from the cache.
|
||||
if err := c.imageStore.Update(ctx, r); err != nil {
|
||||
return errors.Wrapf(err, "update image store for %q", r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTLSConfig returns a TLSConfig configured with a CA/Cert/Key specified by registryTLSConfig
|
||||
func (c *criService) getTLSConfig(registryTLSConfig criconfig.TLSConfig) (*tls.Config, error) {
|
||||
var (
|
||||
tlsConfig = &tls.Config{}
|
||||
cert tls.Certificate
|
||||
err error
|
||||
)
|
||||
if registryTLSConfig.CertFile != "" && registryTLSConfig.KeyFile == "" {
|
||||
return nil, errors.Errorf("cert file %q was specified, but no corresponding key file was specified", registryTLSConfig.CertFile)
|
||||
}
|
||||
if registryTLSConfig.CertFile == "" && registryTLSConfig.KeyFile != "" {
|
||||
return nil, errors.Errorf("key file %q was specified, but no corresponding cert file was specified", registryTLSConfig.KeyFile)
|
||||
}
|
||||
if registryTLSConfig.CertFile != "" && registryTLSConfig.KeyFile != "" {
|
||||
cert, err = tls.LoadX509KeyPair(registryTLSConfig.CertFile, registryTLSConfig.KeyFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load cert file")
|
||||
}
|
||||
if len(cert.Certificate) != 0 {
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
tlsConfig.BuildNameToCertificate() // nolint:staticcheck
|
||||
}
|
||||
|
||||
if registryTLSConfig.CAFile != "" {
|
||||
caCertPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get system cert pool")
|
||||
}
|
||||
caCert, err := ioutil.ReadFile(registryTLSConfig.CAFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load CA file")
|
||||
}
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
tlsConfig.RootCAs = caCertPool
|
||||
}
|
||||
|
||||
tlsConfig.InsecureSkipVerify = registryTLSConfig.InsecureSkipVerify
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
// registryHosts is the registry hosts to be used by the resolver.
|
||||
func (c *criService) registryHosts(auth *runtime.AuthConfig) docker.RegistryHosts {
|
||||
return func(host string) ([]docker.RegistryHost, error) {
|
||||
var registries []docker.RegistryHost
|
||||
|
||||
endpoints, err := c.registryEndpoints(host)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get registry endpoints")
|
||||
}
|
||||
for _, e := range endpoints {
|
||||
u, err := url.Parse(e)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "parse registry endpoint %q from mirrors", e)
|
||||
}
|
||||
|
||||
var (
|
||||
transport = newTransport()
|
||||
client = &http.Client{Transport: transport}
|
||||
config = c.config.Registry.Configs[u.Host]
|
||||
)
|
||||
|
||||
if config.TLS != nil {
|
||||
transport.TLSClientConfig, err = c.getTLSConfig(*config.TLS)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get TLSConfig for registry %q", e)
|
||||
}
|
||||
}
|
||||
|
||||
if auth == nil && config.Auth != nil {
|
||||
auth = toRuntimeAuthConfig(*config.Auth)
|
||||
}
|
||||
|
||||
if u.Path == "" {
|
||||
u.Path = "/v2"
|
||||
}
|
||||
|
||||
registries = append(registries, docker.RegistryHost{
|
||||
Client: client,
|
||||
Authorizer: docker.NewDockerAuthorizer(
|
||||
docker.WithAuthClient(client),
|
||||
docker.WithAuthCreds(func(host string) (string, string, error) {
|
||||
return ParseAuth(auth, host)
|
||||
})),
|
||||
Host: u.Host,
|
||||
Scheme: u.Scheme,
|
||||
Path: u.Path,
|
||||
Capabilities: docker.HostCapabilityResolve | docker.HostCapabilityPull,
|
||||
})
|
||||
}
|
||||
return registries, nil
|
||||
}
|
||||
}
|
||||
|
||||
// defaultScheme returns the default scheme for a registry host.
|
||||
func defaultScheme(host string) string {
|
||||
if h, _, err := net.SplitHostPort(host); err == nil {
|
||||
host = h
|
||||
}
|
||||
if host == "localhost" || host == "127.0.0.1" || host == "::1" {
|
||||
return "http"
|
||||
}
|
||||
return "https"
|
||||
}
|
||||
|
||||
// addDefaultScheme returns the endpoint with default scheme
|
||||
func addDefaultScheme(endpoint string) (string, error) {
|
||||
if strings.Contains(endpoint, "://") {
|
||||
return endpoint, nil
|
||||
}
|
||||
ue := "dummy://" + endpoint
|
||||
u, err := url.Parse(ue)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s://%s", defaultScheme(u.Host), endpoint), nil
|
||||
}
|
||||
|
||||
// registryEndpoints returns endpoints for a given host.
|
||||
// It adds default registry endpoint if it does not exist in the passed-in endpoint list.
|
||||
// It also supports wildcard host matching with `*`.
|
||||
func (c *criService) registryEndpoints(host string) ([]string, error) {
|
||||
var endpoints []string
|
||||
_, ok := c.config.Registry.Mirrors[host]
|
||||
if ok {
|
||||
endpoints = c.config.Registry.Mirrors[host].Endpoints
|
||||
} else {
|
||||
endpoints = c.config.Registry.Mirrors["*"].Endpoints
|
||||
}
|
||||
defaultHost, err := docker.DefaultHost(host)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get default host")
|
||||
}
|
||||
for i := range endpoints {
|
||||
en, err := addDefaultScheme(endpoints[i])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "parse endpoint url")
|
||||
}
|
||||
endpoints[i] = en
|
||||
}
|
||||
for _, e := range endpoints {
|
||||
u, err := url.Parse(e)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "parse endpoint url")
|
||||
}
|
||||
if u.Host == host {
|
||||
// Do not add default if the endpoint already exists.
|
||||
return endpoints, nil
|
||||
}
|
||||
}
|
||||
return append(endpoints, defaultScheme(defaultHost)+"://"+defaultHost), nil
|
||||
}
|
||||
|
||||
// newTransport returns a new HTTP transport used to pull image.
|
||||
// TODO(random-liu): Create a library and share this code with `ctr`.
|
||||
func newTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
FallbackDelay: 300 * time.Millisecond,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 10,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 5 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// encryptedImagesPullOpts returns the necessary list of pull options required
|
||||
// for decryption of encrypted images based on the cri decryption configuration.
|
||||
func (c *criService) encryptedImagesPullOpts() []containerd.RemoteOpt {
|
||||
if c.config.ImageDecryption.KeyModel == criconfig.KeyModelNode {
|
||||
ltdd := imgcrypt.Payload{}
|
||||
decUnpackOpt := encryption.WithUnpackConfigApplyOpts(encryption.WithDecryptedUnpack(<dd))
|
||||
opt := containerd.WithUnpackOpts([]containerd.UnpackOpt{decUnpackOpt})
|
||||
return []containerd.RemoteOpt{opt}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
// targetRefLabel is a label which contains image reference and will be passed
|
||||
// to snapshotters.
|
||||
targetRefLabel = "containerd.io/snapshot/cri.image-ref"
|
||||
// targetDigestLabel is a label which contains layer digest and will be passed
|
||||
// to snapshotters.
|
||||
targetDigestLabel = "containerd.io/snapshot/cri.layer-digest"
|
||||
// targetImageLayersLabel is a label which contains layer digests contained in
|
||||
// the target image and will be passed to snapshotters for preparing layers in
|
||||
// parallel. Skipping some layers is allowed and only affects performance.
|
||||
targetImageLayersLabel = "containerd.io/snapshot/cri.image-layers"
|
||||
)
|
||||
|
||||
// appendInfoHandlerWrapper makes a handler which appends some basic information
|
||||
// of images to each layer descriptor as annotations during unpack. These
|
||||
// annotations will be passed to snapshotters as labels. These labels will be
|
||||
// used mainly by stargz-based snapshotters for querying image contents from the
|
||||
// registry.
|
||||
func appendInfoHandlerWrapper(ref string) func(f containerdimages.Handler) containerdimages.Handler {
|
||||
return func(f containerdimages.Handler) containerdimages.Handler {
|
||||
return containerdimages.HandlerFunc(func(ctx context.Context, desc imagespec.Descriptor) ([]imagespec.Descriptor, error) {
|
||||
children, err := f.Handle(ctx, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch desc.MediaType {
|
||||
case imagespec.MediaTypeImageManifest, containerdimages.MediaTypeDockerSchema2Manifest:
|
||||
for i := range children {
|
||||
c := &children[i]
|
||||
if containerdimages.IsLayerType(c.MediaType) {
|
||||
if c.Annotations == nil {
|
||||
c.Annotations = make(map[string]string)
|
||||
}
|
||||
c.Annotations[targetRefLabel] = ref
|
||||
c.Annotations[targetDigestLabel] = c.Digest.String()
|
||||
c.Annotations[targetImageLayersLabel] = getLayers(ctx, targetImageLayersLabel, children[i:], labels.Validate)
|
||||
}
|
||||
}
|
||||
}
|
||||
return children, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getLayers returns comma-separated digests based on the passed list of
|
||||
// descriptors. The returned list contains as many digests as possible as well
|
||||
// as meets the label validation.
|
||||
func getLayers(ctx context.Context, key string, descs []imagespec.Descriptor, validate func(k, v string) error) (layers string) {
|
||||
var item string
|
||||
for _, l := range descs {
|
||||
if containerdimages.IsLayerType(l.MediaType) {
|
||||
item = l.Digest.String()
|
||||
if layers != "" {
|
||||
item = "," + item
|
||||
}
|
||||
// This avoids the label hits the size limitation.
|
||||
if err := validate(key, layers+item); err != nil {
|
||||
log.G(ctx).WithError(err).WithField("label", key).Debugf("%q is omitted in the layers list", l.Digest.String())
|
||||
break
|
||||
}
|
||||
layers += item
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
65
vendor/github.com/containerd/cri/pkg/server/image_remove.go
generated
vendored
65
vendor/github.com/containerd/cri/pkg/server/image_remove.go
generated
vendored
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/store"
|
||||
)
|
||||
|
||||
// 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 *criService) RemoveImage(ctx context.Context, r *runtime.RemoveImageRequest) (*runtime.RemoveImageResponse, error) {
|
||||
image, err := c.localResolve(r.GetImage().GetImage())
|
||||
if err != nil {
|
||||
if err == store.ErrNotExist {
|
||||
// return empty without error when image not found.
|
||||
return &runtime.RemoveImageResponse{}, nil
|
||||
}
|
||||
return nil, errors.Wrapf(err, "can not resolve %q locally", r.GetImage().GetImage())
|
||||
}
|
||||
|
||||
// Remove all image references.
|
||||
for i, ref := range image.References {
|
||||
var opts []images.DeleteOpt
|
||||
if i == len(image.References)-1 {
|
||||
// Delete the last image reference synchronously to trigger garbage collection.
|
||||
// This is best effort. It is possible that the image reference is deleted by
|
||||
// someone else before this point.
|
||||
opts = []images.DeleteOpt{images.SynchronousDelete()}
|
||||
}
|
||||
err = c.client.ImageService().Delete(ctx, ref, opts...)
|
||||
if err == nil || errdefs.IsNotFound(err) {
|
||||
// Update image store to reflect the newest state in containerd.
|
||||
if err := c.imageStore.Update(ctx, ref); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to update image reference %q for %q", ref, image.ID)
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed to delete image reference %q for %q", ref, image.ID)
|
||||
}
|
||||
return &runtime.RemoveImageResponse{}, nil
|
||||
}
|
||||
105
vendor/github.com/containerd/cri/pkg/server/image_status.go
generated
vendored
105
vendor/github.com/containerd/cri/pkg/server/image_status.go
generated
vendored
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/containerd/containerd/log"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/store"
|
||||
imagestore "github.com/containerd/cri/pkg/store/image"
|
||||
)
|
||||
|
||||
// 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 *criService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (*runtime.ImageStatusResponse, error) {
|
||||
image, err := c.localResolve(r.GetImage().GetImage())
|
||||
if err != nil {
|
||||
if err == store.ErrNotExist {
|
||||
// return empty without error when image not found.
|
||||
return &runtime.ImageStatusResponse{}, nil
|
||||
}
|
||||
return nil, errors.Wrapf(err, "can not resolve %q locally", r.GetImage().GetImage())
|
||||
}
|
||||
// TODO(random-liu): [P0] Make sure corresponding snapshot exists. What if snapshot
|
||||
// doesn't exist?
|
||||
|
||||
runtimeImage := toCRIImage(image)
|
||||
info, err := c.toCRIImageInfo(ctx, &image, r.GetVerbose())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate image info")
|
||||
}
|
||||
|
||||
return &runtime.ImageStatusResponse{
|
||||
Image: runtimeImage,
|
||||
Info: info,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// toCRIImage converts internal image object to CRI runtime.Image.
|
||||
func toCRIImage(image imagestore.Image) *runtime.Image {
|
||||
repoTags, repoDigests := parseImageReferences(image.References)
|
||||
runtimeImage := &runtime.Image{
|
||||
Id: image.ID,
|
||||
RepoTags: repoTags,
|
||||
RepoDigests: 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 *criService) 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 {
|
||||
log.G(ctx).WithError(err).Errorf("failed to marshal info %v", imi)
|
||||
info["info"] = err.Error()
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
52
vendor/github.com/containerd/cri/pkg/server/imagefs_info.go
generated
vendored
52
vendor/github.com/containerd/cri/pkg/server/imagefs_info.go
generated
vendored
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// ImageFsInfo returns information of the filesystem that is used to store images.
|
||||
// TODO(windows): Usage for windows is always 0 right now. Support this for windows.
|
||||
func (c *criService) 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
|
||||
}
|
||||
490
vendor/github.com/containerd/cri/pkg/server/instrumented_service.go
generated
vendored
490
vendor/github.com/containerd/cri/pkg/server/instrumented_service.go
generated
vendored
@@ -1,490 +0,0 @@
|
||||
/*
|
||||
Copyright 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"
|
||||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
)
|
||||
|
||||
// instrumentedService wraps service with containerd namespace and logs.
|
||||
type instrumentedService struct {
|
||||
c *criService
|
||||
}
|
||||
|
||||
func newInstrumentedService(c *criService) 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
|
||||
}
|
||||
log.G(ctx).Infof("RunPodsandbox for %+v", r.GetConfig().GetMetadata())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("RunPodSandbox for %+v failed, error", r.GetConfig().GetMetadata())
|
||||
} else {
|
||||
log.G(ctx).Infof("RunPodSandbox for %+v returns sandbox id %q", r.GetConfig().GetMetadata(), res.GetPodSandboxId())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.RunPodSandbox(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("ListPodSandbox with filter %+v", r.GetFilter())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Error("ListPodSandbox failed")
|
||||
} else {
|
||||
log.G(ctx).Tracef("ListPodSandbox returns pod sandboxes %+v", res.GetItems())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ListPodSandbox(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("PodSandboxStatus for %q", r.GetPodSandboxId())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("PodSandboxStatus for %q failed", r.GetPodSandboxId())
|
||||
} else {
|
||||
log.G(ctx).Tracef("PodSandboxStatus for %q returns status %+v", r.GetPodSandboxId(), res.GetStatus())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.PodSandboxStatus(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (_ *runtime.StopPodSandboxResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("StopPodSandbox for %q", r.GetPodSandboxId())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("StopPodSandbox for %q failed", r.GetPodSandboxId())
|
||||
} else {
|
||||
log.G(ctx).Infof("StopPodSandbox for %q returns successfully", r.GetPodSandboxId())
|
||||
}
|
||||
}()
|
||||
res, err := in.c.StopPodSandbox(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodSandboxRequest) (_ *runtime.RemovePodSandboxResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("RemovePodSandbox for %q", r.GetPodSandboxId())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("RemovePodSandbox for %q failed", r.GetPodSandboxId())
|
||||
} else {
|
||||
log.G(ctx).Infof("RemovePodSandbox %q returns successfully", r.GetPodSandboxId())
|
||||
}
|
||||
}()
|
||||
res, err := in.c.RemovePodSandbox(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (res *runtime.PortForwardResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("Portforward for %q port %v", r.GetPodSandboxId(), r.GetPort())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Portforward for %q failed", r.GetPodSandboxId())
|
||||
} else {
|
||||
log.G(ctx).Infof("Portforward for %q returns URL %q", r.GetPodSandboxId(), res.GetUrl())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.PortForward(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (res *runtime.CreateContainerResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("CreateContainer within sandbox %q for container %+v",
|
||||
r.GetPodSandboxId(), r.GetConfig().GetMetadata())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("CreateContainer within sandbox %q for %+v failed",
|
||||
r.GetPodSandboxId(), r.GetConfig().GetMetadata())
|
||||
} else {
|
||||
log.G(ctx).Infof("CreateContainer within sandbox %q for %+v returns container id %q",
|
||||
r.GetPodSandboxId(), r.GetConfig().GetMetadata(), res.GetContainerId())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.CreateContainer(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (_ *runtime.StartContainerResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("StartContainer for %q", r.GetContainerId())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("StartContainer for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Infof("StartContainer for %q returns successfully", r.GetContainerId())
|
||||
}
|
||||
}()
|
||||
res, err := in.c.StartContainer(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("ListContainers with filter %+v", r.GetFilter())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("ListContainers with filter %+v failed", r.GetFilter())
|
||||
} else {
|
||||
log.G(ctx).Tracef("ListContainers with filter %+v returns containers %+v",
|
||||
r.GetFilter(), res.GetContainers())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ListContainers(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("ContainerStatus for %q", r.GetContainerId())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("ContainerStatus for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Tracef("ContainerStatus for %q returns status %+v", r.GetContainerId(), res.GetStatus())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ContainerStatus(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) StopContainer(ctx context.Context, r *runtime.StopContainerRequest) (res *runtime.StopContainerResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("StopContainer for %q with timeout %d (s)", r.GetContainerId(), r.GetTimeout())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("StopContainer for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Infof("StopContainer for %q returns successfully", r.GetContainerId())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.StopContainer(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) RemoveContainer(ctx context.Context, r *runtime.RemoveContainerRequest) (res *runtime.RemoveContainerResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("RemoveContainer for %q", r.GetContainerId())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("RemoveContainer for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Infof("RemoveContainer for %q returns successfully", r.GetContainerId())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.RemoveContainer(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) ExecSync(ctx context.Context, r *runtime.ExecSyncRequest) (res *runtime.ExecSyncResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("ExecSync for %q with command %+v and timeout %d (s)", r.GetContainerId(), r.GetCmd(), r.GetTimeout())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("ExecSync for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Infof("ExecSync for %q returns with exit code %d", r.GetContainerId(), res.GetExitCode())
|
||||
log.G(ctx).Debugf("ExecSync for %q outputs - stdout: %q, stderr: %q", r.GetContainerId(),
|
||||
res.GetStdout(), res.GetStderr())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ExecSync(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) Exec(ctx context.Context, r *runtime.ExecRequest) (res *runtime.ExecResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).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 {
|
||||
log.G(ctx).WithError(err).Errorf("Exec for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Infof("Exec for %q returns URL %q", r.GetContainerId(), res.GetUrl())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.Exec(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) Attach(ctx context.Context, r *runtime.AttachRequest) (res *runtime.AttachResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("Attach for %q with tty %v and stdin %v", r.GetContainerId(), r.GetTty(), r.GetStdin())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Attach for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Infof("Attach for %q returns URL %q", r.GetContainerId(), res.Url)
|
||||
}
|
||||
}()
|
||||
res, err = in.c.Attach(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (res *runtime.UpdateContainerResourcesResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("UpdateContainerResources for %q with %+v", r.GetContainerId(), r.GetLinux())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("UpdateContainerResources for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Infof("UpdateContainerResources for %q returns successfully", r.GetContainerId())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.UpdateContainerResources(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (res *runtime.PullImageResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("PullImage %q", r.GetImage().GetImage())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("PullImage %q failed", r.GetImage().GetImage())
|
||||
} else {
|
||||
log.G(ctx).Infof("PullImage %q returns image reference %q",
|
||||
r.GetImage().GetImage(), res.GetImageRef())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.PullImage(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("ListImages with filter %+v", r.GetFilter())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("ListImages with filter %+v failed", r.GetFilter())
|
||||
} else {
|
||||
log.G(ctx).Tracef("ListImages with filter %+v returns image list %+v",
|
||||
r.GetFilter(), res.GetImages())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ListImages(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("ImageStatus for %q", r.GetImage().GetImage())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("ImageStatus for %q failed", r.GetImage().GetImage())
|
||||
} else {
|
||||
log.G(ctx).Tracef("ImageStatus for %q returns image status %+v",
|
||||
r.GetImage().GetImage(), res.GetImage())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ImageStatus(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) RemoveImage(ctx context.Context, r *runtime.RemoveImageRequest) (_ *runtime.RemoveImageResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Infof("RemoveImage %q", r.GetImage().GetImage())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("RemoveImage %q failed", r.GetImage().GetImage())
|
||||
} else {
|
||||
log.G(ctx).Infof("RemoveImage %q returns successfully", r.GetImage().GetImage())
|
||||
}
|
||||
}()
|
||||
res, err := in.c.RemoveImage(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) ImageFsInfo(ctx context.Context, r *runtime.ImageFsInfoRequest) (res *runtime.ImageFsInfoResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Debugf("ImageFsInfo")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Error("ImageFsInfo failed")
|
||||
} else {
|
||||
log.G(ctx).Debugf("ImageFsInfo returns filesystem info %+v", res.ImageFilesystems)
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ImageFsInfo(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) ContainerStats(ctx context.Context, r *runtime.ContainerStatsRequest) (res *runtime.ContainerStatsResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Debugf("ContainerStats for %q", r.GetContainerId())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("ContainerStats for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Debugf("ContainerStats for %q returns stats %+v", r.GetContainerId(), res.GetStats())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ContainerStats(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("ListContainerStats with filter %+v", r.GetFilter())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Error("ListContainerStats failed")
|
||||
} else {
|
||||
log.G(ctx).Tracef("ListContainerStats returns stats %+v", res.GetStats())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ListContainerStats(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("Status")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Error("Status failed")
|
||||
} else {
|
||||
log.G(ctx).Tracef("Status returns status %+v", res.GetStatus())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.Status(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
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.G(ctx).Tracef("Version with client side version %q", r.GetVersion())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Error("Version failed")
|
||||
} else {
|
||||
log.G(ctx).Tracef("Version returns %+v", res)
|
||||
}
|
||||
}()
|
||||
res, err = in.c.Version(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateRuntimeConfigRequest) (res *runtime.UpdateRuntimeConfigResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Debugf("UpdateRuntimeConfig with config %+v", r.GetRuntimeConfig())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Error("UpdateRuntimeConfig failed")
|
||||
} else {
|
||||
log.G(ctx).Debug("UpdateRuntimeConfig returns returns successfully")
|
||||
}
|
||||
}()
|
||||
res, err = in.c.UpdateRuntimeConfig(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
|
||||
func (in *instrumentedService) ReopenContainerLog(ctx context.Context, r *runtime.ReopenContainerLogRequest) (res *runtime.ReopenContainerLogResponse, err error) {
|
||||
if err := in.checkInitialized(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Debugf("ReopenContainerLog for %q", r.GetContainerId())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("ReopenContainerLog for %q failed", r.GetContainerId())
|
||||
} else {
|
||||
log.G(ctx).Debugf("ReopenContainerLog for %q returns successfully", r.GetContainerId())
|
||||
}
|
||||
}()
|
||||
res, err = in.c.ReopenContainerLog(ctrdutil.WithNamespace(ctx), r)
|
||||
return res, errdefs.ToGRPC(err)
|
||||
}
|
||||
236
vendor/github.com/containerd/cri/pkg/server/io/container_io.go
generated
vendored
236
vendor/github.com/containerd/cri/pkg/server/io/container_io.go
generated
vendored
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
Copyright 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 io
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd/cio"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
cioutil "github.com/containerd/cri/pkg/ioutil"
|
||||
"github.com/containerd/cri/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
|
||||
|
||||
// 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
|
||||
if c.stdout != nil {
|
||||
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 && c.stderr != nil {
|
||||
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) {
|
||||
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)
|
||||
c.stdoutGroup.Add(stdoutKey, wc)
|
||||
go attachStream(stdoutKey, close)
|
||||
}
|
||||
if !opts.Tty && opts.Stderr != nil {
|
||||
wg.Add(1)
|
||||
wc, close := cioutil.NewWriteCloseInformer(opts.Stderr)
|
||||
c.stderrGroup.Add(stderrKey, wc)
|
||||
go attachStream(stderrKey, close)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// AddOutput adds new write closers to the container stream, and returns existing
|
||||
// write closers if there are any.
|
||||
func (c *ContainerIO) AddOutput(name string, stdout, stderr io.WriteCloser) (io.WriteCloser, io.WriteCloser) {
|
||||
var oldStdout, oldStderr io.WriteCloser
|
||||
if stdout != nil {
|
||||
key := streamKey(c.id, name, Stdout)
|
||||
oldStdout = c.stdoutGroup.Get(key)
|
||||
c.stdoutGroup.Add(key, stdout)
|
||||
}
|
||||
if stderr != nil {
|
||||
key := streamKey(c.id, name, Stderr)
|
||||
oldStderr = c.stderrGroup.Get(key)
|
||||
c.stderrGroup.Add(key, stderr)
|
||||
}
|
||||
return oldStdout, oldStderr
|
||||
}
|
||||
|
||||
// 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/pkg/server/io/exec_io.go
generated
vendored
146
vendor/github.com/containerd/cri/pkg/server/io/exec_io.go
generated
vendored
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
Copyright 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 io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd/cio"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
cioutil "github.com/containerd/cri/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
|
||||
}
|
||||
144
vendor/github.com/containerd/cri/pkg/server/io/helpers.go
generated
vendored
144
vendor/github.com/containerd/cri/pkg/server/io/helpers.go
generated
vendored
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
Copyright 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 io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/cio"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/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 = openPipe(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 fifos.Stdout != "" {
|
||||
if f, err = openPipe(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 fifos.Stderr != "" {
|
||||
if f, err = openPipe(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
|
||||
}
|
||||
31
vendor/github.com/containerd/cri/pkg/server/io/helpers_unix.go
generated
vendored
31
vendor/github.com/containerd/cri/pkg/server/io/helpers_unix.go
generated
vendored
@@ -1,31 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/fifo"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func openPipe(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||
return fifo.OpenFifo(ctx, fn, flag, perm)
|
||||
}
|
||||
81
vendor/github.com/containerd/cri/pkg/server/io/helpers_windows.go
generated
vendored
81
vendor/github.com/containerd/cri/pkg/server/io/helpers_windows.go
generated
vendored
@@ -1,81 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
winio "github.com/Microsoft/go-winio"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type pipe struct {
|
||||
l net.Listener
|
||||
con net.Conn
|
||||
conErr error
|
||||
conWg sync.WaitGroup
|
||||
}
|
||||
|
||||
func openPipe(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||
l, err := winio.ListenPipe(fn, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &pipe{l: l}
|
||||
p.conWg.Add(1)
|
||||
go func() {
|
||||
defer p.conWg.Done()
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
p.conErr = err
|
||||
return
|
||||
}
|
||||
p.con = c
|
||||
}()
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *pipe) Write(b []byte) (int, error) {
|
||||
p.conWg.Wait()
|
||||
if p.conErr != nil {
|
||||
return 0, errors.Wrap(p.conErr, "connection error")
|
||||
}
|
||||
return p.con.Write(b)
|
||||
}
|
||||
|
||||
func (p *pipe) Read(b []byte) (int, error) {
|
||||
p.conWg.Wait()
|
||||
if p.conErr != nil {
|
||||
return 0, errors.Wrap(p.conErr, "connection error")
|
||||
}
|
||||
return p.con.Read(b)
|
||||
}
|
||||
|
||||
func (p *pipe) Close() error {
|
||||
p.l.Close()
|
||||
p.conWg.Wait()
|
||||
if p.con != nil {
|
||||
return p.con.Close()
|
||||
}
|
||||
return p.conErr
|
||||
}
|
||||
196
vendor/github.com/containerd/cri/pkg/server/io/logger.go
generated
vendored
196
vendor/github.com/containerd/cri/pkg/server/io/logger.go
generated
vendored
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
Copyright 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 io
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
cioutil "github.com/containerd/cri/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
|
||||
// defaultBufSize is the default size of the read buffer in bytes.
|
||||
defaultBufSize = 4096
|
||||
)
|
||||
|
||||
// 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. It also
|
||||
// returns a channel which indicates whether the logger is stopped.
|
||||
// maxLen is the max length limit of a line. A line longer than the
|
||||
// limit will be cut into multiple lines.
|
||||
func NewCRILogger(path string, w io.Writer, stream StreamType, maxLen int) (io.WriteCloser, <-chan struct{}) {
|
||||
logrus.Debugf("Start writing stream %q to log file %q", stream, path)
|
||||
prc, pwc := io.Pipe()
|
||||
stop := make(chan struct{})
|
||||
go func() {
|
||||
redirectLogs(path, prc, w, stream, maxLen)
|
||||
close(stop)
|
||||
}()
|
||||
return pwc, stop
|
||||
}
|
||||
|
||||
// bufio.ReadLine in golang eats both read errors and tailing newlines
|
||||
// (See https://golang.org/pkg/bufio/#Reader.ReadLine). When reading
|
||||
// to io.EOF, it is impossible for the caller to figure out whether
|
||||
// there is a newline at the end, for example:
|
||||
// 1) When reading "CONTENT\n", it returns "CONTENT" without error;
|
||||
// 2) When reading "CONTENT", it also returns "CONTENT" without error.
|
||||
//
|
||||
// To differentiate these 2 cases, we need to write a readLine function
|
||||
// ourselves to not ignore the error.
|
||||
//
|
||||
// The code is similar with https://golang.org/src/bufio/bufio.go?s=9537:9604#L359.
|
||||
// The only difference is that it returns all errors from `ReadSlice`.
|
||||
//
|
||||
// readLine returns err != nil if and only if line does not end with a new line.
|
||||
func readLine(b *bufio.Reader) (line []byte, isPrefix bool, err error) {
|
||||
line, err = b.ReadSlice('\n')
|
||||
if err == bufio.ErrBufferFull {
|
||||
// Handle the case where "\r\n" straddles the buffer.
|
||||
if len(line) > 0 && line[len(line)-1] == '\r' {
|
||||
// Unread the last '\r'
|
||||
if err := b.UnreadByte(); err != nil {
|
||||
panic(fmt.Sprintf("invalid unread %v", err))
|
||||
}
|
||||
line = line[:len(line)-1]
|
||||
}
|
||||
return line, true, nil
|
||||
}
|
||||
|
||||
if len(line) == 0 {
|
||||
if err != nil {
|
||||
line = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if line[len(line)-1] == '\n' {
|
||||
// "ReadSlice returns err != nil if and only if line does not end in delim"
|
||||
// (See https://golang.org/pkg/bufio/#Reader.ReadSlice).
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("full read with unexpected error %v", err))
|
||||
}
|
||||
drop := 1
|
||||
if len(line) > 1 && line[len(line)-2] == '\r' {
|
||||
drop = 2
|
||||
}
|
||||
line = line[:len(line)-drop]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func redirectLogs(path string, rc io.ReadCloser, w io.Writer, s StreamType, maxLen int) {
|
||||
defer rc.Close()
|
||||
var (
|
||||
stream = []byte(s)
|
||||
delimiter = []byte{delimiter}
|
||||
partial = []byte(runtime.LogTagPartial)
|
||||
full = []byte(runtime.LogTagFull)
|
||||
buf [][]byte
|
||||
length int
|
||||
bufSize = defaultBufSize
|
||||
)
|
||||
// Make sure bufSize <= maxLen
|
||||
if maxLen > 0 && maxLen < bufSize {
|
||||
bufSize = maxLen
|
||||
}
|
||||
r := bufio.NewReaderSize(rc, bufSize)
|
||||
writeLine := func(tag, line []byte) {
|
||||
timestamp := time.Now().AppendFormat(nil, timestampFormat)
|
||||
data := bytes.Join([][]byte{timestamp, stream, tag, line}, delimiter)
|
||||
data = append(data, eol)
|
||||
if _, err := w.Write(data); err != nil {
|
||||
logrus.WithError(err).Errorf("Fail to write %q log to log file %q", s, path)
|
||||
// Continue on write error to drain the container output.
|
||||
}
|
||||
}
|
||||
for {
|
||||
var stop bool
|
||||
newLine, isPrefix, err := readLine(r)
|
||||
// NOTE(random-liu): readLine can return actual content even if there is an error.
|
||||
if len(newLine) > 0 {
|
||||
// Buffer returned by ReadLine will change after
|
||||
// next read, copy it.
|
||||
l := make([]byte, len(newLine))
|
||||
copy(l, newLine)
|
||||
buf = append(buf, l)
|
||||
length += len(l)
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
logrus.Debugf("Getting EOF from stream %q while redirecting to log file %q", s, path)
|
||||
} else {
|
||||
logrus.WithError(err).Errorf("An error occurred when redirecting stream %q to log file %q", s, path)
|
||||
}
|
||||
if length == 0 {
|
||||
// No content left to write, break.
|
||||
break
|
||||
}
|
||||
// Stop after writing the content left in buffer.
|
||||
stop = true
|
||||
}
|
||||
if maxLen > 0 && length > maxLen {
|
||||
exceedLen := length - maxLen
|
||||
last := buf[len(buf)-1]
|
||||
if exceedLen > len(last) {
|
||||
// exceedLen must <= len(last), or else the buffer
|
||||
// should have be written in the previous iteration.
|
||||
panic("exceed length should <= last buffer size")
|
||||
}
|
||||
buf[len(buf)-1] = last[:len(last)-exceedLen]
|
||||
writeLine(partial, bytes.Join(buf, nil))
|
||||
buf = [][]byte{last[len(last)-exceedLen:]}
|
||||
length = exceedLen
|
||||
}
|
||||
if isPrefix {
|
||||
continue
|
||||
}
|
||||
if stop {
|
||||
// readLine only returns error when the message doesn't
|
||||
// end with a newline, in that case it should be treated
|
||||
// as a partial line.
|
||||
writeLine(partial, bytes.Join(buf, nil))
|
||||
} else {
|
||||
writeLine(full, bytes.Join(buf, nil))
|
||||
}
|
||||
buf = nil
|
||||
length = 0
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
}
|
||||
logrus.Debugf("Finish redirecting stream %q to log file %q", s, path)
|
||||
}
|
||||
51
vendor/github.com/containerd/cri/pkg/server/opts.go
generated
vendored
51
vendor/github.com/containerd/cri/pkg/server/opts.go
generated
vendored
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/nri"
|
||||
v1 "github.com/containerd/nri/types/v1"
|
||||
)
|
||||
|
||||
// WithNRISandboxDelete calls delete for a sandbox'd task
|
||||
func WithNRISandboxDelete(sandboxID string) containerd.ProcessDeleteOpts {
|
||||
return func(ctx context.Context, p containerd.Process) error {
|
||||
task, ok := p.(containerd.Task)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
nric, err := nri.New()
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Error("unable to create nri client")
|
||||
return nil
|
||||
}
|
||||
if nric == nil {
|
||||
return nil
|
||||
}
|
||||
sb := &nri.Sandbox{
|
||||
ID: sandboxID,
|
||||
}
|
||||
if _, err := nric.InvokeWithSandbox(ctx, task, v1.Delete, sb); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to delete nri for %q", task.ID())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
483
vendor/github.com/containerd/cri/pkg/server/restart.go
generated
vendored
483
vendor/github.com/containerd/cri/pkg/server/restart.go
generated
vendored
@@ -1,483 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
goruntime "runtime"
|
||||
"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/log"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
"github.com/containerd/cri/pkg/netns"
|
||||
cio "github.com/containerd/cri/pkg/server/io"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
// NOTE: The recovery logic has following assumption: when the cri plugin is down:
|
||||
// 1) Files (e.g. root directory, netns) and checkpoint maintained by the plugin 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 *criService) recover(ctx context.Context) error {
|
||||
// Recover all sandboxes.
|
||||
sandboxes, err := c.client.Containers(ctx, filterLabel(containerKindLabel, containerKindSandbox))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to list sandbox containers")
|
||||
}
|
||||
for _, sandbox := range sandboxes {
|
||||
sb, err := c.loadSandbox(ctx, sandbox)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to load sandbox %q", sandbox.ID())
|
||||
continue
|
||||
}
|
||||
log.G(ctx).Debugf("Loaded sandbox %+v", sb)
|
||||
if err := c.sandboxStore.Add(sb); err != nil {
|
||||
return errors.Wrapf(err, "failed to add sandbox %q to store", sandbox.ID())
|
||||
}
|
||||
if err := c.sandboxNameIndex.Reserve(sb.Name, sb.ID); err != nil {
|
||||
return errors.Wrapf(err, "failed to reserve sandbox name %q", sb.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Recover all containers.
|
||||
containers, err := c.client.Containers(ctx, filterLabel(containerKindLabel, containerKindContainer))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to list containers")
|
||||
}
|
||||
for _, container := range containers {
|
||||
cntr, err := c.loadContainer(ctx, container)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to load container %q", container.ID())
|
||||
continue
|
||||
}
|
||||
log.G(ctx).Debugf("Loaded container %+v", cntr)
|
||||
if err := c.containerStore.Add(cntr); err != nil {
|
||||
return errors.Wrapf(err, "failed to add container %q to store", container.ID())
|
||||
}
|
||||
if err := c.containerNameIndex.Reserve(cntr.Name, cntr.ID); err != nil {
|
||||
return errors.Wrapf(err, "failed to reserve container name %q", cntr.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Recover all images.
|
||||
cImages, err := c.client.ListImages(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to list images")
|
||||
}
|
||||
c.loadImages(ctx, cImages)
|
||||
|
||||
// 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 and container directories without corresponding containerd container.
|
||||
for _, cleanup := range []struct {
|
||||
cntrs []containerd.Container
|
||||
base string
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
cntrs: sandboxes,
|
||||
base: filepath.Join(c.config.RootDir, sandboxesDir),
|
||||
errMsg: "failed to cleanup orphaned sandbox directories",
|
||||
},
|
||||
{
|
||||
cntrs: sandboxes,
|
||||
base: filepath.Join(c.config.StateDir, sandboxesDir),
|
||||
errMsg: "failed to cleanup orphaned volatile sandbox directories",
|
||||
},
|
||||
{
|
||||
cntrs: containers,
|
||||
base: filepath.Join(c.config.RootDir, containersDir),
|
||||
errMsg: "failed to cleanup orphaned container directories",
|
||||
},
|
||||
{
|
||||
cntrs: containers,
|
||||
base: filepath.Join(c.config.StateDir, containersDir),
|
||||
errMsg: "failed to cleanup orphaned volatile container directories",
|
||||
},
|
||||
} {
|
||||
if err := cleanupOrphanedIDDirs(ctx, cleanup.cntrs, cleanup.base); err != nil {
|
||||
return errors.Wrap(err, cleanup.errMsg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadContainerTimeout is the default timeout for loading a container/sandbox.
|
||||
// One container/sandbox hangs (e.g. containerd#2438) should not affect other
|
||||
// containers/sandboxes.
|
||||
// Most CRI container/sandbox related operations are per container, the ones
|
||||
// which handle multiple containers at a time are:
|
||||
// * ListPodSandboxes: Don't talk with containerd services.
|
||||
// * ListContainers: Don't talk with containerd services.
|
||||
// * ListContainerStats: Not in critical code path, a default timeout will
|
||||
// be applied at CRI level.
|
||||
// * Recovery logic: We should set a time for each container/sandbox recovery.
|
||||
// * Event monitor: We should set a timeout for each container/sandbox event handling.
|
||||
const loadContainerTimeout = 10 * time.Second
|
||||
|
||||
// loadContainer loads container from containerd and status checkpoint.
|
||||
func (c *criService) loadContainer(ctx context.Context, cntr containerd.Container) (containerstore.Container, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, loadContainerTimeout)
|
||||
defer cancel()
|
||||
id := cntr.ID()
|
||||
containerDir := c.getContainerRootDir(id)
|
||||
volatileContainerDir := c.getVolatileContainerRootDir(id)
|
||||
var container containerstore.Container
|
||||
// Load container metadata.
|
||||
exts, err := cntr.Extensions(ctx)
|
||||
if err != nil {
|
||||
return container, errors.Wrap(err, "failed to get container extensions")
|
||||
}
|
||||
ext, ok := exts[containerMetadataExtension]
|
||||
if !ok {
|
||||
return container, errors.Errorf("metadata extension %q not found", containerMetadataExtension)
|
||||
}
|
||||
data, err := typeurl.UnmarshalAny(&ext)
|
||||
if err != nil {
|
||||
return container, errors.Wrapf(err, "failed to unmarshal metadata extension %q", ext)
|
||||
}
|
||||
meta := data.(*containerstore.Metadata)
|
||||
|
||||
// Load status from checkpoint.
|
||||
status, err := containerstore.LoadStatus(containerDir, id)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("Failed to load container status for %q", id)
|
||||
status = unknownContainerStatus()
|
||||
}
|
||||
|
||||
var containerIO *cio.ContainerIO
|
||||
err = func() error {
|
||||
// Load up-to-date status from containerd.
|
||||
t, err := cntr.Task(ctx, func(fifos *containerdio.FIFOSet) (_ containerdio.IO, err error) {
|
||||
stdoutWC, stderrWC, err := c.createContainerLoggers(meta.LogPath, meta.Config.GetTty())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if stdoutWC != nil {
|
||||
stdoutWC.Close()
|
||||
}
|
||||
if stderrWC != nil {
|
||||
stderrWC.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
containerIO, err = cio.NewContainerIO(id,
|
||||
cio.WithFIFOs(fifos),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerIO.AddOutput("log", stdoutWC, stderrWC)
|
||||
containerIO.Pipe()
|
||||
return containerIO, nil
|
||||
})
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to load task")
|
||||
}
|
||||
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 errors.Wrap(err, "failed to get task status")
|
||||
}
|
||||
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
|
||||
// containerd got restarted during that. In that case, we still
|
||||
// treat the container as `CREATED`.
|
||||
containerIO, err = cio.NewContainerIO(id,
|
||||
cio.WithNewFIFOs(volatileContainerDir, meta.Config.GetTty(), meta.Config.GetStdin()),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create container io")
|
||||
}
|
||||
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 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 errors.Wrap(err, "failed to delete task")
|
||||
}
|
||||
if status.State() != runtime.ContainerState_CONTAINER_CREATED {
|
||||
return errors.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 containerd is down".
|
||||
switch status.State() {
|
||||
case runtime.ContainerState_CONTAINER_EXITED:
|
||||
return errors.Errorf("unexpected container state for running task: %q", status.State())
|
||||
case runtime.ContainerState_CONTAINER_RUNNING:
|
||||
default:
|
||||
// This may happen if containerd gets restarted after task is started, but
|
||||
// before status is checkpointed.
|
||||
status.StartedAt = time.Now().UnixNano()
|
||||
status.Pid = t.Pid()
|
||||
}
|
||||
// Wait for the task for exit monitor.
|
||||
// wait is a long running background request, no timeout needed.
|
||||
exitCh, err := t.Wait(ctrdutil.NamespacedContext())
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to wait for task")
|
||||
}
|
||||
// Container was in running state, but its task has been deleted,
|
||||
// set unknown exited state.
|
||||
status.FinishedAt = time.Now().UnixNano()
|
||||
status.ExitCode = unknownExitCode
|
||||
status.Reason = unknownExitReason
|
||||
} else {
|
||||
// Start exit monitor.
|
||||
c.eventMonitor.startExitMonitor(context.Background(), id, status.Pid, exitCh)
|
||||
}
|
||||
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 errors.Wrap(err, "failed to delete task")
|
||||
}
|
||||
status.FinishedAt = s.ExitTime.UnixNano()
|
||||
status.ExitCode = int32(s.ExitStatus)
|
||||
default:
|
||||
return errors.Errorf("unexpected task status %q", s.Status)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to load container status for %q", id)
|
||||
// Only set the unknown field in this case, because other fields may
|
||||
// contain useful information loaded from the checkpoint.
|
||||
status.Unknown = true
|
||||
}
|
||||
opts := []containerstore.Opts{
|
||||
containerstore.WithStatus(status, containerDir),
|
||||
containerstore.WithContainer(cntr),
|
||||
}
|
||||
// containerIO could be nil for container in unknown state.
|
||||
if containerIO != nil {
|
||||
opts = append(opts, containerstore.WithContainerIO(containerIO))
|
||||
}
|
||||
return containerstore.NewContainer(*meta, opts...)
|
||||
}
|
||||
|
||||
// loadSandbox loads sandbox from containerd.
|
||||
func (c *criService) loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.Sandbox, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, loadContainerTimeout)
|
||||
defer cancel()
|
||||
var sandbox sandboxstore.Sandbox
|
||||
// Load sandbox metadata.
|
||||
exts, err := cntr.Extensions(ctx)
|
||||
if err != nil {
|
||||
return sandbox, errors.Wrap(err, "failed to get sandbox container extensions")
|
||||
}
|
||||
ext, ok := exts[sandboxMetadataExtension]
|
||||
if !ok {
|
||||
return sandbox, errors.Errorf("metadata extension %q not found", sandboxMetadataExtension)
|
||||
}
|
||||
data, err := typeurl.UnmarshalAny(&ext)
|
||||
if err != nil {
|
||||
return sandbox, errors.Wrapf(err, "failed to unmarshal metadata extension %q", ext)
|
||||
}
|
||||
meta := data.(*sandboxstore.Metadata)
|
||||
|
||||
s, err := func() (sandboxstore.Status, error) {
|
||||
status := unknownSandboxStatus()
|
||||
// Load sandbox created timestamp.
|
||||
info, err := cntr.Info(ctx)
|
||||
if err != nil {
|
||||
return status, errors.Wrap(err, "failed to get sandbox container info")
|
||||
}
|
||||
status.CreatedAt = info.CreatedAt
|
||||
|
||||
// Load sandbox state.
|
||||
t, err := cntr.Task(ctx, nil)
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return status, errors.Wrap(err, "failed to load task")
|
||||
}
|
||||
var taskStatus containerd.Status
|
||||
var notFound bool
|
||||
if errdefs.IsNotFound(err) {
|
||||
// Task is not found.
|
||||
notFound = true
|
||||
} else {
|
||||
// Task is found. Get task status.
|
||||
taskStatus, err = t.Status(ctx)
|
||||
if err != nil {
|
||||
// It's still possible that task is deleted during this window.
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return status, errors.Wrap(err, "failed to get task status")
|
||||
}
|
||||
notFound = true
|
||||
}
|
||||
}
|
||||
if notFound {
|
||||
// Task does not exist, set sandbox state as NOTREADY.
|
||||
status.State = sandboxstore.StateNotReady
|
||||
} else {
|
||||
if taskStatus.Status == containerd.Running {
|
||||
// Wait for the task for sandbox monitor.
|
||||
// wait is a long running background request, no timeout needed.
|
||||
exitCh, err := t.Wait(ctrdutil.NamespacedContext())
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return status, errors.Wrap(err, "failed to wait for task")
|
||||
}
|
||||
status.State = sandboxstore.StateNotReady
|
||||
} else {
|
||||
// Task is running, set sandbox state as READY.
|
||||
status.State = sandboxstore.StateReady
|
||||
status.Pid = t.Pid()
|
||||
c.eventMonitor.startExitMonitor(context.Background(), meta.ID, status.Pid, exitCh)
|
||||
}
|
||||
} 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 status, errors.Wrap(err, "failed to delete task")
|
||||
}
|
||||
status.State = sandboxstore.StateNotReady
|
||||
}
|
||||
}
|
||||
return status, nil
|
||||
}()
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to load sandbox status for %q", cntr.ID())
|
||||
}
|
||||
|
||||
sandbox = sandboxstore.NewSandbox(*meta, s)
|
||||
sandbox.Container = cntr
|
||||
|
||||
// Load network namespace.
|
||||
if goruntime.GOOS != "windows" &&
|
||||
meta.Config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE {
|
||||
// Don't need to load netns for host network sandbox.
|
||||
return sandbox, nil
|
||||
}
|
||||
sandbox.NetNS = netns.LoadNetNS(meta.NetNSPath)
|
||||
|
||||
// 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.
|
||||
func (c *criService) loadImages(ctx context.Context, cImages []containerd.Image) {
|
||||
snapshotter := c.config.ContainerdConfig.Snapshotter
|
||||
for _, i := range cImages {
|
||||
ok, _, _, _, err := containerdimages.Check(ctx, i.ContentStore(), i.Target(), platforms.Default())
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to check image content readiness for %q", i.Name())
|
||||
continue
|
||||
}
|
||||
if !ok {
|
||||
log.G(ctx).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 {
|
||||
log.G(ctx).WithError(err).Warnf("Failed to check whether image is unpacked for image %s", i.Name())
|
||||
continue
|
||||
}
|
||||
if !unpacked {
|
||||
log.G(ctx).Warnf("The image %s is not unpacked.", i.Name())
|
||||
// TODO(random-liu): Consider whether we should try unpack here.
|
||||
}
|
||||
if err := c.updateImage(ctx, i.Name()); err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("Failed to update reference for image %q", i.Name())
|
||||
continue
|
||||
}
|
||||
log.G(ctx).Debugf("Loaded image %q", i.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupOrphanedIDDirs(ctx context.Context, cntrs []containerd.Container, base string) error {
|
||||
// Cleanup orphaned id directories.
|
||||
dirs, err := ioutil.ReadDir(base)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return errors.Wrap(err, "failed to read base directory")
|
||||
}
|
||||
idsMap := make(map[string]containerd.Container)
|
||||
for _, cntr := range cntrs {
|
||||
idsMap[cntr.ID()] = cntr
|
||||
}
|
||||
for _, d := range dirs {
|
||||
if !d.IsDir() {
|
||||
log.G(ctx).Warnf("Invalid file %q found in base directory %q", d.Name(), base)
|
||||
continue
|
||||
}
|
||||
if _, ok := idsMap[d.Name()]; ok {
|
||||
// Do not remove id directory if corresponding container is found.
|
||||
continue
|
||||
}
|
||||
dir := filepath.Join(base, d.Name())
|
||||
if err := ensureRemoveAll(ctx, dir); err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("Failed to remove id directory %q", dir)
|
||||
} else {
|
||||
log.G(ctx).Debugf("Cleanup orphaned id directory %q", dir)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
101
vendor/github.com/containerd/cri/pkg/server/sandbox_list.go
generated
vendored
101
vendor/github.com/containerd/cri/pkg/server/sandbox_list.go
generated
vendored
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
// ListPodSandbox returns a list of Sandbox.
|
||||
func (c *criService) 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(),
|
||||
RuntimeHandler: meta.RuntimeHandler,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *criService) normalizePodSandboxFilter(filter *runtime.PodSandboxFilter) {
|
||||
if sb, err := c.sandboxStore.Get(filter.GetId()); err == nil {
|
||||
filter.Id = sb.ID
|
||||
}
|
||||
}
|
||||
|
||||
// filterCRISandboxes filters CRISandboxes.
|
||||
func (c *criService) 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
|
||||
}
|
||||
38
vendor/github.com/containerd/cri/pkg/server/sandbox_portforward.go
generated
vendored
38
vendor/github.com/containerd/cri/pkg/server/sandbox_portforward.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
// PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
|
||||
func (c *criService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (retRes *runtime.PortForwardResponse, retErr error) {
|
||||
sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to find sandbox %q", r.GetPodSandboxId())
|
||||
}
|
||||
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)
|
||||
}
|
||||
128
vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_unix.go
generated
vendored
128
vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_unix.go
generated
vendored
@@ -1,128 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// portForward uses netns to enter the sandbox namespace, and forwards a stream inside the
|
||||
// the namespace to a specific port. It keeps forwarding until it exits or client disconnect.
|
||||
func (c *criService) portForward(ctx context.Context, id string, port int32, stream io.ReadWriteCloser) error {
|
||||
s, err := c.sandboxStore.Get(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to find sandbox %q in store", id)
|
||||
}
|
||||
|
||||
var netNSDo func(func(ns.NetNS) error) error
|
||||
// netNSPath is the network namespace path for logging.
|
||||
var netNSPath string
|
||||
securityContext := s.Config.GetLinux().GetSecurityContext()
|
||||
hostNet := securityContext.GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE
|
||||
if !hostNet {
|
||||
if closed, err := s.NetNS.Closed(); err != nil {
|
||||
return errors.Wrapf(err, "failed to check netwok namespace closed for sandbox %q", id)
|
||||
} else if closed {
|
||||
return errors.Errorf("network namespace for sandbox %q is closed", id)
|
||||
}
|
||||
netNSDo = s.NetNS.Do
|
||||
netNSPath = s.NetNS.GetPath()
|
||||
} else {
|
||||
// Run the function directly for host network.
|
||||
netNSDo = func(do func(_ ns.NetNS) error) error {
|
||||
return do(nil)
|
||||
}
|
||||
netNSPath = "host"
|
||||
}
|
||||
|
||||
log.G(ctx).Infof("Executing port forwarding in network namespace %q", netNSPath)
|
||||
err = netNSDo(func(_ ns.NetNS) error {
|
||||
defer stream.Close()
|
||||
// TODO: hardcoded to tcp4 because localhost resolves to ::1 by default if the system has IPv6 enabled.
|
||||
// Theoretically happy eyeballs will try IPv6 first and fallback to IPv4
|
||||
// but resolving localhost doesn't seem to return and IPv4 address, thus failing the connection.
|
||||
conn, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to dial %d", port)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
errCh := make(chan error, 2)
|
||||
// Copy from the the namespace port connection to the client stream
|
||||
go func() {
|
||||
log.G(ctx).Debugf("PortForward copying data from namespace %q port %d to the client stream", id, port)
|
||||
_, err := io.Copy(stream, conn)
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
// Copy from the client stream to the namespace port connection
|
||||
go func() {
|
||||
log.G(ctx).Debugf("PortForward copying data from client stream to namespace %q port %d", id, port)
|
||||
_, err := io.Copy(conn, stream)
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
// Wait until the first error is returned by one of the connections
|
||||
// we use errFwd to store the result of the port forwarding operation
|
||||
// if the context is cancelled close everything and return
|
||||
var errFwd error
|
||||
select {
|
||||
case errFwd = <-errCh:
|
||||
log.G(ctx).Debugf("PortForward stop forwarding in one direction in network namespace %q port %d: %v", id, port, errFwd)
|
||||
case <-ctx.Done():
|
||||
log.G(ctx).Debugf("PortForward cancelled in network namespace %q port %d: %v", id, port, ctx.Err())
|
||||
return ctx.Err()
|
||||
}
|
||||
// give a chance to terminate gracefully or timeout
|
||||
// after 1s
|
||||
// https://linux.die.net/man/1/socat
|
||||
const timeout = time.Second
|
||||
select {
|
||||
case e := <-errCh:
|
||||
if errFwd == nil {
|
||||
errFwd = e
|
||||
}
|
||||
log.G(ctx).Debugf("PortForward stopped forwarding in both directions in network namespace %q port %d: %v", id, port, e)
|
||||
case <-time.After(timeout):
|
||||
log.G(ctx).Debugf("PortForward timed out waiting to close the connection in network namespace %q port %d", id, port)
|
||||
case <-ctx.Done():
|
||||
log.G(ctx).Debugf("PortForward cancelled in network namespace %q port %d: %v", id, port, ctx.Err())
|
||||
errFwd = ctx.Err()
|
||||
}
|
||||
|
||||
return errFwd
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to execute portforward in network namespace %q", netNSPath)
|
||||
}
|
||||
log.G(ctx).Infof("Finish port forwarding for %q port %d", id, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
80
vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_windows.go
generated
vendored
80
vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_windows.go
generated
vendored
@@ -1,80 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/utils/exec"
|
||||
|
||||
"github.com/containerd/cri/pkg/ioutil"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
func (c *criService) portForward(ctx context.Context, id string, port int32, stream io.ReadWriter) error {
|
||||
stdout := ioutil.NewNopWriteCloser(stream)
|
||||
stderrBuffer := new(bytes.Buffer)
|
||||
stderr := ioutil.NewNopWriteCloser(stderrBuffer)
|
||||
// localhost is resolved to 127.0.0.1 in ipv4, and ::1 in ipv6.
|
||||
// Explicitly using ipv4 IP address in here to avoid flakiness.
|
||||
cmd := []string{"wincat.exe", "127.0.0.1", fmt.Sprint(port)}
|
||||
err := c.execInSandbox(ctx, id, cmd, stream, stdout, stderr)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to execute port forward in sandbox: %s", stderrBuffer.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *criService) execInSandbox(ctx context.Context, sandboxID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser) error {
|
||||
// Get sandbox from our sandbox store.
|
||||
sb, err := c.sandboxStore.Get(sandboxID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to find sandbox %q in store", sandboxID)
|
||||
}
|
||||
|
||||
// Check the sandbox state
|
||||
state := sb.Status.Get().State
|
||||
if state != sandboxstore.StateReady {
|
||||
return errors.Errorf("sandbox is in %s state", fmt.Sprint(state))
|
||||
}
|
||||
|
||||
opts := execOptions{
|
||||
cmd: cmd,
|
||||
stdin: stdin,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
tty: false,
|
||||
resize: nil,
|
||||
}
|
||||
exitCode, err := c.execInternal(ctx, sb.Container, sandboxID, opts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to exec in sandbox")
|
||||
}
|
||||
if *exitCode == 0 {
|
||||
return nil
|
||||
}
|
||||
return &exec.CodeExitError{
|
||||
Err: errors.Errorf("error executing command %v, exit code %d", cmd, *exitCode),
|
||||
Code: int(*exitCode),
|
||||
}
|
||||
}
|
||||
115
vendor/github.com/containerd/cri/pkg/server/sandbox_remove.go
generated
vendored
115
vendor/github.com/containerd/cri/pkg/server/sandbox_remove.go
generated
vendored
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/store"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
// RemovePodSandbox removes the sandbox. If there are running containers in the
|
||||
// sandbox, they should be forcibly removed.
|
||||
func (c *criService) 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, errors.Wrapf(err, "an error occurred when try to find sandbox %q",
|
||||
r.GetPodSandboxId())
|
||||
}
|
||||
// Do not return error if the id doesn't exist.
|
||||
log.G(ctx).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 or unknown.
|
||||
state := sandbox.Status.Get().State
|
||||
if state == sandboxstore.StateReady || state == sandboxstore.StateUnknown {
|
||||
logrus.Infof("Forcibly stopping sandbox %q", id)
|
||||
if err := c.stopPodSandbox(ctx, sandbox); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to forcibly stop sandbox %q", id)
|
||||
}
|
||||
}
|
||||
|
||||
// Return error if sandbox network namespace is not closed yet.
|
||||
if sandbox.NetNS != nil {
|
||||
nsPath := sandbox.NetNS.GetPath()
|
||||
if closed, err := sandbox.NetNS.Closed(); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to check sandbox network namespace %q closed", nsPath)
|
||||
} else if !closed {
|
||||
return nil, errors.Errorf("sandbox network namespace %q is not fully closed", nsPath)
|
||||
}
|
||||
}
|
||||
|
||||
// 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, errors.Wrapf(err, "failed to remove container %q", cntr.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the sandbox root directories.
|
||||
sandboxRootDir := c.getSandboxRootDir(id)
|
||||
if err := ensureRemoveAll(ctx, sandboxRootDir); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to remove sandbox root directory %q",
|
||||
sandboxRootDir)
|
||||
}
|
||||
volatileSandboxRootDir := c.getVolatileSandboxRootDir(id)
|
||||
if err := ensureRemoveAll(ctx, volatileSandboxRootDir); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to remove volatile sandbox root directory %q",
|
||||
volatileSandboxRootDir)
|
||||
}
|
||||
|
||||
// Delete sandbox container.
|
||||
if err := sandbox.Container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return nil, errors.Wrapf(err, "failed to delete sandbox container %q", id)
|
||||
}
|
||||
log.G(ctx).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
|
||||
}
|
||||
548
vendor/github.com/containerd/cri/pkg/server/sandbox_run.go
generated
vendored
548
vendor/github.com/containerd/cri/pkg/server/sandbox_run.go
generated
vendored
@@ -1,548 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"encoding/json"
|
||||
"math"
|
||||
goruntime "runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
containerdio "github.com/containerd/containerd/cio"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
cni "github.com/containerd/go-cni"
|
||||
"github.com/containerd/nri"
|
||||
v1 "github.com/containerd/nri/types/v1"
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/annotations"
|
||||
criconfig "github.com/containerd/cri/pkg/config"
|
||||
customopts "github.com/containerd/cri/pkg/containerd/opts"
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
"github.com/containerd/cri/pkg/netns"
|
||||
"github.com/containerd/cri/pkg/server/bandwidth"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
"github.com/containerd/cri/pkg/util"
|
||||
selinux "github.com/opencontainers/selinux/go-selinux"
|
||||
)
|
||||
|
||||
func init() {
|
||||
typeurl.Register(&sandboxstore.Metadata{},
|
||||
"github.com/containerd/cri/pkg/store/sandbox", "Metadata")
|
||||
}
|
||||
|
||||
// RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
|
||||
// the sandbox is in ready state.
|
||||
func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) {
|
||||
config := r.GetConfig()
|
||||
log.G(ctx).Debugf("Sandbox config %+v", config)
|
||||
|
||||
// Generate unique id and name for the sandbox and reserve the name.
|
||||
id := util.GenerateID()
|
||||
metadata := config.GetMetadata()
|
||||
if metadata == nil {
|
||||
return nil, errors.New("sandbox config must include metadata")
|
||||
}
|
||||
name := makeSandboxName(metadata)
|
||||
log.G(ctx).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, errors.Wrapf(err, "failed to reserve sandbox name %q", name)
|
||||
}
|
||||
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,
|
||||
RuntimeHandler: r.GetRuntimeHandler(),
|
||||
},
|
||||
sandboxstore.Status{
|
||||
State: sandboxstore.StateUnknown,
|
||||
},
|
||||
)
|
||||
|
||||
// Ensure sandbox container image snapshot.
|
||||
image, err := c.ensureImageExists(ctx, c.config.SandboxImage, config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get sandbox image %q", c.config.SandboxImage)
|
||||
}
|
||||
containerdImage, err := c.toContainerdImage(ctx, *image)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get image from containerd %q", image.ID)
|
||||
}
|
||||
|
||||
ociRuntime, err := c.getSandboxRuntime(config, r.GetRuntimeHandler())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get sandbox runtime")
|
||||
}
|
||||
log.G(ctx).Debugf("Use OCI %+v for sandbox %q", ociRuntime, id)
|
||||
|
||||
podNetwork := true
|
||||
// Pod network is always needed on windows.
|
||||
if goruntime.GOOS != "windows" &&
|
||||
config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE {
|
||||
// Pod network is not needed on linux with host network.
|
||||
podNetwork = false
|
||||
}
|
||||
if podNetwork {
|
||||
// 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 = netns.NewNetNS()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create network namespace for sandbox %q", id)
|
||||
}
|
||||
sandbox.NetNSPath = sandbox.NetNS.GetPath()
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Teardown network if an error is returned.
|
||||
if err := c.teardownPodNetwork(ctx, sandbox); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
|
||||
}
|
||||
|
||||
if err := sandbox.NetNS.Remove(); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to remove network namespace %s for sandbox %q", sandbox.NetNSPath, id)
|
||||
}
|
||||
sandbox.NetNSPath = ""
|
||||
}
|
||||
}()
|
||||
|
||||
// Setup network for sandbox.
|
||||
// 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 future 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.
|
||||
if err := c.setupPodNetwork(ctx, &sandbox); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to setup network for sandbox %q", id)
|
||||
}
|
||||
}
|
||||
|
||||
// Create sandbox container.
|
||||
// NOTE: sandboxContainerSpec SHOULD NOT have side
|
||||
// effect, e.g. accessing/creating files, so that we can test
|
||||
// it safely.
|
||||
spec, err := c.sandboxContainerSpec(id, config, &image.ImageSpec.Config, sandbox.NetNSPath, ociRuntime.PodAnnotations)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate sandbox container spec")
|
||||
}
|
||||
log.G(ctx).Debugf("Sandbox container %q spec: %#+v", id, spew.NewFormatter(spec))
|
||||
sandbox.ProcessLabel = spec.Process.SelinuxLabel
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
selinux.ReleaseLabel(sandbox.ProcessLabel)
|
||||
}
|
||||
}()
|
||||
|
||||
// handle any KVM based runtime
|
||||
if err := modifyProcessLabel(ociRuntime.Type, spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.GetLinux().GetSecurityContext().GetPrivileged() {
|
||||
// If privileged don't set selinux label, but we still record the MCS label so that
|
||||
// the unused label can be freed later.
|
||||
spec.Process.SelinuxLabel = ""
|
||||
}
|
||||
|
||||
// Generate spec options that will be applied to the spec later.
|
||||
specOpts, err := c.sandboxContainerSpecOpts(config, &image.ImageSpec.Config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate sanbdox container spec options")
|
||||
}
|
||||
|
||||
sandboxLabels := buildLabels(config.Labels, containerKindSandbox)
|
||||
|
||||
runtimeOpts, err := generateRuntimeOptions(ociRuntime, c.config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate runtime options")
|
||||
}
|
||||
opts := []containerd.NewContainerOpts{
|
||||
containerd.WithSnapshotter(c.config.ContainerdConfig.Snapshotter),
|
||||
customopts.WithNewSnapshot(id, containerdImage),
|
||||
containerd.WithSpec(spec, specOpts...),
|
||||
containerd.WithContainerLabels(sandboxLabels),
|
||||
containerd.WithContainerExtension(sandboxMetadataExtension, &sandbox.Metadata),
|
||||
containerd.WithRuntime(ociRuntime.Type, runtimeOpts)}
|
||||
|
||||
container, err := c.client.NewContainer(ctx, id, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create containerd container")
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||
defer deferCancel()
|
||||
if err := container.Delete(deferCtx, containerd.WithSnapshotCleanup); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to delete containerd container %q", id)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Create sandbox container root directories.
|
||||
sandboxRootDir := c.getSandboxRootDir(id)
|
||||
if err := c.os.MkdirAll(sandboxRootDir, 0755); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create sandbox root directory %q",
|
||||
sandboxRootDir)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Cleanup the sandbox root directory.
|
||||
if err := c.os.RemoveAll(sandboxRootDir); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to remove sandbox root directory %q",
|
||||
sandboxRootDir)
|
||||
}
|
||||
}
|
||||
}()
|
||||
volatileSandboxRootDir := c.getVolatileSandboxRootDir(id)
|
||||
if err := c.os.MkdirAll(volatileSandboxRootDir, 0755); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create volatile sandbox root directory %q",
|
||||
volatileSandboxRootDir)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Cleanup the volatile sandbox root directory.
|
||||
if err := c.os.RemoveAll(volatileSandboxRootDir); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to remove volatile sandbox root directory %q",
|
||||
volatileSandboxRootDir)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Setup files required for the sandbox.
|
||||
if err = c.setupSandboxFiles(id, config); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to setup sandbox files")
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if err = c.cleanupSandboxFiles(id, config); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to cleanup sandbox files in %q",
|
||||
sandboxRootDir)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Update sandbox created timestamp.
|
||||
info, err := container.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get sandbox container info")
|
||||
}
|
||||
|
||||
// Create sandbox task in containerd.
|
||||
log.G(ctx).Tracef("Create sandbox container (id=%q, name=%q).",
|
||||
id, name)
|
||||
|
||||
taskOpts := c.taskOpts(ociRuntime.Type)
|
||||
// We don't need stdio for sandbox container.
|
||||
task, err := container.NewTask(ctx, containerdio.NullIO, taskOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create containerd task")
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||
defer deferCancel()
|
||||
// Cleanup the sandbox container if an error is returned.
|
||||
if _, err := task.Delete(deferCtx, WithNRISandboxDelete(id), containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to delete sandbox container %q", id)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// wait is a long running background request, no timeout needed.
|
||||
exitCh, err := task.Wait(ctrdutil.NamespacedContext())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to wait for sandbox container task")
|
||||
}
|
||||
|
||||
nric, err := nri.New()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to create nri client")
|
||||
}
|
||||
if nric != nil {
|
||||
nriSB := &nri.Sandbox{
|
||||
ID: id,
|
||||
Labels: config.Labels,
|
||||
}
|
||||
if _, err := nric.InvokeWithSandbox(ctx, task, v1.Create, nriSB); err != nil {
|
||||
return nil, errors.Wrap(err, "nri invoke")
|
||||
}
|
||||
}
|
||||
|
||||
if err := task.Start(ctx); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to start sandbox container task %q", id)
|
||||
}
|
||||
|
||||
if err := sandbox.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
|
||||
// Set the pod sandbox as ready after successfully start sandbox container.
|
||||
status.Pid = task.Pid()
|
||||
status.State = sandboxstore.StateReady
|
||||
status.CreatedAt = info.CreatedAt
|
||||
return status, nil
|
||||
}); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to update sandbox status")
|
||||
}
|
||||
|
||||
// Add sandbox into sandbox store in INIT state.
|
||||
sandbox.Container = container
|
||||
|
||||
if err := c.sandboxStore.Add(sandbox); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to add sandbox %+v into store", sandbox)
|
||||
}
|
||||
|
||||
// start the monitor after adding sandbox into the store, this ensures
|
||||
// that sandbox is in the store, when event monitor receives the TaskExit event.
|
||||
//
|
||||
// TaskOOM from containerd may come before sandbox is added to store,
|
||||
// but we don't care about sandbox TaskOOM right now, so it is fine.
|
||||
c.eventMonitor.startExitMonitor(context.Background(), id, task.Pid(), exitCh)
|
||||
|
||||
return &runtime.RunPodSandboxResponse{PodSandboxId: id}, nil
|
||||
}
|
||||
|
||||
// setupPodNetwork setups up the network for a pod
|
||||
func (c *criService) setupPodNetwork(ctx context.Context, sandbox *sandboxstore.Sandbox) error {
|
||||
var (
|
||||
id = sandbox.ID
|
||||
config = sandbox.Config
|
||||
path = sandbox.NetNSPath
|
||||
)
|
||||
if c.netPlugin == nil {
|
||||
return errors.New("cni config not initialized")
|
||||
}
|
||||
|
||||
opts, err := cniNamespaceOpts(id, config)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get cni namespace options")
|
||||
}
|
||||
|
||||
result, err := c.netPlugin.Setup(ctx, id, path, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logDebugCNIResult(ctx, id, result)
|
||||
// Check if the default interface has IP config
|
||||
if configs, ok := result.Interfaces[defaultIfName]; ok && len(configs.IPConfigs) > 0 {
|
||||
sandbox.IP, sandbox.AdditionalIPs = selectPodIPs(configs.IPConfigs)
|
||||
sandbox.CNIResult = result
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf("failed to find network info for sandbox %q", id)
|
||||
}
|
||||
|
||||
// cniNamespaceOpts get CNI namespace options from sandbox config.
|
||||
func cniNamespaceOpts(id string, config *runtime.PodSandboxConfig) ([]cni.NamespaceOpts, error) {
|
||||
opts := []cni.NamespaceOpts{
|
||||
cni.WithLabels(toCNILabels(id, config)),
|
||||
}
|
||||
|
||||
portMappings := toCNIPortMappings(config.GetPortMappings())
|
||||
if len(portMappings) > 0 {
|
||||
opts = append(opts, cni.WithCapabilityPortMap(portMappings))
|
||||
}
|
||||
|
||||
// Will return an error if the bandwidth limitation has the wrong unit
|
||||
// or an unreasonable value see validateBandwidthIsReasonable()
|
||||
bandWidth, err := toCNIBandWidth(config.Annotations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bandWidth != nil {
|
||||
opts = append(opts, cni.WithCapabilityBandWidth(*bandWidth))
|
||||
}
|
||||
|
||||
dns := toCNIDNS(config.GetDnsConfig())
|
||||
if dns != nil {
|
||||
opts = append(opts, cni.WithCapabilityDNS(*dns))
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// toCNILabels adds pod metadata into CNI labels.
|
||||
func toCNILabels(id string, config *runtime.PodSandboxConfig) map[string]string {
|
||||
return map[string]string{
|
||||
"K8S_POD_NAMESPACE": config.GetMetadata().GetNamespace(),
|
||||
"K8S_POD_NAME": config.GetMetadata().GetName(),
|
||||
"K8S_POD_INFRA_CONTAINER_ID": id,
|
||||
"IgnoreUnknown": "1",
|
||||
}
|
||||
}
|
||||
|
||||
// toCNIBandWidth converts CRI annotations to CNI bandwidth.
|
||||
func toCNIBandWidth(annotations map[string]string) (*cni.BandWidth, error) {
|
||||
ingress, egress, err := bandwidth.ExtractPodBandwidthResources(annotations)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "reading pod bandwidth annotations")
|
||||
}
|
||||
|
||||
if ingress == nil && egress == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
bandWidth := &cni.BandWidth{}
|
||||
|
||||
if ingress != nil {
|
||||
bandWidth.IngressRate = uint64(ingress.Value())
|
||||
bandWidth.IngressBurst = math.MaxUint32
|
||||
}
|
||||
|
||||
if egress != nil {
|
||||
bandWidth.EgressRate = uint64(egress.Value())
|
||||
bandWidth.EgressBurst = math.MaxUint32
|
||||
}
|
||||
|
||||
return bandWidth, nil
|
||||
}
|
||||
|
||||
// toCNIPortMappings converts CRI port mappings to CNI.
|
||||
func toCNIPortMappings(criPortMappings []*runtime.PortMapping) []cni.PortMapping {
|
||||
var portMappings []cni.PortMapping
|
||||
for _, mapping := range criPortMappings {
|
||||
if mapping.HostPort <= 0 {
|
||||
continue
|
||||
}
|
||||
portMappings = append(portMappings, cni.PortMapping{
|
||||
HostPort: mapping.HostPort,
|
||||
ContainerPort: mapping.ContainerPort,
|
||||
Protocol: strings.ToLower(mapping.Protocol.String()),
|
||||
HostIP: mapping.HostIp,
|
||||
})
|
||||
}
|
||||
return portMappings
|
||||
}
|
||||
|
||||
// toCNIDNS converts CRI DNSConfig to CNI.
|
||||
func toCNIDNS(dns *runtime.DNSConfig) *cni.DNS {
|
||||
if dns == nil {
|
||||
return nil
|
||||
}
|
||||
return &cni.DNS{
|
||||
Servers: dns.GetServers(),
|
||||
Searches: dns.GetSearches(),
|
||||
Options: dns.GetOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
// selectPodIPs select an ip from the ip list. It prefers ipv4 more than ipv6
|
||||
// and returns the additional ips
|
||||
// TODO(random-liu): Revisit the ip order in the ipv6 beta stage. (cri#1278)
|
||||
func selectPodIPs(ipConfigs []*cni.IPConfig) (string, []string) {
|
||||
var (
|
||||
additionalIPs []string
|
||||
ip string
|
||||
)
|
||||
for _, c := range ipConfigs {
|
||||
if c.IP.To4() != nil && ip == "" {
|
||||
ip = c.IP.String()
|
||||
} else {
|
||||
additionalIPs = append(additionalIPs, c.IP.String())
|
||||
}
|
||||
}
|
||||
if ip != "" {
|
||||
return ip, additionalIPs
|
||||
}
|
||||
if len(ipConfigs) == 1 {
|
||||
return additionalIPs[0], nil
|
||||
}
|
||||
return additionalIPs[0], additionalIPs[1:]
|
||||
}
|
||||
|
||||
// untrustedWorkload returns true if the sandbox contains untrusted workload.
|
||||
func untrustedWorkload(config *runtime.PodSandboxConfig) bool {
|
||||
return config.GetAnnotations()[annotations.UntrustedWorkload] == "true"
|
||||
}
|
||||
|
||||
// hostAccessingSandbox returns true if the sandbox configuration
|
||||
// requires additional host access for the sandbox.
|
||||
func hostAccessingSandbox(config *runtime.PodSandboxConfig) bool {
|
||||
securityContext := config.GetLinux().GetSecurityContext()
|
||||
|
||||
namespaceOptions := securityContext.GetNamespaceOptions()
|
||||
if namespaceOptions.GetNetwork() == runtime.NamespaceMode_NODE ||
|
||||
namespaceOptions.GetPid() == runtime.NamespaceMode_NODE ||
|
||||
namespaceOptions.GetIpc() == runtime.NamespaceMode_NODE {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// getSandboxRuntime returns the runtime configuration for sandbox.
|
||||
// If the sandbox contains untrusted workload, runtime for untrusted workload will be returned,
|
||||
// or else default runtime will be returned.
|
||||
func (c *criService) getSandboxRuntime(config *runtime.PodSandboxConfig, runtimeHandler string) (criconfig.Runtime, error) {
|
||||
if untrustedWorkload(config) {
|
||||
// If the untrusted annotation is provided, runtimeHandler MUST be empty.
|
||||
if runtimeHandler != "" && runtimeHandler != criconfig.RuntimeUntrusted {
|
||||
return criconfig.Runtime{}, errors.New("untrusted workload with explicit runtime handler is not allowed")
|
||||
}
|
||||
|
||||
// If the untrusted workload is requesting access to the host/node, this request will fail.
|
||||
//
|
||||
// Note: If the workload is marked untrusted but requests privileged, this can be granted, as the
|
||||
// runtime may support this. For example, in a virtual-machine isolated runtime, privileged
|
||||
// is a supported option, granting the workload to access the entire guest VM instead of host.
|
||||
// TODO(windows): Deprecate this so that we don't need to handle it for windows.
|
||||
if hostAccessingSandbox(config) {
|
||||
return criconfig.Runtime{}, errors.New("untrusted workload with host access is not allowed")
|
||||
}
|
||||
|
||||
runtimeHandler = criconfig.RuntimeUntrusted
|
||||
}
|
||||
|
||||
if runtimeHandler == "" {
|
||||
runtimeHandler = c.config.ContainerdConfig.DefaultRuntimeName
|
||||
}
|
||||
|
||||
handler, ok := c.config.ContainerdConfig.Runtimes[runtimeHandler]
|
||||
if !ok {
|
||||
return criconfig.Runtime{}, errors.Errorf("no runtime for %q is configured", runtimeHandler)
|
||||
}
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func logDebugCNIResult(ctx context.Context, sandboxID string, result *cni.CNIResult) {
|
||||
if logrus.GetLevel() < logrus.DebugLevel {
|
||||
return
|
||||
}
|
||||
cniResult, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to marshal CNI result for sandbox %q: %v", sandboxID, err)
|
||||
return
|
||||
}
|
||||
log.G(ctx).Debugf("cni result for sandbox %q: %s", sandboxID, string(cniResult))
|
||||
}
|
||||
310
vendor/github.com/containerd/cri/pkg/server/sandbox_run_unix.go
generated
vendored
310
vendor/github.com/containerd/cri/pkg/server/sandbox_run_unix.go
generated
vendored
@@ -1,310 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
selinux "github.com/opencontainers/selinux/go-selinux"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/annotations"
|
||||
customopts "github.com/containerd/cri/pkg/containerd/opts"
|
||||
osinterface "github.com/containerd/cri/pkg/os"
|
||||
)
|
||||
|
||||
func (c *criService) sandboxContainerSpec(id string, config *runtime.PodSandboxConfig,
|
||||
imageConfig *imagespec.ImageConfig, nsPath string, runtimePodAnnotations []string) (_ *runtimespec.Spec, retErr error) {
|
||||
// Creates a spec Generator with the default spec.
|
||||
// TODO(random-liu): [P1] Compare the default settings with docker and containerd default.
|
||||
specOpts := []oci.SpecOpts{
|
||||
customopts.WithoutRunMount,
|
||||
customopts.WithoutDefaultSecuritySettings,
|
||||
customopts.WithRelativeRoot(relativeRootfsPath),
|
||||
oci.WithEnv(imageConfig.Env),
|
||||
oci.WithRootFSReadonly(),
|
||||
oci.WithHostname(config.GetHostname()),
|
||||
}
|
||||
if imageConfig.WorkingDir != "" {
|
||||
specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
|
||||
}
|
||||
|
||||
if len(imageConfig.Entrypoint) == 0 && len(imageConfig.Cmd) == 0 {
|
||||
// Pause image must have entrypoint or cmd.
|
||||
return nil, errors.Errorf("invalid empty entrypoint and cmd in image config %+v", imageConfig)
|
||||
}
|
||||
specOpts = append(specOpts, oci.WithProcessArgs(append(imageConfig.Entrypoint, imageConfig.Cmd...)...))
|
||||
|
||||
// Set cgroups parent.
|
||||
if c.config.DisableCgroup {
|
||||
specOpts = append(specOpts, customopts.WithDisabledCgroups)
|
||||
} else {
|
||||
if config.GetLinux().GetCgroupParent() != "" {
|
||||
cgroupsPath := getCgroupsPath(config.GetLinux().GetCgroupParent(), id)
|
||||
specOpts = append(specOpts, oci.WithCgroup(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.
|
||||
var (
|
||||
securityContext = config.GetLinux().GetSecurityContext()
|
||||
nsOptions = securityContext.GetNamespaceOptions()
|
||||
)
|
||||
if nsOptions.GetNetwork() == runtime.NamespaceMode_NODE {
|
||||
specOpts = append(specOpts, customopts.WithoutNamespace(runtimespec.NetworkNamespace))
|
||||
specOpts = append(specOpts, customopts.WithoutNamespace(runtimespec.UTSNamespace))
|
||||
} else {
|
||||
specOpts = append(specOpts, oci.WithLinuxNamespace(
|
||||
runtimespec.LinuxNamespace{
|
||||
Type: runtimespec.NetworkNamespace,
|
||||
Path: nsPath,
|
||||
}))
|
||||
}
|
||||
if nsOptions.GetPid() == runtime.NamespaceMode_NODE {
|
||||
specOpts = append(specOpts, customopts.WithoutNamespace(runtimespec.PIDNamespace))
|
||||
}
|
||||
if nsOptions.GetIpc() == runtime.NamespaceMode_NODE {
|
||||
specOpts = append(specOpts, customopts.WithoutNamespace(runtimespec.IPCNamespace))
|
||||
}
|
||||
|
||||
// It's fine to generate the spec before the sandbox /dev/shm
|
||||
// is actually created.
|
||||
sandboxDevShm := c.getSandboxDevShm(id)
|
||||
if nsOptions.GetIpc() == runtime.NamespaceMode_NODE {
|
||||
sandboxDevShm = devShm
|
||||
}
|
||||
specOpts = append(specOpts, oci.WithMounts([]runtimespec.Mount{
|
||||
{
|
||||
Source: sandboxDevShm,
|
||||
Destination: devShm,
|
||||
Type: "bind",
|
||||
Options: []string{"rbind", "ro"},
|
||||
},
|
||||
// Add resolv.conf for katacontainers to setup the DNS of pod VM properly.
|
||||
{
|
||||
Source: c.getResolvPath(id),
|
||||
Destination: resolvConfPath,
|
||||
Type: "bind",
|
||||
Options: []string{"rbind", "ro"},
|
||||
},
|
||||
}))
|
||||
|
||||
processLabel, mountLabel, err := initLabelsFromOpt(securityContext.GetSelinuxOptions())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to init selinux options %+v", securityContext.GetSelinuxOptions())
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
selinux.ReleaseLabel(processLabel)
|
||||
}
|
||||
}()
|
||||
|
||||
supplementalGroups := securityContext.GetSupplementalGroups()
|
||||
specOpts = append(specOpts,
|
||||
customopts.WithSelinuxLabels(processLabel, mountLabel),
|
||||
customopts.WithSupplementalGroups(supplementalGroups),
|
||||
)
|
||||
|
||||
// Add sysctls
|
||||
sysctls := config.GetLinux().GetSysctls()
|
||||
specOpts = append(specOpts, customopts.WithSysctls(sysctls))
|
||||
|
||||
// Note: LinuxSandboxSecurityContext does not currently provide an apparmor profile
|
||||
|
||||
if !c.config.DisableCgroup {
|
||||
specOpts = append(specOpts, customopts.WithDefaultSandboxShares)
|
||||
}
|
||||
specOpts = append(specOpts, customopts.WithPodOOMScoreAdj(int(defaultSandboxOOMAdj), c.config.RestrictOOMScoreAdj))
|
||||
|
||||
for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
|
||||
runtimePodAnnotations) {
|
||||
specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
|
||||
}
|
||||
|
||||
specOpts = append(specOpts,
|
||||
customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeSandbox),
|
||||
customopts.WithAnnotation(annotations.SandboxID, id),
|
||||
customopts.WithAnnotation(annotations.SandboxLogDir, config.GetLogDirectory()),
|
||||
)
|
||||
|
||||
return c.runtimeSpec(id, "", specOpts...)
|
||||
}
|
||||
|
||||
// sandboxContainerSpecOpts generates OCI spec options for
|
||||
// the sandbox container.
|
||||
func (c *criService) sandboxContainerSpecOpts(config *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
|
||||
var (
|
||||
securityContext = config.GetLinux().GetSecurityContext()
|
||||
specOpts []oci.SpecOpts
|
||||
)
|
||||
seccompSpecOpts, err := c.generateSeccompSpecOpts(
|
||||
securityContext.GetSeccompProfilePath(),
|
||||
securityContext.GetPrivileged(),
|
||||
c.seccompEnabled())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate seccomp spec opts")
|
||||
}
|
||||
if seccompSpecOpts != nil {
|
||||
specOpts = append(specOpts, seccompSpecOpts)
|
||||
}
|
||||
|
||||
userstr, err := generateUserString(
|
||||
"",
|
||||
securityContext.GetRunAsUser(),
|
||||
securityContext.GetRunAsGroup(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate user string")
|
||||
}
|
||||
if userstr == "" {
|
||||
// Lastly, since no user override was passed via CRI try to set via OCI
|
||||
// Image
|
||||
userstr = imageConfig.User
|
||||
}
|
||||
if userstr != "" {
|
||||
specOpts = append(specOpts, oci.WithUser(userstr))
|
||||
}
|
||||
return specOpts, nil
|
||||
}
|
||||
|
||||
// setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts,
|
||||
// /etc/resolv.conf and /etc/hostname.
|
||||
func (c *criService) setupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
|
||||
sandboxEtcHostname := c.getSandboxHostname(id)
|
||||
hostname := config.GetHostname()
|
||||
if hostname == "" {
|
||||
var err error
|
||||
hostname, err = c.os.Hostname()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get hostname")
|
||||
}
|
||||
}
|
||||
if err := c.os.WriteFile(sandboxEtcHostname, []byte(hostname+"\n"), 0644); err != nil {
|
||||
return errors.Wrapf(err, "failed to write hostname to %q", sandboxEtcHostname)
|
||||
}
|
||||
|
||||
// TODO(random-liu): Consider whether we should maintain /etc/hosts and /etc/resolv.conf in kubelet.
|
||||
sandboxEtcHosts := c.getSandboxHosts(id)
|
||||
if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0644); err != nil {
|
||||
return errors.Wrapf(err, "failed to generate sandbox hosts file %q", sandboxEtcHosts)
|
||||
}
|
||||
|
||||
// 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 errors.Wrapf(err, "failed to parse sandbox DNSConfig %+v", dnsConfig)
|
||||
}
|
||||
}
|
||||
resolvPath := c.getResolvPath(id)
|
||||
if resolvContent == "" {
|
||||
// copy host's resolv.conf to resolvPath
|
||||
err = c.os.CopyFile(resolvConfPath, resolvPath, 0644)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to copy host's resolv.conf to %q", resolvPath)
|
||||
}
|
||||
} else {
|
||||
err = c.os.WriteFile(resolvPath, []byte(resolvContent), 0644)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to write resolv content to %q", resolvPath)
|
||||
}
|
||||
}
|
||||
|
||||
// Setup sandbox /dev/shm.
|
||||
if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetIpc() == runtime.NamespaceMode_NODE {
|
||||
if _, err := c.os.Stat(devShm); err != nil {
|
||||
return errors.Wrapf(err, "host %q is not available for host ipc", devShm)
|
||||
}
|
||||
} else {
|
||||
sandboxDevShm := c.getSandboxDevShm(id)
|
||||
if err := c.os.MkdirAll(sandboxDevShm, 0700); err != nil {
|
||||
return errors.Wrap(err, "failed to create sandbox shm")
|
||||
}
|
||||
shmproperty := fmt.Sprintf("mode=1777,size=%d", defaultShmSize)
|
||||
if err := c.os.(osinterface.UNIX).Mount("shm", sandboxDevShm, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), shmproperty); err != nil {
|
||||
return errors.Wrap(err, "failed to mount sandbox shm")
|
||||
}
|
||||
}
|
||||
|
||||
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 "", errors.Errorf("DNSOption.Searches has more than %d domains", maxDNSSearches)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// cleanupSandboxFiles unmount some sandbox files, we rely on the removal of sandbox root directory to
|
||||
// remove these files. Unmount should *NOT* return error if the mount point is already unmounted.
|
||||
func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
|
||||
if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetIpc() != runtime.NamespaceMode_NODE {
|
||||
path, err := c.os.FollowSymlinkInScope(c.getSandboxDevShm(id), "/")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to follow symlink")
|
||||
}
|
||||
if err := c.os.(osinterface.UNIX).Unmount(path); err != nil && !os.IsNotExist(err) {
|
||||
return errors.Wrapf(err, "failed to unmount %q", path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// taskOpts generates task options for a (sandbox) container.
|
||||
func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
|
||||
// TODO(random-liu): Remove this after shim v1 is deprecated.
|
||||
var taskOpts []containerd.NewTaskOpts
|
||||
if c.config.NoPivot && (runtimeType == plugin.RuntimeRuncV1 || runtimeType == plugin.RuntimeRuncV2) {
|
||||
taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
|
||||
}
|
||||
return taskOpts
|
||||
}
|
||||
91
vendor/github.com/containerd/cri/pkg/server/sandbox_run_windows.go
generated
vendored
91
vendor/github.com/containerd/cri/pkg/server/sandbox_run_windows.go
generated
vendored
@@ -1,91 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/annotations"
|
||||
customopts "github.com/containerd/cri/pkg/containerd/opts"
|
||||
)
|
||||
|
||||
func (c *criService) sandboxContainerSpec(id string, config *runtime.PodSandboxConfig,
|
||||
imageConfig *imagespec.ImageConfig, nsPath string, runtimePodAnnotations []string) (*runtimespec.Spec, error) {
|
||||
// Creates a spec Generator with the default spec.
|
||||
specOpts := []oci.SpecOpts{
|
||||
oci.WithEnv(imageConfig.Env),
|
||||
oci.WithHostname(config.GetHostname()),
|
||||
}
|
||||
if imageConfig.WorkingDir != "" {
|
||||
specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
|
||||
}
|
||||
|
||||
if len(imageConfig.Entrypoint) == 0 && len(imageConfig.Cmd) == 0 {
|
||||
// Pause image must have entrypoint or cmd.
|
||||
return nil, errors.Errorf("invalid empty entrypoint and cmd in image config %+v", imageConfig)
|
||||
}
|
||||
specOpts = append(specOpts, oci.WithProcessArgs(append(imageConfig.Entrypoint, imageConfig.Cmd...)...))
|
||||
|
||||
specOpts = append(specOpts,
|
||||
// Clear the root location since hcsshim expects it.
|
||||
// NOTE: readonly rootfs doesn't work on windows.
|
||||
customopts.WithoutRoot,
|
||||
customopts.WithWindowsNetworkNamespace(nsPath),
|
||||
)
|
||||
|
||||
specOpts = append(specOpts, customopts.WithWindowsDefaultSandboxShares)
|
||||
|
||||
for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
|
||||
runtimePodAnnotations) {
|
||||
specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
|
||||
}
|
||||
|
||||
specOpts = append(specOpts,
|
||||
customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeSandbox),
|
||||
customopts.WithAnnotation(annotations.SandboxID, id),
|
||||
customopts.WithAnnotation(annotations.SandboxLogDir, config.GetLogDirectory()),
|
||||
)
|
||||
|
||||
return c.runtimeSpec(id, "", specOpts...)
|
||||
}
|
||||
|
||||
// No sandbox container spec options for windows yet.
|
||||
func (c *criService) sandboxContainerSpecOpts(config *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// No sandbox files needed for windows.
|
||||
func (c *criService) setupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// No sandbox files needed for windows.
|
||||
func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// No task options needed for windows.
|
||||
func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
|
||||
return nil
|
||||
}
|
||||
217
vendor/github.com/containerd/cri/pkg/server/sandbox_status.go
generated
vendored
217
vendor/github.com/containerd/cri/pkg/server/sandbox_status.go
generated
vendored
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"encoding/json"
|
||||
goruntime "runtime"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
cni "github.com/containerd/go-cni"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
// PodSandboxStatus returns the status of the PodSandbox.
|
||||
func (c *criService) PodSandboxStatus(ctx context.Context, r *runtime.PodSandboxStatusRequest) (*runtime.PodSandboxStatusResponse, error) {
|
||||
sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "an error occurred when try to find sandbox")
|
||||
}
|
||||
|
||||
ip, additionalIPs, err := c.getIPs(sandbox)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get sandbox ip")
|
||||
}
|
||||
status := toCRISandboxStatus(sandbox.Metadata, sandbox.Status.Get(), ip, additionalIPs)
|
||||
if status.GetCreatedAt() == 0 {
|
||||
// CRI doesn't allow CreatedAt == 0.
|
||||
info, err := sandbox.Container.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get CreatedAt for sandbox container in %q state", status.State)
|
||||
}
|
||||
status.CreatedAt = info.CreatedAt.UnixNano()
|
||||
}
|
||||
if !r.GetVerbose() {
|
||||
return &runtime.PodSandboxStatusResponse{Status: status}, nil
|
||||
}
|
||||
|
||||
// Generate verbose information.
|
||||
info, err := toCRISandboxInfo(ctx, sandbox)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get verbose sandbox container info")
|
||||
}
|
||||
|
||||
return &runtime.PodSandboxStatusResponse{
|
||||
Status: status,
|
||||
Info: info,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *criService) getIPs(sandbox sandboxstore.Sandbox) (string, []string, error) {
|
||||
config := sandbox.Config
|
||||
|
||||
if goruntime.GOOS != "windows" &&
|
||||
config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE {
|
||||
// For sandboxes using the node network we are not
|
||||
// responsible for reporting the IP.
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
if closed, err := sandbox.NetNS.Closed(); err != nil {
|
||||
return "", nil, errors.Wrap(err, "check network namespace closed")
|
||||
} else if closed {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
return sandbox.IP, sandbox.AdditionalIPs, nil
|
||||
}
|
||||
|
||||
// toCRISandboxStatus converts sandbox metadata into CRI pod sandbox status.
|
||||
func toCRISandboxStatus(meta sandboxstore.Metadata, status sandboxstore.Status, ip string, additionalIPs []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()
|
||||
var ips []*runtime.PodIP
|
||||
for _, additionalIP := range additionalIPs {
|
||||
ips = append(ips, &runtime.PodIP{Ip: additionalIP})
|
||||
}
|
||||
return &runtime.PodSandboxStatus{
|
||||
Id: meta.ID,
|
||||
Metadata: meta.Config.GetMetadata(),
|
||||
State: state,
|
||||
CreatedAt: status.CreatedAt.UnixNano(),
|
||||
Network: &runtime.PodSandboxNetworkStatus{
|
||||
Ip: ip,
|
||||
AdditionalIps: ips,
|
||||
},
|
||||
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(),
|
||||
RuntimeHandler: meta.RuntimeHandler,
|
||||
}
|
||||
}
|
||||
|
||||
// SandboxInfo is extra information for sandbox.
|
||||
// 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"`
|
||||
// Note: a new field `RuntimeHandler` has been added into the CRI PodSandboxStatus struct, and
|
||||
// should be set. This `RuntimeHandler` field will be deprecated after containerd 1.3 (tracked
|
||||
// in https://github.com/containerd/cri/issues/1064).
|
||||
RuntimeHandler string `json:"runtimeHandler"` // see the Note above
|
||||
RuntimeType string `json:"runtimeType"`
|
||||
RuntimeOptions interface{} `json:"runtimeOptions"`
|
||||
Config *runtime.PodSandboxConfig `json:"config"`
|
||||
RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"`
|
||||
CNIResult *cni.CNIResult `json:"cniResult"`
|
||||
}
|
||||
|
||||
// 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, errors.Wrap(err, "failed to get sandbox container task")
|
||||
}
|
||||
|
||||
var processStatus containerd.ProcessStatus
|
||||
if task != nil {
|
||||
taskStatus, err := task.Status(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get task status")
|
||||
}
|
||||
|
||||
processStatus = taskStatus.Status
|
||||
}
|
||||
|
||||
si := &SandboxInfo{
|
||||
Pid: sandbox.Status.Get().Pid,
|
||||
RuntimeHandler: sandbox.RuntimeHandler,
|
||||
Status: string(processStatus),
|
||||
Config: sandbox.Config,
|
||||
CNIResult: sandbox.CNIResult,
|
||||
}
|
||||
|
||||
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.NetNS != nil {
|
||||
// Add network closed information if sandbox is not using host network.
|
||||
closed, err := sandbox.NetNS.Closed()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to check network namespace closed")
|
||||
}
|
||||
si.NetNSClosed = closed
|
||||
}
|
||||
|
||||
spec, err := container.Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get sandbox container runtime spec")
|
||||
}
|
||||
si.RuntimeSpec = spec
|
||||
|
||||
ctrInfo, err := container.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get sandbox container info")
|
||||
}
|
||||
// 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
|
||||
|
||||
runtimeOptions, err := getRuntimeOptions(ctrInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get runtime options")
|
||||
}
|
||||
si.RuntimeType = ctrInfo.Runtime.Name
|
||||
si.RuntimeOptions = runtimeOptions
|
||||
|
||||
infoBytes, err := json.Marshal(si)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal info %v", si)
|
||||
}
|
||||
return map[string]string{
|
||||
"info": string(infoBytes),
|
||||
}, nil
|
||||
}
|
||||
195
vendor/github.com/containerd/cri/pkg/server/sandbox_stop.go
generated
vendored
195
vendor/github.com/containerd/cri/pkg/server/sandbox_stop.go
generated
vendored
@@ -1,195 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
eventtypes "github.com/containerd/containerd/api/events"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
)
|
||||
|
||||
// StopPodSandbox stops the sandbox. If there are any running containers in the
|
||||
// sandbox, they should be forcibly terminated.
|
||||
func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (*runtime.StopPodSandboxResponse, error) {
|
||||
sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "an error occurred when try to find sandbox %q",
|
||||
r.GetPodSandboxId())
|
||||
}
|
||||
|
||||
if err := c.stopPodSandbox(ctx, sandbox); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &runtime.StopPodSandboxResponse{}, nil
|
||||
}
|
||||
|
||||
func (c *criService) stopPodSandbox(ctx context.Context, sandbox sandboxstore.Sandbox) error {
|
||||
// Use the full sandbox id.
|
||||
id := sandbox.ID
|
||||
|
||||
// Stop all containers inside the sandbox. This terminates the container forcibly,
|
||||
// and container may still be created, so production should not rely on this behavior.
|
||||
// TODO(random-liu): Introduce a state in sandbox to avoid future container creation.
|
||||
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 errors.Wrapf(err, "failed to stop container %q", container.ID)
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.cleanupSandboxFiles(id, sandbox.Config); err != nil {
|
||||
return errors.Wrap(err, "failed to cleanup sandbox files")
|
||||
}
|
||||
|
||||
// Only stop sandbox container when it's running or unknown.
|
||||
state := sandbox.Status.Get().State
|
||||
if state == sandboxstore.StateReady || state == sandboxstore.StateUnknown {
|
||||
if err := c.stopSandboxContainer(ctx, sandbox); err != nil {
|
||||
return errors.Wrapf(err, "failed to stop sandbox container %q in %q state", id, state)
|
||||
}
|
||||
}
|
||||
|
||||
// Teardown network for sandbox.
|
||||
if sandbox.NetNS != nil {
|
||||
// Use empty netns path if netns is not available. This is defined in:
|
||||
// https://github.com/containernetworking/cni/blob/v0.7.0-alpha1/SPEC.md
|
||||
if closed, err := sandbox.NetNS.Closed(); err != nil {
|
||||
return errors.Wrap(err, "failed to check network namespace closed")
|
||||
} else if closed {
|
||||
sandbox.NetNSPath = ""
|
||||
}
|
||||
if err := c.teardownPodNetwork(ctx, sandbox); err != nil {
|
||||
return errors.Wrapf(err, "failed to destroy network for sandbox %q", id)
|
||||
}
|
||||
if err := sandbox.NetNS.Remove(); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove network namespace for sandbox %q", id)
|
||||
}
|
||||
}
|
||||
|
||||
log.G(ctx).Infof("TearDown network for sandbox %q successfully", id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// stopSandboxContainer kills the sandbox container.
|
||||
// `task.Delete` is not called here because it will be called when
|
||||
// the event monitor handles the `TaskExit` event.
|
||||
func (c *criService) stopSandboxContainer(ctx context.Context, sandbox sandboxstore.Sandbox) error {
|
||||
id := sandbox.ID
|
||||
container := sandbox.Container
|
||||
state := sandbox.Status.Get().State
|
||||
task, err := container.Task(ctx, nil)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to get sandbox container")
|
||||
}
|
||||
// Don't return for unknown state, some cleanup needs to be done.
|
||||
if state == sandboxstore.StateUnknown {
|
||||
return cleanupUnknownSandbox(ctx, id, sandbox)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle unknown state.
|
||||
// The cleanup logic is the same with container unknown state.
|
||||
if state == sandboxstore.StateUnknown {
|
||||
// Start an exit handler for containers in unknown state.
|
||||
waitCtx, waitCancel := context.WithCancel(ctrdutil.NamespacedContext())
|
||||
defer waitCancel()
|
||||
exitCh, err := task.Wait(waitCtx)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to wait for task")
|
||||
}
|
||||
return cleanupUnknownSandbox(ctx, id, sandbox)
|
||||
}
|
||||
|
||||
exitCtx, exitCancel := context.WithCancel(context.Background())
|
||||
stopCh := c.eventMonitor.startExitMonitor(exitCtx, id, task.Pid(), exitCh)
|
||||
defer func() {
|
||||
exitCancel()
|
||||
// This ensures that exit monitor is stopped before
|
||||
// `Wait` is cancelled, so no exit event is generated
|
||||
// because of the `Wait` cancellation.
|
||||
<-stopCh
|
||||
}()
|
||||
}
|
||||
|
||||
// Kill the sandbox container.
|
||||
if err = task.Kill(ctx, syscall.SIGKILL); err != nil && !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to kill sandbox container")
|
||||
}
|
||||
|
||||
return c.waitSandboxStop(ctx, sandbox)
|
||||
}
|
||||
|
||||
// waitSandboxStop waits for sandbox to be stopped until context is cancelled or
|
||||
// the context deadline is exceeded.
|
||||
func (c *criService) waitSandboxStop(ctx context.Context, sandbox sandboxstore.Sandbox) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Wrapf(ctx.Err(), "wait sandbox container %q", sandbox.ID)
|
||||
case <-sandbox.Stopped():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// teardownPodNetwork removes the network from the pod
|
||||
func (c *criService) teardownPodNetwork(ctx context.Context, sandbox sandboxstore.Sandbox) error {
|
||||
if c.netPlugin == nil {
|
||||
return errors.New("cni config not initialized")
|
||||
}
|
||||
|
||||
var (
|
||||
id = sandbox.ID
|
||||
path = sandbox.NetNSPath
|
||||
config = sandbox.Config
|
||||
)
|
||||
opts, err := cniNamespaceOpts(id, config)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get cni namespace options")
|
||||
}
|
||||
|
||||
return c.netPlugin.Remove(ctx, id, path, opts...)
|
||||
}
|
||||
|
||||
// cleanupUnknownSandbox cleanup stopped sandbox in unknown state.
|
||||
func cleanupUnknownSandbox(ctx context.Context, id string, sandbox sandboxstore.Sandbox) error {
|
||||
// Reuse handleSandboxExit to do the cleanup.
|
||||
return handleSandboxExit(ctx, &eventtypes.TaskExit{
|
||||
ContainerID: id,
|
||||
ID: id,
|
||||
Pid: 0,
|
||||
ExitStatus: unknownExitCode,
|
||||
ExitedAt: time.Now(),
|
||||
}, sandbox)
|
||||
}
|
||||
325
vendor/github.com/containerd/cri/pkg/server/service.go
generated
vendored
325
vendor/github.com/containerd/cri/pkg/server/service.go
generated
vendored
@@ -1,325 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/cri/pkg/streaming"
|
||||
cni "github.com/containerd/go-cni"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/store/label"
|
||||
|
||||
"github.com/containerd/cri/pkg/atomic"
|
||||
criconfig "github.com/containerd/cri/pkg/config"
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
osinterface "github.com/containerd/cri/pkg/os"
|
||||
"github.com/containerd/cri/pkg/registrar"
|
||||
containerstore "github.com/containerd/cri/pkg/store/container"
|
||||
imagestore "github.com/containerd/cri/pkg/store/image"
|
||||
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
|
||||
snapshotstore "github.com/containerd/cri/pkg/store/snapshot"
|
||||
)
|
||||
|
||||
// grpcServices are all the grpc services provided by cri containerd.
|
||||
type grpcServices interface {
|
||||
runtime.RuntimeServiceServer
|
||||
runtime.ImageServiceServer
|
||||
}
|
||||
|
||||
// CRIService is the interface implement CRI remote service server.
|
||||
type CRIService interface {
|
||||
Run() error
|
||||
// io.Closer is used by containerd to gracefully stop cri service.
|
||||
io.Closer
|
||||
plugin.Service
|
||||
grpcServices
|
||||
}
|
||||
|
||||
// criService implements CRIService.
|
||||
type criService struct {
|
||||
// config contains all configurations.
|
||||
config criconfig.Config
|
||||
// imageFSPath is the path to image filesystem.
|
||||
imageFSPath string
|
||||
// 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 cni.CNI
|
||||
// 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
|
||||
// cniNetConfMonitor is used to reload cni network conf if there is
|
||||
// any valid fs change events from cni network conf dir.
|
||||
cniNetConfMonitor *cniNetConfSyncer
|
||||
// baseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
|
||||
baseOCISpecs map[string]*oci.Spec
|
||||
}
|
||||
|
||||
// NewCRIService returns a new instance of CRIService
|
||||
func NewCRIService(config criconfig.Config, client *containerd.Client) (CRIService, error) {
|
||||
var err error
|
||||
labels := label.NewStore()
|
||||
c := &criService{
|
||||
config: config,
|
||||
client: client,
|
||||
os: osinterface.RealOS{},
|
||||
sandboxStore: sandboxstore.NewStore(labels),
|
||||
containerStore: containerstore.NewStore(labels),
|
||||
imageStore: imagestore.NewStore(client),
|
||||
snapshotStore: snapshotstore.NewStore(),
|
||||
sandboxNameIndex: registrar.NewRegistrar(),
|
||||
containerNameIndex: registrar.NewRegistrar(),
|
||||
initialized: atomic.NewBool(false),
|
||||
}
|
||||
|
||||
if client.SnapshotService(c.config.ContainerdConfig.Snapshotter) == nil {
|
||||
return nil, errors.Errorf("failed to find snapshotter %q", c.config.ContainerdConfig.Snapshotter)
|
||||
}
|
||||
|
||||
c.imageFSPath = imageFSPath(config.ContainerdRootDir, config.ContainerdConfig.Snapshotter)
|
||||
logrus.Infof("Get image filesystem path %q", c.imageFSPath)
|
||||
|
||||
if err := c.initPlatform(); err != nil {
|
||||
return nil, errors.Wrap(err, "initialize platform")
|
||||
}
|
||||
|
||||
// prepare streaming server
|
||||
c.streamServer, err = newStreamServer(c, config.StreamServerAddress, config.StreamServerPort, config.StreamIdleTimeout)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create stream server")
|
||||
}
|
||||
|
||||
c.eventMonitor = newEventMonitor(c)
|
||||
|
||||
c.cniNetConfMonitor, err = newCNINetConfSyncer(c.config.NetworkPluginConfDir, c.netPlugin, c.cniLoadOptions())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create cni conf monitor")
|
||||
}
|
||||
|
||||
// Preload base OCI specs
|
||||
c.baseOCISpecs, err = loadBaseOCISpecs(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Register registers all required services onto a specific grpc server.
|
||||
// This is used by containerd cri plugin.
|
||||
func (c *criService) Register(s *grpc.Server) error {
|
||||
return c.register(s)
|
||||
}
|
||||
|
||||
// RegisterTCP register all required services onto a GRPC server on TCP.
|
||||
// This is used by containerd CRI plugin.
|
||||
func (c *criService) RegisterTCP(s *grpc.Server) error {
|
||||
if !c.config.DisableTCPService {
|
||||
return c.register(s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run starts the CRI service.
|
||||
func (c *criService) Run() error {
|
||||
logrus.Info("Start subscribing containerd event")
|
||||
c.eventMonitor.subscribe(c.client)
|
||||
|
||||
logrus.Infof("Start recovering state")
|
||||
if err := c.recover(ctrdutil.NamespacedContext()); err != nil {
|
||||
return errors.Wrap(err, "failed to recover state")
|
||||
}
|
||||
|
||||
// Start event handler.
|
||||
logrus.Info("Start event monitor")
|
||||
eventMonitorErrCh := c.eventMonitor.start()
|
||||
|
||||
// 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 CNI network conf syncer
|
||||
logrus.Info("Start cni network conf syncer")
|
||||
cniNetConfMonitorErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(cniNetConfMonitorErrCh)
|
||||
cniNetConfMonitorErrCh <- c.cniNetConfMonitor.syncLoop()
|
||||
}()
|
||||
|
||||
// Start streaming server.
|
||||
logrus.Info("Start streaming server")
|
||||
streamServerErrCh := make(chan error)
|
||||
go func() {
|
||||
defer close(streamServerErrCh)
|
||||
if err := c.streamServer.Start(true); err != nil && err != http.ErrServerClosed {
|
||||
logrus.WithError(err).Error("Failed to start streaming server")
|
||||
streamServerErrCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
// Set the server as initialized. GRPC services could start serving traffic.
|
||||
c.initialized.Set()
|
||||
|
||||
var eventMonitorErr, streamServerErr, cniNetConfMonitorErr error
|
||||
// Stop the whole CRI service if any of the critical service exits.
|
||||
select {
|
||||
case eventMonitorErr = <-eventMonitorErrCh:
|
||||
case streamServerErr = <-streamServerErrCh:
|
||||
case cniNetConfMonitorErr = <-cniNetConfMonitorErrCh:
|
||||
}
|
||||
if err := c.Close(); err != nil {
|
||||
return errors.Wrap(err, "failed to stop cri service")
|
||||
}
|
||||
// If the error is set above, err from channel must be nil here, because
|
||||
// the channel is supposed to be closed. Or else, we wait and set it.
|
||||
if err := <-eventMonitorErrCh; err != nil {
|
||||
eventMonitorErr = err
|
||||
}
|
||||
logrus.Info("Event monitor stopped")
|
||||
// There is a race condition with http.Server.Serve.
|
||||
// When `Close` is called at the same time with `Serve`, `Close`
|
||||
// may finish first, and `Serve` may still block.
|
||||
// See https://github.com/golang/go/issues/20239.
|
||||
// Here we set a 2 second timeout for the stream server wait,
|
||||
// if it timeout, an error log is generated.
|
||||
// TODO(random-liu): Get rid of this after https://github.com/golang/go/issues/20239
|
||||
// is fixed.
|
||||
const streamServerStopTimeout = 2 * time.Second
|
||||
select {
|
||||
case err := <-streamServerErrCh:
|
||||
if err != nil {
|
||||
streamServerErr = err
|
||||
}
|
||||
logrus.Info("Stream server stopped")
|
||||
case <-time.After(streamServerStopTimeout):
|
||||
logrus.Errorf("Stream server is not stopped in %q", streamServerStopTimeout)
|
||||
}
|
||||
if eventMonitorErr != nil {
|
||||
return errors.Wrap(eventMonitorErr, "event monitor error")
|
||||
}
|
||||
if streamServerErr != nil {
|
||||
return errors.Wrap(streamServerErr, "stream server error")
|
||||
}
|
||||
if cniNetConfMonitorErr != nil {
|
||||
return errors.Wrap(cniNetConfMonitorErr, "cni network conf monitor error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close stops the CRI service.
|
||||
// TODO(random-liu): Make close synchronous.
|
||||
func (c *criService) Close() error {
|
||||
logrus.Info("Stop CRI service")
|
||||
if err := c.cniNetConfMonitor.stop(); err != nil {
|
||||
logrus.WithError(err).Error("failed to stop cni network conf monitor")
|
||||
}
|
||||
c.eventMonitor.stop()
|
||||
if err := c.streamServer.Stop(); err != nil {
|
||||
return errors.Wrap(err, "failed to stop stream server")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *criService) register(s *grpc.Server) error {
|
||||
instrumented := newInstrumentedService(c)
|
||||
runtime.RegisterRuntimeServiceServer(s, instrumented)
|
||||
runtime.RegisterImageServiceServer(s, instrumented)
|
||||
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))
|
||||
}
|
||||
|
||||
func loadOCISpec(filename string) (*oci.Spec, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to open base OCI spec: %s", filename)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
spec := oci.Spec{}
|
||||
if err := json.NewDecoder(file).Decode(&spec); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse base OCI spec file")
|
||||
}
|
||||
|
||||
return &spec, nil
|
||||
}
|
||||
|
||||
func loadBaseOCISpecs(config *criconfig.Config) (map[string]*oci.Spec, error) {
|
||||
specs := map[string]*oci.Spec{}
|
||||
for _, cfg := range config.Runtimes {
|
||||
if cfg.BaseRuntimeSpec == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Don't load same file twice
|
||||
if _, ok := specs[cfg.BaseRuntimeSpec]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
spec, err := loadOCISpec(cfg.BaseRuntimeSpec)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to load base OCI spec from file: %s", cfg.BaseRuntimeSpec)
|
||||
}
|
||||
|
||||
specs[cfg.BaseRuntimeSpec] = spec
|
||||
}
|
||||
|
||||
return specs, nil
|
||||
}
|
||||
72
vendor/github.com/containerd/cri/pkg/server/service_unix.go
generated
vendored
72
vendor/github.com/containerd/cri/pkg/server/service_unix.go
generated
vendored
@@ -1,72 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/containerd/containerd/sys"
|
||||
cni "github.com/containerd/go-cni"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// networkAttachCount is the minimum number of networks the PodSandbox
|
||||
// attaches to
|
||||
const networkAttachCount = 2
|
||||
|
||||
// initPlatform handles linux specific initialization for the CRI service.
|
||||
func (c *criService) initPlatform() error {
|
||||
var err error
|
||||
|
||||
if sys.RunningInUserNS() {
|
||||
if !(c.config.DisableCgroup && !c.apparmorEnabled() && c.config.RestrictOOMScoreAdj) {
|
||||
logrus.Warn("Running containerd in a user namespace typically requires disable_cgroup, disable_apparmor, restrict_oom_score_adj set to be true")
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.EnableSelinux {
|
||||
if !selinux.GetEnabled() {
|
||||
logrus.Warn("Selinux is not supported")
|
||||
}
|
||||
if r := c.config.SelinuxCategoryRange; r > 0 {
|
||||
selinux.CategoryRange = uint32(r)
|
||||
}
|
||||
} else {
|
||||
selinux.SetDisabled()
|
||||
}
|
||||
|
||||
// Pod needs to attach to at least loopback network and a non host network,
|
||||
// hence networkAttachCount is 2. If there are more network configs the
|
||||
// pod will be attached to all the networks but we will only use the ip
|
||||
// of the default network interface as the pod IP.
|
||||
c.netPlugin, err = cni.New(cni.WithMinNetworkCount(networkAttachCount),
|
||||
cni.WithPluginConfDir(c.config.NetworkPluginConfDir),
|
||||
cni.WithPluginMaxConfNum(c.config.NetworkPluginMaxConfNum),
|
||||
cni.WithPluginDir([]string{c.config.NetworkPluginBinDir}))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to initialize cni")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cniLoadOptions returns cni load options for the linux.
|
||||
func (c *criService) cniLoadOptions() []cni.CNIOpt {
|
||||
return []cni.CNIOpt{cni.WithLoNetwork, cni.WithDefaultConf}
|
||||
}
|
||||
52
vendor/github.com/containerd/cri/pkg/server/service_windows.go
generated
vendored
52
vendor/github.com/containerd/cri/pkg/server/service_windows.go
generated
vendored
@@ -1,52 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 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 (
|
||||
cni "github.com/containerd/go-cni"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// windowsNetworkAttachCount is the minimum number of networks the PodSandbox
|
||||
// attaches to
|
||||
const windowsNetworkAttachCount = 1
|
||||
|
||||
// initPlatform handles linux specific initialization for the CRI service.
|
||||
func (c *criService) initPlatform() error {
|
||||
var err error
|
||||
// For windows, the loopback network is added as default.
|
||||
// There is no need to explicitly add one hence networkAttachCount is 1.
|
||||
// If there are more network configs the pod will be attached to all the
|
||||
// networks but we will only use the ip of the default network interface
|
||||
// as the pod IP.
|
||||
c.netPlugin, err = cni.New(cni.WithMinNetworkCount(windowsNetworkAttachCount),
|
||||
cni.WithPluginConfDir(c.config.NetworkPluginConfDir),
|
||||
cni.WithPluginMaxConfNum(c.config.NetworkPluginMaxConfNum),
|
||||
cni.WithPluginDir([]string{c.config.NetworkPluginBinDir}))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to initialize cni")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cniLoadOptions returns cni load options for the windows.
|
||||
func (c *criService) cniLoadOptions() []cni.CNIOpt {
|
||||
return []cni.CNIOpt{cni.WithDefaultConf}
|
||||
}
|
||||
120
vendor/github.com/containerd/cri/pkg/server/snapshots.go
generated
vendored
120
vendor/github.com/containerd/cri/pkg/server/snapshots.go
generated
vendored
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
snapshot "github.com/containerd/containerd/snapshots"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
snapshotstore "github.com/containerd/cri/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 {
|
||||
ctx := ctrdutil.NamespacedContext()
|
||||
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(ctx, func(ctx context.Context, info snapshot.Info) error {
|
||||
snapshots = append(snapshots, info)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "walk all snapshots failed")
|
||||
}
|
||||
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(ctx, 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
|
||||
}
|
||||
83
vendor/github.com/containerd/cri/pkg/server/status.go
generated
vendored
83
vendor/github.com/containerd/cri/pkg/server/status.go
generated
vendored
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
goruntime "runtime"
|
||||
|
||||
"github.com/containerd/containerd/log"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// networkNotReadyReason is the reason reported when network is not ready.
|
||||
const networkNotReadyReason = "NetworkPluginNotReady"
|
||||
|
||||
// Status returns the status of the runtime.
|
||||
func (c *criService) Status(ctx context.Context, r *runtime.StatusRequest) (*runtime.StatusResponse, error) {
|
||||
// As a containerd plugin, if CRI plugin is serving request,
|
||||
// containerd must be ready.
|
||||
runtimeCondition := &runtime.RuntimeCondition{
|
||||
Type: runtime.RuntimeReady,
|
||||
Status: true,
|
||||
}
|
||||
networkCondition := &runtime.RuntimeCondition{
|
||||
Type: runtime.NetworkReady,
|
||||
Status: true,
|
||||
}
|
||||
// Check the status of the cni initialization
|
||||
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)
|
||||
|
||||
cniConfig, err := json.Marshal(c.netPlugin.GetConfig())
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("Failed to marshal CNI config %v", err)
|
||||
}
|
||||
resp.Info["cniconfig"] = string(cniConfig)
|
||||
|
||||
lastCNILoadStatus := "OK"
|
||||
if lerr := c.cniNetConfMonitor.lastStatus(); lerr != nil {
|
||||
lastCNILoadStatus = lerr.Error()
|
||||
}
|
||||
resp.Info["lastCNILoadStatus"] = lastCNILoadStatus
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
239
vendor/github.com/containerd/cri/pkg/server/streaming.go
generated
vendored
239
vendor/github.com/containerd/cri/pkg/server/streaming.go
generated
vendored
@@ -1,239 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
k8snet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
k8scert "k8s.io/client-go/util/cert"
|
||||
"k8s.io/utils/exec"
|
||||
|
||||
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
|
||||
"github.com/containerd/cri/pkg/streaming"
|
||||
)
|
||||
|
||||
type streamListenerMode int
|
||||
|
||||
const (
|
||||
x509KeyPairTLS streamListenerMode = iota
|
||||
selfSignTLS
|
||||
withoutTLS
|
||||
)
|
||||
|
||||
func getStreamListenerMode(c *criService) (streamListenerMode, error) {
|
||||
if c.config.EnableTLSStreaming {
|
||||
if c.config.X509KeyPairStreaming.TLSCertFile != "" && c.config.X509KeyPairStreaming.TLSKeyFile != "" {
|
||||
return x509KeyPairTLS, nil
|
||||
}
|
||||
if c.config.X509KeyPairStreaming.TLSCertFile != "" && c.config.X509KeyPairStreaming.TLSKeyFile == "" {
|
||||
return -1, errors.New("must set X509KeyPairStreaming.TLSKeyFile")
|
||||
}
|
||||
if c.config.X509KeyPairStreaming.TLSCertFile == "" && c.config.X509KeyPairStreaming.TLSKeyFile != "" {
|
||||
return -1, errors.New("must set X509KeyPairStreaming.TLSCertFile")
|
||||
}
|
||||
return selfSignTLS, nil
|
||||
}
|
||||
if c.config.X509KeyPairStreaming.TLSCertFile != "" {
|
||||
return -1, errors.New("X509KeyPairStreaming.TLSCertFile is set but EnableTLSStreaming is not set")
|
||||
}
|
||||
if c.config.X509KeyPairStreaming.TLSKeyFile != "" {
|
||||
return -1, errors.New("X509KeyPairStreaming.TLSKeyFile is set but EnableTLSStreaming is not set")
|
||||
}
|
||||
return withoutTLS, nil
|
||||
}
|
||||
|
||||
func newStreamServer(c *criService, addr, port, streamIdleTimeout string) (streaming.Server, error) {
|
||||
if addr == "" {
|
||||
a, err := k8snet.ResolveBindAddress(nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get stream server address")
|
||||
}
|
||||
addr = a.String()
|
||||
}
|
||||
config := streaming.DefaultConfig
|
||||
if streamIdleTimeout != "" {
|
||||
var err error
|
||||
config.StreamIdleTimeout, err = time.ParseDuration(streamIdleTimeout)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid stream idle timeout")
|
||||
}
|
||||
}
|
||||
config.Addr = net.JoinHostPort(addr, port)
|
||||
run := newStreamRuntime(c)
|
||||
tlsMode, err := getStreamListenerMode(c)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid stream server configuration")
|
||||
}
|
||||
switch tlsMode {
|
||||
case x509KeyPairTLS:
|
||||
tlsCert, err := tls.LoadX509KeyPair(c.config.X509KeyPairStreaming.TLSCertFile, c.config.X509KeyPairStreaming.TLSKeyFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load x509 key pair for stream server")
|
||||
}
|
||||
config.TLSConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
}
|
||||
return streaming.NewServer(config, run)
|
||||
case selfSignTLS:
|
||||
tlsCert, err := newTLSCert()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate tls certificate for stream server")
|
||||
}
|
||||
config.TLSConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
return streaming.NewServer(config, run)
|
||||
case withoutTLS:
|
||||
return streaming.NewServer(config, run)
|
||||
default:
|
||||
return nil, errors.New("invalid configuration for the stream listener")
|
||||
}
|
||||
}
|
||||
|
||||
type streamRuntime struct {
|
||||
c *criService
|
||||
}
|
||||
|
||||
func newStreamRuntime(c *criService) 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(ctrdutil.NamespacedContext(), containerID, execOptions{
|
||||
cmd: cmd,
|
||||
stdin: stdin,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
tty: tty,
|
||||
resize: resize,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to exec in container")
|
||||
}
|
||||
if *exitCode == 0 {
|
||||
return nil
|
||||
}
|
||||
return &exec.CodeExitError{
|
||||
Err: errors.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(ctrdutil.NamespacedContext(), 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 errors.Errorf("invalid port %d", port)
|
||||
}
|
||||
ctx := ctrdutil.NamespacedContext()
|
||||
return s.c.portForward(ctx, podSandboxID, port, stream)
|
||||
}
|
||||
|
||||
// handleResizing spawns a goroutine that processes the resize channel, calling resizeFunc for each
|
||||
// remotecommand.TerminalSize received from the channel.
|
||||
func handleResizing(ctx context.Context, resize <-chan remotecommand.TerminalSize, resizeFunc func(size remotecommand.TerminalSize)) {
|
||||
if resize == nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer runtime.HandleCrash()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case size, ok := <-resize:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if size.Height < 1 || size.Width < 1 {
|
||||
continue
|
||||
}
|
||||
resizeFunc(size)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// newTLSCert returns a self CA signed tls.certificate.
|
||||
// TODO (mikebrow): replace / rewrite this function to support using CA
|
||||
// signing of the certificate. Requires a security plan for kubernetes regarding
|
||||
// CRI connections / streaming, etc. For example, kubernetes could configure or
|
||||
// require a CA service and pass a configuration down through CRI.
|
||||
func newTLSCert() (tls.Certificate, error) {
|
||||
fail := func(err error) (tls.Certificate, error) { return tls.Certificate{}, err }
|
||||
|
||||
hostName, err := os.Hostname()
|
||||
if err != nil {
|
||||
return fail(errors.Wrap(err, "failed to get hostname"))
|
||||
}
|
||||
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return fail(errors.Wrap(err, "failed to get host IP addresses"))
|
||||
}
|
||||
|
||||
var alternateIPs []net.IP
|
||||
var alternateDNS []string
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
alternateIPs = append(alternateIPs, ip)
|
||||
alternateDNS = append(alternateDNS, ip.String())
|
||||
}
|
||||
|
||||
// Generate a self signed certificate key (CA is self)
|
||||
certPem, keyPem, err := k8scert.GenerateSelfSignedCertKey(hostName, alternateIPs, alternateDNS)
|
||||
if err != nil {
|
||||
return fail(errors.Wrap(err, "certificate key could not be created"))
|
||||
}
|
||||
|
||||
// Load the tls certificate
|
||||
tlsCert, err := tls.X509KeyPair(certPem, keyPem)
|
||||
if err != nil {
|
||||
return fail(errors.Wrap(err, "certificate could not be loaded"))
|
||||
}
|
||||
|
||||
return tlsCert, nil
|
||||
}
|
||||
128
vendor/github.com/containerd/cri/pkg/server/update_runtime_config.go
generated
vendored
128
vendor/github.com/containerd/cri/pkg/server/update_runtime_config.go
generated
vendored
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// cniConfigTemplate contains the values containerd will overwrite
|
||||
// in the cni config template.
|
||||
type cniConfigTemplate struct {
|
||||
// PodCIDR is the cidr for pods on the node.
|
||||
PodCIDR string
|
||||
// PodCIDRRanges is the cidr ranges for pods on the node.
|
||||
PodCIDRRanges []string
|
||||
// Routes is a list of routes configured.
|
||||
Routes []string
|
||||
}
|
||||
|
||||
const (
|
||||
// cniConfigFileName is the name of cni config file generated by containerd.
|
||||
cniConfigFileName = "10-containerd-net.conflist"
|
||||
// zeroCIDRv6 is the null route for IPv6.
|
||||
zeroCIDRv6 = "::/0"
|
||||
// zeroCIDRv4 is the null route for IPv4.
|
||||
zeroCIDRv4 = "0.0.0.0/0"
|
||||
)
|
||||
|
||||
// UpdateRuntimeConfig updates the runtime config. Currently only handles podCIDR updates.
|
||||
func (c *criService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateRuntimeConfigRequest) (*runtime.UpdateRuntimeConfigResponse, error) {
|
||||
podCIDRs := r.GetRuntimeConfig().GetNetworkConfig().GetPodCidr()
|
||||
if podCIDRs == "" {
|
||||
return &runtime.UpdateRuntimeConfigResponse{}, nil
|
||||
}
|
||||
cidrs := strings.Split(podCIDRs, ",")
|
||||
for i := range cidrs {
|
||||
cidrs[i] = strings.TrimSpace(cidrs[i])
|
||||
}
|
||||
routes, err := getRoutes(cidrs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get routes")
|
||||
}
|
||||
|
||||
confTemplate := c.config.NetworkPluginConfTemplate
|
||||
if confTemplate == "" {
|
||||
log.G(ctx).Info("No cni config template is specified, wait for other system components to drop the config.")
|
||||
return &runtime.UpdateRuntimeConfigResponse{}, nil
|
||||
}
|
||||
if err := c.netPlugin.Status(); err == nil {
|
||||
log.G(ctx).Infof("Network plugin is ready, skip generating cni config from template %q", confTemplate)
|
||||
return &runtime.UpdateRuntimeConfigResponse{}, nil
|
||||
} else if err := c.netPlugin.Load(c.cniLoadOptions()...); err == nil {
|
||||
log.G(ctx).Infof("CNI config is successfully loaded, skip generating cni config from template %q", confTemplate)
|
||||
return &runtime.UpdateRuntimeConfigResponse{}, nil
|
||||
}
|
||||
log.G(ctx).Infof("Generating cni config from template %q", confTemplate)
|
||||
// generate cni config file from the template with updated pod cidr.
|
||||
t, err := template.ParseFiles(confTemplate)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse cni config template %q", confTemplate)
|
||||
}
|
||||
if err := os.MkdirAll(c.config.NetworkPluginConfDir, 0755); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create cni config directory: %q", c.config.NetworkPluginConfDir)
|
||||
}
|
||||
confFile := filepath.Join(c.config.NetworkPluginConfDir, cniConfigFileName)
|
||||
f, err := os.OpenFile(confFile, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to open cni config file %q", confFile)
|
||||
}
|
||||
defer f.Close()
|
||||
if err := t.Execute(f, cniConfigTemplate{
|
||||
PodCIDR: cidrs[0],
|
||||
PodCIDRRanges: cidrs,
|
||||
Routes: routes,
|
||||
}); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to generate cni config file %q", confFile)
|
||||
}
|
||||
return &runtime.UpdateRuntimeConfigResponse{}, nil
|
||||
}
|
||||
|
||||
// getRoutes generates required routes for the passed in cidrs.
|
||||
func getRoutes(cidrs []string) ([]string, error) {
|
||||
var (
|
||||
routes []string
|
||||
hasV4, hasV6 bool
|
||||
)
|
||||
for _, c := range cidrs {
|
||||
_, cidr, err := net.ParseCIDR(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cidr.IP.To4() != nil {
|
||||
hasV4 = true
|
||||
} else {
|
||||
hasV6 = true
|
||||
}
|
||||
}
|
||||
if hasV4 {
|
||||
routes = append(routes, zeroCIDRv4)
|
||||
}
|
||||
if hasV6 {
|
||||
routes = append(routes, zeroCIDRv6)
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
42
vendor/github.com/containerd/cri/pkg/server/version.go
generated
vendored
42
vendor/github.com/containerd/cri/pkg/server/version.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"github.com/containerd/containerd/version"
|
||||
"golang.org/x/net/context"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
"github.com/containerd/cri/pkg/constants"
|
||||
)
|
||||
|
||||
const (
|
||||
containerName = "containerd"
|
||||
// kubeAPIVersion is the api version of kubernetes.
|
||||
// TODO(random-liu): Change this to actual CRI version.
|
||||
kubeAPIVersion = "0.1.0"
|
||||
)
|
||||
|
||||
// Version returns the runtime name, runtime version and runtime API version.
|
||||
func (c *criService) Version(ctx context.Context, r *runtime.VersionRequest) (*runtime.VersionResponse, error) {
|
||||
return &runtime.VersionResponse{
|
||||
Version: kubeAPIVersion,
|
||||
RuntimeName: containerName,
|
||||
RuntimeVersion: version.Version,
|
||||
RuntimeApiVersion: constants.CRIVersion,
|
||||
}, nil
|
||||
}
|
||||
71
vendor/github.com/containerd/cri/pkg/seutil/seutil.go
generated
vendored
71
vendor/github.com/containerd/cri/pkg/seutil/seutil.go
generated
vendored
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
Copyright 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 seutil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
)
|
||||
|
||||
var seTypes map[string]struct{}
|
||||
|
||||
const typePath = "/etc/selinux/targeted/contexts/customizable_types"
|
||||
|
||||
func init() {
|
||||
seTypes = make(map[string]struct{})
|
||||
if !selinux.GetEnabled() {
|
||||
return
|
||||
}
|
||||
f, err := os.Open(typePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
seTypes[s.Text()] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// HasType returns true if the underlying system has the
|
||||
// provided selinux type enabled.
|
||||
func HasType(name string) bool {
|
||||
_, ok := seTypes[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ChangeToKVM process label
|
||||
func ChangeToKVM(l string) (string, error) {
|
||||
if l == "" || !selinux.GetEnabled() {
|
||||
return "", nil
|
||||
}
|
||||
proc, _ := selinux.KVMContainerLabels()
|
||||
selinux.ReleaseLabel(proc)
|
||||
|
||||
current, err := selinux.NewContext(l)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
next, err := selinux.NewContext(proc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
current["type"] = next["type"]
|
||||
return current.Get(), nil
|
||||
}
|
||||
177
vendor/github.com/containerd/cri/pkg/store/container/container.go
generated
vendored
177
vendor/github.com/containerd/cri/pkg/store/container/container.go
generated
vendored
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
Copyright 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 container
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/cri/pkg/store/label"
|
||||
"github.com/docker/docker/pkg/truncindex"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
|
||||
cio "github.com/containerd/cri/pkg/server/io"
|
||||
"github.com/containerd/cri/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 could only be nil when the container is in unknown state.
|
||||
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.NewStopCh(),
|
||||
}
|
||||
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
|
||||
labels *label.Store
|
||||
}
|
||||
|
||||
// NewStore creates a container store.
|
||||
func NewStore(labels *label.Store) *Store {
|
||||
return &Store{
|
||||
containers: make(map[string]Container),
|
||||
idIndex: truncindex.NewTruncIndex([]string{}),
|
||||
labels: labels,
|
||||
}
|
||||
}
|
||||
|
||||
// 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.labels.Reserve(c.ProcessLabel); err != nil {
|
||||
return err
|
||||
}
|
||||
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.labels.Release(s.containers[id].ProcessLabel)
|
||||
s.idIndex.Delete(id) // nolint: errcheck
|
||||
delete(s.containers, id)
|
||||
}
|
||||
62
vendor/github.com/containerd/cri/pkg/store/container/fake_status.go
generated
vendored
62
vendor/github.com/containerd/cri/pkg/store/container/fake_status.go
generated
vendored
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
Copyright 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 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
|
||||
}
|
||||
89
vendor/github.com/containerd/cri/pkg/store/container/metadata.go
generated
vendored
89
vendor/github.com/containerd/cri/pkg/store/container/metadata.go
generated
vendored
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
Copyright 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 container
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/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
|
||||
// StopSignal is the system call signal that will be sent to the container to exit.
|
||||
// TODO(random-liu): Add integration test for stop signal.
|
||||
StopSignal string
|
||||
// ProcessLabel is the SELinux process label for the container
|
||||
ProcessLabel 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 errors.Errorf("unsupported version: %q", versioned.Version)
|
||||
}
|
||||
247
vendor/github.com/containerd/cri/pkg/store/container/status.go
generated
vendored
247
vendor/github.com/containerd/cri/pkg/store/container/status.go
generated
vendored
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
Copyright 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 container
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/continuity"
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// The container state machine in the CRI plugin:
|
||||
//
|
||||
// + +
|
||||
// | |
|
||||
// | Create | Load
|
||||
// | |
|
||||
// +----v----+ |
|
||||
// | | |
|
||||
// | CREATED <---------+-----------+
|
||||
// | | | |
|
||||
// +----+----- | |
|
||||
// | | |
|
||||
// | Start | |
|
||||
// | | |
|
||||
// +----v----+ | |
|
||||
// Exec +--------+ | | |
|
||||
// Attach | | RUNNING <---------+ |
|
||||
// LogReopen +--------> | | |
|
||||
// +----+----+ | |
|
||||
// | | |
|
||||
// | Stop/Exit | |
|
||||
// | | |
|
||||
// +----v----+ | |
|
||||
// | <---------+ +----v----+
|
||||
// | EXITED | | |
|
||||
// | <----------------+ UNKNOWN |
|
||||
// +----+----+ Stop | |
|
||||
// | +---------+
|
||||
// | Remove
|
||||
// v
|
||||
// DELETED
|
||||
|
||||
// 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
|
||||
// Starting indicates that the container is in starting state.
|
||||
// This field doesn't need to be checkpointed.
|
||||
Starting bool `json:"-"`
|
||||
// Removing indicates that the container is in removing state.
|
||||
// This field doesn't need to be checkpointed.
|
||||
Removing bool `json:"-"`
|
||||
// Unknown indicates that the container status is not fully loaded.
|
||||
// This field doesn't need to be checkpointed.
|
||||
Unknown bool `json:"-"`
|
||||
}
|
||||
|
||||
// State returns current state of the container based on the container status.
|
||||
func (s Status) State() runtime.ContainerState {
|
||||
if s.Unknown {
|
||||
return runtime.ContainerState_CONTAINER_UNKNOWN
|
||||
}
|
||||
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 errors.New("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, errors.Wrap(err, "failed to encode status")
|
||||
}
|
||||
path := filepath.Join(root, "status")
|
||||
if err := continuity.AtomicWriteFile(path, data, 0600); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to checkpoint status to %q", path)
|
||||
}
|
||||
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{}, errors.Wrapf(err, "failed to read status from %q", path)
|
||||
}
|
||||
var status Status
|
||||
if err := status.decode(data); err != nil {
|
||||
return Status{}, errors.Wrapf(err, "failed to decode status %q", data)
|
||||
}
|
||||
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 errors.Wrap(err, "failed to encode status")
|
||||
}
|
||||
if err := continuity.AtomicWriteFile(s.path, data, 0600); err != nil {
|
||||
return errors.Wrapf(err, "failed to checkpoint status to %q", s.path)
|
||||
}
|
||||
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)
|
||||
}
|
||||
33
vendor/github.com/containerd/cri/pkg/store/errors.go
generated
vendored
33
vendor/github.com/containerd/cri/pkg/store/errors.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
Copyright 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 store
|
||||
|
||||
import "github.com/containerd/containerd/errdefs"
|
||||
|
||||
var (
|
||||
// ErrAlreadyExist is the error returned when data added in the store
|
||||
// already exists.
|
||||
//
|
||||
// This error has been DEPRECATED and will be removed in 1.5. Please switch
|
||||
// usage directly to `errdefs.ErrAlreadyExists`.
|
||||
ErrAlreadyExist = errdefs.ErrAlreadyExists
|
||||
// ErrNotExist is the error returned when data is not in the store.
|
||||
//
|
||||
// This error has been DEPRECATED and will be removed in 1.5. Please switch
|
||||
// usage directly to `errdefs.ErrNotFound`.
|
||||
ErrNotExist = errdefs.ErrNotFound
|
||||
)
|
||||
34
vendor/github.com/containerd/cri/pkg/store/image/fake_image.go
generated
vendored
34
vendor/github.com/containerd/cri/pkg/store/image/fake_image.go
generated
vendored
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
Copyright 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 image
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// NewFakeStore returns an image store with predefined images.
|
||||
// Update is not allowed for this fake store.
|
||||
func NewFakeStore(images []Image) (*Store, error) {
|
||||
s := NewStore(nil)
|
||||
for _, i := range images {
|
||||
for _, ref := range i.References {
|
||||
s.refCache[ref] = i.ID
|
||||
}
|
||||
if err := s.store.add(i); err != nil {
|
||||
return nil, errors.Wrapf(err, "add image %+v", i)
|
||||
}
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
256
vendor/github.com/containerd/cri/pkg/store/image/image.go
generated
vendored
256
vendor/github.com/containerd/cri/pkg/store/image/image.go
generated
vendored
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
Copyright 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 image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
imagedigest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/go-digest/digestset"
|
||||
imageidentity "github.com/opencontainers/image-spec/identity"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
storeutil "github.com/containerd/cri/pkg/store"
|
||||
"github.com/containerd/cri/pkg/util"
|
||||
)
|
||||
|
||||
// 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
|
||||
// References are references to the image, e.g. RepoTag and RepoDigest.
|
||||
References []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
|
||||
}
|
||||
|
||||
// Store stores all images.
|
||||
type Store struct {
|
||||
lock sync.RWMutex
|
||||
// refCache is a containerd image reference to image id cache.
|
||||
refCache map[string]string
|
||||
// client is the containerd client.
|
||||
client *containerd.Client
|
||||
// store is the internal image store indexed by image id.
|
||||
store *store
|
||||
}
|
||||
|
||||
// NewStore creates an image store.
|
||||
func NewStore(client *containerd.Client) *Store {
|
||||
return &Store{
|
||||
refCache: make(map[string]string),
|
||||
client: client,
|
||||
store: &store{
|
||||
images: make(map[string]Image),
|
||||
digestSet: digestset.NewSet(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates cache for a reference.
|
||||
func (s *Store) Update(ctx context.Context, ref string) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
i, err := s.client.GetImage(ctx, ref)
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return errors.Wrap(err, "get image from containerd")
|
||||
}
|
||||
var img *Image
|
||||
if err == nil {
|
||||
img, err = getImage(ctx, i)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get image info from containerd")
|
||||
}
|
||||
}
|
||||
return s.update(ref, img)
|
||||
}
|
||||
|
||||
// update updates the internal cache. img == nil means that
|
||||
// the image does not exist in containerd.
|
||||
func (s *Store) update(ref string, img *Image) error {
|
||||
oldID, oldExist := s.refCache[ref]
|
||||
if img == nil {
|
||||
// The image reference doesn't exist in containerd.
|
||||
if oldExist {
|
||||
// Remove the reference from the store.
|
||||
s.store.delete(oldID, ref)
|
||||
delete(s.refCache, ref)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if oldExist {
|
||||
if oldID == img.ID {
|
||||
return nil
|
||||
}
|
||||
// Updated. Remove tag from old image.
|
||||
s.store.delete(oldID, ref)
|
||||
}
|
||||
// New image. Add new image.
|
||||
s.refCache[ref] = img.ID
|
||||
return s.store.add(*img)
|
||||
}
|
||||
|
||||
// getImage gets image information from containerd.
|
||||
func getImage(ctx context.Context, i containerd.Image) (*Image, error) {
|
||||
// Get image information.
|
||||
diffIDs, err := i.RootFS(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get image diffIDs")
|
||||
}
|
||||
chainID := imageidentity.ChainID(diffIDs)
|
||||
|
||||
size, err := i.Size(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get image compressed resource size")
|
||||
}
|
||||
|
||||
desc, err := i.Config(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get image config descriptor")
|
||||
}
|
||||
id := desc.Digest.String()
|
||||
|
||||
rb, err := content.ReadBlob(ctx, i.ContentStore(), desc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "read image config from content store")
|
||||
}
|
||||
var ociimage imagespec.Image
|
||||
if err := json.Unmarshal(rb, &ociimage); err != nil {
|
||||
return nil, errors.Wrapf(err, "unmarshal image config %s", rb)
|
||||
}
|
||||
|
||||
return &Image{
|
||||
ID: id,
|
||||
References: []string{i.Name()},
|
||||
ChainID: chainID.String(),
|
||||
Size: size,
|
||||
ImageSpec: ociimage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Resolve resolves a image reference to image id.
|
||||
func (s *Store) Resolve(ref string) (string, error) {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
id, ok := s.refCache[ref]
|
||||
if !ok {
|
||||
return "", storeutil.ErrNotExist
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Get gets image metadata by image id. The id can be truncated.
|
||||
// Returns various validation errors if the image id is invalid.
|
||||
// Returns storeutil.ErrNotExist if the image doesn't exist.
|
||||
func (s *Store) Get(id string) (Image, error) {
|
||||
return s.store.get(id)
|
||||
}
|
||||
|
||||
// List lists all images.
|
||||
func (s *Store) List() []Image {
|
||||
return s.store.list()
|
||||
}
|
||||
|
||||
type store struct {
|
||||
lock sync.RWMutex
|
||||
images map[string]Image
|
||||
digestSet *digestset.Set
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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(imagedigest.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 references.
|
||||
i.References = util.MergeStringSlices(i.References, img.References)
|
||||
s.images[img.ID] = i
|
||||
return nil
|
||||
}
|
||||
|
||||
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 = storeutil.ErrNotExist
|
||||
}
|
||||
return Image{}, err
|
||||
}
|
||||
if i, ok := s.images[digest.String()]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return Image{}, storeutil.ErrNotExist
|
||||
}
|
||||
|
||||
func (s *store) delete(id, ref 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
|
||||
}
|
||||
i, ok := s.images[digest.String()]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
i.References = util.SubtractStringSlice(i.References, ref)
|
||||
if len(i.References) != 0 {
|
||||
s.images[digest.String()] = i
|
||||
return
|
||||
}
|
||||
// Remove the image if it is not referenced any more.
|
||||
s.digestSet.Remove(digest) // nolint: errcheck
|
||||
delete(s.images, digest.String())
|
||||
}
|
||||
90
vendor/github.com/containerd/cri/pkg/store/label/label.go
generated
vendored
90
vendor/github.com/containerd/cri/pkg/store/label/label.go
generated
vendored
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
Copyright 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 label
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
sync.Mutex
|
||||
levels map[string]int
|
||||
Releaser func(string)
|
||||
Reserver func(string)
|
||||
}
|
||||
|
||||
func NewStore() *Store {
|
||||
return &Store{
|
||||
levels: map[string]int{},
|
||||
Releaser: selinux.ReleaseLabel,
|
||||
Reserver: selinux.ReserveLabel,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) Reserve(label string) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
context, err := selinux.NewContext(label)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
level := context["level"]
|
||||
// no reason to count empty
|
||||
if level == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, ok := s.levels[level]; !ok {
|
||||
s.Reserver(label)
|
||||
}
|
||||
|
||||
s.levels[level]++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) Release(label string) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
context, err := selinux.NewContext(label)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
level := context["level"]
|
||||
if level == "" {
|
||||
return
|
||||
}
|
||||
|
||||
count, ok := s.levels[level]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case count == 1:
|
||||
s.Releaser(label)
|
||||
delete(s.levels, level)
|
||||
case count < 1:
|
||||
delete(s.levels, level)
|
||||
case count > 1:
|
||||
s.levels[level] = count - 1
|
||||
}
|
||||
}
|
||||
89
vendor/github.com/containerd/cri/pkg/store/sandbox/metadata.go
generated
vendored
89
vendor/github.com/containerd/cri/pkg/store/sandbox/metadata.go
generated
vendored
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
Copyright 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 (
|
||||
"encoding/json"
|
||||
|
||||
cni "github.com/containerd/go-cni"
|
||||
"github.com/pkg/errors"
|
||||
runtime "k8s.io/cri-api/pkg/apis/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
|
||||
// AdditionalIPs of the Pod if it is attached to non host network
|
||||
AdditionalIPs []string
|
||||
// RuntimeHandler is the runtime handler name of the pod.
|
||||
RuntimeHandler string
|
||||
// CNIresult resulting configuration for attached network namespace interfaces
|
||||
CNIResult *cni.CNIResult
|
||||
// ProcessLabel is the SELinux process label for the container
|
||||
ProcessLabel 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 errors.Errorf("unsupported version: %q", versioned.Version)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user