Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu 2019-08-16 16:38:22 -07:00
parent c62b7444ef
commit 9cbd18ac76
67 changed files with 1754 additions and 1118 deletions

View File

@ -42,20 +42,20 @@ github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
gotest.tools v2.3.0
github.com/google/go-cmp v0.2.0
go.etcd.io/bbolt v1.3.3
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f
# cri dependencies
github.com/containerd/cri b213648c5bd0a1d2ee42709c10dff63fbfee3ad7 # master
github.com/containerd/go-cni 22460c018b64cf8bf4151b3ff9c4d077e6a88cbf
github.com/containernetworking/cni v0.6.0
github.com/containernetworking/plugins v0.7.0
github.com/davecgh/go-spew v1.1.0
github.com/containerd/cri f1d492b0cdd14e76476ee4dd024696ce3634e501 # master
github.com/containerd/go-cni 49fbd9b210f3c8ee3b7fd3cd797aabaf364627c1
github.com/containernetworking/cni v0.7.1
github.com/containernetworking/plugins v0.7.6
github.com/davecgh/go-spew v1.1.1
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/emicklei/go-restful v2.2.1
github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f
github.com/google/gofuzz 24818f796faf91cd76ec7bddd72458fbced7a6c1
github.com/json-iterator/go 1.1.5
github.com/modern-go/reflect2 1.0.1
github.com/modern-go/concurrent 1.0.3
@ -63,9 +63,9 @@ github.com/opencontainers/selinux v1.2.2
github.com/seccomp/libseccomp-golang v0.9.1
github.com/tchap/go-patricia v2.2.6
golang.org/x/crypto 88737f569e3a9c7ab309cdc09a07fe7fc87233c3
golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4
golang.org/x/oauth2 9f3314589c9a9136388751d9adae6b0ed400978a
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
gopkg.in/inf.v0 v0.9.0
gopkg.in/yaml.v2 v2.2.1
k8s.io/api kubernetes-1.15.0
k8s.io/apimachinery kubernetes-1.15.0

View File

