Update to cri v1.0.0-rc.0

Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu
2018-03-26 22:20:45 +00:00
parent 2aa2aecbb3
commit 8958b489ba
173 changed files with 11085 additions and 5687 deletions

View File

@@ -1,31 +1,43 @@
# cri-containerd
# cri
<p align="center">
<img src="https://github.com/kubernetes/kubernetes/blob/master/logo/logo.png" width="50" height="50">
<img src="https://github.com/containerd/containerd/blob/master/docs/images/containerd-dark.png" width="200" >
</p>
[![Build Status](https://api.travis-ci.org/containerd/cri-containerd.svg?style=flat-square)](https://travis-ci.org/containerd/cri-containerd)
[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/cri-containerd?style=flat-square)](https://goreportcard.com/report/github.com/containerd/cri-containerd)
*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`.*
`cri-containerd` is a [containerd](https://containerd.io/) based implementation of Kubernetes [container runtime interface (CRI)](https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto).
*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.*
[![Build Status](https://api.travis-ci.org/containerd/cri.svg?style=flat-square)](https://travis-ci.org/containerd/cri)
[![Go Report Card](https://goreportcard.com/badge/github.com/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/kubernetes/blob/master/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto).
With it, you could run Kubernetes using containerd as the container runtime.
![cri-containerd](./docs/cri-containerd.png)
![cri](./docs/cri.png)
## Current Status
`cri-containerd` is in beta:
`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 beta version) works with Kubernetes >= 1.9.
* 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/cri-validation.md).
* It has passed all regular [node e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-node-tests.md).
* It has passed all regular [e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-tests.md).
* It has passed all [node e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-node-tests.md).
* It has passed all [e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-tests.md).
See [test dashboard](https://k8s-testgrid.appspot.com/sig-node-containerd)
## Support Metrics
| CRI-Containerd Version | Kubernetes Version |
|:----------------------:|:------------------:|
| v1.0.0-alpha.x | 1.7, 1.8 |
| v1.0.0-beta.x | 1.9 |
| HEAD | 1.10+ |
| 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 | 1.10+ | v1alpha2 |
| | HEAD | 1.10+ | 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
@@ -35,33 +47,33 @@ For non ansible users, you can download the `cri-containerd` release tarball and
kubernetes cluster using kubeadm as described [here](docs/installation.md).
## Getting Started for Developers
### Binary Dependencies and Specifications
The current release of `cri-containerd` has the following dependencies:
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-containerd` is tested with.
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-containerd` with these releases on a
weekly/monthly basis. Similarly, given that `cri-containerd` uses the Open
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-containerd` to the latest releases of these
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-containerd and runc seccomp support. `libseccomp-dev` (Ubuntu, Debian) / `libseccomp-devel`
* **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.
* **libapparmor development library.** Required by cri-containerd and runc apparmor support. To use apparmor on Debian, Ubuntu, and related distributions the installation of `libapparmor-dev` is required.
* **libapparmor development library.** Required by `cri` and runc apparmor support. To use apparmor on Debian, Ubuntu, and related distributions the installation of `libapparmor-dev` is required.
* **btrfs development library.** Required by containerd btrfs support. `btrfs-tools`(Ubuntu, Debian) / `btrfs-progs-devel`(Fedora, CentOS, RHEL)
2. Install other dependencies:
* **`nsenter`**: Required by CNI and portforward.
* **`nsenter`**: Required by portforward.
* **`socat`**: Required by portforward.
3. Install and setup a go 1.10 development environment.
4. Make a local clone of this repository.
5. Install binary dependencies by running the following command from your cloned `cri-containerd/` project directory:
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
@@ -69,15 +81,18 @@ backport version of `libseccomp-dev` is required. See [travis.yml](.travis.yml)
# `cni`, please follow instructions in their documents.
make install.deps
```
### Build and Install cri-containerd
To build and install `cri-containerd` enter the following commands from your `cri-containerd` project directory:
### 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-containerd` supports optional build tags for compiling support of various features.
To add build tags to the make option the `BUILDTAGS` variable must be set.
`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'
@@ -88,31 +103,28 @@ make BUILD_TAGS='seccomp apparmor'
| seccomp | syscall filtering | libseccomp development library |
| selinux | selinux process and mount labeling | <none> |
| apparmor | apparmor profile support | libapparmor development library |
### Validate Your cri-containerd Setup
### Validate Your `cri` Setup
A Kubernetes incubator project called [cri-tools](https://github.com/kubernetes-incubator/cri-tools)
includes programs for exercising CRI implementations such as `cri-containerd`.
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/cri-validation.md).
Run the CRI Validation test to validate your installation of `cri-containerd`:
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-containerd` in a local cluster:
version, you can try `cri` in a local cluster:
1. Start `containerd` as root in a first terminal:
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. Start `cri-containerd` as root in a second terminal:
2. From the Kubernetes project directory startup a local cluster using `containerd`:
```bash
sudo cri-containerd
```
3. From the Kubernetes project directory startup a local cluster using `cri-containerd`:
```bash
CONTAINER_RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT='/var/run/cri-containerd.sock' ./hack/local-up-cluster.sh
CONTAINER_RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT='/run/containerd/containerd.sock' ./hack/local-up-cluster.sh
```
### Test
See [here](./docs/testing.md) for information about test.

View File

@@ -49,9 +49,9 @@ var loadCommand = cli.Command{
timeout = context.GlobalDuration("timeout")
cancel gocontext.CancelFunc
)
cl, err := client.NewCRIContainerdClient(address, timeout)
cl, err := client.NewCRIPluginClient(address, timeout)
if err != nil {
return fmt.Errorf("failed to create grpc client: %v", err)
return errors.Wrap(err, "failed to create grpc client")
}
if timeout > 0 {
ctx, cancel = gocontext.WithTimeout(gocontext.Background(), timeout)

View File

@@ -63,13 +63,11 @@ func initCRIService(ic *plugin.InitContext) (interface{}, error) {
ctx := ic.Context
pluginConfig := ic.Config.(*criconfig.PluginConfig)
c := criconfig.Config{
PluginConfig: *pluginConfig,
// This is a hack. We assume that containerd root directory
// is one level above plugin directory.
// TODO(random-liu): Expose containerd config to plugin.
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)
@@ -92,7 +90,7 @@ func initCRIService(ic *plugin.InitContext) (interface{}, error) {
return nil, errors.Wrap(err, "failed to create containerd client")
}
s, err := server.NewCRIContainerdService(c, client)
s, err := server.NewCRIService(c, client)
if err != nil {
return nil, errors.Wrap(err, "failed to create CRI service")
}

View File

@@ -31,4 +31,8 @@ const (
// SandboxID is the sandbox ID annotation
SandboxID = "io.kubernetes.cri.sandbox-id"
// 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"
)

View File

@@ -1,5 +1,5 @@
/*
Copyright 2017 The Kubernetes Authors.
Copyright 2018 The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by protoc-gen-gogo.
// source: api.proto
// DO NOT EDIT!
@@ -101,66 +100,66 @@ var _ grpc.ClientConn
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for CRIContainerdService service
// Client API for CRIPluginService service
type CRIContainerdServiceClient interface {
type CRIPluginServiceClient interface {
// LoadImage loads a image into containerd.
LoadImage(ctx context.Context, in *LoadImageRequest, opts ...grpc.CallOption) (*LoadImageResponse, error)
}
type cRIContainerdServiceClient struct {
type cRIPluginServiceClient struct {
cc *grpc.ClientConn
}
func NewCRIContainerdServiceClient(cc *grpc.ClientConn) CRIContainerdServiceClient {
return &cRIContainerdServiceClient{cc}
func NewCRIPluginServiceClient(cc *grpc.ClientConn) CRIPluginServiceClient {
return &cRIPluginServiceClient{cc}
}
func (c *cRIContainerdServiceClient) LoadImage(ctx context.Context, in *LoadImageRequest, opts ...grpc.CallOption) (*LoadImageResponse, error) {
func (c *cRIPluginServiceClient) LoadImage(ctx context.Context, in *LoadImageRequest, opts ...grpc.CallOption) (*LoadImageResponse, error) {
out := new(LoadImageResponse)
err := grpc.Invoke(ctx, "/api.v1.CRIContainerdService/LoadImage", in, out, c.cc, opts...)
err := grpc.Invoke(ctx, "/api.v1.CRIPluginService/LoadImage", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for CRIContainerdService service
// Server API for CRIPluginService service
type CRIContainerdServiceServer interface {
type CRIPluginServiceServer interface {
// LoadImage loads a image into containerd.
LoadImage(context.Context, *LoadImageRequest) (*LoadImageResponse, error)
}
func RegisterCRIContainerdServiceServer(s *grpc.Server, srv CRIContainerdServiceServer) {
s.RegisterService(&_CRIContainerdService_serviceDesc, srv)
func RegisterCRIPluginServiceServer(s *grpc.Server, srv CRIPluginServiceServer) {
s.RegisterService(&_CRIPluginService_serviceDesc, srv)
}
func _CRIContainerdService_LoadImage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
func _CRIPluginService_LoadImage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LoadImageRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CRIContainerdServiceServer).LoadImage(ctx, in)
return srv.(CRIPluginServiceServer).LoadImage(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.v1.CRIContainerdService/LoadImage",
FullMethod: "/api.v1.CRIPluginService/LoadImage",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CRIContainerdServiceServer).LoadImage(ctx, req.(*LoadImageRequest))
return srv.(CRIPluginServiceServer).LoadImage(ctx, req.(*LoadImageRequest))
}
return interceptor(ctx, in, info, handler)
}
var _CRIContainerdService_serviceDesc = grpc.ServiceDesc{
ServiceName: "api.v1.CRIContainerdService",
HandlerType: (*CRIContainerdServiceServer)(nil),
var _CRIPluginService_serviceDesc = grpc.ServiceDesc{
ServiceName: "api.v1.CRIPluginService",
HandlerType: (*CRIPluginServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "LoadImage",
Handler: _CRIContainerdService_LoadImage_Handler,
Handler: _CRIPluginService_LoadImage_Handler,
},
},
Streams: []grpc.StreamDesc{},
@@ -580,7 +579,7 @@ var (
func init() { proto.RegisterFile("api.proto", fileDescriptorApi) }
var fileDescriptorApi = []byte{
// 223 bytes of a gzipped FileDescriptorProto
// 219 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x2c, 0xc8, 0xd4,
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x03, 0x31, 0xcb, 0x0c, 0xa5, 0x74, 0xd3, 0x33, 0x4b,
0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0xd3, 0xf3, 0xd3, 0xf3, 0xf5, 0xc1, 0xd2, 0x49,
@@ -588,11 +587,11 @@ var fileDescriptorApi = []byte{
0xa6, 0x78, 0xe6, 0x26, 0xa6, 0xa7, 0x06, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0x49, 0x71,
0x71, 0xb8, 0x65, 0xe6, 0xa4, 0x06, 0x24, 0x96, 0x64, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06,
0xc1, 0xf9, 0x4a, 0xda, 0x5c, 0x82, 0x48, 0xea, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0xc4,
0xb8, 0xd8, 0xc0, 0x02, 0xc5, 0x12, 0x8c, 0x0a, 0xcc, 0x1a, 0x9c, 0x41, 0x50, 0x9e, 0x51, 0x14,
0x97, 0x88, 0x73, 0x90, 0xa7, 0x73, 0x7e, 0x5e, 0x49, 0x62, 0x66, 0x5e, 0x6a, 0x51, 0x4a, 0x70,
0x6a, 0x51, 0x59, 0x66, 0x72, 0xaa, 0x90, 0x13, 0x17, 0x27, 0xdc, 0x10, 0x21, 0x09, 0x3d, 0x88,
0xcb, 0xf5, 0xd0, 0xdd, 0x21, 0x25, 0x89, 0x45, 0x06, 0x62, 0xa3, 0x12, 0x83, 0x93, 0xcc, 0x89,
0x87, 0x72, 0x8c, 0x37, 0x1e, 0xca, 0x31, 0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1,
0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0x48, 0x62, 0x03, 0xfb,
0xce, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x6a, 0xfe, 0x35, 0x81, 0x21, 0x01, 0x00, 0x00,
0xb8, 0xd8, 0xc0, 0x02, 0xc5, 0x12, 0x8c, 0x0a, 0xcc, 0x1a, 0x9c, 0x41, 0x50, 0x9e, 0x51, 0x18,
0x97, 0x80, 0x73, 0x90, 0x67, 0x40, 0x4e, 0x69, 0x7a, 0x66, 0x5e, 0x70, 0x6a, 0x51, 0x59, 0x66,
0x72, 0xaa, 0x90, 0x13, 0x17, 0x27, 0xdc, 0x00, 0x21, 0x09, 0x3d, 0x88, 0xab, 0xf5, 0xd0, 0xdd,
0x20, 0x25, 0x89, 0x45, 0x06, 0x62, 0x9b, 0x12, 0x83, 0x93, 0xcc, 0x89, 0x87, 0x72, 0x8c, 0x37,
0x1e, 0xca, 0x31, 0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6,
0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0x48, 0x62, 0x03, 0xfb, 0xcc, 0x18, 0x10, 0x00,
0x00, 0xff, 0xff, 0xfc, 0x6f, 0xec, 0xf4, 0x1d, 0x01, 0x00, 0x00,
}

View File

@@ -13,8 +13,8 @@ option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.goproto_unrecognized_all) = false;
// CRIContainerdService defines non-CRI APIs for cri-containerd.
service CRIContainerdService{
// CRIPluginService defines non-CRI APIs for cri plugin.
service CRIPluginService{
// LoadImage loads a image into containerd.
rpc LoadImage(LoadImageRequest) returns (LoadImageResponse) {}
}

View File

@@ -17,21 +17,21 @@ limitations under the License.
package client
import (
"fmt"
"time"
"github.com/pkg/errors"
"google.golang.org/grpc"
"k8s.io/kubernetes/pkg/kubelet/util"
api "github.com/containerd/cri/pkg/api/v1"
)
// NewCRIContainerdClient creates grpc client of cri-containerd
// NewCRIPluginClient creates grpc client of cri plugin
// TODO(random-liu): Wrap grpc functions.
func NewCRIContainerdClient(endpoint string, timeout time.Duration) (api.CRIContainerdServiceClient, error) {
func NewCRIPluginClient(endpoint string, timeout time.Duration) (api.CRIPluginServiceClient, error) {
addr, dialer, err := util.GetAddressAndDialer(endpoint)
if err != nil {
return nil, fmt.Errorf("failed to get dialer: %v", err)
return nil, errors.Wrap(err, "failed to get dialer")
}
conn, err := grpc.Dial(addr,
grpc.WithBlock(),
@@ -41,7 +41,7 @@ func NewCRIContainerdClient(endpoint string, timeout time.Duration) (api.CRICont
grpc.WithDialer(dialer),
)
if err != nil {
return nil, fmt.Errorf("failed to dial: %v", err)
return nil, errors.Wrap(err, "failed to dial")
}
return api.NewCRIContainerdServiceClient(conn), nil
return api.NewCRIPluginServiceClient(conn), nil
}

View File

@@ -18,35 +18,40 @@ package config
import "github.com/containerd/containerd"
// 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.
Engine string `toml:"runtime_engine" json:"runtimeEngine"`
// Root is the directory used by containerd for runtime state.
Root string `toml:"runtime_root" json:"runtimeRoot"`
}
// ContainerdConfig contains toml config related to containerd
type ContainerdConfig struct {
// Snapshotter is the snapshotter used by containerd.
Snapshotter string `toml:"snapshotter" json:"snapshotter,omitempty"`
// Runtime is the runtime to use in containerd. We may support
// other runtimes in the future.
Runtime string `toml:"runtime" json:"runtime,omitempty"`
// RuntimeEngine is the name of the runtime engine used by containerd.
// Containerd default should be "runc"
// We may support other runtime engines in the future.
RuntimeEngine string `toml:"runtime_engine" json:"runtimeEngine,omitempty"`
// RuntimeRoot is the directory used by containerd for runtime state.
// Containerd default should be "/run/containerd/runc"
RuntimeRoot string `toml:"runtime_root" json:"runtimeRoot,omitempty"`
Snapshotter string `toml:"snapshotter" json:"snapshotter"`
// DefaultRuntime is the runtime to use in containerd.
DefaultRuntime Runtime `toml:"default_runtime" json:"defaultRuntime"`
// UntrustedWorkloadRuntime is a runtime to run untrusted workloads on it.
UntrustedWorkloadRuntime Runtime `toml:"untrusted_workload_runtime" json:"untrustedWorkloadRuntime"`
}
// CniConfig contains toml config related to cni
type CniConfig struct {
// NetworkPluginBinDir is the directory in which the binaries for the plugin is kept.
NetworkPluginBinDir string `toml:"bin_dir" json:"binDir,omitempty"`
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,omitempty"`
NetworkPluginConfDir string `toml:"conf_dir" json:"confDir"`
}
// 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.
Endpoints []string `toml:"endpoint" json:"endpoint,omitempty"`
Endpoints []string `toml:"endpoint" json:"endpoint"`
// TODO (Abhi) We might need to add auth per namespace. Looks like
// image auth information is passed by kube itself.
}
@@ -54,33 +59,30 @@ type Mirror struct {
// 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,omitempty"`
Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors"`
}
// PluginConfig contains toml config related to CRI plugin,
// it is a subset of Config.
type PluginConfig struct {
// ContainerdConfig contains config related to containerd
ContainerdConfig `toml:"containerd" json:"containerd,omitempty"`
ContainerdConfig `toml:"containerd" json:"containerd"`
// CniConfig contains config related to cni
CniConfig `toml:"cni" json:"cni,omitempty"`
CniConfig `toml:"cni" json:"cni"`
// Registry contains config related to the registry
Registry `toml:"registry" json:"registry,omitempty"`
Registry `toml:"registry" json:"registry"`
// StreamServerAddress is the ip address streaming server is listening on.
StreamServerAddress string `toml:"stream_server_address" json:"streamServerAddress,omitempty"`
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,omitempty"`
StreamServerPort string `toml:"stream_server_port" json:"streamServerPort"`
// EnableSelinux indicates to enable the selinux support.
EnableSelinux bool `toml:"enable_selinux" json:"enableSelinux,omitempty"`
EnableSelinux bool `toml:"enable_selinux" json:"enableSelinux"`
// SandboxImage is the image used by sandbox container.
SandboxImage string `toml:"sandbox_image" json:"sandboxImage,omitempty"`
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,omitempty"`
StatsCollectPeriod int `toml:"stats_collect_period" json:"statsCollectPeriod"`
// SystemdCgroup enables systemd cgroup support.
SystemdCgroup bool `toml:"systemd_cgroup" json:"systemdCgroup,omitempty"`
// EnableIPv6DAD enables IPv6 DAD.
// TODO(random-liu): Use optimistic_dad when it's GA.
EnableIPv6DAD bool `toml:"enable_ipv6_dad" json:"enableIPv6DAD,omitempty"`
SystemdCgroup bool `toml:"systemd_cgroup" json:"systemdCgroup"`
}
// Config contains all configurations for cri server.
@@ -88,12 +90,14 @@ type Config struct {
// PluginConfig is the config for CRI plugin.
PluginConfig
// ContainerdRootDir is the root directory path for containerd.
ContainerdRootDir string `json:"containerdRootDir,omitempty"`
ContainerdRootDir string `json:"containerdRootDir"`
// ContainerdEndpoint is the containerd endpoint path.
ContainerdEndpoint string `json:"containerdEndpoint,omitempty"`
// RootDir is the root directory path for managing cri-containerd files
ContainerdEndpoint string `json:"containerdEndpoint"`
// RootDir is the root directory path for managing cri plugin files
// (metadata checkpoint etc.)
RootDir string `json:"rootDir,omitempty"`
RootDir string `json:"rootDir"`
// StateDir is the root directory path for managing volatile pod/container data
StateDir string `json:"stateDir"`
}
// DefaultConfig returns default configurations of cri plugin.
@@ -104,18 +108,19 @@ func DefaultConfig() PluginConfig {
NetworkPluginConfDir: "/etc/cni/net.d",
},
ContainerdConfig: ContainerdConfig{
Snapshotter: containerd.DefaultSnapshotter,
Runtime: "io.containerd.runtime.v1.linux",
RuntimeEngine: "",
RuntimeRoot: "",
Snapshotter: containerd.DefaultSnapshotter,
DefaultRuntime: Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "",
Root: "",
},
},
StreamServerAddress: "",
StreamServerPort: "10010",
EnableSelinux: false,
SandboxImage: "gcr.io/google_containers/pause:3.0",
SandboxImage: "k8s.gcr.io/pause:3.1",
StatsCollectPeriod: 10,
SystemdCgroup: false,
EnableIPv6DAD: false,
Registry: Registry{
Mirrors: map[string]Mirror{
"docker.io": {

View File

@@ -30,6 +30,7 @@ import (
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/log"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go"
@@ -81,8 +82,15 @@ func Import(ctx context.Context, client *containerd.Client, reader io.Reader) (_
if err != nil {
return nil, err
}
// TODO(random-liu): Fix this after containerd client is fixed (containerd/containerd#2193)
defer done(ctx) // nolint: errcheck
defer func() {
deferCtx, deferCancel := ctrdutil.DeferContext()
defer deferCancel()
if err := done(deferCtx); err != nil {
// Get lease id from context still works after context is done.
leaseID, _ := leases.Lease(ctx)
log.G(ctx).WithError(err).Errorf("Failed to release lease %q", leaseID)
}
}()
cs := client.ContentStore()
is := client.ImageService()

View File

@@ -271,7 +271,7 @@ func (r *containerdResolver) base(refspec reference.Spec) (*dockerBase, error) {
if urls, ok := r.registry[host]; ok {
urls, err := r.getV2Urls(urls, prefix)
if err != nil {
return nil, fmt.Errorf("failed to fetch v2 urls: %v", err)
return nil, errors.Wrap(err, "failed to fetch v2 urls")
}
base = append(base, urls...)
} else if host == "docker.io" {
@@ -434,7 +434,7 @@ func (r *dockerBase) setTokenAuth(ctx context.Context, params map[string]string)
realmURL, err := url.Parse(realm)
if err != nil {
return fmt.Errorf("invalid token auth challenge realm: %s", err)
return errors.Wrap(err, "invalid token auth challenge realm")
}
to := tokenOptions{
@@ -444,7 +444,7 @@ func (r *dockerBase) setTokenAuth(ctx context.Context, params map[string]string)
to.scopes = getTokenScopes(ctx, params)
if len(to.scopes) == 0 {
return errors.Errorf("no scope specified for token auth challenge")
return errors.New("no scope specified for token auth challenge")
}
if r.secret != "" {
// Credential information is provided, use oauth POST endpoint
@@ -517,7 +517,7 @@ func (r *dockerBase) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (
var tr postTokenResponse
if err = decoder.Decode(&tr); err != nil {
return "", fmt.Errorf("unable to decode token response: %s", err)
return "", errors.Wrap(err, "unable to decode token response")
}
return tr.AccessToken, nil
@@ -569,7 +569,7 @@ func (r *dockerBase) getToken(ctx context.Context, to tokenOptions) (string, err
var tr getTokenResponse
if err = decoder.Decode(&tr); err != nil {
return "", fmt.Errorf("unable to decode token response: %s", err)
return "", errors.Wrap(err, "unable to decode token response")
}
// `access_token` is equivalent to `token` and if both are specified
@@ -591,7 +591,7 @@ func (r *containerdResolver) getV2Urls(urls []string, imagePath string) ([]url.U
for _, u := range urls {
v2Url, err := url.Parse(u)
if err != nil {
return nil, fmt.Errorf("Failed to parse url during getv2 urls: %+v, err:%s", u, err)
return nil, errors.Wrapf(err, "failed to parse url during getv2 urls: %+v", u)
}
v2Url.Path = path.Join("/v2", imagePath)
v2Urls = append(v2Urls, *v2Url)

View File

@@ -25,6 +25,7 @@ import (
containerdmount "github.com/containerd/containerd/mount"
"github.com/containerd/fifo"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/symlink"
"golang.org/x/net/context"
"golang.org/x/sys/unix"
)
@@ -37,6 +38,7 @@ type OS interface {
OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error)
Stat(name string) (os.FileInfo, error)
ResolveSymbolicLink(name string) (string, error)
FollowSymlinkInScope(path, scope string) (string, error)
CopyFile(src, dest string, perm os.FileMode) error
WriteFile(filename string, data []byte, perm os.FileMode) error
Mount(source string, target string, fstype string, flags uintptr, data string) error
@@ -47,7 +49,7 @@ type OS interface {
// RealOS is used to dispatch the real system level operations.
type RealOS struct{}
// MkdirAll will will call os.MkdirAll to create a directory.
// MkdirAll will call os.MkdirAll to create a directory.
func (RealOS) MkdirAll(path string, perm os.FileMode) error {
return os.MkdirAll(path, perm)
}
@@ -79,7 +81,12 @@ func (RealOS) ResolveSymbolicLink(path string) (string, error) {
return filepath.EvalSymlinks(path)
}
// CopyFile copys src file to dest file
// 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 {
@@ -107,17 +114,21 @@ func (RealOS) Mount(source string, target string, fstype string, flags uintptr,
return unix.Mount(source, target, fstype, flags, data)
}
// Unmount will call unix.Unmount to unmount the file. The function doesn't
// return error if target is not mounted.
// Unmount will call Unmount to unmount the file.
func (RealOS) Unmount(target string, flags int) error {
// TODO(random-liu): Follow symlink to make sure the result is correct.
if mounted, err := mount.Mounted(target); err != nil || !mounted {
return err
}
return unix.Unmount(target, flags)
return Unmount(target, flags)
}
// LookupMount gets mount info of a given path.
func (RealOS) LookupMount(path string) (containerdmount.Info, error) {
return containerdmount.Lookup(path)
}
// Unmount will call unix.Unmount to unmount the file. The function doesn't
// return error if target is not mounted.
func Unmount(target string, flags int) error {
if mounted, err := mount.Mounted(target); err != nil || !mounted {
return err
}
return unix.Unmount(target, flags)
}

View File

@@ -17,8 +17,9 @@ limitations under the License.
package registrar
import (
"fmt"
"sync"
"github.com/pkg/errors"
)
// Registrar stores one-to-one name<->key mappings.
@@ -49,19 +50,19 @@ func (r *Registrar) Reserve(name, key string) error {
defer r.lock.Unlock()
if name == "" || key == "" {
return fmt.Errorf("invalid name %q or key %q", name, key)
return errors.Errorf("invalid name %q or key %q", name, key)
}
if k, exists := r.nameToKey[name]; exists {
if k != key {
return fmt.Errorf("name %q is reserved for %q", name, k)
return errors.Errorf("name %q is reserved for %q", name, k)
}
return nil
}
if n, exists := r.keyToName[key]; exists {
if n != name {
return fmt.Errorf("key %q is reserved for %q", key, n)
return errors.Errorf("key %q is reserved for %q", key, n)
}
return nil
}

View File

@@ -17,10 +17,10 @@ limitations under the License.
package server
import (
"fmt"
"io"
"github.com/containerd/containerd"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
"k8s.io/client-go/tools/remotecommand"
@@ -30,35 +30,35 @@ import (
)
// Attach prepares a streaming endpoint to attach to a running container, and returns the address.
func (c *criContainerdService) Attach(ctx context.Context, r *runtime.AttachRequest) (*runtime.AttachResponse, error) {
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, fmt.Errorf("failed to find container in store: %v", err)
return nil, errors.Wrap(err, "failed to find container in store")
}
state := cntr.Status.Get().State()
if state != runtime.ContainerState_CONTAINER_RUNNING {
return nil, fmt.Errorf("container is in %s state", criContainerStateToString(state))
return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
}
return c.streamServer.GetAttach(r)
}
func (c *criContainerdService) attachContainer(ctx context.Context, id string, stdin io.Reader, stdout, stderr io.WriteCloser,
func (c *criService) attachContainer(ctx context.Context, id string, stdin io.Reader, stdout, stderr io.WriteCloser,
tty bool, resize <-chan remotecommand.TerminalSize) error {
// Get container from our container store.
cntr, err := c.containerStore.Get(id)
if err != nil {
return fmt.Errorf("failed to find container %q in store: %v", id, err)
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 fmt.Errorf("container is in %s state", criContainerStateToString(state))
return errors.Errorf("container is in %s state", criContainerStateToString(state))
}
task, err := cntr.Container.Task(ctx, nil)
if err != nil {
return fmt.Errorf("failed to load task: %v", err)
return errors.Wrap(err, "failed to load task")
}
handleResizing(resize, func(size remotecommand.TerminalSize) {
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {

View File

@@ -17,7 +17,6 @@ limitations under the License.
package server
import (
"fmt"
"os"
"path/filepath"
"strings"
@@ -38,6 +37,7 @@ import (
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/runtime-tools/validate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/syndtr/gocapability/capability"
"golang.org/x/net/context"
@@ -73,17 +73,17 @@ func init() {
}
// CreateContainer creates a new container in the given PodSandbox.
func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (_ *runtime.CreateContainerResponse, retErr error) {
func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (_ *runtime.CreateContainerResponse, retErr error) {
config := r.GetConfig()
sandboxConfig := r.GetSandboxConfig()
sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
if err != nil {
return nil, fmt.Errorf("failed to find sandbox id %q: %v", r.GetPodSandboxId(), err)
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, fmt.Errorf("failed to get sandbox container task: %v", err)
return nil, errors.Wrap(err, "failed to get sandbox container task")
}
sandboxPid := s.Pid()
@@ -94,7 +94,7 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
name := makeContainerName(config.GetMetadata(), sandboxConfig.GetMetadata())
logrus.Debugf("Generated id %q for container %q", id, name)
if err = c.containerNameIndex.Reserve(name, id); err != nil {
return nil, fmt.Errorf("failed to reserve container name %q: %v", name, err)
return nil, errors.Wrapf(err, "failed to reserve container name %q", name)
}
defer func() {
// Release the name if the function returns with an error.
@@ -116,17 +116,28 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
imageRef := config.GetImage().GetImage()
image, err := c.localResolve(ctx, imageRef)
if err != nil {
return nil, fmt.Errorf("failed to resolve image %q: %v", imageRef, err)
return nil, errors.Wrapf(err, "failed to resolve image %q", imageRef)
}
if image == nil {
return nil, fmt.Errorf("image %q not found", imageRef)
return nil, errors.Errorf("image %q not found", imageRef)
}
// 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)
}
ociRuntime, err := getRuntimeConfigFromContainerInfo(sandboxInfo)
if err != nil {
return nil, errors.Wrap(err, "failed to get OCI runtime")
}
logrus.Debugf("Use OCI %+v for container %q", ociRuntime, id)
// Create container root directory.
containerRootDir := getContainerRootDir(c.config.RootDir, id)
containerRootDir := c.getContainerRootDir(id)
if err = c.os.MkdirAll(containerRootDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create container root directory %q: %v",
containerRootDir, err)
return nil, errors.Wrapf(err, "failed to create container root directory %q",
containerRootDir)
}
defer func() {
if retErr != nil {
@@ -137,16 +148,30 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
}
}
}()
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 {
logrus.WithError(err).Errorf("Failed to remove volatile container root directory %q",
volatileContainerRootDir)
}
}
}()
// Create container volumes mounts.
volumeMounts := c.generateVolumeMounts(containerRootDir, config.GetMounts(), &image.ImageSpec.Config)
// Generate container runtime spec.
mounts := c.generateContainerMounts(getSandboxRootDir(c.config.RootDir, sandboxID), config)
mounts := c.generateContainerMounts(sandboxID, config)
spec, err := c.generateContainerSpec(id, sandboxID, sandboxPid, config, sandboxConfig, &image.ImageSpec.Config, append(mounts, volumeMounts...))
if err != nil {
return nil, fmt.Errorf("failed to generate container %q spec: %v", id, err)
return nil, errors.Wrapf(err, "failed to generate container %q spec", id)
}
logrus.Debugf("Container %q spec: %#+v", id, spew.NewFormatter(spec))
@@ -177,9 +202,9 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
}
containerIO, err := cio.NewContainerIO(id,
cio.WithNewFIFOs(containerRootDir, config.GetTty(), config.GetStdin()))
cio.WithNewFIFOs(volatileContainerRootDir, config.GetTty(), config.GetStdin()))
if err != nil {
return nil, fmt.Errorf("failed to create container io: %v", err)
return nil, errors.Wrap(err, "failed to create container io")
}
defer func() {
if retErr != nil {
@@ -206,7 +231,7 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
securityContext.GetPrivileged(),
c.apparmorEnabled)
if err != nil {
return nil, fmt.Errorf("failed to generate apparmor spec opts: %v", err)
return nil, errors.Wrap(err, "failed to generate apparmor spec opts")
}
if apparmorSpecOpts != nil {
specOpts = append(specOpts, apparmorSpecOpts)
@@ -217,7 +242,7 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
securityContext.GetPrivileged(),
c.seccompEnabled)
if err != nil {
return nil, fmt.Errorf("failed to generate seccomp spec opts: %v", err)
return nil, errors.Wrap(err, "failed to generate seccomp spec opts")
}
if seccompSpecOpts != nil {
specOpts = append(specOpts, seccompSpecOpts)
@@ -227,16 +252,16 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
opts = append(opts,
containerd.WithSpec(spec, specOpts...),
containerd.WithRuntime(
c.config.ContainerdConfig.Runtime,
ociRuntime.Type,
&runctypes.RuncOptions{
Runtime: c.config.ContainerdConfig.RuntimeEngine,
RuntimeRoot: c.config.ContainerdConfig.RuntimeRoot,
Runtime: ociRuntime.Engine,
RuntimeRoot: ociRuntime.Root,
SystemdCgroup: c.config.SystemdCgroup}), // TODO (mikebrow): add CriuPath when we add support for pause
containerd.WithContainerLabels(containerLabels),
containerd.WithContainerExtension(containerMetadataExtension, &meta))
var cntr containerd.Container
if cntr, err = c.client.NewContainer(ctx, id, opts...); err != nil {
return nil, fmt.Errorf("failed to create containerd container: %v", err)
return nil, errors.Wrap(err, "failed to create containerd container")
}
defer func() {
if retErr != nil {
@@ -255,8 +280,7 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
containerstore.WithContainerIO(containerIO),
)
if err != nil {
return nil, fmt.Errorf("failed to create internal container object for %q: %v",
id, err)
return nil, errors.Wrapf(err, "failed to create internal container object for %q", id)
}
defer func() {
if retErr != nil {
@@ -269,13 +293,13 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
// Add container into container store.
if err := c.containerStore.Add(container); err != nil {
return nil, fmt.Errorf("failed to add container %q into store: %v", id, err)
return nil, errors.Wrapf(err, "failed to add container %q into store", id)
}
return &runtime.CreateContainerResponse{ContainerId: id}, nil
}
func (c *criContainerdService) generateContainerSpec(id string, sandboxID string, sandboxPid uint32, config *runtime.ContainerConfig,
func (c *criService) generateContainerSpec(id string, sandboxID string, sandboxPid uint32, config *runtime.ContainerConfig,
sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig, extraMounts []*runtime.Mount) (*runtimespec.Spec, error) {
// Creates a spec Generator with the default spec.
spec, err := defaultRuntimeSpec(id)
@@ -316,30 +340,30 @@ func (c *criContainerdService) generateContainerSpec(id string, sandboxID string
selinuxOpt := securityContext.GetSelinuxOptions()
processLabel, mountLabel, err := initSelinuxOpts(selinuxOpt)
if err != nil {
return nil, fmt.Errorf("failed to init selinux options %+v: %v", securityContext.GetSelinuxOptions(), err)
return nil, errors.Wrapf(err, "failed to init selinux options %+v", securityContext.GetSelinuxOptions())
}
// Add extra mounts first so that CRI specified mounts can override.
mounts := append(extraMounts, config.GetMounts()...)
if err := c.addOCIBindMounts(&g, mounts, mountLabel); err != nil {
return nil, fmt.Errorf("failed to set OCI bind mounts %+v: %v", mounts, err)
return nil, errors.Wrapf(err, "failed to set OCI bind mounts %+v", mounts)
}
if securityContext.GetPrivileged() {
if !sandboxConfig.GetLinux().GetSecurityContext().GetPrivileged() {
return nil, fmt.Errorf("no privileged container allowed in sandbox")
return nil, errors.New("no privileged container allowed in sandbox")
}
if err := setOCIPrivileged(&g, config); err != nil {
return nil, err
}
} else { // not privileged
if err := c.addOCIDevices(&g, config.GetDevices()); err != nil {
return nil, fmt.Errorf("failed to set devices mapping %+v: %v", config.GetDevices(), err)
return nil, errors.Wrapf(err, "failed to set devices mapping %+v", config.GetDevices())
}
if err := setOCICapabilities(&g, securityContext.GetCapabilities()); err != nil {
return nil, fmt.Errorf("failed to set capabilities %+v: %v",
securityContext.GetCapabilities(), err)
return nil, errors.Wrapf(err, "failed to set capabilities %+v",
securityContext.GetCapabilities())
}
}
@@ -378,7 +402,7 @@ func (c *criContainerdService) generateContainerSpec(id string, sandboxID string
// generateVolumeMounts sets up image volumes for container. Rely on the removal of container
// root directory to do cleanup. Note that image volume will be skipped, if there is criMounts
// specified with the same destination.
func (c *criContainerdService) generateVolumeMounts(containerRootDir string, criMounts []*runtime.Mount, config *imagespec.ImageConfig) []*runtime.Mount {
func (c *criService) generateVolumeMounts(containerRootDir string, criMounts []*runtime.Mount, config *imagespec.ImageConfig) []*runtime.Mount {
if len(config.Volumes) == 0 {
return nil
}
@@ -406,13 +430,13 @@ func (c *criContainerdService) generateVolumeMounts(containerRootDir string, cri
// generateContainerMounts sets up necessary container mounts including /dev/shm, /etc/hosts
// and /etc/resolv.conf.
func (c *criContainerdService) generateContainerMounts(sandboxRootDir string, config *runtime.ContainerConfig) []*runtime.Mount {
func (c *criService) generateContainerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
var mounts []*runtime.Mount
securityContext := config.GetLinux().GetSecurityContext()
if !isInCRIMounts(etcHosts, config.GetMounts()) {
mounts = append(mounts, &runtime.Mount{
ContainerPath: etcHosts,
HostPath: getSandboxHosts(sandboxRootDir),
HostPath: c.getSandboxHosts(sandboxID),
Readonly: securityContext.GetReadonlyRootfs(),
})
}
@@ -422,13 +446,13 @@ func (c *criContainerdService) generateContainerMounts(sandboxRootDir string, co
if !isInCRIMounts(resolvConfPath, config.GetMounts()) {
mounts = append(mounts, &runtime.Mount{
ContainerPath: resolvConfPath,
HostPath: getResolvPath(sandboxRootDir),
HostPath: c.getResolvPath(sandboxID),
Readonly: securityContext.GetReadonlyRootfs(),
})
}
if !isInCRIMounts(devShm, config.GetMounts()) {
sandboxDevShm := getSandboxDevShm(sandboxRootDir)
sandboxDevShm := c.getSandboxDevShm(sandboxID)
if securityContext.GetNamespaceOptions().GetIpc() == runtime.NamespaceMode_NODE {
sandboxDevShm = devShm
}
@@ -457,7 +481,7 @@ func setOCIProcessArgs(g *generate.Generator, config *runtime.ContainerConfig, i
}
}
if len(command) == 0 && len(args) == 0 {
return fmt.Errorf("no command specified")
return errors.New("no command specified")
}
g.SetProcessArgs(append(command, args...))
return nil
@@ -469,7 +493,7 @@ func addImageEnvs(g *generate.Generator, imageEnvs []string) error {
for _, e := range imageEnvs {
kv := strings.SplitN(e, "=", 2)
if len(kv) != 2 {
return fmt.Errorf("invalid environment variable %q", e)
return errors.Errorf("invalid environment variable %q", e)
}
g.AddProcessEnv(kv[0], kv[1])
}
@@ -481,7 +505,7 @@ func setOCIPrivileged(g *generate.Generator, config *runtime.ContainerConfig) er
g.SetupPrivileged(true)
setOCIBindMountsPrivileged(g)
if err := setOCIDevicesPrivileged(g); err != nil {
return fmt.Errorf("failed to set devices mapping %+v: %v", config.GetDevices(), err)
return errors.Wrapf(err, "failed to set devices mapping %+v", config.GetDevices())
}
return nil
}
@@ -497,7 +521,7 @@ func clearReadOnly(m *runtimespec.Mount) {
}
// addDevices set device mapping without privilege.
func (c *criContainerdService) addOCIDevices(g *generate.Generator, devs []*runtime.Device) error {
func (c *criService) addOCIDevices(g *generate.Generator, devs []*runtime.Device) error {
spec := g.Spec()
for _, device := range devs {
path, err := c.os.ResolveSymbolicLink(device.HostPath)
@@ -560,7 +584,7 @@ func setOCIDevicesPrivileged(g *generate.Generator) error {
}
// addOCIBindMounts adds bind mounts.
func (c *criContainerdService) addOCIBindMounts(g *generate.Generator, mounts []*runtime.Mount, mountLabel string) error {
func (c *criService) addOCIBindMounts(g *generate.Generator, mounts []*runtime.Mount, mountLabel string) error {
// Mount cgroup into the container as readonly, which inherits docker's behavior.
g.AddCgroupsMount("ro") // nolint: errcheck
for _, mount := range mounts {
@@ -570,17 +594,17 @@ func (c *criContainerdService) addOCIBindMounts(g *generate.Generator, mounts []
// TODO(random-liu): Add CRI validation test for this case.
if _, err := c.os.Stat(src); err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("failed to stat %q: %v", src, err)
return errors.Wrapf(err, "failed to stat %q", src)
}
if err := c.os.MkdirAll(src, 0755); err != nil {
return fmt.Errorf("failed to mkdir %q: %v", src, err)
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 := c.os.ResolveSymbolicLink(src)
if err != nil {
return fmt.Errorf("failed to resolve symlink %q: %v", src, err)
return errors.Wrapf(err, "failed to resolve symlink %q", src)
}
options := []string{"rbind"}
@@ -619,7 +643,7 @@ func (c *criContainerdService) addOCIBindMounts(g *generate.Generator, mounts []
if mount.GetSelinuxRelabel() {
if err := label.Relabel(src, mountLabel, true); err != nil && err != unix.ENOTSUP {
return fmt.Errorf("relabel %q with %q failed: %v", src, mountLabel, err)
return errors.Wrapf(err, "relabel %q with %q failed", src, mountLabel)
}
}
g.AddBindMount(src, dst, options)
@@ -773,7 +797,7 @@ func generateSeccompSpecOpts(seccompProf string, privileged, seccompEnabled bool
}
if !seccompEnabled {
if seccompProf != "" && seccompProf != unconfinedProfile {
return nil, fmt.Errorf("seccomp is not supported")
return nil, errors.New("seccomp is not supported")
}
return nil, nil
}
@@ -787,7 +811,7 @@ func generateSeccompSpecOpts(seccompProf string, privileged, seccompEnabled bool
default:
// Require and Trim default profile name prefix
if !strings.HasPrefix(seccompProf, profileNamePrefix) {
return nil, fmt.Errorf("invalid seccomp profile %q", seccompProf)
return nil, errors.Errorf("invalid seccomp profile %q", seccompProf)
}
return seccomp.WithProfile(strings.TrimPrefix(seccompProf, profileNamePrefix)), nil
}
@@ -799,7 +823,7 @@ func generateApparmorSpecOpts(apparmorProf string, privileged, apparmorEnabled b
// Should fail loudly if user try to specify apparmor profile
// but we don't support it.
if apparmorProf != "" && apparmorProf != unconfinedProfile {
return nil, fmt.Errorf("apparmor is not supported")
return nil, errors.New("apparmor is not supported")
}
return nil, nil
}
@@ -819,7 +843,7 @@ func generateApparmorSpecOpts(apparmorProf string, privileged, apparmorEnabled b
default:
// Require and Trim default profile name prefix
if !strings.HasPrefix(apparmorProf, profileNamePrefix) {
return nil, fmt.Errorf("invalid apparmor profile %q", apparmorProf)
return nil, errors.Errorf("invalid apparmor profile %q", apparmorProf)
}
return apparmor.WithProfile(strings.TrimPrefix(apparmorProf, profileNamePrefix)), nil
}
@@ -840,7 +864,7 @@ func ensureShared(path string, lookupMount func(string) (mount.Info, error)) err
}
}
return fmt.Errorf("path %q is mounted on %q but it is not a shared mount", path, mountInfo.Mountpoint)
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.
@@ -858,5 +882,5 @@ func ensureSharedOrSlave(path string, lookupMount func(string) (mount.Info, erro
return nil
}
}
return fmt.Errorf("path %q is mounted on %q but it is not a shared or slave mount", path, mountInfo.Mountpoint)
return errors.Errorf("path %q is mounted on %q but it is not a shared or slave mount", path, mountInfo.Mountpoint)
}

View File

@@ -17,21 +17,20 @@ limitations under the License.
package server
import (
"fmt"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)
// Exec prepares a streaming endpoint to execute a command in the container, and returns the address.
func (c *criContainerdService) Exec(ctx context.Context, r *runtime.ExecRequest) (*runtime.ExecResponse, error) {
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, fmt.Errorf("failed to find container %q in store: %v", r.GetContainerId(), err)
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, fmt.Errorf("container is in %s state", criContainerStateToString(state))
return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
}
return c.streamServer.GetExec(r)
}

View File

@@ -18,13 +18,13 @@ package server
import (
"bytes"
"fmt"
"io"
"time"
"github.com/containerd/containerd"
containerdio "github.com/containerd/containerd/cio"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
"golang.org/x/sys/unix"
@@ -39,7 +39,7 @@ import (
// ExecSync executes a command in the container, and returns the stdout output.
// If command exits with a non-zero exit code, an error is returned.
func (c *criContainerdService) ExecSync(ctx context.Context, r *runtime.ExecSyncRequest) (*runtime.ExecSyncResponse, error) {
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(),
@@ -48,7 +48,7 @@ func (c *criContainerdService) ExecSync(ctx context.Context, r *runtime.ExecSync
timeout: time.Duration(r.GetTimeout()) * time.Second,
})
if err != nil {
return nil, fmt.Errorf("failed to exec in container: %v", err)
return nil, errors.Wrap(err, "failed to exec in container")
}
return &runtime.ExecSyncResponse{
@@ -71,7 +71,7 @@ type execOptions struct {
// execInContainer executes a command inside the container synchronously, and
// redirects stdio stream properly.
func (c *criContainerdService) execInContainer(ctx context.Context, id string, opts execOptions) (*uint32, error) {
func (c *criService) execInContainer(ctx context.Context, id string, opts execOptions) (*uint32, error) {
// Cancel the context before returning to ensure goroutines are stopped.
// This is important, because if `Start` returns error, `Wait` will hang
// forever unless we cancel the context.
@@ -81,23 +81,23 @@ func (c *criContainerdService) execInContainer(ctx context.Context, id string, o
// Get container from our container store.
cntr, err := c.containerStore.Get(id)
if err != nil {
return nil, fmt.Errorf("failed to find container %q in store: %v", id, err)
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, fmt.Errorf("container is in %s state", criContainerStateToString(state))
return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
}
container := cntr.Container
spec, err := container.Spec(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get container spec: %v", err)
return nil, errors.Wrap(err, "failed to get container spec")
}
task, err := container.Task(ctx, nil)
if err != nil {
return nil, fmt.Errorf("failed to load task: %v", err)
return nil, errors.Wrap(err, "failed to load task")
}
if opts.tty {
g := newSpecGenerator(spec)
@@ -116,17 +116,17 @@ func (c *criContainerdService) execInContainer(ctx context.Context, id string, o
}
execID := util.GenerateID()
logrus.Debugf("Generated exec id %q for container %q", execID, id)
rootDir := getContainerRootDir(c.config.RootDir, 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, rootDir, opts.tty, opts.stdin != nil)
execIO, err = cio.NewExecIO(id, volatileRootDir, opts.tty, opts.stdin != nil)
return execIO, err
},
)
if err != nil {
return nil, fmt.Errorf("failed to create exec %q: %v", execID, err)
return nil, errors.Wrapf(err, "failed to create exec %q", execID)
}
defer func() {
deferCtx, deferCancel := ctrdutil.DeferContext()
@@ -138,10 +138,10 @@ func (c *criContainerdService) execInContainer(ctx context.Context, id string, o
exitCh, err := process.Wait(ctx)
if err != nil {
return nil, fmt.Errorf("failed to wait for process %q: %v", execID, err)
return nil, errors.Wrapf(err, "failed to wait for process %q", execID)
}
if err := process.Start(ctx); err != nil {
return nil, fmt.Errorf("failed to start exec %q: %v", execID, err)
return nil, errors.Wrapf(err, "failed to start exec %q", execID)
}
handleResizing(opts.resize, func(size remotecommand.TerminalSize) {
@@ -173,7 +173,7 @@ func (c *criContainerdService) execInContainer(ctx context.Context, id string, o
//TODO(Abhi) Use context.WithDeadline instead of timeout.
// Ignore the not found error because the process may exit itself before killing.
if err := process.Kill(ctx, unix.SIGKILL); err != nil && !errdefs.IsNotFound(err) {
return nil, fmt.Errorf("failed to kill exec %q: %v", execID, err)
return nil, errors.Wrapf(err, "failed to kill exec %q", execID)
}
// Wait for the process to be killed.
exitRes := <-exitCh
@@ -181,12 +181,12 @@ func (c *criContainerdService) execInContainer(ctx context.Context, id string, o
execID, exitRes.ExitCode(), exitRes.Error())
<-attachDone
logrus.Debugf("Stream pipe for exec process %q done", execID)
return nil, fmt.Errorf("timeout %v exceeded", opts.timeout)
return nil, errors.Errorf("timeout %v exceeded", opts.timeout)
case exitRes := <-exitCh:
code, _, err := exitRes.Result()
logrus.Infof("Exec process %q exits with exit code %d and error %v", execID, code, err)
if err != nil {
return nil, fmt.Errorf("failed while waiting for exec %q: %v", execID, err)
return nil, errors.Wrapf(err, "failed while waiting for exec %q", execID)
}
<-attachDone
logrus.Debugf("Stream pipe for exec process %q done", execID)

View File

@@ -25,7 +25,7 @@ import (
)
// ListContainers lists all containers matching the filter.
func (c *criContainerdService) ListContainers(ctx context.Context, r *runtime.ListContainersRequest) (*runtime.ListContainersResponse, error) {
func (c *criService) ListContainers(ctx context.Context, r *runtime.ListContainersRequest) (*runtime.ListContainersResponse, error) {
// List all containers from store.
containersInStore := c.containerStore.List()
@@ -54,7 +54,7 @@ func toCRIContainer(container containerstore.Container) *runtime.Container {
}
}
func (c *criContainerdService) normalizeContainerFilter(filter *runtime.ContainerFilter) {
func (c *criService) normalizeContainerFilter(filter *runtime.ContainerFilter) {
if cntr, err := c.containerStore.Get(filter.GetId()); err == nil {
filter.Id = cntr.ID
}
@@ -64,7 +64,7 @@ func (c *criContainerdService) normalizeContainerFilter(filter *runtime.Containe
}
// filterCRIContainers filters CRIContainers.
func (c *criContainerdService) filterCRIContainers(containers []*runtime.Container, filter *runtime.ContainerFilter) []*runtime.Container {
func (c *criService) filterCRIContainers(containers []*runtime.Container, filter *runtime.ContainerFilter) []*runtime.Container {
if filter == nil {
return containers
}

View File

@@ -17,7 +17,7 @@ limitations under the License.
package server
import (
"fmt"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -25,14 +25,14 @@ import (
// 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 *criContainerdService) ReopenContainerLog(ctx context.Context, r *runtime.ReopenContainerLogRequest) (*runtime.ReopenContainerLogResponse, error) {
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, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err)
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, fmt.Errorf("container is not running")
return nil, errors.New("container is not running")
}
// Create new container logger and replace the existing ones.

View File

@@ -17,11 +17,10 @@ limitations under the License.
package server
import (
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -33,11 +32,11 @@ import (
// RemoveContainer removes the container.
// TODO(random-liu): Forcibly stop container if it's running.
func (c *criContainerdService) RemoveContainer(ctx context.Context, r *runtime.RemoveContainerRequest) (_ *runtime.RemoveContainerResponse, retErr error) {
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, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err)
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.Tracef("RemoveContainer called for container %q that does not exist", r.GetContainerId())
@@ -48,7 +47,7 @@ func (c *criContainerdService) RemoveContainer(ctx context.Context, r *runtime.R
// Set removing state to prevent other start/remove operations against this container
// while it's being removed.
if err := setContainerRemoving(container); err != nil {
return nil, fmt.Errorf("failed to set removing state for container %q: %v", id, err)
return nil, errors.Wrapf(err, "failed to set removing state for container %q", id)
}
defer func() {
if retErr != nil {
@@ -67,20 +66,25 @@ func (c *criContainerdService) RemoveContainer(ctx context.Context, r *runtime.R
// Delete containerd container.
if err := container.Container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
if !errdefs.IsNotFound(err) {
return nil, fmt.Errorf("failed to delete containerd container %q: %v", id, err)
return nil, errors.Wrapf(err, "failed to delete containerd container %q", id)
}
log.Tracef("Remove called for containerd container %q that does not exist", id)
}
// Delete container checkpoint.
if err := container.Delete(); err != nil {
return nil, fmt.Errorf("failed to delete container checkpoint for %q: %v", id, err)
return nil, errors.Wrapf(err, "failed to delete container checkpoint for %q", id)
}
containerRootDir := getContainerRootDir(c.config.RootDir, id)
containerRootDir := c.getContainerRootDir(id)
if err := system.EnsureRemoveAll(containerRootDir); err != nil {
return nil, fmt.Errorf("failed to remove container root directory %q: %v",
containerRootDir, err)
return nil, errors.Wrapf(err, "failed to remove container root directory %q",
containerRootDir)
}
volatileContainerRootDir := c.getVolatileContainerRootDir(id)
if err := system.EnsureRemoveAll(volatileContainerRootDir); err != nil {
return nil, errors.Wrapf(err, "failed to remove volatile container root directory %q",
volatileContainerRootDir)
}
c.containerStore.Delete(id)
@@ -96,10 +100,10 @@ func setContainerRemoving(container containerstore.Container) error {
return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
// Do not remove container if it's still running.
if status.State() == runtime.ContainerState_CONTAINER_RUNNING {
return status, fmt.Errorf("container is still running")
return status, errors.New("container is still running")
}
if status.Removing {
return status, fmt.Errorf("container is already in removing state")
return status, errors.New("container is already in removing state")
}
status.Removing = true
return status, nil

View File

@@ -17,13 +17,13 @@ limitations under the License.
package server
import (
"fmt"
"io"
"time"
"github.com/containerd/containerd"
containerdio "github.com/containerd/containerd/cio"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -35,10 +35,10 @@ import (
)
// StartContainer starts the container.
func (c *criContainerdService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (retRes *runtime.StartContainerResponse, retErr error) {
func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (retRes *runtime.StartContainerResponse, retErr error) {
container, err := c.containerStore.Get(r.GetContainerId())
if err != nil {
return nil, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err)
return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
}
var startErr error
@@ -51,14 +51,14 @@ func (c *criContainerdService) StartContainer(ctx context.Context, r *runtime.St
}); startErr != nil {
return nil, startErr
} else if err != nil {
return nil, fmt.Errorf("failed to update container %q metadata: %v", container.ID, err)
return nil, errors.Wrapf(err, "failed to update container %q metadata", container.ID)
}
return &runtime.StartContainerResponse{}, nil
}
// startContainer actually starts the container. The function needs to be run in one transaction. Any updates
// to the status passed in will be applied no matter the function returns error or not.
func (c *criContainerdService) startContainer(ctx context.Context,
func (c *criService) startContainer(ctx context.Context,
cntr containerstore.Container,
status *containerstore.Status) (retErr error) {
id := cntr.ID
@@ -68,11 +68,11 @@ func (c *criContainerdService) startContainer(ctx context.Context,
// Return error if container is not in created state.
if status.State() != runtime.ContainerState_CONTAINER_CREATED {
return fmt.Errorf("container %q is in %s state", id, criContainerStateToString(status.State()))
return errors.Errorf("container %q is in %s state", id, criContainerStateToString(status.State()))
}
// Do not start the container when there is a removal in progress.
if status.Removing {
return fmt.Errorf("container %q is in removing state", id)
return errors.Errorf("container %q is in removing state", id)
}
defer func() {
@@ -89,17 +89,17 @@ func (c *criContainerdService) startContainer(ctx context.Context,
// Get sandbox config from sandbox store.
sandbox, err := c.sandboxStore.Get(meta.SandboxID)
if err != nil {
return fmt.Errorf("sandbox %q not found: %v", meta.SandboxID, err)
return errors.Wrapf(err, "sandbox %q not found", meta.SandboxID)
}
sandboxID := meta.SandboxID
if sandbox.Status.Get().State != sandboxstore.StateReady {
return fmt.Errorf("sandbox container %q is not running", sandboxID)
return errors.Errorf("sandbox container %q is not running", sandboxID)
}
ioCreation := func(id string) (_ containerdio.IO, err error) {
stdoutWC, stderrWC, err := createContainerLoggers(meta.LogPath, config.GetTty())
if err != nil {
return nil, fmt.Errorf("failed to create container loggers: %v", err)
return nil, errors.Wrap(err, "failed to create container loggers")
}
defer func() {
if err != nil {
@@ -118,7 +118,7 @@ func (c *criContainerdService) startContainer(ctx context.Context,
task, err := container.NewTask(ctx, ioCreation)
if err != nil {
return fmt.Errorf("failed to create containerd task: %v", err)
return errors.Wrap(err, "failed to create containerd task")
}
defer func() {
if retErr != nil {
@@ -133,7 +133,7 @@ func (c *criContainerdService) startContainer(ctx context.Context,
// Start containerd task.
if err := task.Start(ctx); err != nil {
return fmt.Errorf("failed to start containerd task %q: %v", id, err)
return errors.Wrapf(err, "failed to start containerd task %q", id)
}
// Update container start timestamp.
@@ -147,7 +147,7 @@ func createContainerLoggers(logPath string, tty bool) (stdout io.WriteCloser, st
if logPath != "" {
// Only generate container log when log path is specified.
if stdout, err = cio.NewCRILogger(logPath, cio.Stdout); err != nil {
return nil, nil, fmt.Errorf("failed to start container stdout logger: %v", err)
return nil, nil, errors.Wrap(err, "failed to start container stdout logger")
}
defer func() {
if err != nil {
@@ -157,7 +157,7 @@ func createContainerLoggers(logPath string, tty bool) (stdout io.WriteCloser, st
// Only redirect stderr when there is no tty.
if !tty {
if stderr, err = cio.NewCRILogger(logPath, cio.Stderr); err != nil {
return nil, nil, fmt.Errorf("failed to start container stderr logger: %v", err)
return nil, nil, errors.Wrap(err, "failed to start container stderr logger")
}
}
} else {

View File

@@ -17,32 +17,31 @@ limitations under the License.
package server
import (
"fmt"
tasks "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)
// ContainerStats returns stats of the container. If the container does not
// exist, the call returns an error.
func (c *criContainerdService) ContainerStats(ctx context.Context, in *runtime.ContainerStatsRequest) (*runtime.ContainerStatsResponse, error) {
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, fmt.Errorf("failed to find container: %v", err)
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, fmt.Errorf("failed to fetch metrics for task: %v", err)
return nil, errors.Wrap(err, "failed to fetch metrics for task")
}
if len(resp.Metrics) != 1 {
return nil, fmt.Errorf("unexpected metrics response: %+v", resp.Metrics)
return nil, errors.Errorf("unexpected metrics response: %+v", resp.Metrics)
}
cs, err := c.getContainerMetrics(cntr.Metadata, resp.Metrics[0])
if err != nil {
return nil, fmt.Errorf("failed to decode container metrics: %v", err)
return nil, errors.Wrap(err, "failed to decode container metrics")
}
return &runtime.ContainerStatsResponse{Stats: cs}, nil
}

View File

@@ -17,12 +17,11 @@ limitations under the License.
package server
import (
"fmt"
"github.com/containerd/cgroups"
tasks "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/api/types"
"github.com/containerd/typeurl"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -30,26 +29,26 @@ import (
)
// ListContainerStats returns stats of all running containers.
func (c *criContainerdService) ListContainerStats(
func (c *criService) ListContainerStats(
ctx context.Context,
in *runtime.ListContainerStatsRequest,
) (*runtime.ListContainerStatsResponse, error) {
request, containers, err := c.buildTaskMetricsRequest(in)
if err != nil {
return nil, fmt.Errorf("failed to build metrics request: %v", err)
return nil, errors.Wrap(err, "failed to build metrics request")
}
resp, err := c.client.TaskService().Metrics(ctx, &request)
if err != nil {
return nil, fmt.Errorf("failed to fetch metrics for tasks: %v", err)
return nil, errors.Wrap(err, "failed to fetch metrics for tasks")
}
criStats, err := c.toCRIContainerStats(resp.Metrics, containers)
if err != nil {
return nil, fmt.Errorf("failed to convert to cri containerd stats format: %v", err)
return nil, errors.Wrap(err, "failed to convert to cri containerd stats format")
}
return criStats, nil
}
func (c *criContainerdService) toCRIContainerStats(
func (c *criService) toCRIContainerStats(
stats []*types.Metric,
containers []containerstore.Container,
) (*runtime.ListContainerStatsResponse, error) {
@@ -61,14 +60,14 @@ func (c *criContainerdService) toCRIContainerStats(
for _, cntr := range containers {
cs, err := c.getContainerMetrics(cntr.Metadata, statsMap[cntr.ID])
if err != nil {
return nil, fmt.Errorf("failed to decode container metrics for %q: %v", cntr.ID, err)
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 *criContainerdService) getContainerMetrics(
func (c *criService) getContainerMetrics(
meta containerstore.Metadata,
stats *types.Metric,
) (*runtime.ContainerStats, error) {
@@ -99,7 +98,7 @@ func (c *criContainerdService) getContainerMetrics(
if stats != nil {
s, err := typeurl.UnmarshalAny(stats.Data)
if err != nil {
return nil, fmt.Errorf("failed to extract container metrics: %v", err)
return nil, errors.Wrap(err, "failed to extract container metrics")
}
metrics := s.(*cgroups.Metrics)
if metrics.CPU != nil && metrics.CPU.Usage != nil {
@@ -119,7 +118,7 @@ func (c *criContainerdService) getContainerMetrics(
return &cs, nil
}
func (c *criContainerdService) normalizeContainerStatsFilter(filter *runtime.ContainerStatsFilter) {
func (c *criService) normalizeContainerStatsFilter(filter *runtime.ContainerStatsFilter) {
if cntr, err := c.containerStore.Get(filter.GetId()); err == nil {
filter.Id = cntr.ID
}
@@ -130,7 +129,7 @@ func (c *criContainerdService) normalizeContainerStatsFilter(filter *runtime.Con
// buildTaskMetricsRequest constructs a tasks.MetricsRequest based on
// the information in the stats request and the containerStore
func (c *criContainerdService) buildTaskMetricsRequest(
func (c *criService) buildTaskMetricsRequest(
r *runtime.ListContainerStatsRequest,
) (tasks.MetricsRequest, []containerstore.Container, error) {
var req tasks.MetricsRequest

View File

@@ -18,21 +18,21 @@ package server
import (
"encoding/json"
"fmt"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
criconfig "github.com/containerd/cri/pkg/config"
containerstore "github.com/containerd/cri/pkg/store/container"
)
// ContainerStatus inspects the container and returns the status.
func (c *criContainerdService) ContainerStatus(ctx context.Context, r *runtime.ContainerStatusRequest) (*runtime.ContainerStatusResponse, error) {
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, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err)
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.
@@ -44,7 +44,7 @@ func (c *criContainerdService) ContainerStatus(ctx context.Context, r *runtime.C
imageRef := container.ImageRef
image, err := c.imageStore.Get(imageRef)
if err != nil {
return nil, fmt.Errorf("failed to get image %q: %v", imageRef, err)
return nil, errors.Wrapf(err, "failed to get image %q", imageRef)
}
if len(image.RepoTags) > 0 {
// Based on current behavior of dockershim, this field should be
@@ -58,7 +58,7 @@ func (c *criContainerdService) ContainerStatus(ctx context.Context, r *runtime.C
status := toCRIContainerStatus(container, spec, imageRef)
info, err := toCRIContainerInfo(ctx, container, r.GetVerbose())
if err != nil {
return nil, fmt.Errorf("failed to get verbose container info: %v", err)
return nil, errors.Wrap(err, "failed to get verbose container info")
}
return &runtime.ContainerStatusResponse{
@@ -106,6 +106,7 @@ type containerInfo struct {
Removing bool `json:"removing"`
SnapshotKey string `json:"snapshotKey"`
Snapshotter string `json:"snapshotter"`
Runtime *criconfig.Runtime `json:"runtime"`
Config *runtime.ContainerConfig `json:"config"`
RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"`
}
@@ -128,24 +129,28 @@ func toCRIContainerInfo(ctx context.Context, container containerstore.Container,
Config: meta.Config,
}
spec, err := container.Container.Spec(ctx)
if err == nil {
ci.RuntimeSpec = spec
} else {
logrus.WithError(err).Errorf("Failed to get container %q spec", container.ID)
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 {
ci.SnapshotKey = ctrInfo.SnapshotKey
ci.Snapshotter = ctrInfo.Snapshotter
} else {
logrus.WithError(err).Errorf("Failed to get container %q info", container.ID)
if err != nil {
return nil, errors.Wrap(err, "failed to get container info")
}
ci.SnapshotKey = ctrInfo.SnapshotKey
ci.Snapshotter = ctrInfo.Snapshotter
ociRuntime, err := getRuntimeConfigFromContainerInfo(ctrInfo)
if err != nil {
return nil, errors.Wrap(err, "failed to get container runtime config")
}
ci.Runtime = &ociRuntime
infoBytes, err := json.Marshal(ci)
if err != nil {
return nil, fmt.Errorf("failed to marshal info %v: %v", ci, err)
return nil, errors.Wrapf(err, "failed to marshal info %v", ci)
}
return map[string]string{
"info": string(infoBytes),

View File

@@ -17,12 +17,12 @@ limitations under the License.
package server
import (
"fmt"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
"github.com/docker/docker/pkg/signal"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
"golang.org/x/sys/unix"
@@ -36,11 +36,11 @@ import (
const killContainerTimeout = 2 * time.Minute
// StopContainer stops a running container with a grace period (i.e., timeout).
func (c *criContainerdService) StopContainer(ctx context.Context, r *runtime.StopContainerRequest) (*runtime.StopContainerResponse, error) {
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, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err)
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 {
@@ -51,7 +51,7 @@ func (c *criContainerdService) StopContainer(ctx context.Context, r *runtime.Sto
}
// stopContainer stops a container based on the container metadata.
func (c *criContainerdService) stopContainer(ctx context.Context, container containerstore.Container, timeout time.Duration) error {
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
@@ -71,27 +71,27 @@ func (c *criContainerdService) stopContainer(ctx context.Context, container cont
// deleted and image is garbage collected before this point. However,
// the chance is really slim, even it happens, it's still fine to return
// an error here.
return fmt.Errorf("failed to get image metadata %q: %v", container.ImageRef, err)
return errors.Wrapf(err, "failed to get image metadata %q", container.ImageRef)
}
if image.ImageSpec.Config.StopSignal != "" {
stopSignal, err = signal.ParseSignal(image.ImageSpec.Config.StopSignal)
if err != nil {
return fmt.Errorf("failed to parse stop signal %q: %v",
image.ImageSpec.Config.StopSignal, err)
return errors.Wrapf(err, "failed to parse stop signal %q",
image.ImageSpec.Config.StopSignal)
}
}
logrus.Infof("Stop container %q with signal %v", id, stopSignal)
task, err := container.Container.Task(ctx, nil)
if err != nil {
if !errdefs.IsNotFound(err) {
return fmt.Errorf("failed to stop container, task not found for container %q: %v", id, err)
return errors.Wrapf(err, "failed to stop container, task not found for container %q", id)
}
return nil
}
if task != nil {
if err = task.Kill(ctx, stopSignal); err != nil {
if !errdefs.IsNotFound(err) {
return fmt.Errorf("failed to stop container %q: %v", id, err)
return errors.Wrapf(err, "failed to stop container %q", id)
}
// Move on to make sure container status is updated.
}
@@ -107,7 +107,7 @@ func (c *criContainerdService) stopContainer(ctx context.Context, container cont
task, err := container.Container.Task(ctx, nil)
if err != nil {
if !errdefs.IsNotFound(err) {
return fmt.Errorf("failed to stop container, task not found for container %q: %v", id, err)
return errors.Wrapf(err, "failed to stop container, task not found for container %q", id)
}
return nil
}
@@ -116,7 +116,7 @@ func (c *criContainerdService) stopContainer(ctx context.Context, container cont
if task != nil {
if err = task.Kill(ctx, unix.SIGKILL, containerd.WithKillAll); err != nil {
if !errdefs.IsNotFound(err) {
return fmt.Errorf("failed to kill container %q: %v", id, err)
return errors.Wrapf(err, "failed to kill container %q", id)
}
// Move on to make sure container status is updated.
}
@@ -124,20 +124,20 @@ func (c *criContainerdService) stopContainer(ctx context.Context, container cont
// Wait for a fixed timeout until container stop is observed by event monitor.
if err := c.waitContainerStop(ctx, container, killContainerTimeout); err != nil {
return fmt.Errorf("an error occurs during waiting for container %q to stop: %v", id, err)
return errors.Wrapf(err, "an error occurs during waiting for container %q to stop", id)
}
return nil
}
// waitContainerStop waits for container to be stopped until timeout exceeds or context is cancelled.
func (c *criContainerdService) waitContainerStop(ctx context.Context, container containerstore.Container, timeout time.Duration) error {
func (c *criService) waitContainerStop(ctx context.Context, container containerstore.Container, timeout time.Duration) error {
timeoutTimer := time.NewTimer(timeout)
defer timeoutTimer.Stop()
select {
case <-ctx.Done():
return fmt.Errorf("wait container %q is cancelled", container.ID)
return errors.Errorf("wait container %q is cancelled", container.ID)
case <-timeoutTimer.C:
return fmt.Errorf("wait container %q stop timeout", container.ID)
return errors.Errorf("wait container %q stop timeout", container.ID)
case <-container.Stopped():
return nil
}

View File

@@ -18,13 +18,13 @@ package server
import (
gocontext "context"
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/typeurl"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -35,10 +35,10 @@ import (
)
// UpdateContainerResources updates ContainerConfig of the container.
func (c *criContainerdService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (retRes *runtime.UpdateContainerResourcesResponse, retErr error) {
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, fmt.Errorf("failed to find container: %v", err)
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.
@@ -46,19 +46,19 @@ func (c *criContainerdService) UpdateContainerResources(ctx context.Context, r *
if err := container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
return status, c.updateContainerResources(ctx, container, r.GetLinux(), status)
}); err != nil {
return nil, fmt.Errorf("failed to update resources: %v", err)
return nil, errors.Wrap(err, "failed to update resources")
}
return &runtime.UpdateContainerResourcesResponse{}, nil
}
func (c *criContainerdService) updateContainerResources(ctx context.Context,
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 fmt.Errorf("container %q is in removing state", id)
return errors.Errorf("container %q is in removing state", id)
}
// Update container spec. If the container is not started yet, updating
@@ -67,11 +67,11 @@ func (c *criContainerdService) updateContainerResources(ctx context.Context,
// the spec will become our source of truth for resource limits.
oldSpec, err := cntr.Container.Spec(ctx)
if err != nil {
return fmt.Errorf("failed to get container spec: %v", err)
return errors.Wrap(err, "failed to get container spec")
}
newSpec, err := updateOCILinuxResource(oldSpec, resources)
if err != nil {
return fmt.Errorf("failed to update resource in spec: %v", err)
return errors.Wrap(err, "failed to update resource in spec")
}
if err := updateContainerSpec(ctx, cntr.Container, newSpec); err != nil {
@@ -100,7 +100,7 @@ func (c *criContainerdService) updateContainerResources(ctx context.Context,
// Task exited already.
return nil
}
return fmt.Errorf("failed to get task: %v", err)
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 {
@@ -108,7 +108,7 @@ func (c *criContainerdService) updateContainerResources(ctx context.Context,
// Task exited already.
return nil
}
return fmt.Errorf("failed to update resources: %v", err)
return errors.Wrap(err, "failed to update resources")
}
return nil
}
@@ -117,13 +117,13 @@ func (c *criContainerdService) updateContainerResources(ctx context.Context,
func updateContainerSpec(ctx context.Context, cntr containerd.Container, spec *runtimespec.Spec) error {
any, err := typeurl.MarshalAny(spec)
if err != nil {
return fmt.Errorf("failed to marshal spec %+v: %v", spec, err)
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 fmt.Errorf("failed to update container spec: %v", err)
return errors.Wrap(err, "failed to update container spec")
}
return nil
}
@@ -133,7 +133,7 @@ func updateOCILinuxResource(spec *runtimespec.Spec, new *runtime.LinuxContainerR
// Copy to make sure old spec is not changed.
var cloned runtimespec.Spec
if err := util.DeepCopy(&cloned, spec); err != nil {
return nil, fmt.Errorf("failed to deep copy: %v", err)
return nil, errors.Wrap(err, "failed to deep copy")
}
g := newSpecGenerator(&cloned)

View File

@@ -17,15 +17,18 @@ limitations under the License.
package server
import (
"errors"
"time"
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"
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
"github.com/containerd/cri/pkg/store"
@@ -33,6 +36,12 @@ import (
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
)
const (
backOffInitDuration = 1 * time.Second
backOffMaxDuration = 5 * time.Minute
backOffExpireCheckDuration = 1 * time.Second
)
// eventMonitor monitors containerd event and updates internal state correspondingly.
// TODO(random-liu): [P1] Figure out is it possible to drop event during containerd
// is running. If it is, we should do periodically list to sync state with containerd.
@@ -43,6 +52,23 @@ type eventMonitor struct {
errCh <-chan error
ctx context.Context
cancel context.CancelFunc
backOff *backOff
}
type backOff struct {
queuePool map[string]*backOffQueue
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
@@ -55,6 +81,7 @@ func newEventMonitor(c *containerstore.Store, s *sandboxstore.Store) *eventMonit
sandboxStore: s,
ctx: ctx,
cancel: cancel,
backOff: newBackOff(),
}
}
@@ -67,6 +94,24 @@ func (em *eventMonitor) subscribe(subscriber events.Subscriber) {
em.ch, em.errCh = subscriber.Subscribe(em.ctx, filters...)
}
func convertEvent(e *gogotypes.Any) (string, interface{}, error) {
containerID := ""
evt, err := typeurl.UnmarshalAny(e)
if err != nil {
return "", nil, errors.Wrap(err, "failed to unmarshalany")
}
switch evt.(type) {
case *eventtypes.TaskExit:
containerID = evt.(*eventtypes.TaskExit).ContainerID
case *eventtypes.TaskOOM:
containerID = evt.(*eventtypes.TaskOOM).ContainerID
default:
return "", nil, errors.New("unsupported event")
}
return containerID, evt, nil
}
// start starts the event monitor which monitors and handles all container events. It returns
// a channel for the caller to wait for the event monitor to stop. start must be called after
// subscribe.
@@ -76,15 +121,41 @@ func (em *eventMonitor) start() (<-chan struct{}, error) {
}
closeCh := make(chan struct{})
go func() {
backOffCheckCh := em.backOff.start()
for {
select {
case e := <-em.ch:
logrus.Debugf("Received containerd event timestamp - %v, namespace - %q, topic - %q", e.Timestamp, e.Namespace, e.Topic)
em.handleEvent(e)
cID, evt, err := convertEvent(e.Event)
if err != nil {
logrus.WithError(err).Errorf("Failed to convert event %+v", e)
break
}
if em.backOff.isInBackOff(cID) {
logrus.Infof("Events for container %q is in backoff, enqueue event %+v", cID, evt)
em.backOff.enBackOff(cID, evt)
break
}
if err := em.handleEvent(evt); err != nil {
logrus.WithError(err).Errorf("Failed to handle event %+v for container %s", evt, cID)
em.backOff.enBackOff(cID, evt)
}
case err := <-em.errCh:
logrus.WithError(err).Error("Failed to handle event stream")
close(closeCh)
return
case <-backOffCheckCh:
cIDs := em.backOff.getExpiredContainers()
for _, cID := range cIDs {
queue := em.backOff.deBackOff(cID)
for i, any := range queue.events {
if err := em.handleEvent(any); err != nil {
logrus.WithError(err).Errorf("Failed to handle backOff event %+v for container %s", any, cID)
em.backOff.reBackOff(cID, queue.events[i:], queue.duration)
break
}
}
}
}
}
}()
@@ -94,17 +165,13 @@ func (em *eventMonitor) start() (<-chan struct{}, error) {
// 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(evt *events.Envelope) {
func (em *eventMonitor) handleEvent(any interface{}) error {
ctx := ctrdutil.NamespacedContext()
any, err := typeurl.UnmarshalAny(evt.Event)
if err != nil {
logrus.WithError(err).Errorf("Failed to convert event envelope %+v", evt)
return
}
switch any.(type) {
// If containerd-shim exits unexpectedly, there will be no corresponding event.
// However, containerd could not retrieve container state in that case, so it's
@@ -112,51 +179,59 @@ func (em *eventMonitor) handleEvent(evt *events.Envelope) {
// TODO(random-liu): [P2] Handle containerd-shim exit.
case *eventtypes.TaskExit:
e := any.(*eventtypes.TaskExit)
logrus.Infof("TaskExit event %+v", e)
cntr, err := em.containerStore.Get(e.ContainerID)
if err == nil {
handleContainerExit(ctx, e, cntr)
return
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 {
logrus.WithError(err).Errorf("Failed to get container %q", e.ContainerID)
return
return errors.Wrap(err, "can't find container for TaskExit event")
}
// Use GetAll to include sandbox in unknown state.
sb, err := em.sandboxStore.GetAll(e.ContainerID)
if err == nil {
handleSandboxExit(ctx, e, sb)
return
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 {
logrus.WithError(err).Errorf("Failed to get sandbox %q", e.ContainerID)
return
return errors.Wrap(err, "can't find sandbox for TaskExit event")
}
return nil
case *eventtypes.TaskOOM:
e := any.(*eventtypes.TaskOOM)
logrus.Infof("TaskOOM event %+v", e)
cntr, err := em.containerStore.Get(e.ContainerID)
if err != nil {
if _, err := em.sandboxStore.Get(e.ContainerID); err == nil {
return
if err != store.ErrNotExist {
return errors.Wrap(err, "can't find container for TaskOOM event")
}
logrus.WithError(err).Errorf("Failed to get container %q", e.ContainerID)
return
if _, err = em.sandboxStore.Get(e.ContainerID); err != nil {
if err != store.ErrNotExist {
return errors.Wrap(err, "can't find sandbox for TaskOOM event")
}
return nil
}
return nil
}
err = cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
status.Reason = oomExitReason
return status, nil
})
if err != nil {
logrus.WithError(err).Errorf("Failed to update container %q oom", e.ContainerID)
return
return errors.Wrap(err, "failed to update container status for TaskOOM event")
}
}
return nil
}
// handleContainerExit handles TaskExit event for container.
func handleContainerExit(ctx context.Context, e *eventtypes.TaskExit, cntr containerstore.Container) {
func handleContainerExit(ctx context.Context, e *eventtypes.TaskExit, cntr containerstore.Container) error {
if e.Pid != cntr.Status.Get().Pid {
// Non-init process died, ignore the event.
return
return nil
}
// Attach container IO so that `Delete` could cleanup the stream properly.
task, err := cntr.Container.Task(ctx,
@@ -166,16 +241,13 @@ func handleContainerExit(ctx context.Context, e *eventtypes.TaskExit, cntr conta
)
if err != nil {
if !errdefs.IsNotFound(err) {
logrus.WithError(err).Errorf("failed to load task for container %q", e.ContainerID)
return
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); err != nil {
// TODO(random-liu): [P0] Enqueue the event and retry.
if !errdefs.IsNotFound(err) {
logrus.WithError(err).Errorf("failed to stop container %q", e.ContainerID)
return
return errors.Wrap(err, "failed to stop container")
}
// Move on to make sure container status is updated.
}
@@ -192,34 +264,30 @@ func handleContainerExit(ctx context.Context, e *eventtypes.TaskExit, cntr conta
return status, nil
})
if err != nil {
logrus.WithError(err).Errorf("Failed to update container %q state", e.ContainerID)
// TODO(random-liu): [P0] Enqueue the event and retry.
return
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) {
func handleSandboxExit(ctx context.Context, e *eventtypes.TaskExit, sb sandboxstore.Sandbox) error {
if e.Pid != sb.Status.Get().Pid {
// Non-init process died, ignore the event.
return
return nil
}
// No stream attached to sandbox container.
task, err := sb.Container.Task(ctx, nil)
if err != nil {
if !errdefs.IsNotFound(err) {
logrus.WithError(err).Errorf("failed to load task for sandbox %q", e.ContainerID)
return
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); err != nil {
// TODO(random-liu): [P0] Enqueue the event and retry.
if !errdefs.IsNotFound(err) {
logrus.WithError(err).Errorf("failed to stop sandbox %q", e.ContainerID)
return
return errors.Wrap(err, "failed to stop sandbox")
}
// Move on to make sure container status is updated.
}
@@ -238,10 +306,84 @@ func handleSandboxExit(ctx context.Context, e *eventtypes.TaskExit, sb sandboxst
return status, nil
})
if err != nil {
logrus.WithError(err).Errorf("Failed to update sandbox %q state", e.ContainerID)
// TODO(random-liu): [P0] Enqueue the event and retry.
return
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) getExpiredContainers() []string {
var containers []string
for c, q := range b.queuePool {
if q.isExpire() {
containers = append(containers, c)
}
}
return containers
}
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.ticker = time.NewTicker(b.checkDuration)
return b.ticker.C
}
func (b *backOff) stop() {
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)
}

View File

@@ -19,14 +19,16 @@ package server
import (
"encoding/json"
"fmt"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/linux/runctypes"
"github.com/containerd/typeurl"
"github.com/docker/distribution/reference"
imagedigest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/identity"
@@ -35,10 +37,11 @@ import (
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
"k8s.io/kubernetes/pkg/util/sysctl"
criconfig "github.com/containerd/cri/pkg/config"
"github.com/containerd/cri/pkg/store"
imagestore "github.com/containerd/cri/pkg/store/image"
"github.com/containerd/cri/pkg/util"
@@ -109,6 +112,14 @@ const (
containerMetadataExtension = criContainerdPrefix + ".container.metadata"
)
const (
// defaultIfName is the default network interface for the pods
defaultIfName = "eth0"
// networkAttachCount is the minimum number of networks the PodSandbox
// attaches to
networkAttachCount = 2
)
// 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 {
@@ -126,9 +137,9 @@ func makeSandboxName(s *runtime.PodSandboxMetadata) string {
func makeContainerName(c *runtime.ContainerMetadata, s *runtime.PodSandboxMetadata) string {
return strings.Join([]string{
c.Name, // 0
s.Name, // 1: sandbox name
s.Namespace, // 2: sandbox namespace
s.Uid, // 3: sandbox uid
s.Name, // 1: pod name
s.Namespace, // 2: pod namespace
s.Uid, // 3: pod uid
fmt.Sprintf("%d", c.Attempt), // 4
}, nameDelimiter)
}
@@ -145,29 +156,42 @@ func getCgroupsPath(cgroupsParent, id string, systemdCgroup bool) string {
}
// getSandboxRootDir returns the root directory for managing sandbox files,
// e.g. named pipes.
func getSandboxRootDir(rootDir, id string) string {
return filepath.Join(rootDir, sandboxesDir, id)
// e.g. hosts files.
func (c *criService) getSandboxRootDir(id string) string {
return filepath.Join(c.config.RootDir, sandboxesDir, id)
}
// getContainerRootDir returns the root directory for managing container files.
func getContainerRootDir(rootDir, id string) string {
return filepath.Join(rootDir, containersDir, 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)
}
// getSandboxHosts returns the hosts file path inside the sandbox root directory.
func getSandboxHosts(sandboxRootDir string) string {
return filepath.Join(sandboxRootDir, "hosts")
func (c *criService) getSandboxHosts(id string) string {
return filepath.Join(c.getSandboxRootDir(id), "hosts")
}
// getResolvPath returns resolv.conf filepath for specified sandbox.
func getResolvPath(sandboxRoot string) string {
return filepath.Join(sandboxRoot, "resolv.conf")
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 getSandboxDevShm(sandboxRootDir string) string {
return filepath.Join(sandboxRootDir, "shm")
func (c *criService) getSandboxDevShm(id string) string {
return filepath.Join(c.getVolatileSandboxRootDir(id), "shm")
}
// getNetworkNamespace returns the network namespace of a process.
@@ -212,7 +236,7 @@ func getRepoDigestAndTag(namedRef reference.Named, digest imagedigest.Digest, sc
// localResolve resolves image reference locally and returns corresponding image metadata. It returns
// nil without error if the reference doesn't exist.
func (c *criContainerdService) localResolve(ctx context.Context, refOrID string) (*imagestore.Image, error) {
func (c *criService) localResolve(ctx context.Context, refOrID string) (*imagestore.Image, error) {
getImageID := func(refOrId string) string {
if _, err := imagedigest.Parse(refOrID); err == nil {
return refOrID
@@ -245,7 +269,7 @@ func (c *criContainerdService) localResolve(ctx context.Context, refOrID string)
if err == store.ErrNotExist {
return nil, nil
}
return nil, fmt.Errorf("failed to get image %q : %v", imageID, err)
return nil, errors.Wrapf(err, "failed to get image %q", imageID)
}
return &image, nil
}
@@ -271,10 +295,10 @@ func getUserFromImage(user string) (*int64, string) {
// ensureImageExists returns corresponding metadata of the image reference, if image is not
// pulled yet, the function will pull the image.
func (c *criContainerdService) ensureImageExists(ctx context.Context, ref string) (*imagestore.Image, error) {
func (c *criService) ensureImageExists(ctx context.Context, ref string) (*imagestore.Image, error) {
image, err := c.localResolve(ctx, ref)
if err != nil {
return nil, fmt.Errorf("failed to resolve image %q: %v", ref, err)
return nil, errors.Wrapf(err, "failed to resolve image %q", ref)
}
if image != nil {
return image, nil
@@ -282,13 +306,13 @@ func (c *criContainerdService) ensureImageExists(ctx context.Context, ref string
// Pull image to ensure the image exists
resp, err := c.PullImage(ctx, &runtime.PullImageRequest{Image: &runtime.ImageSpec{Image: ref}})
if err != nil {
return nil, fmt.Errorf("failed to pull image %q: %v", ref, err)
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, fmt.Errorf("failed to get image %q metadata after pulling: %v", imageID, err)
return nil, errors.Wrapf(err, "failed to get image %q metadata after pulling", imageID)
}
return &newImage, nil
}
@@ -306,28 +330,28 @@ func getImageInfo(ctx context.Context, image containerd.Image) (*imageInfo, erro
// Get image information.
diffIDs, err := image.RootFS(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get image diffIDs: %v", err)
return nil, errors.Wrap(err, "failed to get image diffIDs")
}
chainID := identity.ChainID(diffIDs)
size, err := image.Size(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get image compressed resource size: %v", err)
return nil, errors.Wrap(err, "failed to get image compressed resource size")
}
desc, err := image.Config(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get image config descriptor: %v", err)
return nil, errors.Wrap(err, "failed to get image config descriptor")
}
id := desc.Digest.String()
rb, err := content.ReadBlob(ctx, image.ContentStore(), desc.Digest)
if err != nil {
return nil, fmt.Errorf("failed to read image config from content store: %v", err)
return nil, errors.Wrap(err, "failed to read image config from content store")
}
var ociimage imagespec.Image
if err := json.Unmarshal(rb, &ociimage); err != nil {
return nil, fmt.Errorf("failed to unmarshal image config %s: %v", rb, err)
return nil, errors.Wrapf(err, "failed to unmarshal image config %s", rb)
}
return &imageInfo{
@@ -392,34 +416,31 @@ func newSpecGenerator(spec *runtimespec.Spec) generate.Generator {
return g
}
// disableNetNSDAD disables duplicate address detection in the network namespace.
// DAD has a negative affect on sandbox start latency, since we have to wait
// a second or more for the addresses to leave the "tentative" state.
func disableNetNSDAD(ns string) error {
dad := "net/ipv6/conf/default/accept_dad"
sysctlBin, err := exec.LookPath("sysctl")
if err != nil {
return fmt.Errorf("could not find sysctl binary: %v", err)
func getPodCNILabels(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",
}
nsenterBin, err := exec.LookPath("nsenter")
if err != nil {
return fmt.Errorf("could not find nsenter binary: %v", err)
}
// If the sysctl doesn't exist, it means ipv6 is disabled.
if _, err := sysctl.New().GetSysctl(dad); err != nil {
return nil
}
output, err := exec.Command(nsenterBin,
fmt.Sprintf("--net=%s", ns), "-F", "--",
sysctlBin, "-w", fmt.Sprintf("%s=%s", dad, "0"),
).CombinedOutput()
if err != nil {
return fmt.Errorf("failed to write sysctl %q - output: %s, error: %s",
dad, output, err)
}
return nil
}
// getRuntimeConfigFromContainerInfo gets runtime configuration from containerd
// container info.
func getRuntimeConfigFromContainerInfo(c containers.Container) (criconfig.Runtime, error) {
r := criconfig.Runtime{
Type: c.Runtime.Name,
}
if c.Runtime.Options == nil {
// CRI plugin makes sure that runtime option is always set.
return criconfig.Runtime{}, errors.New("runtime options is nil")
}
data, err := typeurl.UnmarshalAny(c.Runtime.Options)
if err != nil {
return criconfig.Runtime{}, errors.Wrap(err, "failed to unmarshal runtime options")
}
runtimeOpts := data.(*runctypes.RuncOptions)
r.Engine = runtimeOpts.Runtime
r.Root = runtimeOpts.RuntimeRoot
return r, nil
}

View File

@@ -26,7 +26,7 @@ import (
// ListImages lists existing images.
// TODO(random-liu): Add image list filters after CRI defines this more clear, and kubelet
// actually needs it.
func (c *criContainerdService) ListImages(ctx context.Context, r *runtime.ListImagesRequest) (*runtime.ListImagesResponse, error) {
func (c *criService) ListImages(ctx context.Context, r *runtime.ListImagesRequest) (*runtime.ListImagesResponse, error) {
imagesInStore := c.imageStore.List()
var images []*runtime.Image

View File

@@ -17,12 +17,12 @@ limitations under the License.
package server
import (
"fmt"
"golang.org/x/net/context"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
api "github.com/containerd/cri/pkg/api/v1"
"github.com/containerd/cri/pkg/containerd/importer"
@@ -30,23 +30,23 @@ import (
)
// LoadImage loads a image into containerd.
func (c *criContainerdService) LoadImage(ctx context.Context, r *api.LoadImageRequest) (*api.LoadImageResponse, error) {
func (c *criService) LoadImage(ctx context.Context, r *api.LoadImageRequest) (*api.LoadImageResponse, error) {
path := r.GetFilePath()
if !filepath.IsAbs(path) {
return nil, fmt.Errorf("path %q is not an absolute path", path)
return nil, errors.Errorf("path %q is not an absolute path", path)
}
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open file: %v", err)
return nil, errors.Wrap(err, "failed to open file")
}
repoTags, err := importer.Import(ctx, c.client, f)
if err != nil {
return nil, fmt.Errorf("failed to import image: %v", err)
return nil, errors.Wrap(err, "failed to import image")
}
for _, repoTag := range repoTags {
image, err := c.client.GetImage(ctx, repoTag)
if err != nil {
return nil, fmt.Errorf("failed to get image %q: %v", repoTag, err)
return nil, errors.Wrapf(err, "failed to get image %q", repoTag)
}
if err := image.Unpack(ctx, c.config.ContainerdConfig.Snapshotter); err != nil {
logrus.WithError(err).Warnf("Failed to unpack image %q", repoTag)
@@ -54,12 +54,12 @@ func (c *criContainerdService) LoadImage(ctx context.Context, r *api.LoadImageRe
}
info, err := getImageInfo(ctx, image)
if err != nil {
return nil, fmt.Errorf("failed to get image %q info: %v", repoTag, err)
return nil, errors.Wrapf(err, "failed to get image %q info", repoTag)
}
id := info.id
if err := c.createImageReference(ctx, id, image.Target()); err != nil {
return nil, fmt.Errorf("failed to create image reference %q: %v", id, err)
return nil, errors.Wrapf(err, "failed to create image reference %q", id)
}
img := imagestore.Image{
@@ -72,7 +72,7 @@ func (c *criContainerdService) LoadImage(ctx context.Context, r *api.LoadImageRe
}
if err := c.imageStore.Add(img); err != nil {
return nil, fmt.Errorf("failed to add image %q into store: %v", id, err)
return nil, errors.Wrapf(err, "failed to add image %q into store", id)
}
logrus.Debugf("Imported image with id %q, repo tag %q", id, repoTag)
}

View File

@@ -18,7 +18,6 @@ package server
import (
"encoding/base64"
"fmt"
"net/http"
"strings"
@@ -26,6 +25,7 @@ import (
"github.com/containerd/containerd/errdefs"
containerdimages "github.com/containerd/containerd/images"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -77,11 +77,11 @@ import (
// contents are missing but snapshots are ready, is the image still "READY"?
// PullImage pulls an image with authentication config.
func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (*runtime.PullImageResponse, error) {
func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (*runtime.PullImageResponse, error) {
imageRef := r.GetImage().GetImage()
namedRef, err := util.NormalizeImageRef(imageRef)
if err != nil {
return nil, fmt.Errorf("failed to parse image reference %q: %v", imageRef, err)
return nil, errors.Wrapf(err, "failed to parse image reference %q", imageRef)
}
ref := namedRef.String()
if ref != imageRef {
@@ -94,7 +94,7 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma
})
_, desc, err := resolver.Resolve(ctx, ref)
if err != nil {
return nil, fmt.Errorf("failed to resolve image %q: %v", ref, err)
return nil, errors.Wrapf(err, "failed to resolve image %q", ref)
}
// We have to check schema1 here, because after `Pull`, schema1
// image has already been converted.
@@ -106,7 +106,7 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma
containerd.WithResolver(resolver),
)
if err != nil {
return nil, fmt.Errorf("failed to pull image %q: %v", ref, err)
return nil, errors.Wrapf(err, "failed to pull image %q", ref)
}
// Do best effort unpack.
@@ -119,7 +119,7 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma
// Get image information.
info, err := getImageInfo(ctx, image)
if err != nil {
return nil, fmt.Errorf("failed to get image information: %v", err)
return nil, errors.Wrap(err, "failed to get image information")
}
imageID := info.id
@@ -129,7 +129,7 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma
continue
}
if err := c.createImageReference(ctx, r, image.Target()); err != nil {
return nil, fmt.Errorf("failed to update image reference %q: %v", r, err)
return nil, errors.Wrapf(err, "failed to update image reference %q", r)
}
}
@@ -150,7 +150,7 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma
}
if err := c.imageStore.Add(img); err != nil {
return nil, fmt.Errorf("failed to add image %q into store: %v", img.ID, err)
return nil, errors.Wrapf(err, "failed to add image %q into store", img.ID)
}
// NOTE(random-liu): the actual state in containerd is the source of truth, even we maintain
@@ -181,20 +181,20 @@ func ParseAuth(auth *runtime.AuthConfig) (string, string, error) {
}
fields := strings.SplitN(string(decoded), ":", 2)
if len(fields) != 2 {
return "", "", fmt.Errorf("invalid decoded auth: %q", decoded)
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.
return "", "", fmt.Errorf("invalid auth config")
return "", "", errors.New("invalid auth config")
}
// createImageReference creates image reference inside containerd image store.
// Note that because create and update are not finished in one transaction, there could be race. E.g.
// the image reference is deleted by someone else after create returns already exists, but before update
// happens.
func (c *criContainerdService) createImageReference(ctx context.Context, name string, desc imagespec.Descriptor) error {
func (c *criService) createImageReference(ctx context.Context, name string, desc imagespec.Descriptor) error {
img := containerdimages.Image{
Name: name,
Target: desc,
@@ -212,7 +212,7 @@ func (c *criContainerdService) createImageReference(ctx context.Context, name st
return err
}
func (c *criContainerdService) getResolverOptions() map[string][]string {
func (c *criService) getResolverOptions() map[string][]string {
options := make(map[string][]string)
for ns, mirror := range c.config.Mirrors {
options[ns] = append(options[ns], mirror.Endpoints...)

View File

@@ -17,10 +17,9 @@ limitations under the License.
package server
import (
"fmt"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -32,10 +31,10 @@ import (
// TODO(random-liu): We should change CRI to distinguish image id and image spec.
// Remove the whole image no matter the it's image id or reference. This is the
// semantic defined in CRI now.
func (c *criContainerdService) RemoveImage(ctx context.Context, r *runtime.RemoveImageRequest) (*runtime.RemoveImageResponse, error) {
func (c *criService) RemoveImage(ctx context.Context, r *runtime.RemoveImageRequest) (*runtime.RemoveImageResponse, error) {
image, err := c.localResolve(ctx, r.GetImage().GetImage())
if err != nil {
return nil, fmt.Errorf("can not resolve %q locally: %v", r.GetImage().GetImage(), err)
return nil, errors.Wrapf(err, "can not resolve %q locally", r.GetImage().GetImage())
}
if image == nil {
// return empty without error when image not found.
@@ -49,7 +48,7 @@ func (c *criContainerdService) RemoveImage(ctx context.Context, r *runtime.Remov
if errdefs.IsNotFound(err) {
continue
}
return nil, fmt.Errorf("failed to get image %q: %v", tag, err)
return nil, errors.Wrapf(err, "failed to get image %q", tag)
}
desc, err := cImage.Config(ctx)
if err != nil {
@@ -82,12 +81,12 @@ func (c *criContainerdService) RemoveImage(ctx context.Context, r *runtime.Remov
if err == nil || errdefs.IsNotFound(err) {
continue
}
return nil, fmt.Errorf("failed to delete image reference %q for image %q: %v", ref, image.ID, err)
return nil, errors.Wrapf(err, "failed to delete image reference %q for image %q", ref, image.ID)
}
// Delete image id synchronously to trigger garbage collection.
err = c.client.ImageService().Delete(ctx, image.ID, images.SynchronousDelete())
if err != nil && !errdefs.IsNotFound(err) {
return nil, fmt.Errorf("failed to delete image id %q: %v", image.ID, err)
return nil, errors.Wrapf(err, "failed to delete image id %q", image.ID)
}
c.imageStore.Delete(image.ID)
return &runtime.RemoveImageResponse{}, nil

View File

@@ -18,8 +18,8 @@ package server
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -31,10 +31,10 @@ import (
// ImageStatus returns the status of the image, returns nil if the image isn't present.
// TODO(random-liu): We should change CRI to distinguish image id and image spec. (See
// kubernetes/kubernetes#46255)
func (c *criContainerdService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (*runtime.ImageStatusResponse, error) {
func (c *criService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (*runtime.ImageStatusResponse, error) {
image, err := c.localResolve(ctx, r.GetImage().GetImage())
if err != nil {
return nil, fmt.Errorf("can not resolve %q locally: %v", r.GetImage().GetImage(), err)
return nil, errors.Wrapf(err, "can not resolve %q locally", r.GetImage().GetImage())
}
if image == nil {
// return empty without error when image not found.
@@ -46,7 +46,7 @@ func (c *criContainerdService) ImageStatus(ctx context.Context, r *runtime.Image
runtimeImage := toCRIRuntimeImage(image)
info, err := c.toCRIImageInfo(ctx, image, r.GetVerbose())
if err != nil {
return nil, fmt.Errorf("failed to generate image info: %v", err)
return nil, errors.Wrap(err, "failed to generate image info")
}
return &runtime.ImageStatusResponse{
@@ -79,7 +79,7 @@ type verboseImageInfo struct {
}
// toCRIImageInfo converts internal image object information to CRI image status response info map.
func (c *criContainerdService) toCRIImageInfo(ctx context.Context, image *imagestore.Image, verbose bool) (map[string]string, error) {
func (c *criService) toCRIImageInfo(ctx context.Context, image *imagestore.Image, verbose bool) (map[string]string, error) {
if !verbose {
return nil, nil
}

View File

@@ -25,7 +25,7 @@ import (
)
// ImageFsInfo returns information of the filesystem that is used to store images.
func (c *criContainerdService) ImageFsInfo(ctx context.Context, r *runtime.ImageFsInfoRequest) (*runtime.ImageFsInfoResponse, error) {
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

View File

@@ -30,10 +30,10 @@ import (
// instrumentedService wraps service with containerd namespace and logs.
type instrumentedService struct {
c *criContainerdService
c *criService
}
func newInstrumentedService(c *criContainerdService) grpcServices {
func newInstrumentedService(c *criService) grpcServices {
return &instrumentedService{c: c}
}

View File

@@ -19,12 +19,12 @@ package io
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -57,7 +57,7 @@ func NewCRILogger(path string, stream StreamType) (io.WriteCloser, error) {
prc, pwc := io.Pipe()
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0640)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %v", err)
return nil, errors.Wrap(err, "failed to open log file")
}
go redirectLogs(path, prc, f, stream)
return pwc, nil

View File

@@ -17,7 +17,6 @@ limitations under the License.
package server
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
@@ -31,6 +30,7 @@ import (
"github.com/containerd/typeurl"
"github.com/docker/distribution/reference"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -51,11 +51,11 @@ import (
// tolerant tasks being created or started, we prefer that not to happen.
// recover recovers system state from containerd and status checkpoint.
func (c *criContainerdService) recover(ctx context.Context) error {
func (c *criService) recover(ctx context.Context) error {
// Recover all sandboxes.
sandboxes, err := c.client.Containers(ctx, filterLabel(containerKindLabel, containerKindSandbox))
if err != nil {
return fmt.Errorf("failed to list sandbox containers: %v", err)
return errors.Wrap(err, "failed to list sandbox containers")
}
for _, sandbox := range sandboxes {
sb, err := loadSandbox(ctx, sandbox)
@@ -65,47 +65,48 @@ func (c *criContainerdService) recover(ctx context.Context) error {
}
logrus.Debugf("Loaded sandbox %+v", sb)
if err := c.sandboxStore.Add(sb); err != nil {
return fmt.Errorf("failed to add sandbox %q to store: %v", sandbox.ID(), err)
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 fmt.Errorf("failed to reserve sandbox name %q: %v", sb.Name, err)
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 fmt.Errorf("failed to list containers: %v", err)
return errors.Wrap(err, "failed to list containers")
}
for _, container := range containers {
containerDir := getContainerRootDir(c.config.RootDir, container.ID())
cntr, err := loadContainer(ctx, container, containerDir)
containerDir := c.getContainerRootDir(container.ID())
volatileContainerDir := c.getVolatileContainerRootDir(container.ID())
cntr, err := loadContainer(ctx, container, containerDir, volatileContainerDir)
if err != nil {
logrus.WithError(err).Errorf("Failed to load container %q", container.ID())
continue
}
logrus.Debugf("Loaded container %+v", cntr)
if err := c.containerStore.Add(cntr); err != nil {
return fmt.Errorf("failed to add container %q to store: %v", container.ID(), err)
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 fmt.Errorf("failed to reserve container name %q: %v", cntr.Name, err)
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 fmt.Errorf("failed to list images: %v", err)
return errors.Wrap(err, "failed to list images")
}
images, err := loadImages(ctx, cImages, c.config.ContainerdConfig.Snapshotter)
if err != nil {
return fmt.Errorf("failed to load images: %v", err)
return errors.Wrap(err, "failed to load images")
}
for _, image := range images {
logrus.Debugf("Loaded image %+v", image)
if err := c.imageStore.Add(image); err != nil {
return fmt.Errorf("failed to add image %q to store: %v", image.ID, err)
return errors.Wrapf(err, "failed to add image %q to store", image.ID)
}
}
@@ -113,35 +114,56 @@ func (c *criContainerdService) recover(ctx context.Context) error {
// we can't even get metadata, we should cleanup orphaned sandbox/container directories
// with best effort.
// Cleanup orphaned sandbox directories without corresponding containerd container.
if err := cleanupOrphanedSandboxDirs(sandboxes, filepath.Join(c.config.RootDir, "sandboxes")); err != nil {
return fmt.Errorf("failed to cleanup orphaned sandbox directories: %v", err)
// Cleanup orphaned 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(cleanup.cntrs, cleanup.base); err != nil {
return errors.Wrap(err, cleanup.errMsg)
}
}
// Cleanup orphaned container directories without corresponding containerd container.
if err := cleanupOrphanedContainerDirs(containers, filepath.Join(c.config.RootDir, "containers")); err != nil {
return fmt.Errorf("failed to cleanup orphaned container directories: %v", err)
}
return nil
}
// loadContainer loads container from containerd and status checkpoint.
func loadContainer(ctx context.Context, cntr containerd.Container, containerDir string) (containerstore.Container, error) {
func loadContainer(ctx context.Context, cntr containerd.Container, containerDir, volatileContainerDir string) (containerstore.Container, error) {
id := cntr.ID()
var container containerstore.Container
// Load container metadata.
exts, err := cntr.Extensions(ctx)
if err != nil {
return container, fmt.Errorf("failed to get container extensions: %v", err)
return container, errors.Wrap(err, "failed to get container extensions")
}
ext, ok := exts[containerMetadataExtension]
if !ok {
return container, fmt.Errorf("metadata extension %q not found", containerMetadataExtension)
return container, errors.Errorf("metadata extension %q not found", containerMetadataExtension)
}
data, err := typeurl.UnmarshalAny(&ext)
if err != nil {
return container, fmt.Errorf("failed to unmarshal metadata extension %q: %v", ext, err)
return container, errors.Wrapf(err, "failed to unmarshal metadata extension %q", ext)
}
meta := data.(*containerstore.Metadata)
@@ -170,7 +192,7 @@ func loadContainer(ctx context.Context, cntr containerd.Container, containerDir
return containerIO, nil
})
if err != nil && !errdefs.IsNotFound(err) {
return container, fmt.Errorf("failed to load task: %v", err)
return container, errors.Wrap(err, "failed to load task")
}
var s containerd.Status
var notFound bool
@@ -183,7 +205,7 @@ func loadContainer(ctx context.Context, cntr containerd.Container, containerDir
if err != nil {
// It's still possible that task is deleted during this window.
if !errdefs.IsNotFound(err) {
return container, fmt.Errorf("failed to get task status: %v", err)
return container, errors.Wrap(err, "failed to get task status")
}
notFound = true
}
@@ -197,10 +219,10 @@ func loadContainer(ctx context.Context, cntr containerd.Container, containerDir
// containerd got restarted during that. In that case, we still
// treat the container as `CREATED`.
containerIO, err = cio.NewContainerIO(id,
cio.WithNewFIFOs(containerDir, meta.Config.GetTty(), meta.Config.GetStdin()),
cio.WithNewFIFOs(volatileContainerDir, meta.Config.GetTty(), meta.Config.GetStdin()),
)
if err != nil {
return container, fmt.Errorf("failed to create container io: %v", err)
return container, errors.Wrap(err, "failed to create container io")
}
case runtime.ContainerState_CONTAINER_RUNNING:
// Container was in running state, but its task has been deleted,
@@ -219,17 +241,17 @@ func loadContainer(ctx context.Context, cntr containerd.Container, containerDir
// gets restarted during container start.
// Container must be in `CREATED` state.
if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
return container, fmt.Errorf("failed to delete task: %v", err)
return container, errors.Wrap(err, "failed to delete task")
}
if status.State() != runtime.ContainerState_CONTAINER_CREATED {
return container, fmt.Errorf("unexpected container state for created task: %q", status.State())
return container, 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 container, fmt.Errorf("unexpected container state for running task: %q", status.State())
return container, 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
@@ -240,12 +262,12 @@ func loadContainer(ctx context.Context, cntr containerd.Container, containerDir
case containerd.Stopped:
// Task is stopped. Updata status and delete the task.
if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
return container, fmt.Errorf("failed to delete task: %v", err)
return container, errors.Wrap(err, "failed to delete task")
}
status.FinishedAt = s.ExitTime.UnixNano()
status.ExitCode = int32(s.ExitStatus)
default:
return container, fmt.Errorf("unexpected task status %q", s.Status)
return container, errors.Errorf("unexpected task status %q", s.Status)
}
}
opts := []containerstore.Opts{
@@ -282,29 +304,29 @@ func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.S
// Load sandbox metadata.
exts, err := cntr.Extensions(ctx)
if err != nil {
return sandbox, fmt.Errorf("failed to get sandbox container extensions: %v", err)
return sandbox, errors.Wrap(err, "failed to get sandbox container extensions")
}
ext, ok := exts[sandboxMetadataExtension]
if !ok {
return sandbox, fmt.Errorf("metadata extension %q not found", sandboxMetadataExtension)
return sandbox, errors.Errorf("metadata extension %q not found", sandboxMetadataExtension)
}
data, err := typeurl.UnmarshalAny(&ext)
if err != nil {
return sandbox, fmt.Errorf("failed to unmarshal metadata extension %q: %v", ext, err)
return sandbox, errors.Wrapf(err, "failed to unmarshal metadata extension %q", ext)
}
meta := data.(*sandboxstore.Metadata)
// Load sandbox created timestamp.
info, err := cntr.Info(ctx)
if err != nil {
return sandbox, fmt.Errorf("failed to get sandbox container info: %v", err)
return sandbox, errors.Wrap(err, "failed to get sandbox container info")
}
createdAt := info.CreatedAt
// Load sandbox status.
t, err := cntr.Task(ctx, nil)
if err != nil && !errdefs.IsNotFound(err) {
return sandbox, fmt.Errorf("failed to load task: %v", err)
return sandbox, errors.Wrap(err, "failed to load task")
}
var s containerd.Status
var notFound bool
@@ -317,7 +339,7 @@ func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.S
if err != nil {
// It's still possible that task is deleted during this window.
if !errdefs.IsNotFound(err) {
return sandbox, fmt.Errorf("failed to get task status: %v", err)
return sandbox, errors.Wrap(err, "failed to get task status")
}
notFound = true
}
@@ -335,7 +357,7 @@ func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.S
} else {
// Task is not running. Delete the task and set sandbox state as NOTREADY.
if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
return sandbox, fmt.Errorf("failed to delete task: %v", err)
return sandbox, errors.Wrap(err, "failed to delete task")
}
state = sandboxstore.StateNotReady
}
@@ -359,7 +381,7 @@ func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.S
netNS, err := sandboxstore.LoadNetNS(meta.NetNSPath)
if err != nil {
if err != sandboxstore.ErrClosedNetNS {
return sandbox, fmt.Errorf("failed to load netns %q: %v", meta.NetNSPath, err)
return sandbox, errors.Wrapf(err, "failed to load netns %q", meta.NetNSPath)
}
netNS = nil
}
@@ -448,59 +470,30 @@ func loadImages(ctx context.Context, cImages []containerd.Image,
return images, nil
}
func cleanupOrphanedSandboxDirs(cntrs []containerd.Container, sandboxesRoot string) error {
// Cleanup orphaned sandbox directories.
dirs, err := ioutil.ReadDir(sandboxesRoot)
func cleanupOrphanedIDDirs(cntrs []containerd.Container, base string) error {
// Cleanup orphaned id directories.
dirs, err := ioutil.ReadDir(base)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to read pod sandboxes directory %q: %v", sandboxesRoot, err)
return errors.Wrap(err, "failed to read base directory")
}
cntrsMap := make(map[string]containerd.Container)
idsMap := make(map[string]containerd.Container)
for _, cntr := range cntrs {
cntrsMap[cntr.ID()] = cntr
idsMap[cntr.ID()] = cntr
}
for _, d := range dirs {
if !d.IsDir() {
logrus.Warnf("Invalid file %q found in pod sandboxes directory", d.Name())
logrus.Warnf("Invalid file %q found in base directory %q", d.Name(), base)
continue
}
if _, ok := cntrsMap[d.Name()]; ok {
// Do not remove sandbox directory if corresponding container is found.
if _, ok := idsMap[d.Name()]; ok {
// Do not remove id directory if corresponding container is found.
continue
}
sandboxDir := filepath.Join(sandboxesRoot, d.Name())
if err := system.EnsureRemoveAll(sandboxDir); err != nil {
logrus.WithError(err).Warnf("Failed to remove pod sandbox directory %q", sandboxDir)
dir := filepath.Join(base, d.Name())
if err := system.EnsureRemoveAll(dir); err != nil {
logrus.WithError(err).Warnf("Failed to remove id directory %q", dir)
} else {
logrus.Debugf("Cleanup orphaned pod sandbox directory %q", sandboxDir)
}
}
return nil
}
func cleanupOrphanedContainerDirs(cntrs []containerd.Container, containersRoot string) error {
// Cleanup orphaned container directories.
dirs, err := ioutil.ReadDir(containersRoot)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to read containers directory %q: %v", containersRoot, err)
}
cntrsMap := make(map[string]containerd.Container)
for _, cntr := range cntrs {
cntrsMap[cntr.ID()] = cntr
}
for _, d := range dirs {
if !d.IsDir() {
logrus.Warnf("Invalid file %q found in containers directory", d.Name())
continue
}
if _, ok := cntrsMap[d.Name()]; ok {
// Do not remove container directory if corresponding container is found.
continue
}
containerDir := filepath.Join(containersRoot, d.Name())
if err := system.EnsureRemoveAll(containerDir); err != nil {
logrus.WithError(err).Warnf("Failed to remove container directory %q", containerDir)
} else {
logrus.Debugf("Cleanup orphaned container directory %q", containerDir)
logrus.Debugf("Cleanup orphaned id directory %q", dir)
}
}
return nil

View File

@@ -24,7 +24,7 @@ import (
)
// ListPodSandbox returns a list of Sandbox.
func (c *criContainerdService) ListPodSandbox(ctx context.Context, r *runtime.ListPodSandboxRequest) (*runtime.ListPodSandboxResponse, error) {
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
@@ -56,14 +56,14 @@ func toCRISandbox(meta sandboxstore.Metadata, status sandboxstore.Status) *runti
}
}
func (c *criContainerdService) normalizePodSandboxFilter(filter *runtime.PodSandboxFilter) {
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 *criContainerdService) filterCRISandboxes(sandboxes []*runtime.PodSandbox, filter *runtime.PodSandboxFilter) []*runtime.PodSandbox {
func (c *criService) filterCRISandboxes(sandboxes []*runtime.PodSandbox, filter *runtime.PodSandboxFilter) []*runtime.PodSandbox {
if filter == nil {
return sandboxes
}

View File

@@ -18,12 +18,12 @@ package server
import (
"bytes"
"errors"
"fmt"
"io"
"os/exec"
"strings"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -33,11 +33,11 @@ import (
)
// PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
func (c *criContainerdService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (retRes *runtime.PortForwardResponse, retErr error) {
func (c *criService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (retRes *runtime.PortForwardResponse, retErr error) {
// TODO(random-liu): Run a socat container inside the sandbox to do portforward.
sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
if err != nil {
return nil, fmt.Errorf("failed to find sandbox %q: %v", r.GetPodSandboxId(), err)
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")
@@ -49,20 +49,20 @@ func (c *criContainerdService) PortForward(ctx context.Context, r *runtime.PortF
// portForward requires `nsenter` and `socat` on the node, it uses `nsenter` to enter the
// sandbox namespace, and run `socat` inside the namespace to forward stream for a specific
// port. The `socat` command keeps running until it exits or client disconnect.
func (c *criContainerdService) portForward(id string, port int32, stream io.ReadWriteCloser) error {
func (c *criService) portForward(id string, port int32, stream io.ReadWriteCloser) error {
s, err := c.sandboxStore.Get(id)
if err != nil {
return fmt.Errorf("failed to find sandbox %q in store: %v", id, err)
return errors.Wrapf(err, "failed to find sandbox %q in store", id)
}
t, err := s.Container.Task(ctrdutil.NamespacedContext(), nil)
if err != nil {
return fmt.Errorf("failed to get sandbox container task: %v", err)
return errors.Wrap(err, "failed to get sandbox container task")
}
pid := t.Pid()
socat, err := exec.LookPath("socat")
if err != nil {
return fmt.Errorf("failed to find socat: %v", err)
return errors.Wrap(err, "failed to find socat")
}
// Check following links for meaning of the options:
@@ -73,7 +73,7 @@ func (c *criContainerdService) portForward(id string, port int32, stream io.Read
nsenter, err := exec.LookPath("nsenter")
if err != nil {
return fmt.Errorf("failed to find nsenter: %v", err)
return errors.Wrap(err, "failed to find nsenter")
}
logrus.Infof("Executing port forwarding command: %s %s", nsenter, strings.Join(args, " "))
@@ -95,7 +95,7 @@ func (c *criContainerdService) portForward(id string, port int32, stream io.Read
// when the command (socat) exits.
in, err := cmd.StdinPipe()
if err != nil {
return fmt.Errorf("failed to create stdin pipe: %v", err)
return errors.Wrap(err, "failed to create stdin pipe")
}
go func() {
if _, err := io.Copy(in, stream); err != nil {
@@ -106,7 +106,7 @@ func (c *criContainerdService) portForward(id string, port int32, stream io.Read
}()
if err := cmd.Run(); err != nil {
return fmt.Errorf("nsenter command returns error: %v, stderr: %q", err, stderr.String())
return errors.Errorf("nsenter command returns error: %v, stderr: %q", err, stderr.String())
}
logrus.Infof("Finish port forwarding for %q port %d", id, port)

View File

@@ -17,11 +17,10 @@ limitations under the License.
package server
import (
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -32,12 +31,12 @@ import (
// RemovePodSandbox removes the sandbox. If there are running containers in the
// sandbox, they should be forcibly removed.
func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodSandboxRequest) (*runtime.RemovePodSandboxResponse, error) {
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, fmt.Errorf("an error occurred when try to find sandbox %q: %v",
r.GetPodSandboxId(), err)
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.Tracef("RemovePodSandbox called for sandbox %q that does not exist",
@@ -49,12 +48,12 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime.
// Return error if sandbox container is still running.
if sandbox.Status.Get().State == sandboxstore.StateReady {
return nil, fmt.Errorf("sandbox container %q is not fully stopped", id)
return nil, errors.Errorf("sandbox container %q is not fully stopped", id)
}
// Return error if sandbox network namespace is not closed yet.
if sandbox.NetNS != nil && !sandbox.NetNS.Closed() {
return nil, fmt.Errorf("sandbox network namespace %q is not fully closed", sandbox.NetNS.GetPath())
return nil, errors.Errorf("sandbox network namespace %q is not fully closed", sandbox.NetNS.GetPath())
}
// Remove all containers inside the sandbox.
@@ -69,21 +68,26 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime.
}
_, err = c.RemoveContainer(ctx, &runtime.RemoveContainerRequest{ContainerId: cntr.ID})
if err != nil {
return nil, fmt.Errorf("failed to remove container %q: %v", cntr.ID, err)
return nil, errors.Wrapf(err, "failed to remove container %q", cntr.ID)
}
}
// Cleanup the sandbox root directory.
sandboxRootDir := getSandboxRootDir(c.config.RootDir, id)
// Cleanup the sandbox root directories.
sandboxRootDir := c.getSandboxRootDir(id)
if err := system.EnsureRemoveAll(sandboxRootDir); err != nil {
return nil, fmt.Errorf("failed to remove sandbox root directory %q: %v",
sandboxRootDir, err)
return nil, errors.Wrapf(err, "failed to remove sandbox root directory %q",
sandboxRootDir)
}
volatileSandboxRootDir := c.getVolatileSandboxRootDir(id)
if err := system.EnsureRemoveAll(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, fmt.Errorf("failed to delete sandbox container %q: %v", id, err)
return nil, errors.Wrapf(err, "failed to delete sandbox container %q", id)
}
log.Tracef("Remove called for sandbox container %q that does not exist", id)
}

View File

@@ -26,16 +26,18 @@ import (
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/linux/runctypes"
"github.com/containerd/containerd/oci"
cni "github.com/containerd/go-cni"
"github.com/containerd/typeurl"
"github.com/cri-o/ocicni/pkg/ocicni"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
"golang.org/x/sys/unix"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
"github.com/containerd/cri/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/log"
@@ -50,7 +52,7 @@ func init() {
// RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
// the sandbox is in ready state.
func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) {
func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) {
config := r.GetConfig()
// Generate unique id and name for the sandbox and reserve the name.
@@ -60,7 +62,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
// Reserve the sandbox name to avoid concurrent `RunPodSandbox` request starting the
// same sandbox.
if err := c.sandboxNameIndex.Reserve(name, id); err != nil {
return nil, fmt.Errorf("failed to reserve sandbox name %q: %v", name, err)
return nil, errors.Wrapf(err, "failed to reserve sandbox name %q", name)
}
defer func() {
// Release the name if the function returns with an error.
@@ -84,7 +86,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
// Ensure sandbox container image snapshot.
image, err := c.ensureImageExists(ctx, c.config.SandboxImage)
if err != nil {
return nil, fmt.Errorf("failed to get sandbox image %q: %v", c.config.SandboxImage, err)
return nil, errors.Wrapf(err, "failed to get sandbox image %q", c.config.SandboxImage)
}
securityContext := config.GetLinux().GetSecurityContext()
//Create Network Namespace if it is not in host network
@@ -96,7 +98,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
// be used.
sandbox.NetNS, err = sandboxstore.NewNetNS()
if err != nil {
return nil, fmt.Errorf("failed to create network namespace for sandbox %q: %v", id, err)
return nil, errors.Wrapf(err, "failed to create network namespace for sandbox %q", id)
}
sandbox.NetNSPath = sandbox.NetNS.GetPath()
defer func() {
@@ -107,53 +109,38 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
sandbox.NetNSPath = ""
}
}()
if !c.config.EnableIPv6DAD {
// It's a known issue that IPv6 DAD increases sandbox start latency by several seconds.
// Disable it when it's not enabled to avoid the latency.
// See:
// * https://github.com/kubernetes/kubernetes/issues/54651
// * https://www.agwa.name/blog/post/beware_the_ipv6_dad_race_condition
if err := disableNetNSDAD(sandbox.NetNSPath); err != nil {
return nil, fmt.Errorf("failed to disable DAD for sandbox %q: %v", id, err)
}
}
// Setup network for sandbox.
podNetwork := ocicni.PodNetwork{
Name: config.GetMetadata().GetName(),
Namespace: config.GetMetadata().GetNamespace(),
ID: id,
NetNS: sandbox.NetNSPath,
PortMappings: toCNIPortMappings(config.GetPortMappings()),
}
if _, err = c.netPlugin.SetUpPod(podNetwork); err != nil {
return nil, fmt.Errorf("failed to setup network for sandbox %q: %v", id, err)
// 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.
sandbox.IP, err = c.setupPod(id, sandbox.NetNSPath, config)
if err != nil {
return nil, errors.Wrapf(err, "failed to setup network for sandbox %q", id)
}
defer func() {
if retErr != nil {
// Teardown network if an error is returned.
if err := c.netPlugin.TearDownPod(podNetwork); err != nil {
if err := c.teardownPod(id, sandbox.NetNSPath, config); err != nil {
logrus.WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
}
}
}()
ip, err := c.netPlugin.GetPodNetworkStatus(podNetwork)
if err != nil {
return nil, fmt.Errorf("failed to get network status for sandbox %q: %v", id, err)
}
// Certain VM based solutions like clear containers (Issue containerd/cri#524)
// rely on the assumption that CRI shim will not be querying the network namespace to check the
// network states such as IP.
// In furture runtime implementation should avoid relying on CRI shim implementation details.
// In this case however caching the IP will add a subtle performance enhancement by avoiding
// calls to network namespace of the pod to query the IP of the veth interface on every
// SandboxStatus request.
sandbox.IP = ip
}
ociRuntime, err := c.getSandboxRuntime(config)
if err != nil {
return nil, errors.Wrap(err, "failed to get sandbox runtime")
}
logrus.Debugf("Use OCI %+v for sandbox %q", ociRuntime, id)
// Create sandbox container.
spec, err := c.generateSandboxContainerSpec(id, config, &image.ImageSpec.Config, sandbox.NetNSPath)
if err != nil {
return nil, fmt.Errorf("failed to generate sandbox container spec: %v", err)
return nil, errors.Wrap(err, "failed to generate sandbox container spec")
}
logrus.Debugf("Sandbox container spec: %+v", spec)
@@ -167,7 +154,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
securityContext.GetPrivileged(),
c.seccompEnabled)
if err != nil {
return nil, fmt.Errorf("failed to generate seccomp spec opts: %v", err)
return nil, errors.Wrap(err, "failed to generate seccomp spec opts")
}
if seccompSpecOpts != nil {
specOpts = append(specOpts, seccompSpecOpts)
@@ -182,15 +169,15 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
containerd.WithContainerLabels(sandboxLabels),
containerd.WithContainerExtension(sandboxMetadataExtension, &sandbox.Metadata),
containerd.WithRuntime(
c.config.ContainerdConfig.Runtime,
ociRuntime.Type,
&runctypes.RuncOptions{
Runtime: c.config.ContainerdConfig.RuntimeEngine,
RuntimeRoot: c.config.ContainerdConfig.RuntimeRoot,
Runtime: ociRuntime.Engine,
RuntimeRoot: ociRuntime.Root,
SystemdCgroup: c.config.SystemdCgroup})} // TODO (mikebrow): add CriuPath when we add support for pause
container, err := c.client.NewContainer(ctx, id, opts...)
if err != nil {
return nil, fmt.Errorf("failed to create containerd container: %v", err)
return nil, errors.Wrap(err, "failed to create containerd container")
}
defer func() {
if retErr != nil {
@@ -202,11 +189,11 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
}
}()
// Create sandbox container root directory.
sandboxRootDir := getSandboxRootDir(c.config.RootDir, id)
// Create sandbox container root directories.
sandboxRootDir := c.getSandboxRootDir(id)
if err := c.os.MkdirAll(sandboxRootDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create sandbox root directory %q: %v",
sandboxRootDir, err)
return nil, errors.Wrapf(err, "failed to create sandbox root directory %q",
sandboxRootDir)
}
defer func() {
if retErr != nil {
@@ -217,14 +204,28 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
}
}
}()
// Setup sandbox /dev/shm, /etc/hosts and /etc/resolv.conf.
if err = c.setupSandboxFiles(sandboxRootDir, config); err != nil {
return nil, fmt.Errorf("failed to setup sandbox files: %v", err)
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 {
if err = c.unmountSandboxFiles(sandboxRootDir, config); err != nil {
// Cleanup the volatile sandbox root directory.
if err := c.os.RemoveAll(volatileSandboxRootDir); err != nil {
logrus.WithError(err).Errorf("Failed to remove volatile sandbox root directory %q",
volatileSandboxRootDir)
}
}
}()
// Setup sandbox /dev/shm, /etc/hosts and /etc/resolv.conf.
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.unmountSandboxFiles(id, config); err != nil {
logrus.WithError(err).Errorf("Failed to unmount sandbox files in %q",
sandboxRootDir)
}
@@ -234,19 +235,19 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
// Update sandbox created timestamp.
info, err := container.Info(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get sandbox container info: %v", err)
return nil, errors.Wrap(err, "failed to get sandbox container info")
}
if err := sandbox.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
status.CreatedAt = info.CreatedAt
return status, nil
}); err != nil {
return nil, fmt.Errorf("failed to update sandbox created timestamp: %v", err)
return nil, errors.Wrap(err, "failed to update sandbox created timestamp")
}
// Add sandbox into sandbox store in UNKNOWN state.
sandbox.Container = container
if err := c.sandboxStore.Add(sandbox); err != nil {
return nil, fmt.Errorf("failed to add sandbox %+v into store: %v", sandbox, err)
return nil, errors.Wrapf(err, "failed to add sandbox %+v into store", sandbox)
}
defer func() {
// Delete sandbox from sandbox store if there is an error.
@@ -287,7 +288,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
// We don't need stdio for sandbox container.
task, err := container.NewTask(ctx, containerdio.NullIO)
if err != nil {
return status, fmt.Errorf("failed to create containerd task: %v", err)
return status, errors.Wrap(err, "failed to create containerd task")
}
defer func() {
if retErr != nil {
@@ -302,8 +303,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
}()
if err := task.Start(ctx); err != nil {
return status, fmt.Errorf("failed to start sandbox container task %q: %v",
id, err)
return status, errors.Wrapf(err, "failed to start sandbox container task %q", id)
}
// Set the pod sandbox as ready after successfully start sandbox container.
@@ -311,13 +311,13 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
status.State = sandboxstore.StateReady
return status, nil
}); err != nil {
return nil, fmt.Errorf("failed to start sandbox container: %v", err)
return nil, errors.Wrap(err, "failed to start sandbox container")
}
return &runtime.RunPodSandboxResponse{PodSandboxId: id}, nil
}
func (c *criContainerdService) generateSandboxContainerSpec(id string, config *runtime.PodSandboxConfig,
func (c *criService) generateSandboxContainerSpec(id string, config *runtime.PodSandboxConfig,
imageConfig *imagespec.ImageConfig, nsPath string) (*runtimespec.Spec, error) {
// Creates a spec Generator with the default spec.
// TODO(random-liu): [P1] Compare the default settings with docker and containerd default.
@@ -338,7 +338,7 @@ func (c *criContainerdService) generateSandboxContainerSpec(id string, config *r
if len(imageConfig.Entrypoint) == 0 {
// Pause image must have entrypoint.
return nil, fmt.Errorf("invalid empty entrypoint in image config %+v", imageConfig)
return nil, errors.Errorf("invalid empty entrypoint in image config %+v", imageConfig)
}
// Set process commands.
g.SetProcessArgs(append(imageConfig.Entrypoint, imageConfig.Cmd...))
@@ -383,7 +383,7 @@ func (c *criContainerdService) generateSandboxContainerSpec(id string, config *r
selinuxOpt := securityContext.GetSelinuxOptions()
processLabel, mountLabel, err := initSelinuxOpts(selinuxOpt)
if err != nil {
return nil, fmt.Errorf("failed to init selinux options %+v: %v", securityContext.GetSelinuxOptions(), err)
return nil, errors.Wrapf(err, "failed to init selinux options %+v", securityContext.GetSelinuxOptions())
}
g.SetProcessSelinuxLabel(processLabel)
g.SetLinuxMountLabel(mountLabel)
@@ -412,11 +412,11 @@ func (c *criContainerdService) generateSandboxContainerSpec(id string, config *r
// setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts
// and /etc/resolv.conf.
func (c *criContainerdService) setupSandboxFiles(rootDir string, config *runtime.PodSandboxConfig) error {
func (c *criService) setupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
// TODO(random-liu): Consider whether we should maintain /etc/hosts and /etc/resolv.conf in kubelet.
sandboxEtcHosts := getSandboxHosts(rootDir)
sandboxEtcHosts := c.getSandboxHosts(id)
if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0644); err != nil {
return fmt.Errorf("failed to generate sandbox hosts file %q: %v", sandboxEtcHosts, err)
return errors.Wrapf(err, "failed to generate sandbox hosts file %q", sandboxEtcHosts)
}
// Set DNS options. Maintain a resolv.conf for the sandbox.
@@ -425,36 +425,36 @@ func (c *criContainerdService) setupSandboxFiles(rootDir string, config *runtime
if dnsConfig := config.GetDnsConfig(); dnsConfig != nil {
resolvContent, err = parseDNSOptions(dnsConfig.Servers, dnsConfig.Searches, dnsConfig.Options)
if err != nil {
return fmt.Errorf("failed to parse sandbox DNSConfig %+v: %v", dnsConfig, err)
return errors.Wrapf(err, "failed to parse sandbox DNSConfig %+v", dnsConfig)
}
}
resolvPath := getResolvPath(rootDir)
resolvPath := c.getResolvPath(id)
if resolvContent == "" {
// copy host's resolv.conf to resolvPath
err = c.os.CopyFile(resolvConfPath, resolvPath, 0644)
if err != nil {
return fmt.Errorf("failed to copy host's resolv.conf to %q: %v", resolvPath, err)
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 fmt.Errorf("failed to write resolv content to %q: %v", resolvPath, err)
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 fmt.Errorf("host %q is not available for host ipc: %v", devShm, err)
return errors.Wrapf(err, "host %q is not available for host ipc", devShm)
}
} else {
sandboxDevShm := getSandboxDevShm(rootDir)
sandboxDevShm := c.getSandboxDevShm(id)
if err := c.os.MkdirAll(sandboxDevShm, 0700); err != nil {
return fmt.Errorf("failed to create sandbox shm: %v", err)
return errors.Wrap(err, "failed to create sandbox shm")
}
shmproperty := fmt.Sprintf("mode=1777,size=%d", defaultShmSize)
if err := c.os.Mount("shm", sandboxDevShm, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), shmproperty); err != nil {
return fmt.Errorf("failed to mount sandbox shm: %v", err)
return errors.Wrap(err, "failed to mount sandbox shm")
}
}
@@ -467,7 +467,7 @@ func parseDNSOptions(servers, searches, options []string) (string, error) {
resolvContent := ""
if len(searches) > maxDNSSearches {
return "", fmt.Errorf("DNSOption.Searches has more than 6 domains")
return "", errors.New("DNSOption.Searches has more than 6 domains")
}
if len(searches) > 0 {
@@ -489,23 +489,52 @@ func parseDNSOptions(servers, searches, options []string) (string, error) {
// remove these files. Unmount should *NOT* return error when:
// 1) The mount point is already unmounted.
// 2) The mount point doesn't exist.
func (c *criContainerdService) unmountSandboxFiles(rootDir string, config *runtime.PodSandboxConfig) error {
func (c *criService) unmountSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetIpc() != runtime.NamespaceMode_NODE {
if err := c.os.Unmount(getSandboxDevShm(rootDir), unix.MNT_DETACH); err != nil && !os.IsNotExist(err) {
return err
path, err := c.os.FollowSymlinkInScope(c.getSandboxDevShm(id), "/")
if err != nil {
return errors.Wrap(err, "failed to follow symlink")
}
if err := c.os.Unmount(path, unix.MNT_DETACH); err != nil && !os.IsNotExist(err) {
return errors.Wrapf(err, "failed to unmount %q", path)
}
}
return nil
}
// setupPod setups up the network for a pod
func (c *criService) setupPod(id string, path string, config *runtime.PodSandboxConfig) (string, error) {
if c.netPlugin == nil {
return "", errors.New("cni config not intialized")
}
labels := getPodCNILabels(id, config)
result, err := c.netPlugin.Setup(id,
path,
cni.WithLabels(labels),
cni.WithCapabilityPortMap(toCNIPortMappings(config.GetPortMappings())))
if err != nil {
return "", err
}
// Check if the default interface has IP config
if configs, ok := result.Interfaces[defaultIfName]; ok && len(configs.IPConfigs) > 0 {
return configs.IPConfigs[0].IP.String(), nil
}
// If it comes here then the result was invalid so destroy the pod network and return error
if err := c.teardownPod(id, path, config); err != nil {
logrus.WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
}
return "", errors.Errorf("failed to find network info for sandbox %q", id)
}
// toCNIPortMappings converts CRI port mappings to CNI.
func toCNIPortMappings(criPortMappings []*runtime.PortMapping) []ocicni.PortMapping {
var portMappings []ocicni.PortMapping
func toCNIPortMappings(criPortMappings []*runtime.PortMapping) []cni.PortMapping {
var portMappings []cni.PortMapping
for _, mapping := range criPortMappings {
if mapping.HostPort <= 0 {
continue
}
portMappings = append(portMappings, ocicni.PortMapping{
portMappings = append(portMappings, cni.PortMapping{
HostPort: mapping.HostPort,
ContainerPort: mapping.ContainerPort,
Protocol: strings.ToLower(mapping.Protocol.String()),
@@ -514,3 +543,48 @@ func toCNIPortMappings(criPortMappings []*runtime.PortMapping) []ocicni.PortMapp
}
return portMappings
}
// untrustedWorkload returns true if the sandbox contains untrusted workload.
func untrustedWorkload(config *runtime.PodSandboxConfig) bool {
return config.GetAnnotations()[annotations.UntrustedWorkload] == "true"
}
// hostPrivilegedSandbox returns true if the sandbox configuration
// requires additional host privileges for the sandbox.
func hostPrivilegedSandbox(config *runtime.PodSandboxConfig) bool {
securityContext := config.GetLinux().GetSecurityContext()
if securityContext.GetPrivileged() {
return true
}
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) (criconfig.Runtime, error) {
untrusted := false
if untrustedWorkload(config) {
// TODO(random-liu): Figure out we should return error or not.
if hostPrivilegedSandbox(config) {
return criconfig.Runtime{}, errors.New("untrusted workload with host privilege is not allowed")
}
untrusted = true
}
if untrusted {
if c.config.ContainerdConfig.UntrustedWorkloadRuntime.Type == "" {
return criconfig.Runtime{}, errors.New("no runtime for untrusted workload is configured")
}
return c.config.ContainerdConfig.UntrustedWorkloadRuntime, nil
}
return c.config.ContainerdConfig.DefaultRuntime, nil
}

View File

@@ -18,22 +18,23 @@ package server
import (
"encoding/json"
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
criconfig "github.com/containerd/cri/pkg/config"
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
)
// PodSandboxStatus returns the status of the PodSandbox.
func (c *criContainerdService) PodSandboxStatus(ctx context.Context, r *runtime.PodSandboxStatusRequest) (*runtime.PodSandboxStatusResponse, error) {
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, fmt.Errorf("an error occurred when try to find sandbox: %v", err)
return nil, errors.Wrap(err, "an error occurred when try to find sandbox")
}
ip := c.getIP(sandbox)
@@ -45,7 +46,7 @@ func (c *criContainerdService) PodSandboxStatus(ctx context.Context, r *runtime.
// Generate verbose information.
info, err := toCRISandboxInfo(ctx, sandbox)
if err != nil {
return nil, fmt.Errorf("failed to get verbose sandbox container info: %v", err)
return nil, errors.Wrap(err, "failed to get verbose sandbox container info")
}
return &runtime.PodSandboxStatusResponse{
@@ -54,7 +55,7 @@ func (c *criContainerdService) PodSandboxStatus(ctx context.Context, r *runtime.
}, nil
}
func (c *criContainerdService) getIP(sandbox sandboxstore.Sandbox) string {
func (c *criService) getIP(sandbox sandboxstore.Sandbox) string {
config := sandbox.Config
if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE {
@@ -107,6 +108,7 @@ type sandboxInfo struct {
Image string `json:"image"`
SnapshotKey string `json:"snapshotKey"`
Snapshotter string `json:"snapshotter"`
Runtime *criconfig.Runtime `json:"runtime"`
Config *runtime.PodSandboxConfig `json:"config"`
RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"`
}
@@ -116,14 +118,14 @@ func toCRISandboxInfo(ctx context.Context, sandbox sandboxstore.Sandbox) (map[st
container := sandbox.Container
task, err := container.Task(ctx, nil)
if err != nil && !errdefs.IsNotFound(err) {
return nil, fmt.Errorf("failed to get sandbox container task: %v", err)
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, fmt.Errorf("failed to get task status: %v", err)
return nil, errors.Wrap(err, "failed to get task status")
}
processStatus = taskStatus.Status
@@ -148,13 +150,13 @@ func toCRISandboxInfo(ctx context.Context, sandbox sandboxstore.Sandbox) (map[st
spec, err := container.Spec(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get sandbox container runtime spec: %v", err)
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, fmt.Errorf("failed to get sandbox container info: %v", err)
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
@@ -163,9 +165,15 @@ func toCRISandboxInfo(ctx context.Context, sandbox sandboxstore.Sandbox) (map[st
si.SnapshotKey = ctrInfo.SnapshotKey
si.Snapshotter = ctrInfo.Snapshotter
ociRuntime, err := getRuntimeConfigFromContainerInfo(ctrInfo)
if err != nil {
return nil, errors.Wrap(err, "failed to get sandbox container runtime config")
}
si.Runtime = &ociRuntime
infoBytes, err := json.Marshal(si)
if err != nil {
return nil, fmt.Errorf("failed to marshal info %v: %v", si, err)
return nil, errors.Wrapf(err, "failed to marshal info %v", si)
}
return map[string]string{
"info": string(infoBytes),

View File

@@ -17,13 +17,13 @@ limitations under the License.
package server
import (
"fmt"
"os"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
"github.com/cri-o/ocicni/pkg/ocicni"
cni "github.com/containerd/go-cni"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -33,11 +33,11 @@ import (
// StopPodSandbox stops the sandbox. If there are any running containers in the
// sandbox, they should be forcibly terminated.
func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (*runtime.StopPodSandboxResponse, error) {
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, fmt.Errorf("an error occurred when try to find sandbox %q: %v",
r.GetPodSandboxId(), err)
return nil, errors.Wrapf(err, "an error occurred when try to find sandbox %q",
r.GetPodSandboxId())
}
// Use the full sandbox id.
id := sandbox.ID
@@ -54,7 +54,7 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St
// Forcibly stop the container. Do not use `StopContainer`, because it introduces a race
// if a container is removed after list.
if err = c.stopContainer(ctx, container, 0); err != nil {
return nil, fmt.Errorf("failed to stop container %q: %v", container.ID, err)
return nil, errors.Wrapf(err, "failed to stop container %q", container.ID)
}
}
@@ -62,17 +62,11 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St
if sandbox.NetNSPath != "" && sandbox.NetNS != nil {
if _, err := os.Stat(sandbox.NetNSPath); err != nil {
if !os.IsNotExist(err) {
return nil, fmt.Errorf("failed to stat network namespace path %s :%v", sandbox.NetNSPath, err)
return nil, errors.Wrapf(err, "failed to stat network namespace path %s", sandbox.NetNSPath)
}
} else {
if teardownErr := c.netPlugin.TearDownPod(ocicni.PodNetwork{
Name: sandbox.Config.GetMetadata().GetName(),
Namespace: sandbox.Config.GetMetadata().GetNamespace(),
ID: id,
NetNS: sandbox.NetNSPath,
PortMappings: toCNIPortMappings(sandbox.Config.GetPortMappings()),
}); teardownErr != nil {
return nil, fmt.Errorf("failed to destroy network for sandbox %q: %v", id, teardownErr)
if teardownErr := c.teardownPod(id, sandbox.NetNSPath, sandbox.Config); teardownErr != nil {
return nil, errors.Wrapf(teardownErr, "failed to destroy network for sandbox %q", id)
}
}
/*TODO:It is still possible that containerd crashes after we teardown the network, but before we remove the network namespace.
@@ -81,56 +75,68 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St
//Close the sandbox network namespace if it was created
if err = sandbox.NetNS.Remove(); err != nil {
return nil, fmt.Errorf("failed to remove network namespace for sandbox %q: %v", id, err)
return nil, errors.Wrapf(err, "failed to remove network namespace for sandbox %q", id)
}
}
logrus.Infof("TearDown network for sandbox %q successfully", id)
sandboxRoot := getSandboxRootDir(c.config.RootDir, id)
if err := c.unmountSandboxFiles(sandboxRoot, sandbox.Config); err != nil {
return nil, fmt.Errorf("failed to unmount sandbox files in %q: %v", sandboxRoot, err)
if err := c.unmountSandboxFiles(id, sandbox.Config); err != nil {
return nil, errors.Wrap(err, "failed to unmount sandbox files")
}
// Only stop sandbox container when it's running.
if sandbox.Status.Get().State == sandboxstore.StateReady {
if err := c.stopSandboxContainer(ctx, sandbox); err != nil {
return nil, fmt.Errorf("failed to stop sandbox container %q: %v", id, err)
return nil, errors.Wrapf(err, "failed to stop sandbox container %q", id)
}
}
return &runtime.StopPodSandboxResponse{}, nil
}
// stopSandboxContainer kills and deletes sandbox container.
func (c *criContainerdService) stopSandboxContainer(ctx context.Context, sandbox sandboxstore.Sandbox) error {
func (c *criService) stopSandboxContainer(ctx context.Context, sandbox sandboxstore.Sandbox) error {
container := sandbox.Container
task, err := container.Task(ctx, nil)
if err != nil {
if errdefs.IsNotFound(err) {
return nil
}
return fmt.Errorf("failed to get sandbox container: %v", err)
return errors.Wrap(err, "failed to get sandbox container")
}
// Delete the sandbox container from containerd.
_, err = task.Delete(ctx, containerd.WithProcessKill)
if err != nil && !errdefs.IsNotFound(err) {
return fmt.Errorf("failed to delete sandbox container: %v", err)
return errors.Wrap(err, "failed to delete sandbox container")
}
return c.waitSandboxStop(ctx, sandbox, killContainerTimeout)
}
// waitSandboxStop waits for sandbox to be stopped until timeout exceeds or context is cancelled.
func (c *criContainerdService) waitSandboxStop(ctx context.Context, sandbox sandboxstore.Sandbox, timeout time.Duration) error {
func (c *criService) waitSandboxStop(ctx context.Context, sandbox sandboxstore.Sandbox, timeout time.Duration) error {
timeoutTimer := time.NewTimer(timeout)
defer timeoutTimer.Stop()
select {
case <-ctx.Done():
return fmt.Errorf("wait sandbox container %q is cancelled", sandbox.ID)
return errors.Errorf("wait sandbox container %q is cancelled", sandbox.ID)
case <-timeoutTimer.C:
return fmt.Errorf("wait sandbox container %q stop timeout", sandbox.ID)
return errors.Errorf("wait sandbox container %q stop timeout", sandbox.ID)
case <-sandbox.Stopped():
return nil
}
}
// teardownPod removes the network from the pod
func (c *criService) teardownPod(id string, path string, config *runtime.PodSandboxConfig) error {
if c.netPlugin == nil {
return errors.New("cni config not intialized")
}
labels := getPodCNILabels(id, config)
return c.netPlugin.Remove(id,
path,
cni.WithLabels(labels),
cni.WithCapabilityPortMap(toCNIPortMappings(config.GetPortMappings())))
}

View File

@@ -24,10 +24,11 @@ import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/plugin"
"github.com/cri-o/ocicni/pkg/ocicni"
cni "github.com/containerd/go-cni"
runcapparmor "github.com/opencontainers/runc/libcontainer/apparmor"
runcseccomp "github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
@@ -49,11 +50,11 @@ import (
type grpcServices interface {
runtime.RuntimeServiceServer
runtime.ImageServiceServer
api.CRIContainerdServiceServer
api.CRIPluginServiceServer
}
// CRIContainerdService is the interface implement CRI remote service server.
type CRIContainerdService interface {
// 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
@@ -61,8 +62,8 @@ type CRIContainerdService interface {
grpcServices
}
// criContainerdService implements CRIContainerdService.
type criContainerdService struct {
// criService implements CRIService.
type criService struct {
// config contains all configurations.
config criconfig.Config
// imageFSPath is the path to image filesystem.
@@ -88,7 +89,7 @@ type criContainerdService struct {
// snapshotStore stores information of all snapshots.
snapshotStore *snapshotstore.Store
// netPlugin is used to setup and teardown network when run/stop pod sandbox.
netPlugin ocicni.CNIPlugin
netPlugin cni.CNI
// client is an instance of the containerd client
client *containerd.Client
// streamServer is the streaming server serves container streaming request.
@@ -100,10 +101,10 @@ type criContainerdService struct {
initialized atomic.Bool
}
// NewCRIContainerdService returns a new instance of CRIContainerdService
func NewCRIContainerdService(config criconfig.Config, client *containerd.Client) (CRIContainerdService, error) {
// NewCRIService returns a new instance of CRIService
func NewCRIService(config criconfig.Config, client *containerd.Client) (CRIService, error) {
var err error
c := &criContainerdService{
c := &criService{
config: config,
client: client,
apparmorEnabled: runcapparmor.IsEnabled(),
@@ -129,15 +130,26 @@ func NewCRIContainerdService(config criconfig.Config, client *containerd.Client)
c.imageFSPath = imageFSPath(config.ContainerdRootDir, config.ContainerdConfig.Snapshotter)
logrus.Infof("Get image filesystem path %q", c.imageFSPath)
c.netPlugin, err = ocicni.InitCNI(config.NetworkPluginConfDir, config.NetworkPluginBinDir)
// Pod needs to attach to atleast 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(config.NetworkPluginConfDir),
cni.WithPluginDir([]string{config.NetworkPluginBinDir}))
if err != nil {
return nil, fmt.Errorf("failed to initialize cni plugin: %v", err)
return nil, errors.Wrap(err, "failed to initialize cni")
}
// Try to load the config if it exists. Just log the error if load fails
// This is not disruptive for containerd to panic
if err := c.netPlugin.Load(cni.WithLoNetwork(), cni.WithDefaultConf()); err != nil {
logrus.WithError(err).Error("Failed to load cni during init, please check CRI plugin status before setting up network for pods")
}
// prepare streaming server
c.streamServer, err = newStreamServer(c, config.StreamServerAddress, config.StreamServerPort)
if err != nil {
return nil, fmt.Errorf("failed to create stream server: %v", err)
return nil, errors.Wrap(err, "failed to create stream server")
}
c.eventMonitor = newEventMonitor(c.containerStore, c.sandboxStore)
@@ -147,29 +159,29 @@ func NewCRIContainerdService(config criconfig.Config, client *containerd.Client)
// Register registers all required services onto a specific grpc server.
// This is used by containerd cri plugin.
func (c *criContainerdService) Register(s *grpc.Server) error {
func (c *criService) Register(s *grpc.Server) error {
instrumented := newInstrumentedService(c)
runtime.RegisterRuntimeServiceServer(s, instrumented)
runtime.RegisterImageServiceServer(s, instrumented)
api.RegisterCRIContainerdServiceServer(s, instrumented)
api.RegisterCRIPluginServiceServer(s, instrumented)
return nil
}
// Run starts the cri-containerd service.
func (c *criContainerdService) Run() error {
// 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 fmt.Errorf("failed to recover state: %v", err)
return errors.Wrap(err, "failed to recover state")
}
// Start event handler.
logrus.Info("Start event monitor")
eventMonitorCloseCh, err := c.eventMonitor.start()
if err != nil {
return fmt.Errorf("failed to start event monitor: %v", err)
return errors.Wrap(err, "failed to start event monitor")
}
// Start snapshot stats syncer, it doesn't need to be stopped.
@@ -194,13 +206,13 @@ func (c *criContainerdService) Run() error {
// Set the server as initialized. GRPC services could start serving traffic.
c.initialized.Set()
// Stop the whole cri-containerd service if any of the critical service exits.
// Stop the whole CRI service if any of the critical service exits.
select {
case <-eventMonitorCloseCh:
case <-streamServerCloseCh:
}
if err := c.Close(); err != nil {
return fmt.Errorf("failed to stop cri service: %v", err)
return errors.Wrap(err, "failed to stop cri service")
}
<-eventMonitorCloseCh
@@ -223,13 +235,13 @@ func (c *criContainerdService) Run() error {
return nil
}
// Stop stops the cri-containerd service.
func (c *criContainerdService) Close() error {
logrus.Info("Stop cri-containerd service")
// Stop stops the CRI service.
func (c *criService) Close() error {
logrus.Info("Stop CRI service")
// TODO(random-liu): Make event monitor stop synchronous.
c.eventMonitor.stop()
if err := c.streamServer.Stop(); err != nil {
return fmt.Errorf("failed to stop stream server: %v", err)
return errors.Wrap(err, "failed to stop stream server")
}
return nil
}

View File

@@ -18,11 +18,11 @@ package server
import (
"context"
"fmt"
"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"
@@ -80,7 +80,7 @@ func (s *snapshotsSyncer) sync() error {
snapshots = append(snapshots, info)
return nil
}); err != nil {
return fmt.Errorf("walk all snapshots failed: %v", err)
return errors.Wrap(err, "walk all snapshots failed")
}
for _, info := range snapshots {
sn, err := s.store.Get(info.Name)

View File

@@ -21,6 +21,7 @@ import (
"fmt"
goruntime "runtime"
cni "github.com/containerd/go-cni"
"golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)
@@ -29,7 +30,7 @@ import (
const networkNotReadyReason = "NetworkPluginNotReady"
// Status returns the status of the runtime.
func (c *criContainerdService) Status(ctx context.Context, r *runtime.StatusRequest) (*runtime.StatusResponse, error) {
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{
@@ -40,10 +41,14 @@ func (c *criContainerdService) Status(ctx context.Context, r *runtime.StatusRequ
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)
// If it is not initialized, then load the config and retry
if err = c.netPlugin.Load(cni.WithLoNetwork(), cni.WithDefaultConf()); err != nil {
networkCondition.Status = false
networkCondition.Reason = networkNotReadyReason
networkCondition.Message = fmt.Sprintf("Network plugin returns error: %v", err)
}
}
resp := &runtime.StatusResponse{

View File

@@ -17,39 +17,65 @@ limitations under the License.
package server
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io"
"math"
"math/big"
"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/kubernetes/pkg/kubelet/server/streaming"
"k8s.io/utils/exec"
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
)
func newStreamServer(c *criContainerdService, addr, port string) (streaming.Server, error) {
const (
// certOrganizationName is the name of this organization, used for certificates etc.
certOrganizationName = "containerd"
// certCommonName is the common name of the CRI plugin
certCommonName = "cri"
)
func newStreamServer(c *criService, addr, port string) (streaming.Server, error) {
if addr == "" {
a, err := k8snet.ChooseBindAddress(nil)
if err != nil {
return nil, fmt.Errorf("failed to get stream server address: %v", err)
return nil, errors.Wrap(err, "failed to get stream server address")
}
addr = a.String()
}
config := streaming.DefaultConfig
config.Addr = net.JoinHostPort(addr, port)
runtime := newStreamRuntime(c)
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, runtime)
}
type streamRuntime struct {
c *criContainerdService
c *criService
}
func newStreamRuntime(c *criContainerdService) streaming.Runtime {
func newStreamRuntime(c *criService) streaming.Runtime {
return &streamRuntime{c: c}
}
@@ -66,13 +92,13 @@ func (s *streamRuntime) Exec(containerID string, cmd []string, stdin io.Reader,
resize: resize,
})
if err != nil {
return fmt.Errorf("failed to exec in container: %v", err)
return errors.Wrap(err, "failed to exec in container")
}
if *exitCode == 0 {
return nil
}
return &exec.CodeExitError{
Err: fmt.Errorf("error executing command %v, exit code %d", cmd, *exitCode),
Err: errors.Errorf("error executing command %v, exit code %d", cmd, *exitCode),
Code: int(*exitCode),
}
}
@@ -84,7 +110,7 @@ func (s *streamRuntime) Attach(containerID string, in io.Reader, out, err io.Wri
func (s *streamRuntime) PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error {
if port <= 0 || port > math.MaxUint16 {
return fmt.Errorf("invalid port %d", port)
return errors.Errorf("invalid port %d", port)
}
return s.c.portForward(podSandboxID, port, stream)
}
@@ -112,3 +138,87 @@ func handleResizing(resize <-chan remotecommand.TerminalSize, resizeFunc func(si
}
}()
}
// newTLSCert returns a tls.certificate loaded from a newly generated
// x509certificate from a newly generated rsa public/private key pair. The
// x509certificate is self signed.
// TODO (mikebrow): replace / rewrite this function to support using CA
// signing of the cetificate. 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 }
var years = 1 // duration of certificate
// Generate new private key
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return fail(errors.Wrap(err, "private key cannot be created"))
}
// Generate pem block using the private key
keyPem := pem.EncodeToMemory(&pem.Block{
Type: k8scert.RSAPrivateKeyBlockType,
Bytes: x509.MarshalPKCS1PrivateKey(privKey),
})
// Generate a new random serial number for certificate
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return fail(errors.Wrap(err, "failed to generate serial number"))
}
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"))
}
// Configure and create new certificate
tml := x509.Certificate{
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(years, 0, 0),
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: fmt.Sprintf("%s:%s:%s", certOrganizationName, certCommonName, hostName),
Organization: []string{certOrganizationName},
},
BasicConstraintsValid: true,
}
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
}
tml.IPAddresses = append(tml.IPAddresses, ip)
tml.DNSNames = append(tml.DNSNames, ip.String())
}
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &privKey.PublicKey, privKey)
if err != nil {
return fail(errors.Wrap(err, "certificate cannot be created"))
}
// Generate a pem block with the certificate
certPem := pem.EncodeToMemory(&pem.Block{
Type: k8scert.CertificateBlockType,
Bytes: cert,
})
// 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
}

View File

@@ -24,6 +24,6 @@ import (
// UpdateRuntimeConfig updates the runtime config. Currently only handles podCIDR updates.
// TODO(random-liu): Figure out how to handle pod cidr in the cri plugin.
func (c *criContainerdService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateRuntimeConfigRequest) (*runtime.UpdateRuntimeConfigResponse, error) {
func (c *criService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateRuntimeConfigRequest) (*runtime.UpdateRuntimeConfigResponse, error) {
return &runtime.UpdateRuntimeConfigResponse{}, nil
}

View File

@@ -32,7 +32,7 @@ const (
)
// Version returns the runtime name, runtime version and runtime API version.
func (c *criContainerdService) Version(ctx context.Context, r *runtime.VersionRequest) (*runtime.VersionResponse, error) {
func (c *criService) Version(ctx context.Context, r *runtime.VersionRequest) (*runtime.VersionResponse, error) {
return &runtime.VersionResponse{
Version: kubeAPIVersion,
RuntimeName: containerName,

View File

@@ -18,8 +18,8 @@ package container
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)
@@ -80,5 +80,5 @@ func (c *Metadata) UnmarshalJSON(data []byte) error {
*c = Metadata(versioned.Metadata)
return nil
}
return fmt.Errorf("unsupported version: %q", versioned.Version)
return errors.Errorf("unsupported version: %q", versioned.Version)
}

View File

@@ -18,13 +18,13 @@ package container
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"github.com/docker/docker/pkg/ioutils"
"github.com/pkg/errors"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)
@@ -95,7 +95,7 @@ func (s *Status) decode(data []byte) error {
*s = versioned.Status
return nil
}
return fmt.Errorf("unsupported version")
return errors.New("unsupported version")
}
// UpdateFunc is function used to update the container status. If there
@@ -125,11 +125,11 @@ type StatusStorage interface {
func StoreStatus(root, id string, status Status) (StatusStorage, error) {
data, err := status.encode()
if err != nil {
return nil, fmt.Errorf("failed to encode status: %v", err)
return nil, errors.Wrap(err, "failed to encode status")
}
path := filepath.Join(root, "status")
if err := ioutils.AtomicWriteFile(path, data, 0600); err != nil {
return nil, fmt.Errorf("failed to checkpoint status to %q: %v", path, err)
return nil, errors.Wrapf(err, "failed to checkpoint status to %q", path)
}
return &statusStorage{
path: path,
@@ -143,11 +143,11 @@ func LoadStatus(root, id string) (Status, error) {
path := filepath.Join(root, "status")
data, err := ioutil.ReadFile(path)
if err != nil {
return Status{}, fmt.Errorf("failed to read status from %q: %v", path, err)
return Status{}, errors.Wrapf(err, "failed to read status from %q", path)
}
var status Status
if err := status.decode(data); err != nil {
return Status{}, fmt.Errorf("failed to decode status %q: %v", data, err)
return Status{}, errors.Wrapf(err, "failed to decode status %q", data)
}
return status, nil
}
@@ -175,10 +175,10 @@ func (s *statusStorage) UpdateSync(u UpdateFunc) error {
}
data, err := newStatus.encode()
if err != nil {
return fmt.Errorf("failed to encode status: %v", err)
return errors.Wrap(err, "failed to encode status")
}
if err := ioutils.AtomicWriteFile(s.path, data, 0600); err != nil {
return fmt.Errorf("failed to checkpoint status to %q: %v", s.path, err)
return errors.Wrapf(err, "failed to checkpoint status to %q", s.path)
}
s.status = newStatus
return nil

View File

@@ -18,8 +18,8 @@ package sandbox
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)
@@ -76,5 +76,5 @@ func (c *Metadata) UnmarshalJSON(data []byte) error {
*c = Metadata(versioned.Metadata)
return nil
}
return fmt.Errorf("unsupported version: %q", versioned.Version)
return errors.Errorf("unsupported version: %q", versioned.Version)
}

View File

@@ -17,15 +17,15 @@ limitations under the License.
package sandbox
import (
"errors"
"fmt"
"os"
"sync"
cnins "github.com/containernetworking/plugins/pkg/ns"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/symlink"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
osinterface "github.com/containerd/cri/pkg/os"
)
// ErrClosedNetNS is the error returned when network namespace is closed.
@@ -43,7 +43,7 @@ type NetNS struct {
func NewNetNS() (*NetNS, error) {
netns, err := cnins.NewNS()
if err != nil {
return nil, fmt.Errorf("failed to setup network namespace %v", err)
return nil, errors.Wrap(err, "failed to setup network namespace")
}
n := new(NetNS)
n.ns = netns
@@ -63,7 +63,7 @@ func LoadNetNS(path string) (*NetNS, error) {
os.RemoveAll(path) // nolint: errcheck
return nil, ErrClosedNetNS
}
return nil, fmt.Errorf("failed to load network namespace %v", err)
return nil, errors.Wrap(err, "failed to load network namespace")
}
return &NetNS{ns: ns, restored: true}, nil
}
@@ -76,36 +76,28 @@ func (n *NetNS) Remove() error {
if !n.closed {
err := n.ns.Close()
if err != nil {
return fmt.Errorf("failed to close network namespace: %v", err)
return errors.Wrap(err, "failed to close network namespace")
}
n.closed = true
}
if n.restored {
path := n.ns.Path()
// TODO(random-liu): Add util function for unmount.
// Check netns existence.
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("failed to stat netns: %v", err)
return errors.Wrap(err, "failed to stat netns")
}
path, err := symlink.FollowSymlinkInScope(path, "/")
if err != nil {
return fmt.Errorf("failed to follow symlink: %v", err)
return errors.Wrap(err, "failed to follow symlink")
}
mounted, err := mount.Mounted(path)
if err != nil {
return fmt.Errorf("failed to check netns mounted: %v", err)
}
if mounted {
err := unix.Unmount(path, unix.MNT_DETACH)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to umount netns: %v", err)
}
if err := osinterface.Unmount(path, unix.MNT_DETACH); err != nil && !os.IsNotExist(err) {
return errors.Wrap(err, "failed to umount netns")
}
if err := os.RemoveAll(path); err != nil {
return fmt.Errorf("failed to remove netns: %v", err)
return errors.Wrap(err, "failed to remove netns")
}
n.restored = false
}

View File

@@ -18,24 +18,25 @@ package util
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
)
// DeepCopy makes a deep copy from src into dst.
func DeepCopy(dst interface{}, src interface{}) error {
if dst == nil {
return fmt.Errorf("dst cannot be nil")
return errors.New("dst cannot be nil")
}
if src == nil {
return fmt.Errorf("src cannot be nil")
return errors.New("src cannot be nil")
}
bytes, err := json.Marshal(src)
if err != nil {
return fmt.Errorf("unable to marshal src: %s", err)
return errors.Wrap(err, "unable to marshal src")
}
err = json.Unmarshal(bytes, dst)
if err != nil {
return fmt.Errorf("unable to unmarshal into dst: %s", err)
return errors.Wrap(err, "unable to unmarshal into dst")
}
return nil
}

View File

@@ -3,16 +3,16 @@ github.com/blang/semver v3.1.0
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130
github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
github.com/containerd/containerd 3013762fc58941e33ba70e8f8d9256911f134124
github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
github.com/containerd/go-runc 4f6e87ae043f859a38255247b49c9abc262d002f
github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
github.com/containerd/containerd 8a7e17ef96678507a4b23d2bc66e5bbe5b50ad37
github.com/containerd/continuity 3e8f2ea4b190484acb976a5b378d373429639a1a
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/go-runc bcb223a061a3dd7de1a89c0b402a60f4dd9bd307
github.com/containerd/go-cni f2d7272f12d045b16ed924f50e91f9f9cecc55a7
github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788
github.com/containernetworking/cni v0.6.0
github.com/containernetworking/plugins v0.6.0
github.com/containernetworking/plugins v0.7.0
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
github.com/cri-o/ocicni 9b451e26eb7c694d564991fbf44f77d0afb9b03c
github.com/davecgh/go-spew v1.1.0
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
@@ -21,7 +21,6 @@ github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098
github.com/docker/go-units v0.3.1
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46
github.com/fsnotify/fsnotify 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1
github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee
github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f
github.com/gogo/protobuf v0.5
@@ -31,14 +30,13 @@ github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
github.com/json-iterator/go 1.0.4
github.com/matttproud/golang_protobuf_extensions v1.0.0
github.com/Microsoft/go-winio v0.4.5
github.com/Microsoft/hcsshim v0.6.7
github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc a618ab5a0186905949ee463dbb762c3d23e12a80
github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
github.com/opencontainers/runtime-spec v1.0.1
github.com/opencontainers/runtime-tools 6073aff4ac61897f75895123f7e24135204a404d
github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206
@@ -48,10 +46,8 @@ github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823
github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
github.com/renstrom/dedent 020d11c3b9c0c7a3c2efcc8e5cf5b9ef7bcea21f
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
github.com/sirupsen/logrus v1.0.0
github.com/spf13/cobra v0.0.1
github.com/spf13/pflag v1.0.0
github.com/stevvooe/ttrpc d4528379866b0ce7e9d71f3eb96f0582fc374577
github.com/stretchr/testify v1.1.4
@@ -63,13 +59,14 @@ golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
google.golang.org/grpc v1.7.4
gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77
k8s.io/api a1d6dce6736a6c75929bb75111e89077e35a5856
k8s.io/apimachinery 8259d997cf059cd83dc47e5f8074b7a7d7967c09
k8s.io/apiserver 8e45eac9dff86447a5c2effe6a3d2cba70121ebf
k8s.io/client-go 33bd23f75b6de861994706a322b0afab824b2171
k8s.io/kubernetes 05944b1d2ca7f60b09762a330425108f48f6b603
k8s.io/api 5584376ceeffeb13a2e98b5e9f0e9dab37de4bab
k8s.io/apimachinery fcb9a12f7875d01f8390b28faedc37dcf2e713b9
k8s.io/apiserver 837069aa36757a586e4a8165f1ff5ca06170aa4a
k8s.io/client-go 484f27892430b961df38fe6715cc396409207d9f
k8s.io/kubernetes v1.10.0-rc.1
k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e

201
vendor/github.com/containerd/go-cni/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

46
vendor/github.com/containerd/go-cni/README.md generated vendored Normal file
View File

@@ -0,0 +1,46 @@
# go-cni
A generic CNI library to provide APIs for CNI plugin interactions. The library provides APIs to:
- Setup networks for container namespace
- Remove networks from container namespace
- Query status of CNI network plugin initialization
go-cni aims to support plugins that implement [Container Network Interface](https://github.com/containernetworking/cni)
## Usage
```
func main() {
id := "123456"
netns := "/proc/9999/ns/net"
defaultIfName := "eth0"
// Initialize library
l = gocni.New(gocni.WithMinNetworkCount(2),
gocni.WithLoNetwork(),
gocni.WithPluginConfDir("/etc/mycni/net.d"),
gocni.WithPluginDir([]string{"/opt/mycni/bin", "/opt/cni/bin"}),
gocni.WithDefaultIfName(defaultIfName))
// Setup network for namespace.
labels := map[string]string{
"K8S_POD_NAMESPACE": "namespace1",
"K8S_POD_NAME": "pod1",
"K8S_POD_INFRA_CONTAINER_ID": id,
}
result, err := l.Setup(id, netns, gocni.WithLabels(labels))
if err != nil {
return nil, fmt.Errorf("failed to setup network for namespace %q: %v", id, err)
}
defer func() {
if retErr != nil {
// Teardown network if an error is returned.
if err := l.Remove(id, netns, gocni.WithLabels(labels)); err != nil {
fmt.Errorf("Failed to destroy network for namespace %q", id)
}
}
}()
// Get IP of the default interface
IP := result.Interfaces[defaultIfName].IPConfigs[0].IP.String()
fmt.Printf("IP of the default interface %s:%s", defaultIfName, IP)
}
```

141
vendor/github.com/containerd/go-cni/cni.go generated vendored Normal file
View File

@@ -0,0 +1,141 @@
/*
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 cni
import (
"fmt"
"sync"
cnilibrary "github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/pkg/errors"
)
type CNI interface {
// Setup setup the network for the namespace
Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error)
// Remove tears down the network of the namespace.
Remove(id string, path string, opts ...NamespaceOpts) error
// Load loads the cni network config
Load(opts ...LoadOption) error
// Status checks the status of the cni initialization
Status() error
}
type libcni struct {
config
cniConfig cnilibrary.CNI
networkCount int // minimum network plugin configurations needed to initialize cni
networks []*Network
sync.RWMutex
}
func defaultCNIConfig() *libcni {
return &libcni{
config: config{
pluginDirs: []string{DefaultCNIDir},
pluginConfDir: DefaultNetDir,
prefix: DefaultPrefix,
},
cniConfig: &cnilibrary.CNIConfig{
Path: []string{DefaultCNIDir},
},
networkCount: 1,
}
}
func New(config ...ConfigOption) (CNI, error) {
cni := defaultCNIConfig()
var err error
for _, c := range config {
if err = c(cni); err != nil {
return nil, err
}
}
return cni, nil
}
func (c *libcni) Load(opts ...LoadOption) error {
var err error
// Reset the networks on a load operation to ensure
// config happens on a clean slate
c.reset()
for _, o := range opts {
if err = o(c); err != nil {
return errors.Wrapf(ErrLoad, fmt.Sprintf("cni config load failed: %v", err))
}
}
return c.Status()
}
func (c *libcni) Status() error {
c.RLock()
defer c.RUnlock()
if len(c.networks) < c.networkCount {
return ErrCNINotInitialized
}
return nil
}
// Setup setups the network in the namespace
func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error) {
if err:=c.Status();err!=nil{
return nil,err
}
ns, err := newNamespace(id, path, opts...)
if err != nil {
return nil, err
}
var results []*current.Result
c.RLock()
defer c.RUnlock()
for _, network := range c.networks {
r, err := network.Attach(ns)
if err != nil {
return nil, err
}
results = append(results, r)
}
return c.GetCNIResultFromResults(results)
}
// Remove removes the network config from the namespace
func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error {
if err:=c.Status();err!=nil{
return err
}
ns, err := newNamespace(id, path, opts...)
if err != nil {
return err
}
c.RLock()
defer c.RUnlock()
for _, network := range c.networks {
if err := network.Remove(ns); err != nil {
return err
}
}
return nil
}
func (c *libcni) reset() {
c.Lock()
defer c.Unlock()
c.networks = nil
}

39
vendor/github.com/containerd/go-cni/errors.go generated vendored Normal file
View File

@@ -0,0 +1,39 @@
package cni
import (
"github.com/pkg/errors"
)
var (
ErrCNINotInitialized = errors.New("cni plugin not initialized")
ErrInvalidConfig = errors.New("invalid cni config")
ErrNotFound = errors.New("not found")
ErrRead = errors.New("failed to read config file")
ErrInvalidResult = errors.New("invalid result")
ErrLoad = errors.New("failed to load cni config")
)
// IsCNINotInitialized returns true if the error is due cni config not being intialized
func IsCNINotInitialized(err error) bool {
return errors.Cause(err) == ErrCNINotInitialized
}
// IsInvalidConfig returns true if the error is invalid cni config
func IsInvalidConfig(err error) bool {
return errors.Cause(err) == ErrInvalidConfig
}
// IsNotFound returns true if the error is due to a missing config or result
func IsNotFound(err error) bool {
return errors.Cause(err) == ErrNotFound
}
// IsReadFailure return true if the error is a config read failure
func IsReadFailure(err error) bool {
return errors.Cause(err) == ErrRead
}
// IsInvalidResult return true if the error is due to invalid cni result
func IsInvalidResult(err error) bool {
return errors.Cause(err) == ErrInvalidResult
}

41
vendor/github.com/containerd/go-cni/helper.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
/*
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 cni
import (
"fmt"
"github.com/containernetworking/cni/pkg/types/current"
)
func validateInterfaceConfig(ipConf *current.IPConfig, ifs int) error {
if ipConf == nil {
return fmt.Errorf("invalid IP configuration")
}
if ipConf.Interface != nil && *ipConf.Interface > ifs {
return fmt.Errorf("invalid IP configuration with invalid interface %d", *ipConf.Interface)
}
return nil
}
func getIfName(prefix string, i int) string {
return fmt.Sprintf("%s%d", prefix, i)
}
func defaultInterface(prefix string) string {
return getIfName(prefix, 0)
}

75
vendor/github.com/containerd/go-cni/namespace.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
/*
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 cni
import (
cnilibrary "github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types/current"
)
type Network struct {
cni cnilibrary.CNI
config *cnilibrary.NetworkConfigList
ifName string
}
func (n *Network) Attach(ns *Namespace) (*current.Result, error) {
r, err := n.cni.AddNetworkList(n.config, ns.config(n.ifName))
if err != nil {
return nil, err
}
return current.NewResultFromResult(r)
}
func (n *Network) Remove(ns *Namespace) error {
return n.cni.DelNetworkList(n.config, ns.config(n.ifName))
}
type Namespace struct {
id string
path string
capabilityArgs map[string]interface{}
args map[string]string
}
func newNamespace(id, path string, opts ...NamespaceOpts) (*Namespace, error) {
ns := &Namespace{
id: id,
path: path,
capabilityArgs: make(map[string]interface{}),
args: make(map[string]string),
}
for _, o := range opts {
if err := o(ns); err != nil {
return nil, err
}
}
return ns, nil
}
func (ns *Namespace) config(ifName string) *cnilibrary.RuntimeConf {
c := &cnilibrary.RuntimeConf{
ContainerID: ns.id,
NetNS: ns.path,
IfName: ifName,
}
for k, v := range ns.args {
c.Args = append(c.Args, [2]string{k, v})
}
c.CapabilityArgs = ns.capabilityArgs
return c
}

58
vendor/github.com/containerd/go-cni/namespace_opts.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
/*
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 cni
type NamespaceOpts func(s *Namespace) error
// Capabilities
func WithCapabilityPortMap(portMapping []PortMapping) NamespaceOpts {
return func(c *Namespace) error {
c.capabilityArgs["portMappings"] = portMapping
return nil
}
}
func WithCapabilityIPRanges(ipRanges []IPRanges) NamespaceOpts {
return func(c *Namespace) error {
c.capabilityArgs["ipRanges"] = ipRanges
return nil
}
}
func WithCapability(name string, capability interface{}) NamespaceOpts {
return func(c *Namespace) error {
c.capabilityArgs[name] = capability
return nil
}
}
// Args
func WithLabels(labels map[string]string) NamespaceOpts {
return func(c *Namespace) error {
for k, v := range labels {
c.args[k] = v
}
return nil
}
}
func WithArgs(k, v string) NamespaceOpts {
return func(c *Namespace) error {
c.args[k] = v
return nil
}
}

226
vendor/github.com/containerd/go-cni/opts.go generated vendored Normal file
View File

@@ -0,0 +1,226 @@
/*
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 cni
import (
"sort"
"strings"
cnilibrary "github.com/containernetworking/cni/libcni"
"github.com/pkg/errors"
)
type ConfigOption func(c *libcni) error
// WithInterfacePrefix sets the prefix for network interfaces
// e.g. eth or wlan
func WithInterfacePrefix(prefix string) ConfigOption {
return func(c *libcni) error {
c.prefix = prefix
return nil
}
}
// WithPluginDir can be used to set the locations of
// the cni plugin binaries
func WithPluginDir(dirs []string) ConfigOption {
return func(c *libcni) error {
c.pluginDirs = dirs
c.cniConfig = &cnilibrary.CNIConfig{Path: dirs}
return nil
}
}
// WithPluginConfDir can be used to configure the
// cni configuration directory.
func WithPluginConfDir(dir string) ConfigOption {
return func(c *libcni) error {
c.pluginConfDir = dir
return nil
}
}
// WithMinNetworkCount can be used to configure the
// minimum networks to be configured and initalized
// for the status to report success. By default its 1.
func WithMinNetworkCount(count int) ConfigOption {
return func(c *libcni) error {
c.networkCount = count
return nil
}
}
// LoadOption can be used with Load API
// to load network configuration from different
// sources.
type LoadOption func(c *libcni) error
// WithLoNetwork can be used to load the loopback
// network config.
func WithLoNetwork() LoadOption {
return func(c *libcni) error {
loConfig, _ := cnilibrary.ConfListFromBytes([]byte(`{
"cniVersion": "0.3.1",
"name": "cni-loopback",
"plugins": [{
"type": "loopback"
}]
}`))
c.Lock()
defer c.Unlock()
c.networks = append(c.networks,&Network{
cni: c.cniConfig,
config: loConfig,
ifName: "lo",
})
return nil
}
}
// WithConf can be used to load config directly
// from byte.
func WithConf(bytes []byte) LoadOption {
return func(c *libcni) error {
conf, err := cnilibrary.ConfFromBytes(bytes)
if err != nil {
return err
}
confList, err := cnilibrary.ConfListFromConf(conf)
if err != nil {
return err
}
c.Lock()
defer c.Unlock()
c.networks = append(c.networks, &Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, 0),
})
return nil
}
}
// WithConfFile can be used to load network config
// from an .conf file. Supported with absolute fileName
// with path only.
func WithConfFile(fileName string) LoadOption {
return func(c *libcni) error {
conf, err := cnilibrary.ConfFromFile(fileName)
if err != nil {
return err
}
// upconvert to conf list
confList, err := cnilibrary.ConfListFromConf(conf)
if err != nil {
return err
}
c.Lock()
defer c.Unlock()
c.networks = append(c.networks, &Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, 0),
})
return nil
}
}
// WithConfListFile can be used to load network config
// from an .conflist file. Supported with absolute fileName
// with path only.
func WithConfListFile(fileName string) LoadOption {
return func(c *libcni) error {
confList, err := cnilibrary.ConfListFromFile(fileName)
if err != nil {
return err
}
c.Lock()
defer c.Unlock()
c.networks = append(c.networks,&Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, 0),
})
return nil
}
}
// WithDefaultConf can be used to detect network config
// files from the configured cni config directory and load
// them.
func WithDefaultConf() LoadOption {
return func(c *libcni) error {
files, err := cnilibrary.ConfFiles(c.pluginConfDir, []string{".conf", ".conflist", ".json"})
switch {
case err != nil:
return errors.Wrapf(ErrRead, "failed to read config file: %v", err)
case len(files) == 0:
return errors.Wrapf(ErrCNINotInitialized, "no network config found in %s", c.pluginConfDir)
}
// files contains the network config files associated with cni network.
// Use lexicographical way as a defined order for network config files.
sort.Strings(files)
// Since the CNI spec does not specify a way to detect default networks,
// the convention chosen is - the first network configuration in the sorted
// list of network conf files as the default network and choose the default
// interface provided during init as the network interface for this default
// network. For every other network use a generated interface id.
i := 0
c.Lock()
defer c.Unlock()
for _, confFile := range files {
var confList *cnilibrary.NetworkConfigList
if strings.HasSuffix(confFile, ".conflist") {
confList, err = cnilibrary.ConfListFromFile(confFile)
if err != nil {
return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config list file %s: %v", confFile, err)
}
} else {
conf, err := cnilibrary.ConfFromFile(confFile)
if err != nil {
return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config file %s: %v", confFile, err)
}
// Ensure the config has a "type" so we know what plugin to run.
// Also catches the case where somebody put a conflist into a conf file.
if conf.Network.Type == "" {
return errors.Wrapf(ErrInvalidConfig, "network type not found in %s", confFile)
}
confList, err = cnilibrary.ConfListFromConf(conf)
if err != nil {
return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to list: %v", confFile, err)
}
}
if len(confList.Plugins) == 0 {
return errors.Wrapf(ErrInvalidConfig, "CNI config list %s has no networks, skipping", confFile)
}
c.networks = append(c.networks, &Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, i),
})
i++
}
if len(c.networks) == 0 {
return errors.Wrapf(ErrCNINotInitialized, "no valid networks found in %s", c.pluginDirs)
}
return nil
}
}

103
vendor/github.com/containerd/go-cni/result.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
/*
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 cni
import (
"net"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/pkg/errors"
)
type IPConfig struct {
IP net.IP
Gateway net.IP
}
type CNIResult struct {
Interfaces map[string]*Config
DNS []types.DNS
Routes []*types.Route
}
type Config struct {
IPConfigs []*IPConfig
Mac string
Sandbox string
}
// GetCNIResultFromResults returns a structured data containing the
// interface configuration for each of the interfaces created in the namespace.
// Conforms with
// Result:
// a) Interfaces list. Depending on the plugin, this can include the sandbox
// (eg, container or hypervisor) interface name and/or the host interface
// name, the hardware addresses of each interface, and details about the
// sandbox (if any) the interface is in.
// b) IP configuration assigned to each interface. The IPv4 and/or IPv6 addresses,
// gateways, and routes assigned to sandbox and/or host interfaces.
// c) DNS information. Dictionary that includes DNS information for nameservers,
// domain, search domains and options.
func (c *libcni) GetCNIResultFromResults(results []*current.Result) (*CNIResult, error) {
r := &CNIResult{
Interfaces: make(map[string]*Config),
}
// Plugins may not need to return Interfaces in result if
// if there are no multiple interfaces created. In that case
// all configs should be applied against default interface
r.Interfaces[defaultInterface(c.prefix)] = &Config{}
// Walk through all the results
for _, result := range results {
// Walk through all the interface in each result
for _, intf := range result.Interfaces {
r.Interfaces[intf.Name] = &Config{
Mac: intf.Mac,
Sandbox: intf.Sandbox,
}
}
// Walk through all the IPs in the result and attach it to corresponding
// interfaces
for _, ipConf := range result.IPs {
if err := validateInterfaceConfig(ipConf, len(result.Interfaces)); err != nil {
return nil, errors.Wrapf(ErrInvalidResult, "failed to valid interface config: %v", err)
}
name := c.getInterfaceName(result.Interfaces, ipConf)
r.Interfaces[name].IPConfigs = append(r.Interfaces[name].IPConfigs,
&IPConfig{IP: ipConf.Address.IP, Gateway: ipConf.Gateway})
}
r.DNS = append(r.DNS, result.DNS)
r.Routes = append(r.Routes, result.Routes...)
}
if _, ok := r.Interfaces[defaultInterface(c.prefix)]; !ok {
return nil, errors.Wrapf(ErrNotFound, "default network not found")
}
return r, nil
}
// getInterfaceName returns the interface name if the plugins
// return the result with associated interfaces. If interface
// is not present then default interface name is used
func (c *libcni) getInterfaceName(interfaces []*current.Interface,
ipConf *current.IPConfig) string {
if ipConf.Interface != nil {
return interfaces[*ipConf.Interface].Name
}
return defaultInterface(c.prefix)
}

78
vendor/github.com/containerd/go-cni/testutils.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
/*
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 cni
import (
"fmt"
"io/ioutil"
"os"
"path"
"testing"
)
func makeTmpDir(prefix string) (string, error) {
tmpDir, err := ioutil.TempDir(os.TempDir(), prefix)
if err != nil {
return "", err
}
return tmpDir, nil
}
func makeFakeCNIConfig(t *testing.T) (string, string) {
cniDir, err := makeTmpDir("fakecni")
if err != nil {
t.Fatalf("Failed to create plugin config dir: %v", err)
}
cniConfDir := path.Join(cniDir, "net.d")
err = os.MkdirAll(cniConfDir, 0777)
if err != nil {
t.Fatalf("Failed to create network config dir: %v", err)
}
networkConfig1 := path.Join(cniConfDir, "mocknetwork1.conf")
f1, err := os.Create(networkConfig1)
if err != nil {
t.Fatalf("Failed to create network config %v: %v", f1, err)
}
networkConfig2 := path.Join(cniConfDir, "mocknetwork2.conf")
f2, err := os.Create(networkConfig2)
if err != nil {
t.Fatalf("Failed to create network config %v: %v", f2, err)
}
cfg1 := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true} }`, "plugin1", "fakecni")
_, err = f1.WriteString(cfg1)
if err != nil {
t.Fatalf("Failed to write network config file %v: %v", f1, err)
}
f1.Close()
cfg2 := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true} }`, "plugin2", "fakecni")
_, err = f2.WriteString(cfg2)
if err != nil {
t.Fatalf("Failed to write network config file %v: %v", f2, err)
}
f2.Close()
return cniDir, cniConfDir
}
func tearDownCNIConfig(t *testing.T, confDir string) {
err := os.RemoveAll(confDir)
if err != nil {
t.Fatalf("Failed to cleanup CNI configs: %v", err)
}
}

45
vendor/github.com/containerd/go-cni/types.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
/*
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 cni
const (
CNIPluginName = "cni"
DefaultNetDir = "/etc/cni/net.d"
DefaultCNIDir = "/opt/cni/bin"
VendorCNIDirTemplate = "%s/opt/%s/bin"
DefaultPrefix = "eth"
)
type config struct {
pluginDirs []string
pluginConfDir string
prefix string
}
type PortMapping struct {
HostPort int32
ContainerPort int32
Protocol string
HostIP string
}
type IPRanges struct {
Subnet string
RangeStart string
RangeEnd string
Gateway string
}

6
vendor/github.com/containerd/go-cni/vendor.conf generated vendored Normal file
View File

@@ -0,0 +1,6 @@
github.com/stretchr/testify b89eecf5ca5db6d3ba60b237ffe3df7bafb7662f
github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
github.com/stretchr/objx 8a3f7159479fbc75b30357fbc48f380b7320f08e
github.com/containernetworking/cni 142cde0c766cd6055cc7fdfdcb44579c0c9c35bf
github.com/pkg/errors v0.8.0