@ -41,14 +41,16 @@ See [test dashboard](https://k8s-testgrid.appspot.com/sig-node-containerd)
**Note:** The support table above specifies the Kubernetes Version that was supported at time of release of the containerd - cri integration.
The following is the current support table for containerd CRI integration taking into account that Kubernetes only supports n-3 minor release versions and 1.10 is now end-of-life.
The following is the current support table for containerd CRI integration taking into account that Kubernetes only supports n-3 minor release versions and 1.10 and 1.11 are now end-of-life.
| Containerd Version | Kubernetes Version | CRI Version |
|:------------------:|:------------------:|:-----------:|
| v1.1 | 1.11+ | v1alpha2 |
| v1.2 | 1.11+ | v1alpha2 |
| HEAD | 1.11+ | v1alpha2 |
| v1.1 | 1.12+ | v1alpha2 |
| v1.2 | 1.12+ | v1alpha2 |
| HEAD | 1.12+ | v1alpha2 |
***Although not recommended, if you still plan to use containerd 1.2+ with Kubernetes
<=1.11, please be sure to set `disable_proc_mount=true`.***
## Production Quality Cluster on GCE
For a production quality cluster on GCE brought up with `kube-up.sh` refer [here](docs/kube-up.md).

View File

@ -17,10 +17,8 @@ limitations under the License.
package cri
import (
"context"
"flag"
"path/filepath"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/api/services/containers/v1"
@ -74,8 +72,8 @@ func initCRIService(ic *plugin.InitContext) (interface{}, error) {
}
log.G(ctx).Infof("Start cri plugin with config %+v", c)
if err := validateConfig(ctx, &c); err != nil {
return nil, errors.Wrap(err, "invalid config")
if err := criconfig.ValidatePluginConfig(ctx, pluginConfig); err != nil {
return nil, errors.Wrap(err, "invalid plugin config")
}
if err := setGLogLevel(); err != nil {
@ -111,67 +109,6 @@ func initCRIService(ic *plugin.InitContext) (interface{}, error) {
return s, nil
}
// validateConfig validates the given configuration.
func validateConfig(ctx context.Context, c *criconfig.Config) error {
if c.ContainerdConfig.Runtimes == nil {
c.ContainerdConfig.Runtimes = make(map[string]criconfig.Runtime)
}
// Validation for deprecated untrusted_workload_runtime.
if c.ContainerdConfig.UntrustedWorkloadRuntime.Type != "" {
log.G(ctx).Warning("`untrusted_workload_runtime` is deprecated, please use `untrusted` runtime in `runtimes` instead")
if _, ok := c.ContainerdConfig.Runtimes[criconfig.RuntimeUntrusted]; ok {
return errors.Errorf("conflicting definitions: configuration includes both `untrusted_workload_runtime` and `runtimes[%q]`", criconfig.RuntimeUntrusted)
}
c.ContainerdConfig.Runtimes[criconfig.RuntimeUntrusted] = c.ContainerdConfig.UntrustedWorkloadRuntime
}
// Validation for deprecated default_runtime field.
if c.ContainerdConfig.DefaultRuntime.Type != "" {
log.G(ctx).Warning("`default_runtime` is deprecated, please use `default_runtime_name` to reference the default configuration you have defined in `runtimes`")
c.ContainerdConfig.DefaultRuntimeName = criconfig.RuntimeDefault
c.ContainerdConfig.Runtimes[criconfig.RuntimeDefault] = c.ContainerdConfig.DefaultRuntime
}
// Validation for default_runtime_name
if c.ContainerdConfig.DefaultRuntimeName == "" {
return errors.New("`default_runtime_name` is empty")
}
if _, ok := c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName]; !ok {
return errors.New("no corresponding runtime configured in `runtimes` for `default_runtime_name`")
}
// Validation for deprecated runtime options.
if c.SystemdCgroup {
if c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName].Type != plugin.RuntimeLinuxV1 {
return errors.Errorf("`systemd_cgroup` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`systemd_cgroup` is deprecated, please use runtime `options` instead")
}
for _, r := range c.ContainerdConfig.Runtimes {
if r.Engine != "" {
if r.Type != plugin.RuntimeLinuxV1 {
return errors.Errorf("`runtime_engine` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`runtime_engine` is deprecated, please use runtime `options` instead")
}
if r.Root != "" {
if r.Type != plugin.RuntimeLinuxV1 {
return errors.Errorf("`runtime_root` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`runtime_root` is deprecated, please use runtime `options` instead")
}
}
// Validation for stream_idle_timeout
if c.StreamIdleTimeout != "" {
if _, err := time.ParseDuration(c.StreamIdleTimeout); err != nil {
return errors.Wrap(err, "invalid stream idle timeout")
}
}
return nil
}
// getServicesOpts get service options from plugin context.
func getServicesOpts(ic *plugin.InitContext) ([]containerd.ServicesOpt, error) {
plugins, err := ic.GetByType(plugin.ServicePlugin)

View File

@ -17,8 +17,14 @@ limitations under the License.
package config
import (
"context"
"time"
"github.com/BurntSushi/toml"
"github.com/containerd/containerd"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
"github.com/pkg/errors"
"k8s.io/kubernetes/pkg/kubelet/server/streaming"
)
@ -41,6 +47,9 @@ type Runtime struct {
// Options are config options for the runtime. If options is loaded
// from toml config, it will be toml.Primitive.
Options *toml.Primitive `toml:"options" json:"options"`
// PrivilegedWithoutHostDevices overloads the default behaviour for adding host devices to the
// runtime spec when the container is privileged. Defaults to false.
PrivilegedWithoutHostDevices bool `toml:"privileged_without_host_devices" json:"privileged_without_host_devices"`
}
// ContainerdConfig contains toml config related to containerd
@ -61,7 +70,6 @@ type ContainerdConfig struct {
Runtimes map[string]Runtime `toml:"runtimes" json:"runtimes"`
// NoPivot disables pivot-root (linux only), required when running a container in a RamDisk with runc
// This only works for runtime type "io.containerd.runtime.v1.linux".
// DEPRECATED: use Runtime.Options instead. Remove when shim v1 is deprecated.
NoPivot bool `toml:"no_pivot" json:"noPivot"`
}
@ -93,6 +101,7 @@ type Mirror struct {
// Endpoints are endpoints for a namespace. CRI plugin will try the endpoints
// one by one until a working one is found. The endpoint must be a valid url
// with host specified.
// The scheme, host and path from the endpoint URL will be used.
Endpoints []string `toml:"endpoint" json:"endpoint"`
}
@ -121,12 +130,23 @@ type TLSConfig struct {
type Registry struct {
// Mirrors are namespace to mirror mapping for all namespaces.
Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors"`
// Configs are configs for each registry.
// The key is the FDQN or IP of the registry.
Configs map[string]RegistryConfig `toml:"configs" json:"configs"`
// Auths are registry endpoint to auth config mapping. The registry endpoint must
// be a valid url with host specified.
// DEPRECATED: Use Configs instead. Remove in containerd 1.4.
Auths map[string]AuthConfig `toml:"auths" json:"auths"`
// TLSConfigs are pairs of CA/Cert/Key which then are used when creating the transport
}
// RegistryConfig contains configuration used to communicate with the registry.
type RegistryConfig struct {
// Auth contains information to authenticate to the registry.
Auth *AuthConfig `toml:"auth" json:"auth"`
// TLS is a pair of CA/Cert/Key which then are used when creating the transport
// that communicates with the registry.
TLSConfigs map[string]TLSConfig `toml:"tls_configs" json:"tlsConfigs"`
TLS *TLSConfig `toml:"tls" json:"tls"`
}
// PluginConfig contains toml config related to CRI plugin,
@ -258,3 +278,84 @@ const (
// RuntimeDefault is the implicit runtime defined for ContainerdConfig.DefaultRuntime
RuntimeDefault = "default"
)
// ValidatePluginConfig validates the given plugin configuration.
func ValidatePluginConfig(ctx context.Context, c *PluginConfig) error {
if c.ContainerdConfig.Runtimes == nil {
c.ContainerdConfig.Runtimes = make(map[string]Runtime)
}
// Validation for deprecated untrusted_workload_runtime.
if c.ContainerdConfig.UntrustedWorkloadRuntime.Type != "" {
log.G(ctx).Warning("`untrusted_workload_runtime` is deprecated, please use `untrusted` runtime in `runtimes` instead")
if _, ok := c.ContainerdConfig.Runtimes[RuntimeUntrusted]; ok {
return errors.Errorf("conflicting definitions: configuration includes both `untrusted_workload_runtime` and `runtimes[%q]`", RuntimeUntrusted)
}
c.ContainerdConfig.Runtimes[RuntimeUntrusted] = c.ContainerdConfig.UntrustedWorkloadRuntime
}
// Validation for deprecated default_runtime field.
if c.ContainerdConfig.DefaultRuntime.Type != "" {
log.G(ctx).Warning("`default_runtime` is deprecated, please use `default_runtime_name` to reference the default configuration you have defined in `runtimes`")
c.ContainerdConfig.DefaultRuntimeName = RuntimeDefault
c.ContainerdConfig.Runtimes[RuntimeDefault] = c.ContainerdConfig.DefaultRuntime
}
// Validation for default_runtime_name
if c.ContainerdConfig.DefaultRuntimeName == "" {
return errors.New("`default_runtime_name` is empty")
}
if _, ok := c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName]; !ok {
return errors.New("no corresponding runtime configured in `runtimes` for `default_runtime_name`")
}
// Validation for deprecated runtime options.
if c.SystemdCgroup {
if c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName].Type != plugin.RuntimeLinuxV1 {
return errors.Errorf("`systemd_cgroup` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`systemd_cgroup` is deprecated, please use runtime `options` instead")
}
if c.NoPivot {
if c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName].Type != plugin.RuntimeLinuxV1 {
return errors.Errorf("`no_pivot` only works for runtime %s", plugin.RuntimeLinuxV1)
}
// NoPivot can't be deprecated yet, because there is no alternative config option
// for `io.containerd.runtime.v1.linux`.
}
for _, r := range c.ContainerdConfig.Runtimes {
if r.Engine != "" {
if r.Type != plugin.RuntimeLinuxV1 {
return errors.Errorf("`runtime_engine` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`runtime_engine` is deprecated, please use runtime `options` instead")
}
if r.Root != "" {
if r.Type != plugin.RuntimeLinuxV1 {
return errors.Errorf("`runtime_root` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`runtime_root` is deprecated, please use runtime `options` instead")
}
}
// Validation for deprecated auths options and mapping it to configs.
if len(c.Registry.Auths) != 0 {
if c.Registry.Configs == nil {
c.Registry.Configs = make(map[string]RegistryConfig)
}
for endpoint, auth := range c.Registry.Auths {
config := c.Registry.Configs[endpoint]
config.Auth = &auth
c.Registry.Configs[endpoint] = config
}
log.G(ctx).Warning("`auths` is deprecated, please use registry`configs` instead")
}
// Validation for stream_idle_timeout
if c.StreamIdleTimeout != "" {
if _, err := time.ParseDuration(c.StreamIdleTimeout); err != nil {
return errors.Wrap(err, "invalid stream idle timeout")
}
}
return nil
}

View File

@ -25,10 +25,10 @@ import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/containerd/continuity/fs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// WithNewSnapshot wraps `containerd.WithNewSnapshot` so that if creating the
@ -80,7 +80,7 @@ func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts {
}
defer func() {
if uerr := mount.Unmount(root, 0); uerr != nil {
logrus.WithError(uerr).Errorf("Failed to unmount snapshot %q", c.SnapshotKey)
log.G(ctx).WithError(uerr).Errorf("Failed to unmount snapshot %q", c.SnapshotKey)
if err == nil {
err = uerr
}

View File

@ -27,18 +27,20 @@ import (
"strings"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/oci"
osinterface "github.com/containerd/cri/pkg/os"
"github.com/containerd/cri/pkg/util"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runc/libcontainer/devices"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
osinterface "github.com/containerd/cri/pkg/os"
"github.com/containerd/cri/pkg/util"
)
const (
@ -250,7 +252,7 @@ func WithMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*ru
s.Linux.RootfsPropagation = "rslave"
}
default:
logrus.Warnf("Unknown propagation mode for hostPath %q", mount.HostPath)
log.G(ctx).Warnf("Unknown propagation mode for hostPath %q", mount.HostPath)
options = append(options, "rprivate")
}

View File

@ -1,29 +0,0 @@
/*
Copyright 2018 The Containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package log
import "github.com/containerd/containerd/log"
// Trace logs a message at level Trace on the standard logger.
func Trace(args ...interface{}) {
log.Trace(log.L, args...)
}
// Tracef logs a message at level Trace on the standard logger.
func Tracef(format string, args ...interface{}) {
log.Tracef(log.L, format, args...)
}

View File

@ -20,8 +20,8 @@ import (
"io"
"github.com/containerd/containerd"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
"k8s.io/client-go/tools/remotecommand"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@ -62,7 +62,7 @@ func (c *criService) attachContainer(ctx context.Context, id string, stdin io.Re
}
handleResizing(resize, func(size remotecommand.TerminalSize) {
if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
logrus.WithError(err).Errorf("Failed to resize task %q console", id)
log.G(ctx).WithError(err).Errorf("Failed to resize task %q console", id)
}
})

View File

@ -26,22 +26,22 @@ import (
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/contrib/apparmor"
"github.com/containerd/containerd/contrib/seccomp"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/oci"
"github.com/containerd/typeurl"
"github.com/davecgh/go-spew/spew"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"github.com/containerd/cri/pkg/annotations"
"github.com/containerd/cri/pkg/config"
customopts "github.com/containerd/cri/pkg/containerd/opts"
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
cio "github.com/containerd/cri/pkg/server/io"
containerstore "github.com/containerd/cri/pkg/store/container"
"github.com/containerd/cri/pkg/util"
"github.com/containerd/typeurl"
"github.com/davecgh/go-spew/spew"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
)
const (
@ -67,7 +67,7 @@ func init() {
// CreateContainer creates a new container in the given PodSandbox.
func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (_ *runtime.CreateContainerResponse, retErr error) {
config := r.GetConfig()
logrus.Debugf("Container config %+v", config)
log.G(ctx).Debugf("Container config %+v", config)
sandboxConfig := r.GetSandboxConfig()
sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
if err != nil {
@ -89,7 +89,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
return nil, errors.New("container config must include metadata")
}
name := makeContainerName(metadata, sandboxConfig.GetMetadata())
logrus.Debugf("Generated id %q for container %q", id, name)
log.G(ctx).Debugf("Generated id %q for container %q", id, name)
if err = c.containerNameIndex.Reserve(name, id); err != nil {
return nil, errors.Wrapf(err, "failed to reserve container name %q", name)
}
@ -135,7 +135,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
if retErr != nil {
// Cleanup the container root directory.
if err = c.os.RemoveAll(containerRootDir); err != nil {
logrus.WithError(err).Errorf("Failed to remove container root directory %q",
log.G(ctx).WithError(err).Errorf("Failed to remove container root directory %q",
containerRootDir)
}
}
@ -149,7 +149,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
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",
log.G(ctx).WithError(err).Errorf("Failed to remove volatile container root directory %q",
volatileContainerRootDir)
}
}
@ -165,15 +165,15 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
if err != nil {
return nil, errors.Wrap(err, "failed to get sandbox runtime")
}
logrus.Debugf("Use OCI runtime %+v for sandbox %q and container %q", ociRuntime, sandboxID, id)
log.G(ctx).Debugf("Use OCI runtime %+v for sandbox %q and container %q", ociRuntime, sandboxID, id)
spec, err := c.generateContainerSpec(id, sandboxID, sandboxPid, config, sandboxConfig,
&image.ImageSpec.Config, append(mounts, volumeMounts...), ociRuntime.PodAnnotations)
&image.ImageSpec.Config, append(mounts, volumeMounts...), ociRuntime)
if err != nil {
return nil, errors.Wrapf(err, "failed to generate container %q spec", id)
}
logrus.Debugf("Container %q spec: %#+v", id, spew.NewFormatter(spec))
log.G(ctx).Debugf("Container %q spec: %#+v", id, spew.NewFormatter(spec))
// Set snapshotter before any other options.
opts := []containerd.NewContainerOpts{
@ -199,10 +199,10 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
// Validate log paths and compose full container log path.
if sandboxConfig.GetLogDirectory() != "" && config.GetLogPath() != "" {
meta.LogPath = filepath.Join(sandboxConfig.GetLogDirectory(), config.GetLogPath())
logrus.Debugf("Composed container full log path %q using sandbox log dir %q and container log path %q",
log.G(ctx).Debugf("Composed container full log path %q using sandbox log dir %q and container log path %q",
meta.LogPath, sandboxConfig.GetLogDirectory(), config.GetLogPath())
} else {
logrus.Infof("Logging will be disabled due to empty log paths for sandbox (%q) or container (%q)",
log.G(ctx).Infof("Logging will be disabled due to empty log paths for sandbox (%q) or container (%q)",
sandboxConfig.GetLogDirectory(), config.GetLogPath())
}
@ -214,7 +214,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
defer func() {
if retErr != nil {
if err := containerIO.Close(); err != nil {
logrus.WithError(err).Errorf("Failed to close container io %q", id)
log.G(ctx).WithError(err).Errorf("Failed to close container io %q", id)
}
}
}()
@ -227,11 +227,16 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
userstr, err := generateUserString(
securityContext.GetRunAsUsername(),
securityContext.GetRunAsUser(),
securityContext.GetRunAsGroup(),
)
securityContext.GetRunAsGroup())
if err != nil {
return nil, errors.Wrap(err, "failed to generate user string")
}
if userstr == "" {
// Lastly, since no user override was passed via CRI try to set via OCI
// Image
userstr = image.ImageSpec.Config.User
}
if userstr != "" {
specOpts = append(specOpts, oci.WithUser(userstr))
}
@ -286,7 +291,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
deferCtx, deferCancel := ctrdutil.DeferContext()
defer deferCancel()
if err := cntr.Delete(deferCtx, containerd.WithSnapshotCleanup); err != nil {
logrus.WithError(err).Errorf("Failed to delete containerd container %q", id)
log.G(ctx).WithError(err).Errorf("Failed to delete containerd container %q", id)
}
}
}()
@ -304,7 +309,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
if retErr != nil {
// Cleanup container checkpoint on error.
if err := container.Delete(); err != nil {
logrus.WithError(err).Errorf("Failed to cleanup container checkpoint for %q", id)
log.G(ctx).WithError(err).Errorf("Failed to cleanup container checkpoint for %q", id)
}
}
}()
@ -318,7 +323,8 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
}
func (c *criService) generateContainerSpec(id string, sandboxID string, sandboxPid uint32, config *runtime.ContainerConfig,
sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig, extraMounts []*runtime.Mount, runtimePodAnnotations []string) (*runtimespec.Spec, error) {
sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig, extraMounts []*runtime.Mount,
ociRuntime config.Runtime) (*runtimespec.Spec, error) {
specOpts := []oci.SpecOpts{
customopts.WithoutRunMount,
@ -380,7 +386,10 @@ func (c *criService) generateContainerSpec(id string, sandboxID string, sandboxP
if !sandboxConfig.GetLinux().GetSecurityContext().GetPrivileged() {
return nil, errors.New("no privileged container allowed in sandbox")
}
specOpts = append(specOpts, oci.WithPrivileged, customopts.WithPrivilegedDevices)
specOpts = append(specOpts, oci.WithPrivileged)
if !ociRuntime.PrivilegedWithoutHostDevices {
specOpts = append(specOpts, customopts.WithPrivilegedDevices)
}
} else { // not privileged
specOpts = append(specOpts, customopts.WithDevices(c.os, config), customopts.WithCapabilities(securityContext))
}
@ -408,8 +417,7 @@ func (c *criService) generateContainerSpec(id string, sandboxID string, sandboxP
} else {
specOpts = append(specOpts, customopts.WithResources(config.GetLinux().GetResources()))
if sandboxConfig.GetLinux().GetCgroupParent() != "" {
cgroupsPath := getCgroupsPath(sandboxConfig.GetLinux().GetCgroupParent(), id,
c.config.SystemdCgroup)
cgroupsPath := getCgroupsPath(sandboxConfig.GetLinux().GetCgroupParent(), id)
specOpts = append(specOpts, oci.WithCgroup(cgroupsPath))
}
}
@ -417,7 +425,7 @@ func (c *criService) generateContainerSpec(id string, sandboxID string, sandboxP
supplementalGroups := securityContext.GetSupplementalGroups()
for pKey, pValue := range getPassthroughAnnotations(sandboxConfig.Annotations,
runtimePodAnnotations) {
ociRuntime.PodAnnotations) {
specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
}
@ -590,7 +598,20 @@ func generateApparmorSpecOpts(apparmorProf string, privileged, apparmorEnabled b
}
}
// generateUserString generates valid user string based on OCI Image Spec v1.0.0.
// generateUserString generates valid user string based on OCI Image Spec
// v1.0.0.
//
// CRI defines that the following combinations are valid:
//
// (none) -> ""
// username -> username
// username, uid -> username
// username, uid, gid -> username:gid
// username, gid -> username:gid
// uid -> uid
// uid, gid -> uid:gid
// gid -> error
//
// TODO(random-liu): Add group name support in CRI.
func generateUserString(username string, uid, gid *runtime.Int64Value) (string, error) {
var userstr, groupstr string

View File

@ -25,9 +25,9 @@ import (
"github.com/containerd/containerd"
containerdio "github.com/containerd/containerd/cio"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/oci"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
"k8s.io/client-go/tools/remotecommand"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@ -104,7 +104,7 @@ func (c *criService) execInContainer(ctx context.Context, id string, opts execOp
pspec.Terminal = opts.tty
if opts.tty {
if err := oci.WithEnv([]string{"TERM=xterm"})(nil, nil, nil, spec); err != nil {
if err := oci.WithEnv([]string{"TERM=xterm"})(ctx, nil, nil, spec); err != nil {
return nil, errors.Wrap(err, "add TERM env var to spec")
}
}
@ -118,7 +118,7 @@ func (c *criService) execInContainer(ctx context.Context, id string, opts execOp
opts.stderr = cio.NewDiscardLogger()
}
execID := util.GenerateID()
logrus.Debugf("Generated exec id %q for container %q", execID, id)
log.G(ctx).Debugf("Generated exec id %q for container %q", execID, id)
volatileRootDir := c.getVolatileContainerRootDir(id)
var execIO *cio.ExecIO
process, err := task.Exec(ctx, execID, pspec,
@ -135,7 +135,7 @@ func (c *criService) execInContainer(ctx context.Context, id string, opts execOp
deferCtx, deferCancel := ctrdutil.DeferContext()
defer deferCancel()
if _, err := process.Delete(deferCtx, containerd.WithProcessKill); err != nil {
logrus.WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id)
log.G(ctx).WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id)
}
}()
@ -149,7 +149,7 @@ func (c *criService) execInContainer(ctx context.Context, id string, opts execOp
handleResizing(opts.resize, func(size remotecommand.TerminalSize) {
if err := process.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
logrus.WithError(err).Errorf("Failed to resize process %q console for container %q", execID, id)
log.G(ctx).WithError(err).Errorf("Failed to resize process %q console for container %q", execID, id)
}
})
@ -179,19 +179,19 @@ func (c *criService) execInContainer(ctx context.Context, id string, opts execOp
}
// Wait for the process to be killed.
exitRes := <-exitCh
logrus.Infof("Timeout received while waiting for exec process kill %q code %d and error %v",
log.G(ctx).Infof("Timeout received while waiting for exec process kill %q code %d and error %v",
execID, exitRes.ExitCode(), exitRes.Error())
<-attachDone
logrus.Debugf("Stream pipe for exec process %q done", execID)
log.G(ctx).Debugf("Stream pipe for exec process %q done", execID)
return nil, errors.Wrapf(execCtx.Err(), "timeout %v exceeded", opts.timeout)
case exitRes := <-exitCh:
code, _, err := exitRes.Result()
logrus.Infof("Exec process %q exits with exit code %d and error %v", execID, code, err)
log.G(ctx).Infof("Exec process %q exits with exit code %d and error %v", execID, code, err)
if err != nil {
return nil, errors.Wrapf(err, "failed while waiting for exec %q", execID)
}
<-attachDone
logrus.Debugf("Stream pipe for exec process %q done", execID)
log.G(ctx).Debugf("Stream pipe for exec process %q done", execID)
return &code, nil
}
}

View File

@ -19,13 +19,12 @@ package server
import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"github.com/containerd/cri/pkg/log"
"github.com/containerd/cri/pkg/store"
containerstore "github.com/containerd/cri/pkg/store/container"
)
@ -39,7 +38,7 @@ func (c *criService) RemoveContainer(ctx context.Context, r *runtime.RemoveConta
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())
log.G(ctx).Tracef("RemoveContainer called for container %q that does not exist", r.GetContainerId())
return &runtime.RemoveContainerResponse{}, nil
}
id := container.ID
@ -53,7 +52,7 @@ func (c *criService) RemoveContainer(ctx context.Context, r *runtime.RemoveConta
if retErr != nil {
// Reset removing if remove failed.
if err := resetContainerRemoving(container); err != nil {
logrus.WithError(err).Errorf("failed to reset removing state for container %q", id)
log.G(ctx).WithError(err).Errorf("failed to reset removing state for container %q", id)
}
}
}()
@ -68,7 +67,7 @@ func (c *criService) RemoveContainer(ctx context.Context, r *runtime.RemoveConta
if !errdefs.IsNotFound(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)
log.G(ctx).Tracef("Remove called for containerd container %q that does not exist", id)
}
// Delete container checkpoint.

View File

@ -24,6 +24,7 @@ import (
"github.com/containerd/containerd"
containerdio "github.com/containerd/containerd/cio"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -65,11 +66,11 @@ func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContain
status.Message = retErr.Error()
return status, nil
}); err != nil {
logrus.WithError(err).Errorf("failed to set start failure state for container %q", id)
log.G(ctx).WithError(err).Errorf("failed to set start failure state for container %q", id)
}
}
if err := resetContainerStarting(cntr); err != nil {
logrus.WithError(err).Errorf("failed to reset starting state for container %q", id)
log.G(ctx).WithError(err).Errorf("failed to reset starting state for container %q", id)
}
}()
@ -113,7 +114,7 @@ func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContain
defer deferCancel()
// It's possible that task is deleted by event monitor.
if _, err := task.Delete(deferCtx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
logrus.WithError(err).Errorf("Failed to delete containerd task %q", id)
log.G(ctx).WithError(err).Errorf("Failed to delete containerd task %q", id)
}
}
}()

View File

@ -23,8 +23,8 @@ import (
"github.com/containerd/containerd"
eventtypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@ -57,7 +57,7 @@ func (c *criService) stopContainer(ctx context.Context, container containerstore
state := container.Status.Get().State()
if state != runtime.ContainerState_CONTAINER_RUNNING &&
state != runtime.ContainerState_CONTAINER_UNKNOWN {
logrus.Infof("Container to stop %q must be in running or unknown state, current state %q",
log.G(ctx).Infof("Container to stop %q must be in running or unknown state, current state %q",
id, criContainerStateToString(state))
return nil
}
@ -118,7 +118,7 @@ func (c *criService) stopContainer(ctx context.Context, container containerstore
if err != store.ErrNotExist {
return errors.Wrapf(err, "failed to get image %q", container.ImageRef)
}
logrus.Warningf("Image %q not found, stop container with signal %q", container.ImageRef, stopSignal)
log.G(ctx).Warningf("Image %q not found, stop container with signal %q", container.ImageRef, stopSignal)
} else {
if image.ImageSpec.Config.StopSignal != "" {
stopSignal = image.ImageSpec.Config.StopSignal
@ -129,7 +129,7 @@ func (c *criService) stopContainer(ctx context.Context, container containerstore
if err != nil {
return errors.Wrapf(err, "failed to parse stop signal %q", stopSignal)
}
logrus.Infof("Stop container %q with signal %v", id, sig)
log.G(ctx).Infof("Stop container %q with signal %v", id, sig)
if err = task.Kill(ctx, sig); err != nil && !errdefs.IsNotFound(err) {
return errors.Wrapf(err, "failed to stop container %q", id)
}
@ -146,10 +146,10 @@ func (c *criService) stopContainer(ctx context.Context, container containerstore
return ctx.Err()
}
// sigTermCtx was exceeded. Send SIGKILL
logrus.Debugf("Stop container %q with signal %v timed out", id, sig)
log.G(ctx).Debugf("Stop container %q with signal %v timed out", id, sig)
}
logrus.Infof("Kill container %q", id)
log.G(ctx).Infof("Kill container %q", id)
if err = task.Kill(ctx, syscall.SIGKILL); err != nil && !errdefs.IsNotFound(err) {
return errors.Wrapf(err, "failed to kill container %q", id)
}

View File

@ -22,10 +22,10 @@ import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/containerd/typeurl"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@ -70,7 +70,7 @@ func (c *criService) updateContainerResources(ctx context.Context,
if err != nil {
return errors.Wrap(err, "failed to get container spec")
}
newSpec, err := updateOCILinuxResource(oldSpec, resources)
newSpec, err := updateOCILinuxResource(ctx, oldSpec, resources)
if err != nil {
return errors.Wrap(err, "failed to update resource in spec")
}
@ -84,7 +84,7 @@ func (c *criService) updateContainerResources(ctx context.Context,
defer deferCancel()
// Reset spec on error.
if err := updateContainerSpec(deferCtx, cntr.Container, oldSpec); err != nil {
logrus.WithError(err).Errorf("Failed to update spec %+v for container %q", oldSpec, id)
log.G(ctx).WithError(err).Errorf("Failed to update spec %+v for container %q", oldSpec, id)
}
}
}()
@ -130,7 +130,7 @@ func updateContainerSpec(ctx context.Context, cntr containerd.Container, spec *r
}
// updateOCILinuxResource updates container resource limit.
func updateOCILinuxResource(spec *runtimespec.Spec, new *runtime.LinuxContainerResources) (*runtimespec.Spec, error) {
func updateOCILinuxResource(ctx context.Context, spec *runtimespec.Spec, new *runtime.LinuxContainerResources) (*runtimespec.Spec, error) {
// Copy to make sure old spec is not changed.
var cloned runtimespec.Spec
if err := util.DeepCopy(&cloned, spec); err != nil {
@ -139,7 +139,7 @@ func updateOCILinuxResource(spec *runtimespec.Spec, new *runtime.LinuxContainerR
if cloned.Linux == nil {
cloned.Linux = &runtimespec.Linux{}
}
if err := opts.WithResources(new)(nil, nil, nil, &cloned); err != nil {
if err := opts.WithResources(new)(ctx, nil, nil, &cloned); err != nil {
return nil, errors.Wrap(err, "unable to set linux container resources")
}
return &cloned, nil

View File

@ -142,12 +142,12 @@ func makeContainerName(c *runtime.ContainerMetadata, s *runtime.PodSandboxMetada
}
// getCgroupsPath generates container cgroups path.
func getCgroupsPath(cgroupsParent, id string, systemdCgroup bool) string {
if systemdCgroup {
// Convert a.slice/b.slice/c.slice to c.slice.
p := path.Base(cgroupsParent)
func getCgroupsPath(cgroupsParent, id string) string {
base := path.Base(cgroupsParent)
if strings.HasSuffix(base, ".slice") {
// For a.slice/b.slice/c.slice, base is c.slice.
// runc systemd cgroup path format is "slice:prefix:name".
return strings.Join([]string{p, "cri-containerd", id}, ":")
return strings.Join([]string{base, "cri-containerd", id}, ":")
}
return filepath.Join(cgroupsParent, id)
}

View File

@ -30,16 +30,15 @@ import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
containerdimages "github.com/containerd/containerd/images"
"github.com/containerd/containerd/reference"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/remotes/docker"
"github.com/containerd/cri/pkg/config"
distribution "github.com/docker/distribution/reference"
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/cri-api/pkg/apis/runtime/v1alpha2"
criconfig "github.com/containerd/cri/pkg/config"
)
// For image management:
@ -92,16 +91,21 @@ func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest)
}
ref := namedRef.String()
if ref != imageRef {
logrus.Debugf("PullImage using normalized image ref: %q", ref)
log.G(ctx).Debugf("PullImage using normalized image ref: %q", ref)
}
resolver, desc, err := c.getResolver(ctx, ref, c.credentials(r.GetAuth()))
if err != nil {
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.
isSchema1 := desc.MediaType == containerdimages.MediaTypeDockerSchema1Manifest
var (
resolver = docker.NewResolver(docker.ResolverOptions{
Hosts: c.registryHosts(r.GetAuth()),
})
isSchema1 bool
imageHandler containerdimages.HandlerFunc = func(_ context.Context,
desc imagespec.Descriptor) ([]imagespec.Descriptor, error) {
if desc.MediaType == containerdimages.MediaTypeDockerSchema1Manifest {
isSchema1 = true
}
return nil, nil
}
)
image, err := c.client.Pull(ctx, ref,
containerd.WithSchema1Conversion,
containerd.WithResolver(resolver),
@ -109,6 +113,7 @@ func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest)
containerd.WithPullUnpack,
containerd.WithPullLabel(imageLabelKey, imageLabelValue),
containerd.WithMaxConcurrentDownloads(c.config.MaxConcurrentDownloads),
containerd.WithImageHandler(imageHandler),
)
if err != nil {
return nil, errors.Wrapf(err, "failed to pull and unpack image %q", ref)
@ -136,7 +141,7 @@ func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest)
}
}
logrus.Debugf("Pulled image %q with image id %q, repo tag %q, repo digest %q", imageRef, imageID,
log.G(ctx).Debugf("Pulled image %q with image id %q, repo tag %q, repo digest %q", imageRef, imageID,
repoTag, repoDigest)
// NOTE(random-liu): the actual state in containerd is the source of truth, even we maintain
// in-memory image store, it's only for in-memory indexing. The image could be removed
@ -147,10 +152,20 @@ func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest)
}
// ParseAuth parses AuthConfig and returns username and password/secret required by containerd.
func ParseAuth(auth *runtime.AuthConfig) (string, string, error) {
func ParseAuth(auth *runtime.AuthConfig, host string) (string, string, error) {
if auth == nil {
return "", "", nil
}
if auth.ServerAddress != "" {
// Do not return the auth info when server address doesn't match.
u, err := url.Parse(auth.ServerAddress)
if err != nil {
return "", "", errors.Wrap(err, "parse server address")
}
if host != u.Host {
return "", "", nil
}
}
if auth.Username != "" {
return auth.Username, auth.Password, nil
}
@ -234,31 +249,23 @@ func (c *criService) updateImage(ctx context.Context, r string) error {
return nil
}
// credentials returns a credential function for docker resolver to use.
func (c *criService) credentials(auth *runtime.AuthConfig) func(string) (string, string, error) {
return func(host string) (string, string, error) {
if auth == nil {
// Get default auth from config.
for h, ac := range c.config.Registry.Auths {
u, err := url.Parse(h)
if err != nil {
return "", "", errors.Wrapf(err, "parse auth host %q", h)
}
if u.Host == host {
auth = toRuntimeAuthConfig(ac)
break
}
}
}
return ParseAuth(auth)
}
}
// getTLSConfig returns a TLSConfig configured with a CA/Cert/Key specified by registryTLSConfig
func (c *criService) getTLSConfig(registryTLSConfig config.TLSConfig) (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(registryTLSConfig.CertFile, registryTLSConfig.KeyFile)
if err != nil {
return nil, errors.Wrap(err, "failed to load cert file")
func (c *criService) getTLSConfig(registryTLSConfig criconfig.TLSConfig) (*tls.Config, error) {
var (
cert tls.Certificate
err error
)
if registryTLSConfig.CertFile != "" && registryTLSConfig.KeyFile != "" {
cert, err = tls.LoadX509KeyPair(registryTLSConfig.CertFile, registryTLSConfig.KeyFile)
if err != nil {
return nil, errors.Wrap(err, "failed to load cert file")
}
}
if registryTLSConfig.CertFile != "" && registryTLSConfig.KeyFile == "" {
return nil, errors.Errorf("cert file %q was specified, but no corresponding key file was specified", registryTLSConfig.CertFile)
}
if registryTLSConfig.CertFile == "" && registryTLSConfig.KeyFile != "" {
return nil, errors.Errorf("key file %q was specified, but no corresponding cert file was specified", registryTLSConfig.KeyFile)
}
caCertPool, err := x509.SystemCertPool()
@ -272,75 +279,98 @@ func (c *criService) getTLSConfig(registryTLSConfig config.TLSConfig) (*tls.Conf
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
RootCAs: caCertPool,
}
if len(cert.Certificate) != 0 {
tlsConfig.Certificates = []tls.Certificate{cert}
}
tlsConfig.BuildNameToCertificate()
return tlsConfig, nil
}
// getResolver tries registry mirrors and the default registry, and returns the resolver and descriptor
// from the first working registry.
func (c *criService) getResolver(ctx context.Context, ref string, cred func(string) (string, string, error)) (remotes.Resolver, imagespec.Descriptor, error) {
refspec, err := reference.Parse(ref)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrap(err, "parse image reference")
// registryHosts is the registry hosts to be used by the resolver.
func (c *criService) registryHosts(auth *runtime.AuthConfig) docker.RegistryHosts {
return func(host string) ([]docker.RegistryHost, error) {
var registries []docker.RegistryHost
endpoints, err := c.registryEndpoints(host)
if err != nil {
return nil, errors.Wrap(err, "get registry endpoints")
}
for _, e := range endpoints {
u, err := url.Parse(e)
if err != nil {
return nil, errors.Wrapf(err, "parse registry endpoint %q from mirrors", e)
}
var (
transport = newTransport()
client = &http.Client{Transport: transport}
config = c.config.Registry.Configs[u.Host]
)
if u.Scheme != "https" && config.TLS != nil {
return nil, errors.Errorf("tls provided for http endpoint %q", e)
}
if config.TLS != nil {
transport.TLSClientConfig, err = c.getTLSConfig(*config.TLS)
if err != nil {
return nil, errors.Wrapf(err, "get TLSConfig for registry %q", e)
}
}
if auth == nil && config.Auth != nil {
auth = toRuntimeAuthConfig(*config.Auth)
}
if u.Path == "" {
u.Path = "/v2"
}
registries = append(registries, docker.RegistryHost{
Client: client,
Authorizer: docker.NewDockerAuthorizer(
docker.WithAuthClient(client),
docker.WithAuthCreds(func(host string) (string, string, error) {
return ParseAuth(auth, host)
})),
Host: u.Host,
Scheme: u.Scheme,
Path: u.Path,
Capabilities: docker.HostCapabilityResolve | docker.HostCapabilityPull,
})
}
return registries, nil
}
}
var (
transport = newTransport()
httpClient = &http.Client{Transport: transport}
)
// Try mirrors in order first, and then try default host name.
for _, e := range c.config.Registry.Mirrors[refspec.Hostname()].Endpoints {
// registryEndpoints returns endpoints for a given host.
// It adds default registry endpoint if it does not exist in the passed-in endpoint list.
// It also supports wildcard host matching with `*`.
func (c *criService) registryEndpoints(host string) ([]string, error) {
var endpoints []string
_, ok := c.config.Registry.Mirrors[host]
if ok {
endpoints = c.config.Registry.Mirrors[host].Endpoints
} else {
endpoints = c.config.Registry.Mirrors["*"].Endpoints
}
defaultHost, err := docker.DefaultHost(host)
if err != nil {
return nil, errors.Wrap(err, "get default host")
}
for _, e := range endpoints {
u, err := url.Parse(e)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "parse registry endpoint %q", e)
return nil, errors.Wrap(err, "parse endpoint url")
}
if registryTLSConfig, ok := c.config.Registry.TLSConfigs[u.Host]; ok {
transport.TLSClientConfig, err = c.getTLSConfig(registryTLSConfig)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "get TLSConfig for registry %q", refspec.Hostname())
}
}
resolver := docker.NewResolver(docker.ResolverOptions{
Authorizer: docker.NewAuthorizer(httpClient, cred),
Client: httpClient,
Host: func(string) (string, error) { return u.Host, nil },
// By default use "https".
PlainHTTP: u.Scheme == "http",
})
_, desc, err := resolver.Resolve(ctx, ref)
if err == nil {
return resolver, desc, nil
}
logrus.WithError(err).Debugf("Tried registry mirror %q but failed", e)
// Continue to try next endpoint
}
hostname, err := docker.DefaultHost(refspec.Hostname())
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "get host for refspec %q", refspec.Hostname())
}
if registryTLSConfig, ok := c.config.Registry.TLSConfigs[hostname]; ok {
transport.TLSClientConfig, err = c.getTLSConfig(registryTLSConfig)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "get TLSConfig for registry %q", refspec.Hostname())
if u.Host == host {
// Do not add default if the endpoint already exists.
return endpoints, nil
}
}
resolver := docker.NewResolver(docker.ResolverOptions{
Credentials: cred,
Client: httpClient,
})
_, desc, err := resolver.Resolve(ctx, ref)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrap(err, "no available registry endpoint")
}
return resolver, desc, nil
return append(endpoints, "https://"+defaultHost), nil
}
// newTransport returns a new HTTP transport used to pull image.

View File

@ -19,14 +19,14 @@ package server
import (
"encoding/json"
"github.com/containerd/containerd/log"
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/cri-api/pkg/apis/runtime/v1alpha2"
"github.com/containerd/cri/pkg/store"
imagestore "github.com/containerd/cri/pkg/store/image"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
)
// ImageStatus returns the status of the image, returns nil if the image isn't present.
@ -97,7 +97,7 @@ func (c *criService) toCRIImageInfo(ctx context.Context, image *imagestore.Image
if err == nil {
info["info"] = string(m)
} else {
logrus.WithError(err).Errorf("failed to marshal info %v", imi)
log.G(ctx).WithError(err).Errorf("failed to marshal info %v", imi)
info["info"] = err.Error()
}

View File

@ -19,13 +19,12 @@ package server
import (
"errors"
"github.com/sirupsen/logrus"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"github.com/containerd/containerd/errdefs"
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
"github.com/containerd/cri/pkg/log"
)
// instrumentedService wraps service with containerd namespace and logs.
@ -52,12 +51,12 @@ func (in *instrumentedService) RunPodSandbox(ctx context.Context, r *runtime.Run
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("RunPodsandbox for %+v", r.GetConfig().GetMetadata())
log.G(ctx).Infof("RunPodsandbox for %+v", r.GetConfig().GetMetadata())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("RunPodSandbox for %+v failed, error", r.GetConfig().GetMetadata())
log.G(ctx).WithError(err).Errorf("RunPodSandbox for %+v failed, error", r.GetConfig().GetMetadata())
} else {
logrus.Infof("RunPodSandbox for %+v returns sandbox id %q", r.GetConfig().GetMetadata(), res.GetPodSandboxId())
log.G(ctx).Infof("RunPodSandbox for %+v returns sandbox id %q", r.GetConfig().GetMetadata(), res.GetPodSandboxId())
}
}()
res, err = in.c.RunPodSandbox(ctrdutil.WithNamespace(ctx), r)
@ -68,12 +67,12 @@ func (in *instrumentedService) ListPodSandbox(ctx context.Context, r *runtime.Li
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("ListPodSandbox with filter %+v", r.GetFilter())
log.G(ctx).Tracef("ListPodSandbox with filter %+v", r.GetFilter())
defer func() {
if err != nil {
logrus.WithError(err).Error("ListPodSandbox failed")
log.G(ctx).WithError(err).Error("ListPodSandbox failed")
} else {
log.Tracef("ListPodSandbox returns pod sandboxes %+v", res.GetItems())
log.G(ctx).Tracef("ListPodSandbox returns pod sandboxes %+v", res.GetItems())
}
}()
res, err = in.c.ListPodSandbox(ctrdutil.WithNamespace(ctx), r)
@ -84,12 +83,12 @@ func (in *instrumentedService) PodSandboxStatus(ctx context.Context, r *runtime.
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("PodSandboxStatus for %q", r.GetPodSandboxId())
log.G(ctx).Tracef("PodSandboxStatus for %q", r.GetPodSandboxId())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("PodSandboxStatus for %q failed", r.GetPodSandboxId())
log.G(ctx).WithError(err).Errorf("PodSandboxStatus for %q failed", r.GetPodSandboxId())
} else {
log.Tracef("PodSandboxStatus for %q returns status %+v", r.GetPodSandboxId(), res.GetStatus())
log.G(ctx).Tracef("PodSandboxStatus for %q returns status %+v", r.GetPodSandboxId(), res.GetStatus())
}
}()
res, err = in.c.PodSandboxStatus(ctrdutil.WithNamespace(ctx), r)
@ -100,12 +99,12 @@ func (in *instrumentedService) StopPodSandbox(ctx context.Context, r *runtime.St
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("StopPodSandbox for %q", r.GetPodSandboxId())
log.G(ctx).Infof("StopPodSandbox for %q", r.GetPodSandboxId())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("StopPodSandbox for %q failed", r.GetPodSandboxId())
log.G(ctx).WithError(err).Errorf("StopPodSandbox for %q failed", r.GetPodSandboxId())
} else {
logrus.Infof("StopPodSandbox for %q returns successfully", r.GetPodSandboxId())
log.G(ctx).Infof("StopPodSandbox for %q returns successfully", r.GetPodSandboxId())
}
}()
res, err := in.c.StopPodSandbox(ctrdutil.WithNamespace(ctx), r)
@ -116,12 +115,12 @@ func (in *instrumentedService) RemovePodSandbox(ctx context.Context, r *runtime.
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("RemovePodSandbox for %q", r.GetPodSandboxId())
log.G(ctx).Infof("RemovePodSandbox for %q", r.GetPodSandboxId())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("RemovePodSandbox for %q failed", r.GetPodSandboxId())
log.G(ctx).WithError(err).Errorf("RemovePodSandbox for %q failed", r.GetPodSandboxId())
} else {
logrus.Infof("RemovePodSandbox %q returns successfully", r.GetPodSandboxId())
log.G(ctx).Infof("RemovePodSandbox %q returns successfully", r.GetPodSandboxId())
}
}()
res, err := in.c.RemovePodSandbox(ctrdutil.WithNamespace(ctx), r)
@ -132,12 +131,12 @@ func (in *instrumentedService) PortForward(ctx context.Context, r *runtime.PortF
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("Portforward for %q port %v", r.GetPodSandboxId(), r.GetPort())
log.G(ctx).Infof("Portforward for %q port %v", r.GetPodSandboxId(), r.GetPort())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("Portforward for %q failed", r.GetPodSandboxId())
log.G(ctx).WithError(err).Errorf("Portforward for %q failed", r.GetPodSandboxId())
} else {
logrus.Infof("Portforward for %q returns URL %q", r.GetPodSandboxId(), res.GetUrl())
log.G(ctx).Infof("Portforward for %q returns URL %q", r.GetPodSandboxId(), res.GetUrl())
}
}()
res, err = in.c.PortForward(ctrdutil.WithNamespace(ctx), r)
@ -148,14 +147,14 @@ func (in *instrumentedService) CreateContainer(ctx context.Context, r *runtime.C
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("CreateContainer within sandbox %q for container %+v",
log.G(ctx).Infof("CreateContainer within sandbox %q for container %+v",
r.GetPodSandboxId(), r.GetConfig().GetMetadata())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("CreateContainer within sandbox %q for %+v failed",
log.G(ctx).WithError(err).Errorf("CreateContainer within sandbox %q for %+v failed",
r.GetPodSandboxId(), r.GetConfig().GetMetadata())
} else {
logrus.Infof("CreateContainer within sandbox %q for %+v returns container id %q",
log.G(ctx).Infof("CreateContainer within sandbox %q for %+v returns container id %q",
r.GetPodSandboxId(), r.GetConfig().GetMetadata(), res.GetContainerId())
}
}()
@ -167,12 +166,12 @@ func (in *instrumentedService) StartContainer(ctx context.Context, r *runtime.St
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("StartContainer for %q", r.GetContainerId())
log.G(ctx).Infof("StartContainer for %q", r.GetContainerId())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("StartContainer for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("StartContainer for %q failed", r.GetContainerId())
} else {
logrus.Infof("StartContainer for %q returns successfully", r.GetContainerId())
log.G(ctx).Infof("StartContainer for %q returns successfully", r.GetContainerId())
}
}()
res, err := in.c.StartContainer(ctrdutil.WithNamespace(ctx), r)
@ -183,12 +182,12 @@ func (in *instrumentedService) ListContainers(ctx context.Context, r *runtime.Li
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("ListContainers with filter %+v", r.GetFilter())
log.G(ctx).Tracef("ListContainers with filter %+v", r.GetFilter())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("ListContainers with filter %+v failed", r.GetFilter())
log.G(ctx).WithError(err).Errorf("ListContainers with filter %+v failed", r.GetFilter())
} else {
log.Tracef("ListContainers with filter %+v returns containers %+v",
log.G(ctx).Tracef("ListContainers with filter %+v returns containers %+v",
r.GetFilter(), res.GetContainers())
}
}()
@ -200,12 +199,12 @@ func (in *instrumentedService) ContainerStatus(ctx context.Context, r *runtime.C
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("ContainerStatus for %q", r.GetContainerId())
log.G(ctx).Tracef("ContainerStatus for %q", r.GetContainerId())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("ContainerStatus for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("ContainerStatus for %q failed", r.GetContainerId())
} else {
log.Tracef("ContainerStatus for %q returns status %+v", r.GetContainerId(), res.GetStatus())
log.G(ctx).Tracef("ContainerStatus for %q returns status %+v", r.GetContainerId(), res.GetStatus())
}
}()
res, err = in.c.ContainerStatus(ctrdutil.WithNamespace(ctx), r)
@ -216,12 +215,12 @@ func (in *instrumentedService) StopContainer(ctx context.Context, r *runtime.Sto
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("StopContainer for %q with timeout %d (s)", r.GetContainerId(), r.GetTimeout())
log.G(ctx).Infof("StopContainer for %q with timeout %d (s)", r.GetContainerId(), r.GetTimeout())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("StopContainer for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("StopContainer for %q failed", r.GetContainerId())
} else {
logrus.Infof("StopContainer for %q returns successfully", r.GetContainerId())
log.G(ctx).Infof("StopContainer for %q returns successfully", r.GetContainerId())
}
}()
res, err = in.c.StopContainer(ctrdutil.WithNamespace(ctx), r)
@ -232,12 +231,12 @@ func (in *instrumentedService) RemoveContainer(ctx context.Context, r *runtime.R
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("RemoveContainer for %q", r.GetContainerId())
log.G(ctx).Infof("RemoveContainer for %q", r.GetContainerId())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("RemoveContainer for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("RemoveContainer for %q failed", r.GetContainerId())
} else {
logrus.Infof("RemoveContainer for %q returns successfully", r.GetContainerId())
log.G(ctx).Infof("RemoveContainer for %q returns successfully", r.GetContainerId())
}
}()
res, err = in.c.RemoveContainer(ctrdutil.WithNamespace(ctx), r)
@ -248,13 +247,13 @@ func (in *instrumentedService) ExecSync(ctx context.Context, r *runtime.ExecSync
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("ExecSync for %q with command %+v and timeout %d (s)", r.GetContainerId(), r.GetCmd(), r.GetTimeout())
log.G(ctx).Infof("ExecSync for %q with command %+v and timeout %d (s)", r.GetContainerId(), r.GetCmd(), r.GetTimeout())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("ExecSync for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("ExecSync for %q failed", r.GetContainerId())
} else {
logrus.Infof("ExecSync for %q returns with exit code %d", r.GetContainerId(), res.GetExitCode())
logrus.Debugf("ExecSync for %q outputs - stdout: %q, stderr: %q", r.GetContainerId(),
log.G(ctx).Infof("ExecSync for %q returns with exit code %d", r.GetContainerId(), res.GetExitCode())
log.G(ctx).Debugf("ExecSync for %q outputs - stdout: %q, stderr: %q", r.GetContainerId(),
res.GetStdout(), res.GetStderr())
}
}()
@ -266,13 +265,13 @@ func (in *instrumentedService) Exec(ctx context.Context, r *runtime.ExecRequest)
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("Exec for %q with command %+v, tty %v and stdin %v",
log.G(ctx).Infof("Exec for %q with command %+v, tty %v and stdin %v",
r.GetContainerId(), r.GetCmd(), r.GetTty(), r.GetStdin())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("Exec for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("Exec for %q failed", r.GetContainerId())
} else {
logrus.Infof("Exec for %q returns URL %q", r.GetContainerId(), res.GetUrl())
log.G(ctx).Infof("Exec for %q returns URL %q", r.GetContainerId(), res.GetUrl())
}
}()
res, err = in.c.Exec(ctrdutil.WithNamespace(ctx), r)
@ -283,12 +282,12 @@ func (in *instrumentedService) Attach(ctx context.Context, r *runtime.AttachRequ
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("Attach for %q with tty %v and stdin %v", r.GetContainerId(), r.GetTty(), r.GetStdin())
log.G(ctx).Infof("Attach for %q with tty %v and stdin %v", r.GetContainerId(), r.GetTty(), r.GetStdin())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("Attach for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("Attach for %q failed", r.GetContainerId())
} else {
logrus.Infof("Attach for %q returns URL %q", r.GetContainerId(), res.Url)
log.G(ctx).Infof("Attach for %q returns URL %q", r.GetContainerId(), res.Url)
}
}()
res, err = in.c.Attach(ctrdutil.WithNamespace(ctx), r)
@ -299,12 +298,12 @@ func (in *instrumentedService) UpdateContainerResources(ctx context.Context, r *
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("UpdateContainerResources for %q with %+v", r.GetContainerId(), r.GetLinux())
log.G(ctx).Infof("UpdateContainerResources for %q with %+v", r.GetContainerId(), r.GetLinux())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("UpdateContainerResources for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("UpdateContainerResources for %q failed", r.GetContainerId())
} else {
logrus.Infof("UpdateContainerResources for %q returns successfully", r.GetContainerId())
log.G(ctx).Infof("UpdateContainerResources for %q returns successfully", r.GetContainerId())
}
}()
res, err = in.c.UpdateContainerResources(ctrdutil.WithNamespace(ctx), r)
@ -315,12 +314,12 @@ func (in *instrumentedService) PullImage(ctx context.Context, r *runtime.PullIma
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("PullImage %q", r.GetImage().GetImage())
log.G(ctx).Infof("PullImage %q", r.GetImage().GetImage())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("PullImage %q failed", r.GetImage().GetImage())
log.G(ctx).WithError(err).Errorf("PullImage %q failed", r.GetImage().GetImage())
} else {
logrus.Infof("PullImage %q returns image reference %q",
log.G(ctx).Infof("PullImage %q returns image reference %q",
r.GetImage().GetImage(), res.GetImageRef())
}
}()
@ -332,12 +331,12 @@ func (in *instrumentedService) ListImages(ctx context.Context, r *runtime.ListIm
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("ListImages with filter %+v", r.GetFilter())
log.G(ctx).Tracef("ListImages with filter %+v", r.GetFilter())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("ListImages with filter %+v failed", r.GetFilter())
log.G(ctx).WithError(err).Errorf("ListImages with filter %+v failed", r.GetFilter())
} else {
log.Tracef("ListImages with filter %+v returns image list %+v",
log.G(ctx).Tracef("ListImages with filter %+v returns image list %+v",
r.GetFilter(), res.GetImages())
}
}()
@ -349,12 +348,12 @@ func (in *instrumentedService) ImageStatus(ctx context.Context, r *runtime.Image
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("ImageStatus for %q", r.GetImage().GetImage())
log.G(ctx).Tracef("ImageStatus for %q", r.GetImage().GetImage())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("ImageStatus for %q failed", r.GetImage().GetImage())
log.G(ctx).WithError(err).Errorf("ImageStatus for %q failed", r.GetImage().GetImage())
} else {
log.Tracef("ImageStatus for %q returns image status %+v",
log.G(ctx).Tracef("ImageStatus for %q returns image status %+v",
r.GetImage().GetImage(), res.GetImage())
}
}()
@ -366,12 +365,12 @@ func (in *instrumentedService) RemoveImage(ctx context.Context, r *runtime.Remov
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Infof("RemoveImage %q", r.GetImage().GetImage())
log.G(ctx).Infof("RemoveImage %q", r.GetImage().GetImage())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("RemoveImage %q failed", r.GetImage().GetImage())
log.G(ctx).WithError(err).Errorf("RemoveImage %q failed", r.GetImage().GetImage())
} else {
logrus.Infof("RemoveImage %q returns successfully", r.GetImage().GetImage())
log.G(ctx).Infof("RemoveImage %q returns successfully", r.GetImage().GetImage())
}
}()
res, err := in.c.RemoveImage(ctrdutil.WithNamespace(ctx), r)
@ -382,12 +381,12 @@ func (in *instrumentedService) ImageFsInfo(ctx context.Context, r *runtime.Image
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Debugf("ImageFsInfo")
log.G(ctx).Debugf("ImageFsInfo")
defer func() {
if err != nil {
logrus.WithError(err).Error("ImageFsInfo failed")
log.G(ctx).WithError(err).Error("ImageFsInfo failed")
} else {
logrus.Debugf("ImageFsInfo returns filesystem info %+v", res.ImageFilesystems)
log.G(ctx).Debugf("ImageFsInfo returns filesystem info %+v", res.ImageFilesystems)
}
}()
res, err = in.c.ImageFsInfo(ctrdutil.WithNamespace(ctx), r)
@ -398,12 +397,12 @@ func (in *instrumentedService) ContainerStats(ctx context.Context, r *runtime.Co
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Debugf("ContainerStats for %q", r.GetContainerId())
log.G(ctx).Debugf("ContainerStats for %q", r.GetContainerId())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("ContainerStats for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("ContainerStats for %q failed", r.GetContainerId())
} else {
logrus.Debugf("ContainerStats for %q returns stats %+v", r.GetContainerId(), res.GetStats())
log.G(ctx).Debugf("ContainerStats for %q returns stats %+v", r.GetContainerId(), res.GetStats())
}
}()
res, err = in.c.ContainerStats(ctrdutil.WithNamespace(ctx), r)
@ -414,12 +413,12 @@ func (in *instrumentedService) ListContainerStats(ctx context.Context, r *runtim
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("ListContainerStats with filter %+v", r.GetFilter())
log.G(ctx).Tracef("ListContainerStats with filter %+v", r.GetFilter())
defer func() {
if err != nil {
logrus.WithError(err).Error("ListContainerStats failed")
log.G(ctx).WithError(err).Error("ListContainerStats failed")
} else {
log.Tracef("ListContainerStats returns stats %+v", res.GetStats())
log.G(ctx).Tracef("ListContainerStats returns stats %+v", res.GetStats())
}
}()
res, err = in.c.ListContainerStats(ctrdutil.WithNamespace(ctx), r)
@ -430,12 +429,12 @@ func (in *instrumentedService) Status(ctx context.Context, r *runtime.StatusRequ
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("Status")
log.G(ctx).Tracef("Status")
defer func() {
if err != nil {
logrus.WithError(err).Error("Status failed")
log.G(ctx).WithError(err).Error("Status failed")
} else {
log.Tracef("Status returns status %+v", res.GetStatus())
log.G(ctx).Tracef("Status returns status %+v", res.GetStatus())
}
}()
res, err = in.c.Status(ctrdutil.WithNamespace(ctx), r)
@ -446,12 +445,12 @@ func (in *instrumentedService) Version(ctx context.Context, r *runtime.VersionRe
if err := in.checkInitialized(); err != nil {
return nil, err
}
log.Tracef("Version with client side version %q", r.GetVersion())
log.G(ctx).Tracef("Version with client side version %q", r.GetVersion())
defer func() {
if err != nil {
logrus.WithError(err).Error("Version failed")
log.G(ctx).WithError(err).Error("Version failed")
} else {
log.Tracef("Version returns %+v", res)
log.G(ctx).Tracef("Version returns %+v", res)
}
}()
res, err = in.c.Version(ctrdutil.WithNamespace(ctx), r)
@ -462,12 +461,12 @@ func (in *instrumentedService) UpdateRuntimeConfig(ctx context.Context, r *runti
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Debugf("UpdateRuntimeConfig with config %+v", r.GetRuntimeConfig())
log.G(ctx).Debugf("UpdateRuntimeConfig with config %+v", r.GetRuntimeConfig())
defer func() {
if err != nil {
logrus.WithError(err).Error("UpdateRuntimeConfig failed")
log.G(ctx).WithError(err).Error("UpdateRuntimeConfig failed")
} else {
logrus.Debug("UpdateRuntimeConfig returns returns successfully")
log.G(ctx).Debug("UpdateRuntimeConfig returns returns successfully")
}
}()
res, err = in.c.UpdateRuntimeConfig(ctrdutil.WithNamespace(ctx), r)
@ -478,12 +477,12 @@ func (in *instrumentedService) ReopenContainerLog(ctx context.Context, r *runtim
if err := in.checkInitialized(); err != nil {
return nil, err
}
logrus.Debugf("ReopenContainerLog for %q", r.GetContainerId())
log.G(ctx).Debugf("ReopenContainerLog for %q", r.GetContainerId())
defer func() {
if err != nil {
logrus.WithError(err).Errorf("ReopenContainerLog for %q failed", r.GetContainerId())
log.G(ctx).WithError(err).Errorf("ReopenContainerLog for %q failed", r.GetContainerId())
} else {
logrus.Debugf("ReopenContainerLog for %q returns successfully", r.GetContainerId())
log.G(ctx).Debugf("ReopenContainerLog for %q returns successfully", r.GetContainerId())
}
}()
res, err = in.c.ReopenContainerLog(ctrdutil.WithNamespace(ctx), r)

View File

@ -26,11 +26,11 @@ import (
containerdio "github.com/containerd/containerd/cio"
"github.com/containerd/containerd/errdefs"
containerdimages "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/platforms"
"github.com/containerd/typeurl"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@ -60,10 +60,10 @@ func (c *criService) recover(ctx context.Context) error {
for _, sandbox := range sandboxes {
sb, err := c.loadSandbox(ctx, sandbox)
if err != nil {
logrus.WithError(err).Errorf("Failed to load sandbox %q", sandbox.ID())
log.G(ctx).WithError(err).Errorf("Failed to load sandbox %q", sandbox.ID())
continue
}
logrus.Debugf("Loaded sandbox %+v", sb)
log.G(ctx).Debugf("Loaded sandbox %+v", sb)
if err := c.sandboxStore.Add(sb); err != nil {
return errors.Wrapf(err, "failed to add sandbox %q to store", sandbox.ID())
}
@ -80,10 +80,10 @@ func (c *criService) recover(ctx context.Context) error {
for _, container := range containers {
cntr, err := c.loadContainer(ctx, container)
if err != nil {
logrus.WithError(err).Errorf("Failed to load container %q", container.ID())
log.G(ctx).WithError(err).Errorf("Failed to load container %q", container.ID())
continue
}
logrus.Debugf("Loaded container %+v", cntr)
log.G(ctx).Debugf("Loaded container %+v", cntr)
if err := c.containerStore.Add(cntr); err != nil {
return errors.Wrapf(err, "failed to add container %q to store", container.ID())
}
@ -130,7 +130,7 @@ func (c *criService) recover(ctx context.Context) error {
errMsg: "failed to cleanup orphaned volatile container directories",
},
} {
if err := cleanupOrphanedIDDirs(cleanup.cntrs, cleanup.base); err != nil {
if err := cleanupOrphanedIDDirs(ctx, cleanup.cntrs, cleanup.base); err != nil {
return errors.Wrap(err, cleanup.errMsg)
}
}
@ -176,7 +176,7 @@ func (c *criService) loadContainer(ctx context.Context, cntr containerd.Containe
// Load status from checkpoint.
status, err := containerstore.LoadStatus(containerDir, id)
if err != nil {
logrus.WithError(err).Warnf("Failed to load container status for %q", id)
log.G(ctx).WithError(err).Warnf("Failed to load container status for %q", id)
status = unknownContainerStatus()
}
@ -306,7 +306,7 @@ func (c *criService) loadContainer(ctx context.Context, cntr containerd.Containe
return nil
}()
if err != nil {
logrus.WithError(err).Errorf("Failed to load container status for %q", id)
log.G(ctx).WithError(err).Errorf("Failed to load container status for %q", id)
status = unknownContainerStatus()
}
opts := []containerstore.Opts{
@ -400,7 +400,7 @@ func (c *criService) loadSandbox(ctx context.Context, cntr containerd.Container)
return status, nil
}()
if err != nil {
logrus.WithError(err).Errorf("Failed to load sandbox status for %q", cntr.ID())
log.G(ctx).WithError(err).Errorf("Failed to load sandbox status for %q", cntr.ID())
}
sandbox = sandboxstore.NewSandbox(*meta, s)
@ -425,32 +425,32 @@ func (c *criService) loadImages(ctx context.Context, cImages []containerd.Image)
for _, i := range cImages {
ok, _, _, _, err := containerdimages.Check(ctx, i.ContentStore(), i.Target(), platforms.Default())
if err != nil {
logrus.WithError(err).Errorf("Failed to check image content readiness for %q", i.Name())
log.G(ctx).WithError(err).Errorf("Failed to check image content readiness for %q", i.Name())
continue
}
if !ok {
logrus.Warnf("The image content readiness for %q is not ok", i.Name())
log.G(ctx).Warnf("The image content readiness for %q is not ok", i.Name())
continue
}
// Checking existence of top-level snapshot for each image being recovered.
unpacked, err := i.IsUnpacked(ctx, snapshotter)
if err != nil {
logrus.WithError(err).Warnf("Failed to check whether image is unpacked for image %s", i.Name())
log.G(ctx).WithError(err).Warnf("Failed to check whether image is unpacked for image %s", i.Name())
continue
}
if !unpacked {
logrus.Warnf("The image %s is not unpacked.", i.Name())
log.G(ctx).Warnf("The image %s is not unpacked.", i.Name())
// TODO(random-liu): Consider whether we should try unpack here.
}
if err := c.updateImage(ctx, i.Name()); err != nil {
logrus.WithError(err).Warnf("Failed to update reference for image %q", i.Name())
log.G(ctx).WithError(err).Warnf("Failed to update reference for image %q", i.Name())
continue
}
logrus.Debugf("Loaded image %q", i.Name())
log.G(ctx).Debugf("Loaded image %q", i.Name())
}
}
func cleanupOrphanedIDDirs(cntrs []containerd.Container, base string) error {
func cleanupOrphanedIDDirs(ctx context.Context, cntrs []containerd.Container, base string) error {
// Cleanup orphaned id directories.
dirs, err := ioutil.ReadDir(base)
if err != nil && !os.IsNotExist(err) {
@ -462,7 +462,7 @@ func cleanupOrphanedIDDirs(cntrs []containerd.Container, base string) error {
}
for _, d := range dirs {
if !d.IsDir() {
logrus.Warnf("Invalid file %q found in base directory %q", d.Name(), base)
log.G(ctx).Warnf("Invalid file %q found in base directory %q", d.Name(), base)
continue
}
if _, ok := idsMap[d.Name()]; ok {
@ -471,9 +471,9 @@ func cleanupOrphanedIDDirs(cntrs []containerd.Container, base string) error {
}
dir := filepath.Join(base, d.Name())
if err := system.EnsureRemoveAll(dir); err != nil {
logrus.WithError(err).Warnf("Failed to remove id directory %q", dir)
log.G(ctx).WithError(err).Warnf("Failed to remove id directory %q", dir)
} else {
logrus.Debugf("Cleanup orphaned id directory %q", dir)
log.G(ctx).Debugf("Cleanup orphaned id directory %q", dir)
}
}
return nil

View File

@ -23,6 +23,7 @@ import (
"os/exec"
"strings"
"github.com/containerd/containerd/log"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -48,7 +49,7 @@ func (c *criService) PortForward(ctx context.Context, r *runtime.PortForwardRequ
// portForward requires `socat` on the node. It uses netns to enter the sandbox namespace,
// and run `socat` insidethe namespace to forward stream for a specific port. The `socat`
// command keeps running until it exits or client disconnect.
func (c *criService) portForward(id string, port int32, stream io.ReadWriteCloser) error {
func (c *criService) portForward(ctx context.Context, id string, port int32, stream io.ReadWriter) error {
s, err := c.sandboxStore.Get(id)
if err != nil {
return errors.Wrapf(err, "failed to find sandbox %q in store", id)
@ -82,7 +83,7 @@ func (c *criService) portForward(id string, port int32, stream io.ReadWriteClose
// Check https://linux.die.net/man/1/socat for meaning of the options.
args := []string{socat, "-", fmt.Sprintf("TCP4:localhost:%d", port)}
logrus.Infof("Executing port forwarding command %q in network namespace %q", strings.Join(args, " "), netNSPath)
log.G(ctx).Infof("Executing port forwarding command %q in network namespace %q", strings.Join(args, " "), netNSPath)
err = netNSDo(func(_ ns.NetNS) error {
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = stream
@ -119,7 +120,7 @@ func (c *criService) portForward(id string, port int32, stream io.ReadWriteClose
if err != nil {
return errors.Wrapf(err, "failed to execute portforward in network namespace %q", netNSPath)
}
logrus.Infof("Finish port forwarding for %q port %d", id, port)
log.G(ctx).Infof("Finish port forwarding for %q port %d", id, port)
return nil
}

View File

@ -19,12 +19,12 @@ package server
import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"github.com/containerd/cri/pkg/log"
"github.com/containerd/cri/pkg/store"
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
)
@ -39,7 +39,7 @@ func (c *criService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodS
r.GetPodSandboxId())
}
// Do not return error if the id doesn't exist.
log.Tracef("RemovePodSandbox called for sandbox %q that does not exist",
log.G(ctx).Tracef("RemovePodSandbox called for sandbox %q that does not exist",
r.GetPodSandboxId())
return &runtime.RemovePodSandboxResponse{}, nil
}
@ -95,7 +95,7 @@ func (c *criService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodS
if !errdefs.IsNotFound(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)
log.G(ctx).Tracef("Remove called for sandbox container %q that does not exist", id)
}
// Remove sandbox from sandbox store. Note that once the sandbox is successfully

View File

@ -26,6 +26,7 @@ import (
"github.com/containerd/containerd"
containerdio "github.com/containerd/containerd/cio"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/oci"
"github.com/containerd/containerd/plugin"
cni "github.com/containerd/go-cni"
@ -44,7 +45,6 @@ import (
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"
"github.com/containerd/cri/pkg/netns"
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
"github.com/containerd/cri/pkg/util"
@ -59,7 +59,7 @@ func init() {
// the sandbox is in ready state.
func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) {
config := r.GetConfig()
logrus.Debugf("Sandbox config %+v", config)
log.G(ctx).Debugf("Sandbox config %+v", config)
// Generate unique id and name for the sandbox and reserve the name.
id := util.GenerateID()
@ -68,7 +68,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
return nil, errors.New("sandbox config must include metadata")
}
name := makeSandboxName(metadata)
logrus.Debugf("Generated id %q for sandbox %q", id, name)
log.G(ctx).Debugf("Generated id %q for sandbox %q", id, name)
// Reserve the sandbox name to avoid concurrent `RunPodSandbox` request starting the
// same sandbox.
if err := c.sandboxNameIndex.Reserve(name, id); err != nil {
@ -108,7 +108,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
if err != nil {
return nil, errors.Wrap(err, "failed to get sandbox runtime")
}
logrus.Debugf("Use OCI %+v for sandbox %q", ociRuntime, id)
log.G(ctx).Debugf("Use OCI %+v for sandbox %q", ociRuntime, id)
securityContext := config.GetLinux().GetSecurityContext()
//Create Network Namespace if it is not in host network
@ -126,7 +126,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
defer func() {
if retErr != nil {
if err := sandbox.NetNS.Remove(); err != nil {
logrus.WithError(err).Errorf("Failed to remove network namespace %s for sandbox %q", sandbox.NetNSPath, id)
log.G(ctx).WithError(err).Errorf("Failed to remove network namespace %s for sandbox %q", sandbox.NetNSPath, id)
}
sandbox.NetNSPath = ""
}
@ -139,15 +139,15 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
// 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, sandbox.CNIResult, err = c.setupPod(id, sandbox.NetNSPath, config)
sandbox.IP, sandbox.CNIResult, err = c.setupPod(ctx, 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.teardownPod(id, sandbox.NetNSPath, config); err != nil {
logrus.WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
if err := c.teardownPod(ctx, id, sandbox.NetNSPath, config); err != nil {
log.G(ctx).WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
}
}
}()
@ -158,7 +158,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
if err != nil {
return nil, errors.Wrap(err, "failed to generate sandbox container spec")
}
logrus.Debugf("Sandbox container %q spec: %#+v", id, spew.NewFormatter(spec))
log.G(ctx).Debugf("Sandbox container %q spec: %#+v", id, spew.NewFormatter(spec))
var specOpts []oci.SpecOpts
userstr, err := generateUserString(
@ -169,6 +169,11 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
if err != nil {
return nil, errors.Wrap(err, "failed to generate user string")
}
if userstr == "" {
// Lastly, since no user override was passed via CRI try to set via OCI
// Image
userstr = image.ImageSpec.Config.User
}
if userstr != "" {
specOpts = append(specOpts, oci.WithUser(userstr))
}
@ -207,7 +212,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
deferCtx, deferCancel := ctrdutil.DeferContext()
defer deferCancel()
if err := container.Delete(deferCtx, containerd.WithSnapshotCleanup); err != nil {
logrus.WithError(err).Errorf("Failed to delete containerd container %q", id)
log.G(ctx).WithError(err).Errorf("Failed to delete containerd container %q", id)
}
}
}()
@ -222,7 +227,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
if retErr != nil {
// Cleanup the sandbox root directory.
if err := c.os.RemoveAll(sandboxRootDir); err != nil {
logrus.WithError(err).Errorf("Failed to remove sandbox root directory %q",
log.G(ctx).WithError(err).Errorf("Failed to remove sandbox root directory %q",
sandboxRootDir)
}
}
@ -236,7 +241,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
if retErr != 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",
log.G(ctx).WithError(err).Errorf("Failed to remove volatile sandbox root directory %q",
volatileSandboxRootDir)
}
}
@ -249,7 +254,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
defer func() {
if retErr != nil {
if err = c.unmountSandboxFiles(id, config); err != nil {
logrus.WithError(err).Errorf("Failed to unmount sandbox files in %q",
log.G(ctx).WithError(err).Errorf("Failed to unmount sandbox files in %q",
sandboxRootDir)
}
}
@ -262,7 +267,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
}
// Create sandbox task in containerd.
log.Tracef("Create sandbox container (id=%q, name=%q).",
log.G(ctx).Tracef("Create sandbox container (id=%q, name=%q).",
id, name)
var taskOpts []containerd.NewTaskOpts
@ -281,7 +286,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
defer deferCancel()
// Cleanup the sandbox container if an error is returned.
if _, err := task.Delete(deferCtx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
logrus.WithError(err).Errorf("Failed to delete sandbox container %q", id)
log.G(ctx).WithError(err).Errorf("Failed to delete sandbox container %q", id)
}
}
}()
@ -352,8 +357,7 @@ func (c *criService) generateSandboxContainerSpec(id string, config *runtime.Pod
specOpts = append(specOpts, customopts.WithDisabledCgroups)
} else {
if config.GetLinux().GetCgroupParent() != "" {
cgroupsPath := getCgroupsPath(config.GetLinux().GetCgroupParent(), id,
c.config.SystemdCgroup)
cgroupsPath := getCgroupsPath(config.GetLinux().GetCgroupParent(), id)
specOpts = append(specOpts, oci.WithCgroup(cgroupsPath))
}
}
@ -541,7 +545,7 @@ func (c *criService) unmountSandboxFiles(id string, config *runtime.PodSandboxCo
}
// setupPod setups up the network for a pod
func (c *criService) setupPod(id string, path string, config *runtime.PodSandboxConfig) (string, *cni.CNIResult, error) {
func (c *criService) setupPod(ctx context.Context, id string, path string, config *runtime.PodSandboxConfig) (string, *cni.CNIResult, error) {
if c.netPlugin == nil {
return "", nil, errors.New("cni config not initialized")
}
@ -555,7 +559,7 @@ func (c *criService) setupPod(id string, path string, config *runtime.PodSandbox
return "", nil, errors.Wrap(err, "failed to get bandwidth info from annotations")
}
result, err := c.netPlugin.Setup(id,
result, err := c.netPlugin.Setup(ctx, id,
path,
cni.WithLabels(labels),
cni.WithCapabilityPortMap(toCNIPortMappings(config.GetPortMappings())),
@ -565,14 +569,14 @@ func (c *criService) setupPod(id string, path string, config *runtime.PodSandbox
if err != nil {
return "", nil, err
}
logDebugCNIResult(id, result)
logDebugCNIResult(ctx, id, result)
// Check if the default interface has IP config
if configs, ok := result.Interfaces[defaultIfName]; ok && len(configs.IPConfigs) > 0 {
return selectPodIP(configs.IPConfigs), result, 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)
if err := c.teardownPod(ctx, id, path, config); err != nil {
log.G(ctx).WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
}
return "", result, errors.Errorf("failed to find network info for sandbox %q", id)
}
@ -682,14 +686,14 @@ func (c *criService) getSandboxRuntime(config *runtime.PodSandboxConfig, runtime
return handler, nil
}
func logDebugCNIResult(sandboxID string, result *cni.CNIResult) {
func logDebugCNIResult(ctx context.Context, sandboxID string, result *cni.CNIResult) {
if logrus.GetLevel() < logrus.DebugLevel {
return
}
cniResult, err := json.Marshal(result)
if err != nil {
logrus.WithError(err).Errorf("Failed to marshal CNI result for sandbox %q: %v", sandboxID, err)
log.G(ctx).WithError(err).Errorf("Failed to marshal CNI result for sandbox %q: %v", sandboxID, err)
return
}
logrus.Debugf("cni result for sandbox %q: %s", sandboxID, string(cniResult))
log.G(ctx).Debugf("cni result for sandbox %q: %s", sandboxID, string(cniResult))
}

View File

@ -22,9 +22,9 @@ import (
eventtypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
cni "github.com/containerd/go-cni"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@ -80,7 +80,7 @@ func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandb
} else if closed {
netNSPath = ""
}
if err := c.teardownPod(id, netNSPath, sandbox.Config); err != nil {
if err := c.teardownPod(ctx, id, netNSPath, sandbox.Config); err != nil {
return nil, errors.Wrapf(err, "failed to destroy network for sandbox %q", id)
}
if err = sandbox.NetNS.Remove(); err != nil {
@ -88,7 +88,7 @@ func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandb
}
}
logrus.Infof("TearDown network for sandbox %q successfully", id)
log.G(ctx).Infof("TearDown network for sandbox %q successfully", id)
return &runtime.StopPodSandboxResponse{}, nil
}
@ -157,13 +157,13 @@ func (c *criService) waitSandboxStop(ctx context.Context, sandbox sandboxstore.S
}
// teardownPod removes the network from the pod
func (c *criService) teardownPod(id string, path string, config *runtime.PodSandboxConfig) error {
func (c *criService) teardownPod(ctx context.Context, id string, path string, config *runtime.PodSandboxConfig) error {
if c.netPlugin == nil {
return errors.New("cni config not initialized")
}
labels := getPodCNILabels(id, config)
return c.netPlugin.Remove(id,
return c.netPlugin.Remove(ctx, id,
path,
cni.WithLabels(labels),
cni.WithCapabilityPortMap(toCNIPortMappings(config.GetPortMappings())))

View File

@ -21,8 +21,8 @@ import (
"fmt"
goruntime "runtime"
"github.com/containerd/containerd/log"
cni "github.com/containerd/go-cni"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
)
@ -45,7 +45,7 @@ func (c *criService) Status(ctx context.Context, r *runtime.StatusRequest) (*run
// Load the latest cni configuration to be in sync with the latest network configuration
if err := c.netPlugin.Load(cni.WithLoNetwork, cni.WithDefaultConf); err != nil {
logrus.WithError(err).Errorf("Failed to load cni configuration")
log.G(ctx).WithError(err).Errorf("Failed to load cni configuration")
}
// Check the status of the cni initialization
if err := c.netPlugin.Status(); err != nil {
@ -75,7 +75,7 @@ func (c *criService) Status(ctx context.Context, r *runtime.StatusRequest) (*run
cniConfig, err := json.Marshal(c.netPlugin.GetConfig())
if err != nil {
logrus.WithError(err).Errorf("Failed to marshal CNI config %v", err)
log.G(ctx).WithError(err).Errorf("Failed to marshal CNI config %v", err)
}
resp.Info["cniconfig"] = string(cniConfig)
}

View File

@ -25,6 +25,7 @@ import (
"time"
"github.com/pkg/errors"
"golang.org/x/net/context"
k8snet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/remotecommand"
@ -155,7 +156,7 @@ func (s *streamRuntime) PortForward(podSandboxID string, port int32, stream io.R
if port <= 0 || port > math.MaxUint16 {
return errors.Errorf("invalid port %d", port)
}
return s.c.portForward(podSandboxID, port, stream)
return s.c.portForward(context.Background(), podSandboxID, port, stream)
}
// handleResizing spawns a goroutine that processes the resize channel, calling resizeFunc for each

View File

@ -21,9 +21,9 @@ import (
"path/filepath"
"text/template"
"github.com/containerd/containerd/log"
cni "github.com/containerd/go-cni"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
)
@ -46,17 +46,17 @@ func (c *criService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateR
}
confTemplate := c.config.NetworkPluginConfTemplate
if confTemplate == "" {
logrus.Info("No cni config template is specified, wait for other system components to drop the config.")
log.G(ctx).Info("No cni config template is specified, wait for other system components to drop the config.")
return &runtime.UpdateRuntimeConfigResponse{}, nil
}
if err := c.netPlugin.Status(); err == nil {
logrus.Infof("Network plugin is ready, skip generating cni config from template %q", confTemplate)
log.G(ctx).Infof("Network plugin is ready, skip generating cni config from template %q", confTemplate)
return &runtime.UpdateRuntimeConfigResponse{}, nil
} else if err := c.netPlugin.Load(cni.WithLoNetwork, cni.WithDefaultConf); err == nil {
logrus.Infof("CNI config is successfully loaded, skip generating cni config from template %q", confTemplate)
log.G(ctx).Infof("CNI config is successfully loaded, skip generating cni config from template %q", confTemplate)
return &runtime.UpdateRuntimeConfigResponse{}, nil
}
logrus.Infof("Generating cni config from template %q", confTemplate)
log.G(ctx).Infof("Generating cni config from template %q", confTemplate)
// generate cni config file from the template with updated pod cidr.
t, err := template.ParseFiles(confTemplate)
if err != nil {

View File

@ -1,72 +1,80 @@
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/BurntSushi/toml v0.3.1
github.com/containerd/cgroups db272301ab8449d05f062e6db6f13d8a6aaff466
github.com/containerd/console 0650fd9eeb50bab4fc99dceb9f2e14cf58f36e7f
github.com/containerd/containerd 31afff294400b5a69bdb3ec387ecdf5bad57a038
github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/go-cni 22460c018b64cf8bf4151b3ff9c4d077e6a88cbf
github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3
github.com/containerd/ttrpc a5bd8ce9e40bc7c065a11c6936f4d032ce6bfa2b
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containernetworking/cni v0.6.0
github.com/containernetworking/plugins v0.7.5
github.com/coreos/go-systemd v14
github.com/davecgh/go-spew v1.1.0
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098
github.com/docker/go-units v0.4.0
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/emicklei/go-restful v2.2.1
github.com/godbus/dbus v3
github.com/gogo/googleapis v1.2.0
github.com/gogo/protobuf v1.2.1
github.com/golang/protobuf v1.2.0
github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c
github.com/grpc-ecosystem/go-grpc-prometheus v1.1
github.com/json-iterator/go 1.1.5
github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/Microsoft/go-winio 84b4ab48a50763fe7b3abcef38e5205c12027fac
github.com/Microsoft/hcsshim 8abdbb8205e4192c68b5f84c31197156f31be517
github.com/modern-go/concurrent 1.0.3
github.com/modern-go/reflect2 1.0.1
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc v1.0.0-rc8
github.com/opencontainers/runtime-spec 29686dbc5559d93fb1ef402eeda3e35c38d75af4
github.com/opencontainers/selinux v1.2.2
github.com/pkg/errors v0.8.1
github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823
github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
github.com/seccomp/libseccomp-golang v0.9.1
github.com/sirupsen/logrus v1.4.1
github.com/stretchr/testify v1.1.4
github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
# cri dependencies
github.com/tchap/go-patricia v2.2.6
github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
github.com/opencontainers/selinux v1.2.2
github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
# containerd dependencies
go.etcd.io/bbolt 2eb7227adea1d5cf85f0bc2a82b7059b13c2fa68
golang.org/x/crypto 88737f569e3a9c7ab309cdc09a07fe7fc87233c3
golang.org/x/net f3200d17e092c607f615320ecaad13d87ad9a2b3
golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
golang.org/x/sys 4c4f7f33c9ed00de01c4c741d2177abfcfe19307 https://github.com/golang/sys
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
google.golang.org/grpc 25c4f928eaa6d96443009bd842389fb4fa48664e # v1.20.1
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
google.golang.org/grpc 25c4f928eaa6d96443009bd842389fb4fa48664e
gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
gopkg.in/yaml.v2 v2.2.1
k8s.io/api kubernetes-1.15.0
k8s.io/apimachinery kubernetes-1.15.0
k8s.io/apiserver kubernetes-1.15.0
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
golang.org/x/sys 4c4f7f33c9ed00de01c4c741d2177abfcfe19307 https://github.com/golang/sys
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
golang.org/x/net f3200d17e092c607f615320ecaad13d87ad9a2b3
github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
github.com/sirupsen/logrus v1.4.1
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823
github.com/pkg/errors v0.8.1
github.com/opencontainers/runtime-spec 29686dbc5559d93fb1ef402eeda3e35c38d75af4 # v1.0.1-59-g29686db
github.com/opencontainers/runc f4982d86f7fde0b6f953cc62ccc4022c519a10a9 # v1.0.0-rc8-32-gf4982d86
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/grpc-ecosystem/go-grpc-prometheus v1.1
github.com/google/uuid v1.1.1
github.com/golang/protobuf v1.2.0
github.com/gogo/protobuf v1.2.1
github.com/gogo/googleapis v1.2.0
github.com/godbus/dbus v3
github.com/docker/go-units v0.4.0
github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/coreos/go-systemd v14
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containerd/ttrpc 1fb3814edf44a76e0ccf503decf726d994919a9a
github.com/containerd/go-runc 9007c2405372fe28918845901a3276c0915689a1
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4
github.com/containerd/containerd a3a30635ef713b544ea7feff0d12a768fd1ed636
github.com/containerd/console 0650fd9eeb50bab4fc99dceb9f2e14cf58f36e7f
github.com/containerd/cgroups c4b9ac5c7601384c965b9646fc515884e091ebb9
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/Microsoft/hcsshim 8abdbb8205e4192c68b5f84c31197156f31be517
github.com/Microsoft/go-winio v0.4.14
github.com/BurntSushi/toml v0.3.1
# kubernetes dependencies
sigs.k8s.io/yaml v1.1.0
k8s.io/utils c2654d5206da6b7b6ace12841e8f359bb89b443c
k8s.io/kubernetes v1.15.0
k8s.io/klog v0.3.1
k8s.io/cri-api kubernetes-1.15.0
k8s.io/client-go kubernetes-1.15.0
k8s.io/klog v0.3.1
k8s.io/kubernetes v1.15.0
k8s.io/utils c2654d5206da6b7b6ace12841e8f359bb89b443c
sigs.k8s.io/yaml v1.1.0
k8s.io/api kubernetes-1.15.0
k8s.io/apiserver kubernetes-1.15.0
k8s.io/apimachinery kubernetes-1.15.0
gopkg.in/yaml.v2 v2.2.1
gopkg.in/inf.v0 v0.9.0
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
golang.org/x/oauth2 9f3314589c9a9136388751d9adae6b0ed400978a
golang.org/x/crypto 88737f569e3a9c7ab309cdc09a07fe7fc87233c3
github.com/stretchr/testify v1.2.2
github.com/seccomp/libseccomp-golang v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/modern-go/reflect2 1.0.1
github.com/modern-go/concurrent 1.0.3
github.com/json-iterator/go 1.1.5
github.com/google/gofuzz 24818f796faf91cd76ec7bddd72458fbced7a6c1
github.com/emicklei/go-restful v2.2.1
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/davecgh/go-spew v1.1.1
# cni dependencies
github.com/containernetworking/plugins v0.7.6
github.com/containernetworking/cni v0.7.1
github.com/containerd/go-cni 49fbd9b210f3c8ee3b7fd3cd797aabaf364627c1

View File

@ -12,7 +12,7 @@ A generic CNI library to provide APIs for CNI plugin interactions. The library p
go-cni aims to support plugins that implement [Container Network Interface](https://github.com/containernetworking/cni)
## Usage
```
```go
func main() {
id := "123456"
netns := "/proc/9999/ns/net"
@ -24,7 +24,7 @@ func main() {
gocni.WithDefaultIfName(defaultIfName))
// Load the cni configuration
err:= l.Load(gocni.WithLoNetwork,gocni.WithDefaultConf)
err:= l.Load(gocni.WithLoNetwork, gocni.WithDefaultConf)
if err != nil{
log.Errorf("failed to load cni configuration: %v", err)
return

View File

@ -17,6 +17,7 @@
package cni
import (
"context"
"fmt"
"strings"
"sync"
@ -29,9 +30,9 @@ import (
type CNI interface {
// Setup setup the network for the namespace
Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error)
Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*CNIResult, error)
// Remove tears down the network of the namespace.
Remove(id string, path string, opts ...NamespaceOpts) error
Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error
// Load loads the cni network config
Load(opts ...CNIOpt) error
// Status checks the status of the cni initialization
@ -139,7 +140,7 @@ func (c *libcni) Networks() []*Network {
}
// Setup setups the network in the namespace
func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error) {
func (c *libcni) Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*CNIResult, error) {
if err := c.Status(); err != nil {
return nil, err
}
@ -149,7 +150,7 @@ func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResul
}
var results []*current.Result
for _, network := range c.Networks() {
r, err := network.Attach(ns)
r, err := network.Attach(ctx, ns)
if err != nil {
return nil, err
}
@ -159,7 +160,7 @@ func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResul
}
// Remove removes the network config from the namespace
func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error {
func (c *libcni) Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
if err := c.Status(); err != nil {
return err
}
@ -168,7 +169,7 @@ func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error {
return err
}
for _, network := range c.Networks() {
if err := network.Remove(ns); err != nil {
if err := network.Remove(ctx, ns); err != nil {
// Based on CNI spec v0.7.0, empty network namespace is allowed to
// do best effort cleanup. However, it is not handled consistently
// right now:

View File

@ -24,10 +24,10 @@ import (
func validateInterfaceConfig(ipConf *current.IPConfig, ifs int) error {
if ipConf == nil {
return fmt.Errorf("invalid IP configuration")
return fmt.Errorf("invalid IP configuration (nil)")
}
if ipConf.Interface != nil && *ipConf.Interface > ifs {
return fmt.Errorf("invalid IP configuration with invalid interface %d", *ipConf.Interface)
return fmt.Errorf("invalid IP configuration (interface number %d is > number of interfaces %d)", *ipConf.Interface, ifs)
}
return nil
}

View File

@ -17,6 +17,8 @@
package cni
import (
"context"
cnilibrary "github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types/current"
)
@ -27,16 +29,16 @@ type Network struct {
ifName string
}
func (n *Network) Attach(ns *Namespace) (*current.Result, error) {
r, err := n.cni.AddNetworkList(n.config, ns.config(n.ifName))
func (n *Network) Attach(ctx context.Context, ns *Namespace) (*current.Result, error) {
r, err := n.cni.AddNetworkList(ctx, 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))
func (n *Network) Remove(ctx context.Context, ns *Namespace) error {
return n.cni.DelNetworkList(ctx, n.config, ns.config(n.ifName))
}
type Namespace struct {

View File

@ -220,11 +220,11 @@ func loadFromConfDir(c *libcni, max int) error {
confList, err = cnilibrary.ConfListFromConf(conf)
if err != nil {
return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to list: %v", confFile, err)
return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to CNI config list: %v", confFile, err)
}
}
if len(confList.Plugins) == 0 {
return errors.Wrapf(ErrInvalidConfig, "CNI config list %s has no networks, skipping", confFile)
return errors.Wrapf(ErrInvalidConfig, "CNI config list in config file %s has no networks, skipping", confFile)
}
networks = append(networks, &Network{

View File

@ -79,7 +79,7 @@ func (c *libcni) GetCNIResultFromResults(results []*current.Result) (*CNIResult,
// 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)
return nil, errors.Wrapf(ErrInvalidResult, "invalid interface config: %v", err)
}
name := c.getInterfaceName(result.Interfaces, ipConf)
r.Interfaces[name].IPConfigs = append(r.Interfaces[name].IPConfigs,
@ -89,7 +89,7 @@ func (c *libcni) GetCNIResultFromResults(results []*current.Result) (*CNIResult,
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 nil, errors.Wrapf(ErrNotFound, "default network not found for: %s", defaultInterface(c.prefix))
}
return r, nil
}

View File

@ -2,5 +2,5 @@ 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/containernetworking/cni v0.7.1
github.com/pkg/errors v0.8.0

View File

@ -1,4 +1,5 @@
[![Build Status](https://travis-ci.org/containernetworking/cni.svg?branch=master)](https://travis-ci.org/containernetworking/cni)
[![Linux Build Status](https://travis-ci.org/containernetworking/cni.svg?branch=master)](https://travis-ci.org/containernetworking/cni)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/wtrkou8oow7x533e/branch/master?svg=true)](https://ci.appveyor.com/project/cni-bot/cni/branch/master)
[![Coverage Status](https://coveralls.io/repos/github/containernetworking/cni/badge.svg?branch=master)](https://coveralls.io/github/containernetworking/cni?branch=master)
[![Slack Status](https://cryptic-tundra-43194.herokuapp.com/badge.svg)](https://cryptic-tundra-43194.herokuapp.com/)
@ -8,7 +9,9 @@
# Community Sync Meeting
There is a community sync meeting for users and developers every 1-2 months. The next meeting will help on a Google Hangout and the link is in the [agenda](https://docs.google.com/document/d/10ECyT2mBGewsJUcmYmS8QNo1AcNgy2ZIe2xS7lShYhE/edit?usp=sharing) (Notes from previous meeting are also in this doc). The next meeting will be held on *Wednesday, June 21th* at *3:00pm UTC* [Add to Calendar]https://www.worldtimebuddy.com/?qm=1&lid=100,5,2643743,5391959&h=100&date=2017-6-21&sln=15-16).
There is a community sync meeting for users and developers every 1-2 months. The next meeting will help on a Google Hangout and the link is in the [agenda](https://docs.google.com/document/d/10ECyT2mBGewsJUcmYmS8QNo1AcNgy2ZIe2xS7lShYhE/edit?usp=sharing) (Notes from previous meeting are also in this doc).
The next meeting will be held on *Wednesday, January 30th, 2019* at *4:00pm UTC / 11:00am EDT / 8:00am PDT* [Add to Calendar](https://www.worldtimebuddy.com/?qm=1&lid=100,5,2643743,5391959&h=100&date=2019-01-30&sln=16-17).
---
@ -35,11 +38,13 @@ To avoid duplication, we think it is prudent to define a common interface betwee
## Who is using CNI?
### Container runtimes
- [rkt - container engine](https://coreos.com/blog/rkt-cni-networking.html)
- [Kurma - container runtime](http://kurma.io/)
- [Kubernetes - a system to simplify container operations](http://kubernetes.io/docs/admin/network-plugins/)
- [Kubernetes - a system to simplify container operations](https://kubernetes.io/docs/admin/network-plugins/)
- [OpenShift - Kubernetes with additional enterprise features](https://github.com/openshift/origin/blob/master/docs/openshift_networking_requirements.md)
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/cf-networking-release)
- [Mesos - a distributed systems kernel](https://github.com/apache/mesos/blob/master/docs/cni.md)
- [Apache Mesos - a distributed systems kernel](https://github.com/apache/mesos/blob/master/docs/cni.md)
- [Amazon ECS - a highly scalable, high performance container management service](https://aws.amazon.com/ecs/)
- [Singularity - container platform optimized for HPC, EPC, and AI](https://github.com/sylabs/singularity)
- [OpenSVC - orchestrator for legacy and containerized application stacks](https://docs.opensvc.com/latest/fr/agent.configure.cni.html)
### 3rd party plugins
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico-cni)
@ -54,6 +59,15 @@ To avoid duplication, we think it is prudent to define a common interface betwee
- [Nuage CNI - Nuage Networks SDN plugin for network policy kubernetes support ](https://github.com/nuagenetworks/nuage-cni)
- [Silk - a CNI plugin designed for Cloud Foundry](https://github.com/cloudfoundry-incubator/silk)
- [Linen - a CNI plugin designed for overlay networks with Open vSwitch and fit in SDN/OpenFlow network environment](https://github.com/John-Lin/linen-cni)
- [Vhostuser - a Dataplane network plugin - Supports OVS-DPDK & VPP](https://github.com/intel/vhost-user-net-plugin)
- [Amazon ECS CNI Plugins - a collection of CNI Plugins to configure containers with Amazon EC2 elastic network interfaces (ENIs)](https://github.com/aws/amazon-ecs-cni-plugins)
- [Bonding CNI - a Link aggregating plugin to address failover and high availability network](https://github.com/Intel-Corp/bond-cni)
- [ovn-kubernetes - an container network plugin built on Open vSwitch (OVS) and Open Virtual Networking (OVN) with support for both Linux and Windows](https://github.com/openvswitch/ovn-kubernetes)
- [Juniper Contrail](https://www.juniper.net/cloud) / [TungstenFabric](https://tungstenfabric.io) - Provides overlay SDN solution, delivering multicloud networking, hybrid cloud networking, simultaneous overlay-underlay support, network policy enforcement, network isolation, service chaining and flexible load balancing
- [Knitter - a CNI plugin supporting multiple networking for Kubernetes](https://github.com/ZTE/Knitter)
- [DANM - a CNI-compliant networking solution for TelCo workloads running on Kubernetes](https://github.com/nokia/danm)
- [VMware NSX a CNI plugin that enables automated NSX L2/L3 networking and L4/L7 Load Balancing; network isolation at the pod, node, and cluster level; and zero-trust security policy for your Kubernetes cluster.](https://docs.vmware.com/en/VMware-NSX-T/2.2/com.vmware.nsxt.ncp_kubernetes.doc/GUID-6AFA724E-BB62-4693-B95C-321E8DDEA7E1.html)
- [cni-route-override - a meta CNI plugin that override route information](https://github.com/redhat-nfvpe/cni-route-override)
The CNI team also maintains some [core plugins in a separate repository](https://github.com/containernetworking/plugins).
@ -67,7 +81,7 @@ If you intend to contribute to code or documentation, please read [CONTRIBUTING.
### Requirements
The CNI spec is language agnostic. To use the Go language libraries in this repository, you'll need a recent version of Go. Our [automated tests](https://travis-ci.org/containernetworking/cni/builds) cover Go versions 1.7 and 1.8.
The CNI spec is language agnostic. To use the Go language libraries in this repository, you'll need a recent version of Go. You can find the Go versions covered by our [automated tests](https://travis-ci.org/containernetworking/cni/builds) in [.travis.yaml](.travis.yml).
### Reference Plugins
@ -104,6 +118,7 @@ EOF
$ cat >/etc/cni/net.d/99-loopback.conf <<EOF
{
"cniVersion": "0.2.0",
"name": "lo",
"type": "loopback"
}
EOF
@ -115,7 +130,7 @@ Next, build the plugins:
```bash
$ cd $GOPATH/src/github.com/containernetworking/plugins
$ ./build.sh
$ ./build_linux.sh # or build_windows.sh
```
Finally, execute a command (`ifconfig` in this example) in a private network namespace that has joined the `mynet` network:

View File

@ -15,7 +15,12 @@
package libcni
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/containernetworking/cni/pkg/invoke"
@ -23,6 +28,14 @@ import (
"github.com/containernetworking/cni/pkg/version"
)
var (
CacheDir = "/var/lib/cni"
)
// A RuntimeConf holds the arguments to one invocation of a CNI plugin
// excepting the network configuration, with the nested exception that
// the `runtimeConfig` from the network configuration is included
// here.
type RuntimeConf struct {
ContainerID string
NetNS string
@ -34,6 +47,9 @@ type RuntimeConf struct {
// in this map which match the capabilities of the plugin are passed
// to the plugin
CapabilityArgs map[string]interface{}
// A cache directory in which to library data. Defaults to CacheDir
CacheDir string
}
type NetworkConfig struct {
@ -42,33 +58,52 @@ type NetworkConfig struct {
}
type NetworkConfigList struct {
Name string
CNIVersion string
Plugins []*NetworkConfig
Bytes []byte
Name string
CNIVersion string
DisableCheck bool
Plugins []*NetworkConfig
Bytes []byte
}
type CNI interface {
AddNetworkList(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
DelNetworkList(net *NetworkConfigList, rt *RuntimeConf) error
AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
}
type CNIConfig struct {
Path []string
exec invoke.Exec
}
// CNIConfig implements the CNI interface
var _ CNI = &CNIConfig{}
func buildOneConfig(list *NetworkConfigList, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
// NewCNIConfig returns a new CNIConfig object that will search for plugins
// in the given paths and use the given exec interface to run those plugins,
// or if the exec interface is not given, will use a default exec handler.
func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
return &CNIConfig{
Path: path,
exec: exec,
}
}
func buildOneConfig(name, cniVersion string, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
var err error
inject := map[string]interface{}{
"name": list.Name,
"cniVersion": list.CNIVersion,
"name": name,
"cniVersion": cniVersion,
}
// Add previous plugin result
if prevResult != nil {
@ -92,7 +127,7 @@ func buildOneConfig(list *NetworkConfigList, orig *NetworkConfig, prevResult typ
// These capabilities arguments are filtered through the plugin's advertised
// capabilities from its config JSON, and any keys in the CapabilityArgs
// matching plugin capabilities are added to the "runtimeConfig" dictionary
// sent to the plugin via JSON on stdin. For exmaple, if the plugin's
// sent to the plugin via JSON on stdin. For example, if the plugin's
// capabilities include "portMappings", and the CapabilityArgs map includes a
// "portMappings" key, that key and its value are added to the "runtimeConfig"
// dictionary to be passed to the plugin's stdin.
@ -119,45 +154,154 @@ func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig,
return orig, nil
}
// ensure we have a usable exec if the CNIConfig was not given one
func (c *CNIConfig) ensureExec() invoke.Exec {
if c.exec == nil {
c.exec = &invoke.DefaultExec{
RawExec: &invoke.RawExec{Stderr: os.Stderr},
PluginDecoder: version.PluginDecoder{},
}
}
return c.exec
}
func getResultCacheFilePath(netName string, rt *RuntimeConf) string {
cacheDir := rt.CacheDir
if cacheDir == "" {
cacheDir = CacheDir
}
return filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName))
}
func setCachedResult(result types.Result, netName string, rt *RuntimeConf) error {
data, err := json.Marshal(result)
if err != nil {
return err
}
fname := getResultCacheFilePath(netName, rt)
if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
return err
}
return ioutil.WriteFile(fname, data, 0600)
}
func delCachedResult(netName string, rt *RuntimeConf) error {
fname := getResultCacheFilePath(netName, rt)
return os.Remove(fname)
}
func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
fname := getResultCacheFilePath(netName, rt)
data, err := ioutil.ReadFile(fname)
if err != nil {
// Ignore read errors; the cached result may not exist on-disk
return nil, nil
}
// Read the version of the cached result
decoder := version.ConfigDecoder{}
resultCniVersion, err := decoder.Decode(data)
if err != nil {
return nil, err
}
// Ensure we can understand the result
result, err := version.NewResult(resultCniVersion, data)
if err != nil {
return nil, err
}
// Convert to the config version to ensure plugins get prevResult
// in the same version as the config. The cached result version
// should match the config version unless the config was changed
// while the container was running.
result, err = result.GetAsVersion(cniVersion)
if err != nil && resultCniVersion != cniVersion {
return nil, fmt.Errorf("failed to convert cached result version %q to config version %q: %v", resultCniVersion, cniVersion, err)
}
return result, err
}
// GetNetworkListCachedResult returns the cached Result of the previous
// previous AddNetworkList() operation for a network list, or an error.
func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
return getCachedResult(list.Name, list.CNIVersion, rt)
}
// GetNetworkCachedResult returns the cached Result of the previous
// previous AddNetwork() operation for a network, or an error.
func (c *CNIConfig) GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
return getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
}
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return nil, err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return nil, err
}
return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)
}
// AddNetworkList executes a sequence of plugins with the ADD command
func (c *CNIConfig) AddNetworkList(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
var prevResult types.Result
func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
var err error
var result types.Result
for _, net := range list.Plugins {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
if err != nil {
return nil, err
}
newConf, err := buildOneConfig(list, net, prevResult, rt)
if err != nil {
return nil, err
}
prevResult, err = invoke.ExecPluginWithResult(pluginPath, newConf.Bytes, c.args("ADD", rt))
result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)
if err != nil {
return nil, err
}
}
return prevResult, nil
if err = setCachedResult(result, list.Name, rt); err != nil {
return nil, fmt.Errorf("failed to set network %q cached result: %v", list.Name, err)
}
return result, nil
}
// DelNetworkList executes a sequence of plugins with the DEL command
func (c *CNIConfig) DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) error {
for i := len(list.Plugins) - 1; i >= 0; i-- {
net := list.Plugins[i]
func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return err
}
newConf, err := buildOneConfig(list, net, nil, rt)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec)
}
if err := invoke.ExecPluginWithoutResult(pluginPath, newConf.Bytes, c.args("DEL", rt)); err != nil {
// CheckNetworkList executes a sequence of plugins with the CHECK command
func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
// CHECK was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
return err
} else if !gtet {
return fmt.Errorf("configuration version %q does not support the CHECK command", list.CNIVersion)
}
if list.DisableCheck {
return nil
}
cachedResult, err := getCachedResult(list.Name, list.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
}
for _, net := range list.Plugins {
if err := c.checkNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
}
@ -165,45 +309,179 @@ func (c *CNIConfig) DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) err
return nil
}
func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec)
}
// DelNetworkList executes a sequence of plugins with the DEL command
func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
var cachedResult types.Result
// Cached result on DEL was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
return err
} else if gtet {
cachedResult, err = getCachedResult(list.Name, list.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
}
}
for i := len(list.Plugins) - 1; i >= 0; i-- {
net := list.Plugins[i]
if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
}
_ = delCachedResult(list.Name, rt)
return nil
}
// AddNetwork executes the plugin with the ADD command
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt)
if err != nil {
return nil, err
}
net, err = injectRuntimeConfig(net, rt)
if err != nil {
return nil, err
if err = setCachedResult(result, net.Network.Name, rt); err != nil {
return nil, fmt.Errorf("failed to set network %q cached result: %v", net.Network.Name, err)
}
return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
return result, nil
}
// CheckNetwork executes the plugin with the CHECK command
func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
// CHECK was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
return err
} else if !gtet {
return fmt.Errorf("configuration version %q does not support the CHECK command", net.Network.CNIVersion)
}
cachedResult, err := getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err)
}
return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt)
}
// DelNetwork executes the plugin with the DEL command
func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
var cachedResult types.Result
// Cached result on DEL was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
return err
} else if gtet {
cachedResult, err = getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err)
}
}
if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
_ = delCachedResult(net.Network.Name, rt)
return nil
}
// ValidateNetworkList checks that a configuration is reasonably valid.
// - all the specified plugins exist on disk
// - every plugin supports the desired version.
//
// Returns a list of all capabilities supported by the configuration, or error
func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) {
version := list.CNIVersion
// holding map for seen caps (in case of duplicates)
caps := map[string]interface{}{}
errs := []error{}
for _, net := range list.Plugins {
if err := c.validatePlugin(ctx, net.Network.Type, version); err != nil {
errs = append(errs, err)
}
for c, enabled := range net.Network.Capabilities {
if !enabled {
continue
}
caps[c] = struct{}{}
}
}
if len(errs) > 0 {
return nil, fmt.Errorf("%v", errs)
}
// make caps list
cc := make([]string, 0, len(caps))
for c := range caps {
cc = append(cc, c)
}
return cc, nil
}
// ValidateNetwork checks that a configuration is reasonably valid.
// It uses the same logic as ValidateNetworkList)
// Returns a list of capabilities
func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error) {
caps := []string{}
for c, ok := range net.Network.Capabilities {
if ok {
caps = append(caps, c)
}
}
if err := c.validatePlugin(ctx, net.Network.Type, net.Network.CNIVersion); err != nil {
return nil, err
}
return caps, nil
}
// validatePlugin checks that an individual plugin's configuration is sane
func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error {
pluginPath, err := invoke.FindInPath(pluginName, c.Path)
if err != nil {
return err
}
net, err = injectRuntimeConfig(net, rt)
vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt))
for _, vers := range vi.SupportedVersions() {
if vers == expectedVersion {
return nil
}
}
return fmt.Errorf("plugin %s does not support config version %q", pluginName, expectedVersion)
}
// GetVersionInfo reports which versions of the CNI spec are supported by
// the given plugin.
func (c *CNIConfig) GetVersionInfo(pluginType string) (version.PluginInfo, error) {
pluginPath, err := invoke.FindInPath(pluginType, c.Path)
func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(pluginType, c.Path)
if err != nil {
return nil, err
}
return invoke.GetVersionInfo(pluginPath)
return invoke.GetVersionInfo(ctx, pluginPath, c.exec)
}
// =====

View File

@ -45,6 +45,9 @@ func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
if err := json.Unmarshal(bytes, &conf.Network); err != nil {
return nil, fmt.Errorf("error parsing configuration: %s", err)
}
if conf.Network.Type == "" {
return nil, fmt.Errorf("error parsing configuration: missing 'type'")
}
return conf, nil
}
@ -80,10 +83,19 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
}
}
disableCheck := false
if rawDisableCheck, ok := rawList["disableCheck"]; ok {
disableCheck, ok = rawDisableCheck.(bool)
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid disableCheck type %T", rawDisableCheck)
}
}
list := &NetworkConfigList{
Name: name,
CNIVersion: cniVersion,
Bytes: bytes,
Name: name,
DisableCheck: disableCheck,
CNIVersion: cniVersion,
Bytes: bytes,
}
var plugins []interface{}

View File

@ -15,6 +15,7 @@
package invoke
import (
"fmt"
"os"
"strings"
)
@ -22,6 +23,8 @@ import (
type CNIArgs interface {
// For use with os/exec; i.e., return nil to inherit the
// environment from this process
// For use in delegation; inherit the environment from this
// process and allow overrides
AsEnv() []string
}
@ -57,17 +60,17 @@ func (args *Args) AsEnv() []string {
pluginArgsStr = stringify(args.PluginArgs)
}
// Ensure that the custom values are first, so any value present in
// the process environment won't override them.
env = append([]string{
"CNI_COMMAND=" + args.Command,
"CNI_CONTAINERID=" + args.ContainerID,
"CNI_NETNS=" + args.NetNS,
"CNI_ARGS=" + pluginArgsStr,
"CNI_IFNAME=" + args.IfName,
"CNI_PATH=" + args.Path,
}, env...)
return env
// Duplicated values which come first will be overrided, so we must put the
// custom values in the end to avoid being overrided by the process environments.
env = append(env,
"CNI_COMMAND="+args.Command,
"CNI_CONTAINERID="+args.ContainerID,
"CNI_NETNS="+args.NetNS,
"CNI_ARGS="+pluginArgsStr,
"CNI_IFNAME="+args.IfName,
"CNI_PATH="+args.Path,
)
return dedupEnv(env)
}
// taken from rkt/networking/net_plugin.go
@ -80,3 +83,46 @@ func stringify(pluginArgs [][2]string) string {
return strings.Join(entries, ";")
}
// DelegateArgs implements the CNIArgs interface
// used for delegation to inherit from environments
// and allow some overrides like CNI_COMMAND
var _ CNIArgs = &DelegateArgs{}
type DelegateArgs struct {
Command string
}
func (d *DelegateArgs) AsEnv() []string {
env := os.Environ()
// The custom values should come in the end to override the existing
// process environment of the same key.
env = append(env,
"CNI_COMMAND="+d.Command,
)
return dedupEnv(env)
}
// dedupEnv returns a copy of env with any duplicates removed, in favor of later values.
// Items not of the normal environment "key=value" form are preserved unchanged.
func dedupEnv(env []string) []string {
out := make([]string, 0, len(env))
envMap := map[string]string{}
for _, kv := range env {
// find the first "=" in environment, if not, just keep it
eq := strings.Index(kv, "=")
if eq < 0 {
out = append(out, kv)
continue
}
envMap[kv[:eq]] = kv[eq+1:]
}
for k, v := range envMap {
out = append(out, fmt.Sprintf("%s=%s", k, v))
}
return out
}

View File

@ -15,39 +15,66 @@
package invoke
import (
"fmt"
"context"
"os"
"path/filepath"
"github.com/containernetworking/cni/pkg/types"
)
func DelegateAdd(delegatePlugin string, netconf []byte) (types.Result, error) {
if os.Getenv("CNI_COMMAND") != "ADD" {
return nil, fmt.Errorf("CNI_COMMAND is not ADD")
func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) {
if exec == nil {
exec = defaultExec
}
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
pluginPath, err := exec.FindInPath(delegatePlugin, paths)
if err != nil {
return "", nil, err
}
pluginPath, err := FindInPath(delegatePlugin, paths)
return pluginPath, exec, nil
}
// DelegateAdd calls the given delegate plugin with the CNI ADD action and
// JSON configuration
func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) {
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return nil, err
}
return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv())
// DelegateAdd will override the original "CNI_COMMAND" env from process with ADD
return ExecPluginWithResult(ctx, pluginPath, netconf, delegateArgs("ADD"), realExec)
}
func DelegateDel(delegatePlugin string, netconf []byte) error {
if os.Getenv("CNI_COMMAND") != "DEL" {
return fmt.Errorf("CNI_COMMAND is not DEL")
}
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
pluginPath, err := FindInPath(delegatePlugin, paths)
// DelegateCheck calls the given delegate plugin with the CNI CHECK action and
// JSON configuration
func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return err
}
return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv())
// DelegateCheck will override the original CNI_COMMAND env from process with CHECK
return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("CHECK"), realExec)
}
// DelegateDel calls the given delegate plugin with the CNI DEL action and
// JSON configuration
func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return err
}
// DelegateDel will override the original CNI_COMMAND env from process with DEL
return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("DEL"), realExec)
}
// return CNIArgs used by delegation
func delegateArgs(action string) *DelegateArgs {
return &DelegateArgs{
Command: action,
}
}

View File

@ -15,6 +15,7 @@
package invoke
import (
"context"
"fmt"
"os"
@ -22,34 +23,62 @@ import (
"github.com/containernetworking/cni/pkg/version"
)
func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) {
return defaultPluginExec.WithResult(pluginPath, netconf, args)
// Exec is an interface encapsulates all operations that deal with finding
// and executing a CNI plugin. Tests may provide a fake implementation
// to avoid writing fake plugins to temporary directories during the test.
type Exec interface {
ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error)
FindInPath(plugin string, paths []string) (string, error)
Decode(jsonBytes []byte) (version.PluginInfo, error)
}
func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
return defaultPluginExec.WithoutResult(pluginPath, netconf, args)
}
// For example, a testcase could pass an instance of the following fakeExec
// object to ExecPluginWithResult() to verify the incoming stdin and environment
// and provide a tailored response:
//
//import (
// "encoding/json"
// "path"
// "strings"
//)
//
//type fakeExec struct {
// version.PluginDecoder
//}
//
//func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
// net := &types.NetConf{}
// err := json.Unmarshal(stdinData, net)
// if err != nil {
// return nil, fmt.Errorf("failed to unmarshal configuration: %v", err)
// }
// pluginName := path.Base(pluginPath)
// if pluginName != net.Type {
// return nil, fmt.Errorf("plugin name %q did not match config type %q", pluginName, net.Type)
// }
// for _, e := range environ {
// // Check environment for forced failure request
// parts := strings.Split(e, "=")
// if len(parts) > 0 && parts[0] == "FAIL" {
// return nil, fmt.Errorf("failed to execute plugin %s", pluginName)
// }
// }
// return []byte("{\"CNIVersion\":\"0.4.0\"}"), nil
//}
//
//func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) {
// if len(paths) > 0 {
// return path.Join(paths[0], plugin), nil
// }
// return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths)
//}
func GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
return defaultPluginExec.GetVersionInfo(pluginPath)
}
var defaultPluginExec = &PluginExec{
RawExec: &RawExec{Stderr: os.Stderr},
VersionDecoder: &version.PluginDecoder{},
}
type PluginExec struct {
RawExec interface {
ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error)
func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) {
if exec == nil {
exec = defaultExec
}
VersionDecoder interface {
Decode(jsonBytes []byte) (version.PluginInfo, error)
}
}
func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) {
stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())
if err != nil {
return nil, err
}
@ -64,8 +93,11 @@ func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs)
return version.NewResult(confVersion, stdoutBytes)
}
func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
_, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
func ExecPluginWithoutResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) error {
if exec == nil {
exec = defaultExec
}
_, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())
return err
}
@ -73,7 +105,10 @@ func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIAr
// For recent-enough plugins, it uses the information returned by the VERSION
// command. For older plugins which do not recognize that command, it reports
// version 0.1.0
func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
func GetVersionInfo(ctx context.Context, pluginPath string, exec Exec) (version.PluginInfo, error) {
if exec == nil {
exec = defaultExec
}
args := &Args{
Command: "VERSION",
@ -83,7 +118,7 @@ func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, erro
Path: "dummy",
}
stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current()))
stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, stdin, args.AsEnv())
stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, stdin, args.AsEnv())
if err != nil {
if err.Error() == "unknown CNI_COMMAND: VERSION" {
return version.PluginSupports("0.1.0"), nil
@ -91,5 +126,19 @@ func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, erro
return nil, err
}
return e.VersionDecoder.Decode(stdoutBytes)
return exec.Decode(stdoutBytes)
}
// DefaultExec is an object that implements the Exec interface which looks
// for and executes plugins from disk.
type DefaultExec struct {
*RawExec
version.PluginDecoder
}
// DefaultExec implements the Exec interface
var _ Exec = &DefaultExec{}
var defaultExec = &DefaultExec{
RawExec: &RawExec{Stderr: os.Stderr},
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// +build darwin dragonfly freebsd linux netbsd opensbd solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package invoke

View File

@ -16,6 +16,7 @@ package invoke
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
@ -28,17 +29,13 @@ type RawExec struct {
Stderr io.Writer
}
func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
stdout := &bytes.Buffer{}
c := exec.Cmd{
Env: environ,
Path: pluginPath,
Args: []string{pluginPath},
Stdin: bytes.NewBuffer(stdinData),
Stdout: stdout,
Stderr: e.Stderr,
}
c := exec.CommandContext(ctx, pluginPath)
c.Env = environ
c.Stdin = bytes.NewBuffer(stdinData)
c.Stdout = stdout
c.Stderr = e.Stderr
if err := c.Run(); err != nil {
return nil, pluginErr(err, stdout.Bytes())
}
@ -49,7 +46,9 @@ func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []stri
func pluginErr(err error, output []byte) error {
if _, ok := err.(*exec.ExitError); ok {
emsg := types.Error{}
if perr := json.Unmarshal(output, &emsg); perr != nil {
if len(output) == 0 {
emsg.Msg = "netplugin failed with no error message"
} else if perr := json.Unmarshal(output, &emsg); perr != nil {
emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
}
return &emsg
@ -57,3 +56,7 @@ func pluginErr(err error, output []byte) error {
return err
}
func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) {
return FindInPath(plugin, paths)
}

View File

@ -17,6 +17,7 @@ package types020
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
@ -73,11 +74,15 @@ func (r *Result) GetAsVersion(version string) (types.Result, error) {
}
func (r *Result) Print() error {
return r.PrintTo(os.Stdout)
}
func (r *Result) PrintTo(writer io.Writer) error {
data, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
_, err = os.Stdout.Write(data)
_, err = writer.Write(data)
return err
}

View File

@ -17,6 +17,7 @@ package current
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
@ -24,9 +25,9 @@ import (
"github.com/containernetworking/cni/pkg/types/020"
)
const ImplementedSpecVersion string = "0.3.1"
const ImplementedSpecVersion string = "0.4.0"
var SupportedVersions = []string{"0.3.0", ImplementedSpecVersion}
var SupportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion}
func NewResult(data []byte) (types.Result, error) {
result := &Result{}
@ -75,13 +76,9 @@ func convertFrom020(result types.Result) (*Result, error) {
Gateway: oldResult.IP4.Gateway,
})
for _, route := range oldResult.IP4.Routes {
gw := route.GW
if gw == nil {
gw = oldResult.IP4.Gateway
}
newResult.Routes = append(newResult.Routes, &types.Route{
Dst: route.Dst,
GW: gw,
GW: route.GW,
})
}
}
@ -93,21 +90,13 @@ func convertFrom020(result types.Result) (*Result, error) {
Gateway: oldResult.IP6.Gateway,
})
for _, route := range oldResult.IP6.Routes {
gw := route.GW
if gw == nil {
gw = oldResult.IP6.Gateway
}
newResult.Routes = append(newResult.Routes, &types.Route{
Dst: route.Dst,
GW: gw,
GW: route.GW,
})
}
}
if len(newResult.IPs) == 0 {
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
}
return newResult, nil
}
@ -196,7 +185,7 @@ func (r *Result) Version() string {
func (r *Result) GetAsVersion(version string) (types.Result, error) {
switch version {
case "0.3.0", ImplementedSpecVersion:
case "0.3.0", "0.3.1", ImplementedSpecVersion:
r.CNIVersion = version
return r, nil
case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]:
@ -206,11 +195,15 @@ func (r *Result) GetAsVersion(version string) (types.Result, error) {
}
func (r *Result) Print() error {
return r.PrintTo(os.Stdout)
}
func (r *Result) PrintTo(writer io.Writer) error {
data, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
_, err = os.Stdout.Write(data)
_, err = writer.Write(data)
return err
}

View File

@ -18,6 +18,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
)
@ -63,25 +64,31 @@ type NetConf struct {
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Capabilities map[string]bool `json:"capabilities,omitempty"`
IPAM struct {
Type string `json:"type,omitempty"`
} `json:"ipam,omitempty"`
DNS DNS `json:"dns"`
IPAM IPAM `json:"ipam,omitempty"`
DNS DNS `json:"dns"`
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
PrevResult Result `json:"-"`
}
type IPAM struct {
Type string `json:"type,omitempty"`
}
// NetConfList describes an ordered list of networks.
type NetConfList struct {
CNIVersion string `json:"cniVersion,omitempty"`
Name string `json:"name,omitempty"`
Plugins []*NetConf `json:"plugins,omitempty"`
Name string `json:"name,omitempty"`
DisableCheck bool `json:"disableCheck,omitempty"`
Plugins []*NetConf `json:"plugins,omitempty"`
}
type ResultFactoryFunc func([]byte) (Result, error)
// Result is an interface that provides the result of plugin execution
type Result interface {
// The highest CNI specification result verison the result supports
// The highest CNI specification result version the result supports
// without having to convert
Version() string
@ -92,6 +99,9 @@ type Result interface {
// Prints the result in JSON format to stdout
Print() error
// Prints the result in JSON format to provided writer
PrintTo(writer io.Writer) error
// Returns a JSON string representation of the result
String() string
}
@ -167,7 +177,7 @@ func (r *Route) UnmarshalJSON(data []byte) error {
return nil
}
func (r *Route) MarshalJSON() ([]byte, error) {
func (r Route) MarshalJSON() ([]byte, error) {
rt := route{
Dst: IPNet(r.Dst),
GW: r.GW,

View File

@ -18,6 +18,8 @@ import (
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
)
// PluginInfo reports information about CNI versioning
@ -79,3 +81,64 @@ func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) {
}
return &info, nil
}
// ParseVersion parses a version string like "3.0.1" or "0.4.5" into major,
// minor, and micro numbers or returns an error
func ParseVersion(version string) (int, int, int, error) {
var major, minor, micro int
if version == "" {
return -1, -1, -1, fmt.Errorf("invalid version %q: the version is empty", version)
}
parts := strings.Split(version, ".")
if len(parts) >= 4 {
return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version)
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %v", parts[0], err)
}
if len(parts) >= 2 {
minor, err = strconv.Atoi(parts[1])
if err != nil {
return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %v", parts[1], err)
}
}
if len(parts) >= 3 {
micro, err = strconv.Atoi(parts[2])
if err != nil {
return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %v", parts[2], err)
}
}
return major, minor, micro, nil
}
// GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro
// numbers, and compares them to determine whether the first version is greater
// than or equal to the second
func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) {
firstMajor, firstMinor, firstMicro, err := ParseVersion(version)
if err != nil {
return false, err
}
secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion)
if err != nil {
return false, err
}
if firstMajor > secondMajor {
return true, nil
} else if firstMajor == secondMajor {
if firstMinor > secondMinor {
return true, nil
} else if firstMinor == secondMinor && firstMicro >= secondMicro {
return true, nil
}
}
return false, nil
}

View File

@ -15,6 +15,7 @@
package version
import (
"encoding/json"
"fmt"
"github.com/containernetworking/cni/pkg/types"
@ -24,7 +25,7 @@ import (
// Current reports the version of the CNI spec implemented by this library
func Current() string {
return "0.3.1"
return "0.4.0"
}
// Legacy PluginInfo describes a plugin that is backwards compatible with the
@ -35,7 +36,7 @@ func Current() string {
// Any future CNI spec versions which meet this definition should be added to
// this list.
var Legacy = PluginSupports("0.1.0", "0.2.0")
var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1")
var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0")
var resultFactories = []struct {
supportedVersions []string
@ -59,3 +60,24 @@ func NewResult(version string, resultBytes []byte) (types.Result, error) {
return nil, fmt.Errorf("unsupported CNI result version %q", version)
}
// ParsePrevResult parses a prevResult in a NetConf structure and sets
// the NetConf's PrevResult member to the parsed Result object.
func ParsePrevResult(conf *types.NetConf) error {
if conf.RawPrevResult == nil {
return nil
}
resultBytes, err := json.Marshal(conf.RawPrevResult)
if err != nil {
return fmt.Errorf("could not serialize prevResult: %v", err)
}
conf.RawPrevResult = nil
conf.PrevResult, err = NewResult(conf.CNIVersion, resultBytes)
if err != nil {
return fmt.Errorf("could not parse prevResult: %v", err)
}
return nil
}

View File

@ -2,7 +2,7 @@ ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

View File

@ -1,12 +1,9 @@
go-spew
=======
[![Build Status](https://img.shields.io/travis/davecgh/go-spew.svg)]
(https://travis-ci.org/davecgh/go-spew) [![ISC License]
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) [![Coverage Status]
(https://img.shields.io/coveralls/davecgh/go-spew.svg)]
(https://coveralls.io/r/davecgh/go-spew?branch=master)
[![Build Status](https://img.shields.io/travis/davecgh/go-spew.svg)](https://travis-ci.org/davecgh/go-spew)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![Coverage Status](https://img.shields.io/coveralls/davecgh/go-spew.svg)](https://coveralls.io/r/davecgh/go-spew?branch=master)
Go-spew implements a deep pretty printer for Go data structures to aid in
debugging. A comprehensive suite of tests with 100% test coverage is provided
@ -21,8 +18,7 @@ post about it
## Documentation
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
(http://godoc.org/github.com/davecgh/go-spew/spew)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/davecgh/go-spew/spew)
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the excellent GoDoc site here:

View File

@ -16,7 +16,9 @@
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
@ -34,80 +36,49 @@ const (
ptrSize = unsafe.Sizeof((*byte)(nil))
)
var (
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
// internal reflect.Value fields. These values are valid before golang
// commit ecccf07e7f9d which changed the format. The are also valid
// after commit 82f48826c6c7 which changed the format again to mirror
// the original format. Code in the init function updates these offsets
// as necessary.
offsetPtr = uintptr(ptrSize)
offsetScalar = uintptr(0)
offsetFlag = uintptr(ptrSize * 2)
type flag uintptr
// flagKindWidth and flagKindShift indicate various bits that the
// reflect package uses internally to track kind information.
//
// flagRO indicates whether or not the value field of a reflect.Value is
// read-only.
//
// flagIndir indicates whether the value field of a reflect.Value is
// the actual data or a pointer to the data.
//
// These values are valid before golang commit 90a7c3c86944 which
// changed their positions. Code in the init function updates these
// flags as necessary.
flagKindWidth = uintptr(5)
flagKindShift = uintptr(flagKindWidth - 1)
flagRO = uintptr(1 << 0)
flagIndir = uintptr(1 << 1)
var (
// flagRO indicates whether the value field of a reflect.Value
// is read-only.
flagRO flag
// flagAddr indicates whether the address of the reflect.Value's
// value may be taken.
flagAddr flag
)
func init() {
// Older versions of reflect.Value stored small integers directly in the
// ptr field (which is named val in the older versions). Versions
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
// scalar for this purpose which unfortunately came before the flag
// field, so the offset of the flag field is different for those
// versions.
//
// This code constructs a new reflect.Value from a known small integer
// and checks if the size of the reflect.Value struct indicates it has
// the scalar field. When it does, the offsets are updated accordingly.
vv := reflect.ValueOf(0xf00)
if unsafe.Sizeof(vv) == (ptrSize * 4) {
offsetScalar = ptrSize * 2
offsetFlag = ptrSize * 3
}
// flagKindMask holds the bits that make up the kind
// part of the flags field. In all the supported versions,
// it is in the lower 5 bits.
const flagKindMask = flag(0x1f)
// Commit 90a7c3c86944 changed the flag positions such that the low
// order bits are the kind. This code extracts the kind from the flags
// field and ensures it's the correct type. When it's not, the flag
// order has been changed to the newer format, so the flags are updated
// accordingly.
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
upfv := *(*uintptr)(upf)
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
flagKindShift = 0
flagRO = 1 << 5
flagIndir = 1 << 6
// Different versions of Go have used different
// bit layouts for the flags type. This table
// records the known combinations.
var okFlags = []struct {
ro, addr flag
}{{
// From Go 1.4 to 1.5
ro: 1 << 5,
addr: 1 << 7,
}, {
// Up to Go tip.
ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
// Commit adf9b30e5594 modified the flags to separate the
// flagRO flag into two bits which specifies whether or not the
// field is embedded. This causes flagIndir to move over a bit
// and means that flagRO is the combination of either of the
// original flagRO bit and the new bit.
//
// This code detects the change by extracting what used to be
// the indirect bit to ensure it's set. When it's not, the flag
// order has been changed to the newer format, so the flags are
// updated accordingly.
if upfv&flagIndir == 0 {
flagRO = 3 << 5
flagIndir = 1 << 7
}
var flagValOffset = func() uintptr {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
@ -119,34 +90,56 @@ func init() {
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
indirects := 1
vt := v.Type()
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
if rvf&flagIndir != 0 {
vt = reflect.PtrTo(v.Type())
indirects++
} else if offsetScalar != 0 {
// The value is in the scalar field when it's not one of the
// reference types.
switch vt.Kind() {
case reflect.Uintptr:
case reflect.Chan:
case reflect.Func:
case reflect.Map:
case reflect.Ptr:
case reflect.UnsafePointer:
default:
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
offsetScalar)
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
return v
}
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
}
// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
panic("reflect.Value flag field has changed kind")
}
type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
}
}
pv := reflect.NewAt(vt, upv)
rv = pv
for i := 0; i < indirects; i++ {
rv = rv.Elem()
}
return rv
panic("reflect.Value read-only flag has changed semantics")
}

View File

@ -16,7 +16,7 @@
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe
// +build js appengine safe disableunsafe !go1.4
package spew

View File

@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.

View File

@ -35,16 +35,16 @@ var (
// cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump
// them.
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
)
// dumpState contains information about the state of a dump operation.
@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
// Display dereferenced value.
d.w.Write(openParenBytes)
switch {
case nilFound == true:
case nilFound:
d.w.Write(nilAngleBytes)
case cycleFound == true:
case cycleFound:
d.w.Write(circularBytes)
default:

View File

@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
// Display dereferenced value.
switch {
case nilFound == true:
case nilFound:
f.fs.Write(nilAngleBytes)
case cycleFound == true:
case cycleFound:
f.fs.Write(circularShortBytes)
default:

View File

@ -34,21 +34,27 @@ type Fuzzer struct {
nilChance float64
minElements int
maxElements int
maxDepth int
}
// New returns a new Fuzzer. Customize your Fuzzer further by calling Funcs,
// RandSource, NilChance, or NumElements in any order.
func New() *Fuzzer {
return NewWithSeed(time.Now().UnixNano())
}
func NewWithSeed(seed int64) *Fuzzer {
f := &Fuzzer{
defaultFuzzFuncs: fuzzFuncMap{
reflect.TypeOf(&time.Time{}): reflect.ValueOf(fuzzTime),
},
fuzzFuncs: fuzzFuncMap{},
r: rand.New(rand.NewSource(time.Now().UnixNano())),
r: rand.New(rand.NewSource(seed)),
nilChance: .2,
minElements: 1,
maxElements: 10,
maxDepth: 100,
}
return f
}
@ -136,6 +142,14 @@ func (f *Fuzzer) genShouldFill() bool {
return f.r.Float64() > f.nilChance
}
// MaxDepth sets the maximum number of recursive fuzz calls that will be made
// before stopping. This includes struct members, pointers, and map and slice
// elements.
func (f *Fuzzer) MaxDepth(d int) *Fuzzer {
f.maxDepth = d
return f
}
// Fuzz recursively fills all of obj's fields with something random. First
// this tries to find a custom fuzz function (see Funcs). If there is no
// custom function this tests whether the object implements fuzz.Interface and,
@ -144,17 +158,19 @@ func (f *Fuzzer) genShouldFill() bool {
// fails, this will generate random values for all primitive fields and then
// recurse for all non-primitives.
//
// Not safe for cyclic or tree-like structs!
// This is safe for cyclic or tree-like structs, up to a limit. Use the
// MaxDepth method to adjust how deep you need it to recurse.
//
// obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ )
// Intended for tests, so will panic on bad input or unimplemented fields.
// obj must be a pointer. Only exported (public) fields can be set (thanks,
// golang :/ ) Intended for tests, so will panic on bad input or unimplemented
// fields.
func (f *Fuzzer) Fuzz(obj interface{}) {
v := reflect.ValueOf(obj)
if v.Kind() != reflect.Ptr {
panic("needed ptr!")
}
v = v.Elem()
f.doFuzz(v, 0)
f.fuzzWithContext(v, 0)
}
// FuzzNoCustom is just like Fuzz, except that any custom fuzz function for
@ -170,7 +186,7 @@ func (f *Fuzzer) FuzzNoCustom(obj interface{}) {
panic("needed ptr!")
}
v = v.Elem()
f.doFuzz(v, flagNoCustomFuzz)
f.fuzzWithContext(v, flagNoCustomFuzz)
}
const (
@ -178,69 +194,87 @@ const (
flagNoCustomFuzz uint64 = 1 << iota
)
func (f *Fuzzer) doFuzz(v reflect.Value, flags uint64) {
func (f *Fuzzer) fuzzWithContext(v reflect.Value, flags uint64) {
fc := &fuzzerContext{fuzzer: f}
fc.doFuzz(v, flags)
}
// fuzzerContext carries context about a single fuzzing run, which lets Fuzzer
// be thread-safe.
type fuzzerContext struct {
fuzzer *Fuzzer
curDepth int
}
func (fc *fuzzerContext) doFuzz(v reflect.Value, flags uint64) {
if fc.curDepth >= fc.fuzzer.maxDepth {
return
}
fc.curDepth++
defer func() { fc.curDepth-- }()
if !v.CanSet() {
return
}
if flags&flagNoCustomFuzz == 0 {
// Check for both pointer and non-pointer custom functions.
if v.CanAddr() && f.tryCustom(v.Addr()) {
if v.CanAddr() && fc.tryCustom(v.Addr()) {
return
}
if f.tryCustom(v) {
if fc.tryCustom(v) {
return
}
}
if fn, ok := fillFuncMap[v.Kind()]; ok {
fn(v, f.r)
fn(v, fc.fuzzer.r)
return
}
switch v.Kind() {
case reflect.Map:
if f.genShouldFill() {
if fc.fuzzer.genShouldFill() {
v.Set(reflect.MakeMap(v.Type()))
n := f.genElementCount()
n := fc.fuzzer.genElementCount()
for i := 0; i < n; i++ {
key := reflect.New(v.Type().Key()).Elem()
f.doFuzz(key, 0)
fc.doFuzz(key, 0)
val := reflect.New(v.Type().Elem()).Elem()
f.doFuzz(val, 0)
fc.doFuzz(val, 0)
v.SetMapIndex(key, val)
}
return
}
v.Set(reflect.Zero(v.Type()))
case reflect.Ptr:
if f.genShouldFill() {
if fc.fuzzer.genShouldFill() {
v.Set(reflect.New(v.Type().Elem()))
f.doFuzz(v.Elem(), 0)
fc.doFuzz(v.Elem(), 0)
return
}
v.Set(reflect.Zero(v.Type()))
case reflect.Slice:
if f.genShouldFill() {
n := f.genElementCount()
if fc.fuzzer.genShouldFill() {
n := fc.fuzzer.genElementCount()
v.Set(reflect.MakeSlice(v.Type(), n, n))
for i := 0; i < n; i++ {
f.doFuzz(v.Index(i), 0)
fc.doFuzz(v.Index(i), 0)
}
return
}
v.Set(reflect.Zero(v.Type()))
case reflect.Array:
if f.genShouldFill() {
if fc.fuzzer.genShouldFill() {
n := v.Len()
for i := 0; i < n; i++ {
f.doFuzz(v.Index(i), 0)
fc.doFuzz(v.Index(i), 0)
}
return
}
v.Set(reflect.Zero(v.Type()))
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
f.doFuzz(v.Field(i), 0)
fc.doFuzz(v.Field(i), 0)
}
case reflect.Chan:
fallthrough
@ -255,20 +289,20 @@ func (f *Fuzzer) doFuzz(v reflect.Value, flags uint64) {
// tryCustom searches for custom handlers, and returns true iff it finds a match
// and successfully randomizes v.
func (f *Fuzzer) tryCustom(v reflect.Value) bool {
func (fc *fuzzerContext) tryCustom(v reflect.Value) bool {
// First: see if we have a fuzz function for it.
doCustom, ok := f.fuzzFuncs[v.Type()]
doCustom, ok := fc.fuzzer.fuzzFuncs[v.Type()]
if !ok {
// Second: see if it can fuzz itself.
if v.CanInterface() {
intf := v.Interface()
if fuzzable, ok := intf.(Interface); ok {
fuzzable.Fuzz(Continue{f: f, Rand: f.r})
fuzzable.Fuzz(Continue{fc: fc, Rand: fc.fuzzer.r})
return true
}
}
// Finally: see if there is a default fuzz function.
doCustom, ok = f.defaultFuzzFuncs[v.Type()]
doCustom, ok = fc.fuzzer.defaultFuzzFuncs[v.Type()]
if !ok {
return false
}
@ -294,8 +328,8 @@ func (f *Fuzzer) tryCustom(v reflect.Value) bool {
}
doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{
f: f,
Rand: f.r,
fc: fc,
Rand: fc.fuzzer.r,
})})
return true
}
@ -310,7 +344,7 @@ type Interface interface {
// Continue can be passed to custom fuzzing functions to allow them to use
// the correct source of randomness and to continue fuzzing their members.
type Continue struct {
f *Fuzzer
fc *fuzzerContext
// For convenience, Continue implements rand.Rand via embedding.
// Use this for generating any randomness if you want your fuzzing
@ -325,7 +359,7 @@ func (c Continue) Fuzz(obj interface{}) {
panic("needed ptr!")
}
v = v.Elem()
c.f.doFuzz(v, 0)
c.fc.doFuzz(v, 0)
}
// FuzzNoCustom continues fuzzing obj, except that any custom fuzz function for
@ -338,7 +372,7 @@ func (c Continue) FuzzNoCustom(obj interface{}) {
panic("needed ptr!")
}
v = v.Elem()
c.f.doFuzz(v, flagNoCustomFuzz)
c.fc.doFuzz(v, flagNoCustomFuzz)
}
// RandString makes a random string up to 20 characters long. The returned string

2
vendor/golang.org/x/oauth2/LICENSE generated vendored
View File

@ -1,4 +1,4 @@
Copyright (c) 2009 The oauth2 Authors. All rights reserved.
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are

65
vendor/golang.org/x/oauth2/README.md generated vendored
View File

@ -11,64 +11,25 @@ oauth2 package contains a client implementation for OAuth 2.0 spec.
go get golang.org/x/oauth2
~~~~
Or you can manually git clone the repository to
`$(go env GOPATH)/src/golang.org/x/oauth2`.
See godoc for further documentation and examples.
* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
## Policy for new packages
## App Engine
We no longer accept new provider-specific packages in this repo. For
defining provider endpoints and provider-specific OAuth2 behavior, we
encourage you to create packages elsewhere. We'll keep the existing
packages for compatibility.
In change 96e89be (March 2015) we removed the `oauth2.Context2` type in favor
of the [`context.Context`](https://golang.org/x/net/context#Context) type from
the `golang.org/x/net/context` package
## Report Issues / Send Patches
This means its no longer possible to use the "Classic App Engine"
`appengine.Context` type with the `oauth2` package. (You're using
Classic App Engine if you import the package `"appengine"`.)
This repository uses Gerrit for code changes. To learn how to submit changes to
this repository, see https://golang.org/doc/contribute.html.
To work around this, you may use the new `"google.golang.org/appengine"`
package. This package has almost the same API as the `"appengine"` package,
but it can be fetched with `go get` and used on "Managed VMs" and well as
Classic App Engine.
See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app)
for information on updating your app.
If you don't want to update your entire app to use the new App Engine packages,
you may use both sets of packages in parallel, using only the new packages
with the `oauth2` package.
import (
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
newappengine "google.golang.org/appengine"
newurlfetch "google.golang.org/appengine/urlfetch"
"appengine"
)
func handler(w http.ResponseWriter, r *http.Request) {
var c appengine.Context = appengine.NewContext(r)
c.Infof("Logging a message with the old package")
var ctx context.Context = newappengine.NewContext(r)
client := &http.Client{
Transport: &oauth2.Transport{
Source: google.AppEngineTokenSource(ctx, "scope"),
Base: &newurlfetch.Transport{Context: ctx},
},
}
client.Get("...")
}
## Contributing
We appreciate your help!
To contribute, please read the contribution guidelines:
https://golang.org/doc/contribute.html
Note that the Go project does not use GitHub pull requests but
uses Gerrit for code reviews. See the contribution guide for details.
The main issue tracker for the oauth2 repository is located at
https://github.com/golang/oauth2/issues.

View File

@ -1,25 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build appengine
// App Engine hooks.
package oauth2
import (
"net/http"
"golang.org/x/net/context"
"golang.org/x/oauth2/internal"
"google.golang.org/appengine/urlfetch"
)
func init() {
internal.RegisterContextClientFunc(contextClientAppEngine)
}
func contextClientAppEngine(ctx context.Context) (*http.Client, error) {
return urlfetch.Client(ctx), nil
}

10
vendor/golang.org/x/oauth2/go.mod generated vendored Normal file
View File

@ -0,0 +1,10 @@
module golang.org/x/oauth2
go 1.11
require (
cloud.google.com/go v0.34.0
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
google.golang.org/appengine v1.4.0
)

View File

@ -0,0 +1,13 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build appengine
package internal
import "google.golang.org/appengine/urlfetch"
func init() {
appengineClientHook = urlfetch.Client
}

6
vendor/golang.org/x/oauth2/internal/doc.go generated vendored Normal file
View File

@ -0,0 +1,6 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package internal contains support packages for oauth2 package.
package internal

View File

@ -2,18 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package internal contains support packages for oauth2 package.
package internal
import (
"bufio"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"strings"
)
// ParseKey converts the binary contents of a private key file
@ -30,7 +26,7 @@ func ParseKey(key []byte) (*rsa.PrivateKey, error) {
if err != nil {
parsedKey, err = x509.ParsePKCS1PrivateKey(key)
if err != nil {
return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err)
return nil, fmt.Errorf("private key should be a PEM or plain PKCS1 or PKCS8; parse error: %v", err)
}
}
parsed, ok := parsedKey.(*rsa.PrivateKey)
@ -39,38 +35,3 @@ func ParseKey(key []byte) (*rsa.PrivateKey, error) {
}
return parsed, nil
}
func ParseINI(ini io.Reader) (map[string]map[string]string, error) {
result := map[string]map[string]string{
"": {}, // root section
}
scanner := bufio.NewScanner(ini)
currentSection := ""
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, ";") {
// comment.
continue
}
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
currentSection = strings.TrimSpace(line[1 : len(line)-1])
result[currentSection] = map[string]string{}
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 && parts[0] != "" {
result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error scanning ini: %v", err)
}
return result, nil
}
func CondVal(v string) []string {
if v == "" {
return nil
}
return []string{v}
}

View File

@ -2,25 +2,28 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package internal contains support packages for oauth2 package.
package internal
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"mime"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)
// Token represents the crendentials used to authorize
// Token represents the credentials used to authorize
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
@ -60,22 +63,21 @@ type tokenJSON struct {
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in
}
func (e *tokenJSON) expiry() (t time.Time) {
if v := e.ExpiresIn; v != 0 {
return time.Now().Add(time.Duration(v) * time.Second)
}
if v := e.Expires; v != 0 {
return time.Now().Add(time.Duration(v) * time.Second)
}
return
}
type expirationTime int32
func (e *expirationTime) UnmarshalJSON(b []byte) error {
if len(b) == 0 || string(b) == "null" {
return nil
}
var n json.Number
err := json.Unmarshal(b, &n)
if err != nil {
@ -85,92 +87,78 @@ func (e *expirationTime) UnmarshalJSON(b []byte) error {
if err != nil {
return err
}
if i > math.MaxInt32 {
i = math.MaxInt32
}
*e = expirationTime(i)
return nil
}
var brokenAuthHeaderProviders = []string{
"https://accounts.google.com/",
"https://api.codeswholesale.com/oauth/token",
"https://api.dropbox.com/",
"https://api.dropboxapi.com/",
"https://api.instagram.com/",
"https://api.netatmo.net/",
"https://api.odnoklassniki.ru/",
"https://api.pushbullet.com/",
"https://api.soundcloud.com/",
"https://api.twitch.tv/",
"https://app.box.com/",
"https://connect.stripe.com/",
"https://graph.facebook.com", // see https://github.com/golang/oauth2/issues/214
"https://login.microsoftonline.com/",
"https://login.salesforce.com/",
"https://oauth.sandbox.trainingpeaks.com/",
"https://oauth.trainingpeaks.com/",
"https://oauth.vk.com/",
"https://openapi.baidu.com/",
"https://slack.com/",
"https://test-sandbox.auth.corp.google.com",
"https://test.salesforce.com/",
"https://user.gini.net/",
"https://www.douban.com/",
"https://www.googleapis.com/",
"https://www.linkedin.com/",
"https://www.strava.com/oauth/",
"https://www.wunderlist.com/oauth/",
"https://api.patreon.com/",
"https://sandbox.codeswholesale.com/oauth/token",
// RegisterBrokenAuthHeaderProvider previously did something. It is now a no-op.
//
// Deprecated: this function no longer does anything. Caller code that
// wants to avoid potential extra HTTP requests made during
// auto-probing of the provider's auth style should set
// Endpoint.AuthStyle.
func RegisterBrokenAuthHeaderProvider(tokenURL string) {}
// AuthStyle is a copy of the golang.org/x/oauth2 package's AuthStyle type.
type AuthStyle int
const (
AuthStyleUnknown AuthStyle = 0
AuthStyleInParams AuthStyle = 1
AuthStyleInHeader AuthStyle = 2
)
// authStyleCache is the set of tokenURLs we've successfully used via
// RetrieveToken and which style auth we ended up using.
// It's called a cache, but it doesn't (yet?) shrink. It's expected that
// the set of OAuth2 servers a program contacts over time is fixed and
// small.
var authStyleCache struct {
sync.Mutex
m map[string]AuthStyle // keyed by tokenURL
}
// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
var brokenAuthHeaderDomains = []string{
".force.com",
".okta.com",
".oktapreview.com",
// ResetAuthCache resets the global authentication style cache used
// for AuthStyleUnknown token requests.
func ResetAuthCache() {
authStyleCache.Lock()
defer authStyleCache.Unlock()
authStyleCache.m = nil
}
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
// lookupAuthStyle reports which auth style we last used with tokenURL
// when calling RetrieveToken and whether we have ever done so.
func lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) {
authStyleCache.Lock()
defer authStyleCache.Unlock()
style, ok = authStyleCache.m[tokenURL]
return
}
// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
for _, s := range brokenAuthHeaderProviders {
if strings.HasPrefix(tokenURL, s) {
// Some sites fail to implement the OAuth2 spec fully.
return false
}
// setAuthStyle adds an entry to authStyleCache, documented above.
func setAuthStyle(tokenURL string, v AuthStyle) {
authStyleCache.Lock()
defer authStyleCache.Unlock()
if authStyleCache.m == nil {
authStyleCache.m = make(map[string]AuthStyle)
}
if u, err := url.Parse(tokenURL); err == nil {
for _, s := range brokenAuthHeaderDomains {
if strings.HasSuffix(u.Host, s) {
return false
}
}
}
// Assume the provider implements the spec properly
// otherwise. We can add more exceptions as they're
// discovered. We will _not_ be adding configurable hooks
// to this package to let users select server bugs.
return true
authStyleCache.m[tokenURL] = v
}
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
hc, err := ContextClient(ctx)
if err != nil {
return nil, err
}
bustedAuth := !providerAuthHeaderWorks(tokenURL)
if bustedAuth {
// newTokenRequest returns a new *http.Request to retrieve a new token
// from tokenURL using the provided clientID, clientSecret, and POST
// body parameters.
//
// inParams is whether the clientID & clientSecret should be encoded
// as the POST body. An 'inParams' value of true means to send it in
// the POST body (along with any values in v); false means to send it
// in the Authorization header.
func newTokenRequest(tokenURL, clientID, clientSecret string, v url.Values, authStyle AuthStyle) (*http.Request, error) {
if authStyle == AuthStyleInParams {
v = cloneURLValues(v)
if clientID != "" {
v.Set("client_id", clientID)
}
@ -183,20 +171,78 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if !bustedAuth {
req.SetBasicAuth(clientID, clientSecret)
if authStyle == AuthStyleInHeader {
req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
}
r, err := hc.Do(req)
return req, nil
}
func cloneURLValues(v url.Values) url.Values {
v2 := make(url.Values, len(v))
for k, vv := range v {
v2[k] = append([]string(nil), vv...)
}
return v2
}
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle) (*Token, error) {
needsAuthStyleProbe := authStyle == 0
if needsAuthStyleProbe {
if style, ok := lookupAuthStyle(tokenURL); ok {
authStyle = style
needsAuthStyleProbe = false
} else {
authStyle = AuthStyleInHeader // the first way we'll try
}
}
req, err := newTokenRequest(tokenURL, clientID, clientSecret, v, authStyle)
if err != nil {
return nil, err
}
token, err := doTokenRoundTrip(ctx, req)
if err != nil && needsAuthStyleProbe {
// If we get an error, assume the server wants the
// clientID & clientSecret in a different form.
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
//
// We used to maintain a big table in this code of all the sites and which way
// they went, but maintaining it didn't scale & got annoying.
// So just try both ways.
authStyle = AuthStyleInParams // the second way we'll try
req, _ = newTokenRequest(tokenURL, clientID, clientSecret, v, authStyle)
token, err = doTokenRoundTrip(ctx, req)
}
if needsAuthStyleProbe && err == nil {
setAuthStyle(tokenURL, authStyle)
}
// Don't overwrite `RefreshToken` with an empty value
// if this was a token refreshing request.
if token != nil && token.RefreshToken == "" {
token.RefreshToken = v.Get("refresh_token")
}
return token, err
}
func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
r, err := ctxhttp.Do(ctx, ContextClient(ctx), req)
if err != nil {
return nil, err
}
defer r.Body.Close()
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
r.Body.Close()
if err != nil {
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
}
if code := r.StatusCode; code < 200 || code > 299 {
return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
return nil, &RetrieveError{
Response: r,
Body: body,
}
}
var token *Token
@ -214,12 +260,6 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
Raw: vals,
}
e := vals.Get("expires_in")
if e == "" {
// TODO(jbd): Facebook's OAuth2 implementation is broken and
// returns expires_in field in expires. Remove the fallback to expires,
// when Facebook fixes their implementation.
e = vals.Get("expires")
}
expires, _ := strconv.Atoi(e)
if expires != 0 {
token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
@ -238,10 +278,17 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
}
json.Unmarshal(body, &token.Raw) // no error checks for optional fields
}
// Don't overwrite `RefreshToken` with an empty value
// if this was a token refreshing request.
if token.RefreshToken == "" {
token.RefreshToken = v.Get("refresh_token")
if token.AccessToken == "" {
return nil, errors.New("oauth2: server response missing access_token")
}
return token, nil
}
type RetrieveError struct {
Response *http.Response
Body []byte
}
func (r *RetrieveError) Error() string {
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
}

View File

@ -2,13 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package internal contains support packages for oauth2 package.
package internal
import (
"context"
"net/http"
"golang.org/x/net/context"
)
// HTTPClient is the context key to use with golang.org/x/net/context's
@ -20,50 +18,16 @@ var HTTPClient ContextKey
// because nobody else can create a ContextKey, being unexported.
type ContextKey struct{}
// ContextClientFunc is a func which tries to return an *http.Client
// given a Context value. If it returns an error, the search stops
// with that error. If it returns (nil, nil), the search continues
// down the list of registered funcs.
type ContextClientFunc func(context.Context) (*http.Client, error)
var appengineClientHook func(context.Context) *http.Client
var contextClientFuncs []ContextClientFunc
func RegisterContextClientFunc(fn ContextClientFunc) {
contextClientFuncs = append(contextClientFuncs, fn)
}
func ContextClient(ctx context.Context) (*http.Client, error) {
func ContextClient(ctx context.Context) *http.Client {
if ctx != nil {
if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
return hc, nil
return hc
}
}
for _, fn := range contextClientFuncs {
c, err := fn(ctx)
if err != nil {
return nil, err
}
if c != nil {
return c, nil
}
if appengineClientHook != nil {
return appengineClientHook(ctx)
}
return http.DefaultClient, nil
}
func ContextTransport(ctx context.Context) http.RoundTripper {
hc, err := ContextClient(ctx)
// This is a rare error case (somebody using nil on App Engine).
if err != nil {
return ErrorTransport{err}
}
return hc.Transport
}
// ErrorTransport returns the specified error on RoundTrip.
// This RoundTripper should be used in rare error cases where
// error handling can be postponed to response handling time.
type ErrorTransport struct{ Err error }
func (t ErrorTransport) RoundTrip(*http.Request) (*http.Response, error) {
return nil, t.Err
return http.DefaultClient
}

115
vendor/golang.org/x/oauth2/oauth2.go generated vendored
View File

@ -3,19 +3,20 @@
// license that can be found in the LICENSE file.
// Package oauth2 provides support for making
// OAuth2 authorized and authenticated HTTP requests.
// OAuth2 authorized and authenticated HTTP requests,
// as specified in RFC 6749.
// It can additionally grant authorization with Bearer JWT.
package oauth2 // import "golang.org/x/oauth2"
import (
"bytes"
"context"
"errors"
"net/http"
"net/url"
"strings"
"sync"
"golang.org/x/net/context"
"golang.org/x/oauth2/internal"
)
@ -25,17 +26,13 @@ import (
// Deprecated: Use context.Background() or context.TODO() instead.
var NoContext = context.TODO()
// RegisterBrokenAuthHeaderProvider registers an OAuth2 server
// identified by the tokenURL prefix as an OAuth2 implementation
// which doesn't support the HTTP Basic authentication
// scheme to authenticate with the authorization server.
// Once a server is registered, credentials (client_id and client_secret)
// will be passed as query parameters rather than being present
// in the Authorization header.
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
internal.RegisterBrokenAuthHeaderProvider(tokenURL)
}
// RegisterBrokenAuthHeaderProvider previously did something. It is now a no-op.
//
// Deprecated: this function no longer does anything. Caller code that
// wants to avoid potential extra HTTP requests made during
// auto-probing of the provider's auth style should set
// Endpoint.AuthStyle.
func RegisterBrokenAuthHeaderProvider(tokenURL string) {}
// Config describes a typical 3-legged OAuth2 flow, with both the
// client application information and the server's endpoint URLs.
@ -70,13 +67,38 @@ type TokenSource interface {
Token() (*Token, error)
}
// Endpoint contains the OAuth 2.0 provider's authorization and token
// Endpoint represents an OAuth 2.0 provider's authorization and token
// endpoint URLs.
type Endpoint struct {
AuthURL string
TokenURL string
// AuthStyle optionally specifies how the endpoint wants the
// client ID & client secret sent. The zero value means to
// auto-detect.
AuthStyle AuthStyle
}
// AuthStyle represents how requests for tokens are authenticated
// to the server.
type AuthStyle int
const (
// AuthStyleAutoDetect means to auto-detect which authentication
// style the provider wants by trying both ways and caching
// the successful way for the future.
AuthStyleAutoDetect AuthStyle = 0
// AuthStyleInParams sends the "client_id" and "client_secret"
// in the POST body as application/x-www-form-urlencoded parameters.
AuthStyleInParams AuthStyle = 1
// AuthStyleInHeader sends the client_id and client_password
// using HTTP Basic Authorization. This is an optional style
// described in the OAuth2 RFC 6749 section 2.3.1.
AuthStyleInHeader AuthStyle = 2
)
var (
// AccessTypeOnline and AccessTypeOffline are options passed
// to the Options.AuthCodeURL method. They modify the
@ -117,21 +139,30 @@ func SetAuthURLParam(key, value string) AuthCodeOption {
// that asks for permissions for the required scopes explicitly.
//
// State is a token to protect the user from CSRF attacks. You must
// always provide a non-zero string and validate that it matches the
// always provide a non-empty string and validate that it matches the
// the state query parameter on your redirect callback.
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
//
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
// as ApprovalForce.
// It can also be used to pass the PKCE challenge.
// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
var buf bytes.Buffer
buf.WriteString(c.Endpoint.AuthURL)
v := url.Values{
"response_type": {"code"},
"client_id": {c.ClientID},
"redirect_uri": internal.CondVal(c.RedirectURL),
"scope": internal.CondVal(strings.Join(c.Scopes, " ")),
"state": internal.CondVal(state),
}
if c.RedirectURL != "" {
v.Set("redirect_uri", c.RedirectURL)
}
if len(c.Scopes) > 0 {
v.Set("scope", strings.Join(c.Scopes, " "))
}
if state != "" {
// TODO(light): Docs say never to omit state; don't allow empty.
v.Set("state", state)
}
for _, opt := range opts {
opt.setValue(v)
@ -154,15 +185,17 @@ func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
// and when other authorization grant types are not available."
// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
//
// The HTTP client to use is derived from the context.
// If nil, http.DefaultClient is used.
// The provided context optionally controls which HTTP client is used. See the HTTPClient variable.
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
return retrieveToken(ctx, c, url.Values{
v := url.Values{
"grant_type": {"password"},
"username": {username},
"password": {password},
"scope": internal.CondVal(strings.Join(c.Scopes, " ")),
})
}
if len(c.Scopes) > 0 {
v.Set("scope", strings.Join(c.Scopes, " "))
}
return retrieveToken(ctx, c, v)
}
// Exchange converts an authorization code into a token.
@ -170,17 +203,25 @@ func (c *Config) PasswordCredentialsToken(ctx context.Context, username, passwor
// It is used after a resource provider redirects the user back
// to the Redirect URI (the URL obtained from AuthCodeURL).
//
// The HTTP client to use is derived from the context.
// If a client is not provided via the context, http.DefaultClient is used.
// The provided context optionally controls which HTTP client is used. See the HTTPClient variable.
//
// The code will be in the *http.Request.FormValue("code"). Before
// calling Exchange, be sure to validate FormValue("state").
func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
return retrieveToken(ctx, c, url.Values{
"grant_type": {"authorization_code"},
"code": {code},
"redirect_uri": internal.CondVal(c.RedirectURL),
})
//
// Opts may include the PKCE verifier code if previously used in AuthCodeURL.
// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) {
v := url.Values{
"grant_type": {"authorization_code"},
"code": {code},
}
if c.RedirectURL != "" {
v.Set("redirect_uri", c.RedirectURL)
}
for _, opt := range opts {
opt.setValue(v)
}
return retrieveToken(ctx, c, v)
}
// Client returns an HTTP client using the provided token.
@ -291,20 +332,20 @@ var HTTPClient internal.ContextKey
// NewClient creates an *http.Client from a Context and TokenSource.
// The returned client is not valid beyond the lifetime of the context.
//
// Note that if a custom *http.Client is provided via the Context it
// is used only for token acquisition and is not used to configure the
// *http.Client returned from NewClient.
//
// As a special case, if src is nil, a non-OAuth2 client is returned
// using the provided context. This exists to support related OAuth2
// packages.
func NewClient(ctx context.Context, src TokenSource) *http.Client {
if src == nil {
c, err := internal.ContextClient(ctx)
if err != nil {
return &http.Client{Transport: internal.ErrorTransport{Err: err}}
}
return c
return internal.ContextClient(ctx)
}
return &http.Client{
Transport: &Transport{
Base: internal.ContextTransport(ctx),
Base: internal.ContextClient(ctx).Transport,
Source: ReuseTokenSource(nil, src),
},
}

28
vendor/golang.org/x/oauth2/token.go generated vendored
View File

@ -5,13 +5,14 @@
package oauth2
import (
"context"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"golang.org/x/net/context"
"golang.org/x/oauth2/internal"
)
@ -20,7 +21,7 @@ import (
// expirations due to client-server time mismatches.
const expiryDelta = 10 * time.Second
// Token represents the crendentials used to authorize
// Token represents the credentials used to authorize
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
@ -117,13 +118,16 @@ func (t *Token) Extra(key string) interface{} {
return v
}
// timeNow is time.Now but pulled out as a variable for tests.
var timeNow = time.Now
// expired reports whether the token is expired.
// t must be non-nil.
func (t *Token) expired() bool {
if t.Expiry.IsZero() {
return false
}
return t.Expiry.Add(-expiryDelta).Before(time.Now())
return t.Expiry.Round(0).Add(-expiryDelta).Before(timeNow())
}
// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
@ -150,9 +154,25 @@ func tokenFromInternal(t *internal.Token) *Token {
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
// with an error..
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle))
if err != nil {
if rErr, ok := err.(*internal.RetrieveError); ok {
return nil, (*RetrieveError)(rErr)
}
return nil, err
}
return tokenFromInternal(tk), nil
}
// RetrieveError is the error returned when the token endpoint returns a
// non-2XX HTTP status code.
type RetrieveError struct {
Response *http.Response
// Body is the body that was consumed by reading Response.Body.
// It may be truncated.
Body []byte
}
func (r *RetrieveError) Error() string {
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
}

View File

@ -31,9 +31,17 @@ type Transport struct {
}
// RoundTrip authorizes and authenticates the request with an
// access token. If no token exists or token is expired,
// tries to refresh/fetch a new token.
// access token from Transport's Source.
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
reqBodyClosed := false
if req.Body != nil {
defer func() {
if !reqBodyClosed {
req.Body.Close()
}
}()
}
if t.Source == nil {
return nil, errors.New("oauth2: Transport's Source is nil")
}
@ -46,6 +54,10 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
token.SetAuthHeader(req2)
t.setModReq(req, req2)
res, err := t.base().RoundTrip(req2)
// req.Body is assumed to have been closed by the base RoundTripper.
reqBodyClosed = true
if err != nil {
t.setModReq(req, nil)
return nil, err