- if len(parts) != 4 {
- return nil, fmt.Errorf("unexpected output: %v", parts)
- }
- cidr, err := asciiCIDR(parts[1])
- if err != nil {
- return nil, err
- }
- result = append(result, cidr)
- }
- }
- return result, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/bandwidth/unsupported.go b/vendor/github.com/containerd/cri/pkg/server/bandwidth/unsupported.go
deleted file mode 100644
index 12c5ad83b..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/bandwidth/unsupported.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// +build !linux
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2015 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package bandwidth
-
-import (
- "errors"
-
- "k8s.io/apimachinery/pkg/api/resource"
-)
-
-type unsupportedShaper struct {
-}
-
-// NewTCShaper makes a new unsupportedShapper for the given interface
-func NewTCShaper(iface string) Shaper {
- return &unsupportedShaper{}
-}
-
-func (f *unsupportedShaper) Limit(cidr string, egress, ingress *resource.Quantity) error {
- return errors.New("unimplemented")
-}
-
-func (f *unsupportedShaper) Reset(cidr string) error {
- return nil
-}
-
-func (f *unsupportedShaper) ReconcileInterface() error {
- return errors.New("unimplemented")
-}
-
-func (f *unsupportedShaper) ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error {
- return errors.New("unimplemented")
-}
-
-func (f *unsupportedShaper) GetCIDRs() ([]string, error) {
- return []string{}, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/bandwidth/utils.go b/vendor/github.com/containerd/cri/pkg/server/bandwidth/utils.go
deleted file mode 100644
index f90718b6b..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/bandwidth/utils.go
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2015 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package bandwidth
-
-import (
- "fmt"
-
- "k8s.io/apimachinery/pkg/api/resource"
-)
-
-var minRsrc = resource.MustParse("1k")
-var maxRsrc = resource.MustParse("1P")
-
-func validateBandwidthIsReasonable(rsrc *resource.Quantity) error {
- if rsrc.Value() < minRsrc.Value() {
- return fmt.Errorf("resource is unreasonably small (< 1kbit)")
- }
- if rsrc.Value() > maxRsrc.Value() {
- return fmt.Errorf("resoruce is unreasonably large (> 1Pbit)")
- }
- return nil
-}
-
-// ExtractPodBandwidthResources extracts the ingress and egress from the given pod annotations
-func ExtractPodBandwidthResources(podAnnotations map[string]string) (ingress, egress *resource.Quantity, err error) {
- if podAnnotations == nil {
- return nil, nil, nil
- }
- str, found := podAnnotations["kubernetes.io/ingress-bandwidth"]
- if found {
- ingressValue, err := resource.ParseQuantity(str)
- if err != nil {
- return nil, nil, err
- }
- ingress = &ingressValue
- if err := validateBandwidthIsReasonable(ingress); err != nil {
- return nil, nil, err
- }
- }
- str, found = podAnnotations["kubernetes.io/egress-bandwidth"]
- if found {
- egressValue, err := resource.ParseQuantity(str)
- if err != nil {
- return nil, nil, err
- }
- egress = &egressValue
- if err := validateBandwidthIsReasonable(egress); err != nil {
- return nil, nil, err
- }
- }
- return ingress, egress, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/cni_conf_syncer.go b/vendor/github.com/containerd/cri/pkg/server/cni_conf_syncer.go
deleted file mode 100644
index 03131a9be..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/cni_conf_syncer.go
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "os"
- "sync"
-
- cni "github.com/containerd/go-cni"
- "github.com/fsnotify/fsnotify"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// cniNetConfSyncer is used to reload cni network conf triggered by fs change
-// events.
-type cniNetConfSyncer struct {
- // only used for lastSyncStatus
- sync.RWMutex
- lastSyncStatus error
-
- watcher *fsnotify.Watcher
- confDir string
- netPlugin cni.CNI
- loadOpts []cni.CNIOpt
-}
-
-// newCNINetConfSyncer creates cni network conf syncer.
-func newCNINetConfSyncer(confDir string, netPlugin cni.CNI, loadOpts []cni.CNIOpt) (*cniNetConfSyncer, error) {
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- return nil, errors.Wrap(err, "failed to create fsnotify watcher")
- }
-
- if err := os.MkdirAll(confDir, 0700); err != nil {
- return nil, errors.Wrapf(err, "failed to create cni conf dir=%s for watch", confDir)
- }
-
- if err := watcher.Add(confDir); err != nil {
- return nil, errors.Wrapf(err, "failed to watch cni conf dir %s", confDir)
- }
-
- syncer := &cniNetConfSyncer{
- watcher: watcher,
- confDir: confDir,
- netPlugin: netPlugin,
- loadOpts: loadOpts,
- }
-
- if err := syncer.netPlugin.Load(syncer.loadOpts...); err != nil {
- logrus.WithError(err).Error("failed to load cni during init, please check CRI plugin status before setting up network for pods")
- syncer.updateLastStatus(err)
- }
- return syncer, nil
-}
-
-// syncLoop monitors any fs change events from cni conf dir and tries to reload
-// cni configuration.
-func (syncer *cniNetConfSyncer) syncLoop() error {
- for {
- select {
- case event := <-syncer.watcher.Events:
- // Only reload config when receiving write/rename/remove
- // events
- //
- // TODO(fuweid): Might only reload target cni config
- // files to prevent no-ops.
- if event.Op&(fsnotify.Chmod|fsnotify.Create) > 0 {
- logrus.Debugf("ignore event from cni conf dir: %s", event)
- continue
- }
- logrus.Debugf("receiving change event from cni conf dir: %s", event)
-
- lerr := syncer.netPlugin.Load(syncer.loadOpts...)
- if lerr != nil {
- logrus.WithError(lerr).
- Errorf("failed to reload cni configuration after receiving fs change event(%s)", event)
- }
- syncer.updateLastStatus(lerr)
-
- case err := <-syncer.watcher.Errors:
- if err != nil {
- logrus.WithError(err).Error("failed to continue sync cni conf change")
- return err
- }
- }
- }
-}
-
-// lastStatus retrieves last sync status.
-func (syncer *cniNetConfSyncer) lastStatus() error {
- syncer.RLock()
- defer syncer.RUnlock()
- return syncer.lastSyncStatus
-}
-
-// updateLastStatus will be called after every single cni load.
-func (syncer *cniNetConfSyncer) updateLastStatus(err error) {
- syncer.Lock()
- defer syncer.Unlock()
- syncer.lastSyncStatus = err
-}
-
-// stop stops watcher in the syncLoop.
-func (syncer *cniNetConfSyncer) stop() error {
- return syncer.watcher.Close()
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_attach.go b/vendor/github.com/containerd/cri/pkg/server/container_attach.go
deleted file mode 100644
index c8101ff7c..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_attach.go
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "io"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/log"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- "k8s.io/client-go/tools/remotecommand"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- cio "github.com/containerd/cri/pkg/server/io"
-)
-
-// Attach prepares a streaming endpoint to attach to a running container, and returns the address.
-func (c *criService) Attach(ctx context.Context, r *runtime.AttachRequest) (*runtime.AttachResponse, error) {
- cntr, err := c.containerStore.Get(r.GetContainerId())
- if err != nil {
- return nil, errors.Wrap(err, "failed to find container in store")
- }
- state := cntr.Status.Get().State()
- if state != runtime.ContainerState_CONTAINER_RUNNING {
- return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
- }
- return c.streamServer.GetAttach(r)
-}
-
-func (c *criService) attachContainer(ctx context.Context, id string, stdin io.Reader, stdout, stderr io.WriteCloser,
- tty bool, resize <-chan remotecommand.TerminalSize) error {
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
- // Get container from our container store.
- cntr, err := c.containerStore.Get(id)
- if err != nil {
- return errors.Wrapf(err, "failed to find container %q in store", id)
- }
- id = cntr.ID
-
- state := cntr.Status.Get().State()
- if state != runtime.ContainerState_CONTAINER_RUNNING {
- return errors.Errorf("container is in %s state", criContainerStateToString(state))
- }
-
- task, err := cntr.Container.Task(ctx, nil)
- if err != nil {
- return errors.Wrap(err, "failed to load task")
- }
- handleResizing(ctx, resize, func(size remotecommand.TerminalSize) {
- if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to resize task %q console", id)
- }
- })
-
- opts := cio.AttachOptions{
- Stdin: stdin,
- Stdout: stdout,
- Stderr: stderr,
- Tty: tty,
- StdinOnce: cntr.Config.StdinOnce,
- CloseStdin: func() error {
- return task.CloseIO(ctx, containerd.WithStdinCloser)
- },
- }
- // TODO(random-liu): Figure out whether we need to support historical output.
- cntr.IO.Attach(opts)
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_create.go b/vendor/github.com/containerd/cri/pkg/server/container_create.go
deleted file mode 100644
index 01eac7bc8..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_create.go
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "path/filepath"
- "time"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/containers"
- "github.com/containerd/containerd/log"
- "github.com/containerd/containerd/oci"
- "github.com/containerd/typeurl"
- "github.com/davecgh/go-spew/spew"
- imagespec "github.com/opencontainers/image-spec/specs-go/v1"
- runtimespec "github.com/opencontainers/runtime-spec/specs-go"
- selinux "github.com/opencontainers/selinux/go-selinux"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- customopts "github.com/containerd/cri/pkg/containerd/opts"
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- cio "github.com/containerd/cri/pkg/server/io"
- containerstore "github.com/containerd/cri/pkg/store/container"
- "github.com/containerd/cri/pkg/util"
-)
-
-func init() {
- typeurl.Register(&containerstore.Metadata{},
- "github.com/containerd/cri/pkg/store/container", "Metadata")
-}
-
-// CreateContainer creates a new container in the given PodSandbox.
-func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (_ *runtime.CreateContainerResponse, retErr error) {
- config := r.GetConfig()
- log.G(ctx).Debugf("Container config %+v", config)
- sandboxConfig := r.GetSandboxConfig()
- sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
- if err != nil {
- return nil, errors.Wrapf(err, "failed to find sandbox id %q", r.GetPodSandboxId())
- }
- sandboxID := sandbox.ID
- s, err := sandbox.Container.Task(ctx, nil)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get sandbox container task")
- }
- sandboxPid := s.Pid()
-
- // Generate unique id and name for the container and reserve the name.
- // Reserve the container name to avoid concurrent `CreateContainer` request creating
- // the same container.
- id := util.GenerateID()
- metadata := config.GetMetadata()
- if metadata == nil {
- return nil, errors.New("container config must include metadata")
- }
- containerName := metadata.Name
- name := makeContainerName(metadata, sandboxConfig.GetMetadata())
- log.G(ctx).Debugf("Generated id %q for container %q", id, name)
- if err = c.containerNameIndex.Reserve(name, id); err != nil {
- return nil, errors.Wrapf(err, "failed to reserve container name %q", name)
- }
- defer func() {
- // Release the name if the function returns with an error.
- if retErr != nil {
- c.containerNameIndex.ReleaseByName(name)
- }
- }()
-
- // Create initial internal container metadata.
- meta := containerstore.Metadata{
- ID: id,
- Name: name,
- SandboxID: sandboxID,
- Config: config,
- }
-
- // Prepare container image snapshot. For container, the image should have
- // been pulled before creating the container, so do not ensure the image.
- image, err := c.localResolve(config.GetImage().GetImage())
- if err != nil {
- return nil, errors.Wrapf(err, "failed to resolve image %q", config.GetImage().GetImage())
- }
- containerdImage, err := c.toContainerdImage(ctx, image)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get image from containerd %q", image.ID)
- }
-
- // Run container using the same runtime with sandbox.
- sandboxInfo, err := sandbox.Container.Info(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get sandbox %q info", sandboxID)
- }
-
- // Create container root directory.
- containerRootDir := c.getContainerRootDir(id)
- if err = c.os.MkdirAll(containerRootDir, 0755); err != nil {
- return nil, errors.Wrapf(err, "failed to create container root directory %q",
- containerRootDir)
- }
- defer func() {
- if retErr != nil {
- // Cleanup the container root directory.
- if err = c.os.RemoveAll(containerRootDir); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to remove container root directory %q",
- containerRootDir)
- }
- }
- }()
- volatileContainerRootDir := c.getVolatileContainerRootDir(id)
- if err = c.os.MkdirAll(volatileContainerRootDir, 0755); err != nil {
- return nil, errors.Wrapf(err, "failed to create volatile container root directory %q",
- volatileContainerRootDir)
- }
- defer func() {
- if retErr != nil {
- // Cleanup the volatile container root directory.
- if err = c.os.RemoveAll(volatileContainerRootDir); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to remove volatile container root directory %q",
- volatileContainerRootDir)
- }
- }
- }()
-
- var volumeMounts []*runtime.Mount
- if !c.config.IgnoreImageDefinedVolumes {
- // Create container image volumes mounts.
- volumeMounts = c.volumeMounts(containerRootDir, config.GetMounts(), &image.ImageSpec.Config)
- } else if len(image.ImageSpec.Config.Volumes) != 0 {
- log.G(ctx).Debugf("Ignoring volumes defined in image %v because IgnoreImageDefinedVolumes is set", image.ID)
- }
-
- // Generate container mounts.
- mounts := c.containerMounts(sandboxID, config)
-
- ociRuntime, err := c.getSandboxRuntime(sandboxConfig, sandbox.Metadata.RuntimeHandler)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get sandbox runtime")
- }
- log.G(ctx).Debugf("Use OCI runtime %+v for sandbox %q and container %q", ociRuntime, sandboxID, id)
-
- spec, err := c.containerSpec(id, sandboxID, sandboxPid, sandbox.NetNSPath, containerName, config, sandboxConfig,
- &image.ImageSpec.Config, append(mounts, volumeMounts...), ociRuntime)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to generate container %q spec", id)
- }
-
- meta.ProcessLabel = spec.Process.SelinuxLabel
-
- // handle any KVM based runtime
- if err := modifyProcessLabel(ociRuntime.Type, spec); err != nil {
- return nil, err
- }
-
- if config.GetLinux().GetSecurityContext().GetPrivileged() {
- // If privileged don't set the SELinux label but still record it on the container so
- // the unused MCS label can be release later
- spec.Process.SelinuxLabel = ""
- }
- defer func() {
- if retErr != nil {
- selinux.ReleaseLabel(spec.Process.SelinuxLabel)
- }
- }()
-
- log.G(ctx).Debugf("Container %q spec: %#+v", id, spew.NewFormatter(spec))
-
- // Set snapshotter before any other options.
- opts := []containerd.NewContainerOpts{
- containerd.WithSnapshotter(c.config.ContainerdConfig.Snapshotter),
- // Prepare container rootfs. This is always writeable even if
- // the container wants a readonly rootfs since we want to give
- // the runtime (runc) a chance to modify (e.g. to create mount
- // points corresponding to spec.Mounts) before making the
- // rootfs readonly (requested by spec.Root.Readonly).
- customopts.WithNewSnapshot(id, containerdImage),
- }
- if len(volumeMounts) > 0 {
- mountMap := make(map[string]string)
- for _, v := range volumeMounts {
- mountMap[filepath.Clean(v.HostPath)] = v.ContainerPath
- }
- opts = append(opts, customopts.WithVolumes(mountMap))
- }
- meta.ImageRef = image.ID
- meta.StopSignal = image.ImageSpec.Config.StopSignal
-
- // Validate log paths and compose full container log path.
- if sandboxConfig.GetLogDirectory() != "" && config.GetLogPath() != "" {
- meta.LogPath = filepath.Join(sandboxConfig.GetLogDirectory(), config.GetLogPath())
- log.G(ctx).Debugf("Composed container full log path %q using sandbox log dir %q and container log path %q",
- meta.LogPath, sandboxConfig.GetLogDirectory(), config.GetLogPath())
- } else {
- log.G(ctx).Infof("Logging will be disabled due to empty log paths for sandbox (%q) or container (%q)",
- sandboxConfig.GetLogDirectory(), config.GetLogPath())
- }
-
- containerIO, err := cio.NewContainerIO(id,
- cio.WithNewFIFOs(volatileContainerRootDir, config.GetTty(), config.GetStdin()))
- if err != nil {
- return nil, errors.Wrap(err, "failed to create container io")
- }
- defer func() {
- if retErr != nil {
- if err := containerIO.Close(); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to close container io %q", id)
- }
- }
- }()
-
- specOpts, err := c.containerSpecOpts(config, &image.ImageSpec.Config)
- if err != nil {
- return nil, errors.Wrap(err, "")
- }
-
- containerLabels := buildLabels(config.Labels, containerKindContainer)
-
- runtimeOptions, err := getRuntimeOptions(sandboxInfo)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get runtime options")
- }
- opts = append(opts,
- containerd.WithSpec(spec, specOpts...),
- containerd.WithRuntime(sandboxInfo.Runtime.Name, runtimeOptions),
- containerd.WithContainerLabels(containerLabels),
- containerd.WithContainerExtension(containerMetadataExtension, &meta))
- var cntr containerd.Container
- if cntr, err = c.client.NewContainer(ctx, id, opts...); err != nil {
- return nil, errors.Wrap(err, "failed to create containerd container")
- }
- defer func() {
- if retErr != nil {
- deferCtx, deferCancel := ctrdutil.DeferContext()
- defer deferCancel()
- if err := cntr.Delete(deferCtx, containerd.WithSnapshotCleanup); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to delete containerd container %q", id)
- }
- }
- }()
-
- status := containerstore.Status{CreatedAt: time.Now().UnixNano()}
- container, err := containerstore.NewContainer(meta,
- containerstore.WithStatus(status, containerRootDir),
- containerstore.WithContainer(cntr),
- containerstore.WithContainerIO(containerIO),
- )
- if err != nil {
- return nil, errors.Wrapf(err, "failed to create internal container object for %q", id)
- }
- defer func() {
- if retErr != nil {
- // Cleanup container checkpoint on error.
- if err := container.Delete(); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to cleanup container checkpoint for %q", id)
- }
- }
- }()
-
- // Add container into container store.
- if err := c.containerStore.Add(container); err != nil {
- return nil, errors.Wrapf(err, "failed to add container %q into store", id)
- }
-
- return &runtime.CreateContainerResponse{ContainerId: id}, nil
-}
-
-// volumeMounts sets up image volumes for container. Rely on the removal of container
-// root directory to do cleanup. Note that image volume will be skipped, if there is criMounts
-// specified with the same destination.
-func (c *criService) volumeMounts(containerRootDir string, criMounts []*runtime.Mount, config *imagespec.ImageConfig) []*runtime.Mount {
- if len(config.Volumes) == 0 {
- return nil
- }
- var mounts []*runtime.Mount
- for dst := range config.Volumes {
- if isInCRIMounts(dst, criMounts) {
- // Skip the image volume, if there is CRI defined volume mapping.
- // TODO(random-liu): This should be handled by Kubelet in the future.
- // Kubelet should decide what to use for image volume, and also de-duplicate
- // the image volume and user mounts.
- continue
- }
- volumeID := util.GenerateID()
- src := filepath.Join(containerRootDir, "volumes", volumeID)
- // addOCIBindMounts will create these volumes.
- mounts = append(mounts, &runtime.Mount{
- ContainerPath: dst,
- HostPath: src,
- SelinuxRelabel: true,
- })
- }
- return mounts
-}
-
-// runtimeSpec returns a default runtime spec used in cri-containerd.
-func (c *criService) runtimeSpec(id string, baseSpecFile string, opts ...oci.SpecOpts) (*runtimespec.Spec, error) {
- // GenerateSpec needs namespace.
- ctx := ctrdutil.NamespacedContext()
- container := &containers.Container{ID: id}
-
- if baseSpecFile != "" {
- baseSpec, ok := c.baseOCISpecs[baseSpecFile]
- if !ok {
- return nil, errors.Errorf("can't find base OCI spec %q", baseSpecFile)
- }
-
- spec := oci.Spec{}
- if err := util.DeepCopy(&spec, &baseSpec); err != nil {
- return nil, errors.Wrap(err, "failed to clone OCI spec")
- }
-
- // Fix up cgroups path
- applyOpts := append([]oci.SpecOpts{oci.WithNamespacedCgroup()}, opts...)
-
- if err := oci.ApplyOpts(ctx, nil, container, &spec, applyOpts...); err != nil {
- return nil, errors.Wrap(err, "failed to apply OCI options")
- }
-
- return &spec, nil
- }
-
- spec, err := oci.GenerateSpec(ctx, nil, container, opts...)
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate spec")
- }
-
- return spec, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_create_unix.go b/vendor/github.com/containerd/cri/pkg/server/container_create_unix.go
deleted file mode 100644
index 28863cb0c..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_create_unix.go
+++ /dev/null
@@ -1,460 +0,0 @@
-// +build !windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "bufio"
- "io"
- "os"
- "strconv"
- "strings"
-
- "github.com/containerd/cgroups"
- "github.com/containerd/containerd/contrib/apparmor"
- "github.com/containerd/containerd/contrib/seccomp"
- "github.com/containerd/containerd/oci"
- imagespec "github.com/opencontainers/image-spec/specs-go/v1"
- runtimespec "github.com/opencontainers/runtime-spec/specs-go"
- selinux "github.com/opencontainers/selinux/go-selinux"
- "github.com/opencontainers/selinux/go-selinux/label"
- "github.com/pkg/errors"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/annotations"
- "github.com/containerd/cri/pkg/config"
- customopts "github.com/containerd/cri/pkg/containerd/opts"
-)
-
-const (
- // profileNamePrefix is the prefix for loading profiles on a localhost. Eg. AppArmor localhost/profileName.
- profileNamePrefix = "localhost/" // TODO (mikebrow): get localhost/ & runtime/default from CRI kubernetes/kubernetes#51747
- // runtimeDefault indicates that we should use or create a runtime default profile.
- runtimeDefault = "runtime/default"
- // dockerDefault indicates that we should use or create a docker default profile.
- dockerDefault = "docker/default"
- // appArmorDefaultProfileName is name to use when creating a default apparmor profile.
- appArmorDefaultProfileName = "cri-containerd.apparmor.d"
- // unconfinedProfile is a string indicating one should run a pod/containerd without a security profile
- unconfinedProfile = "unconfined"
- // seccompDefaultProfile is the default seccomp profile.
- seccompDefaultProfile = dockerDefault
-)
-
-// containerMounts sets up necessary container system file mounts
-// including /dev/shm, /etc/hosts and /etc/resolv.conf.
-func (c *criService) containerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
- var mounts []*runtime.Mount
- securityContext := config.GetLinux().GetSecurityContext()
- if !isInCRIMounts(etcHostname, config.GetMounts()) {
- // /etc/hostname is added since 1.1.6, 1.2.4 and 1.3.
- // For in-place upgrade, the old sandbox doesn't have the hostname file,
- // do not mount this in that case.
- // TODO(random-liu): Remove the check and always mount this when
- // containerd 1.1 and 1.2 are deprecated.
- hostpath := c.getSandboxHostname(sandboxID)
- if _, err := c.os.Stat(hostpath); err == nil {
- mounts = append(mounts, &runtime.Mount{
- ContainerPath: etcHostname,
- HostPath: hostpath,
- Readonly: securityContext.GetReadonlyRootfs(),
- })
- }
- }
-
- if !isInCRIMounts(etcHosts, config.GetMounts()) {
- mounts = append(mounts, &runtime.Mount{
- ContainerPath: etcHosts,
- HostPath: c.getSandboxHosts(sandboxID),
- Readonly: securityContext.GetReadonlyRootfs(),
- })
- }
-
- // Mount sandbox resolv.config.
- // TODO: Need to figure out whether we should always mount it as read-only
- if !isInCRIMounts(resolvConfPath, config.GetMounts()) {
- mounts = append(mounts, &runtime.Mount{
- ContainerPath: resolvConfPath,
- HostPath: c.getResolvPath(sandboxID),
- Readonly: securityContext.GetReadonlyRootfs(),
- })
- }
-
- if !isInCRIMounts(devShm, config.GetMounts()) {
- sandboxDevShm := c.getSandboxDevShm(sandboxID)
- if securityContext.GetNamespaceOptions().GetIpc() == runtime.NamespaceMode_NODE {
- sandboxDevShm = devShm
- }
- mounts = append(mounts, &runtime.Mount{
- ContainerPath: devShm,
- HostPath: sandboxDevShm,
- Readonly: false,
- })
- }
- return mounts
-}
-
-func (c *criService) containerSpec(id string, sandboxID string, sandboxPid uint32, netNSPath string, containerName string,
- config *runtime.ContainerConfig, sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig,
- extraMounts []*runtime.Mount, ociRuntime config.Runtime) (_ *runtimespec.Spec, retErr error) {
-
- specOpts := []oci.SpecOpts{
- customopts.WithoutRunMount,
- customopts.WithoutDefaultSecuritySettings,
- customopts.WithRelativeRoot(relativeRootfsPath),
- customopts.WithProcessArgs(config, imageConfig),
- oci.WithDefaultPathEnv,
- // this will be set based on the security context below
- oci.WithNewPrivileges,
- }
- if config.GetWorkingDir() != "" {
- specOpts = append(specOpts, oci.WithProcessCwd(config.GetWorkingDir()))
- } else if imageConfig.WorkingDir != "" {
- specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
- }
-
- if config.GetTty() {
- specOpts = append(specOpts, oci.WithTTY)
- }
-
- // Add HOSTNAME env.
- var (
- err error
- hostname = sandboxConfig.GetHostname()
- )
- if hostname == "" {
- if hostname, err = c.os.Hostname(); err != nil {
- return nil, err
- }
- }
- specOpts = append(specOpts, oci.WithEnv([]string{hostnameEnv + "=" + hostname}))
-
- // Apply envs from image config first, so that envs from container config
- // can override them.
- env := imageConfig.Env
- for _, e := range config.GetEnvs() {
- env = append(env, e.GetKey()+"="+e.GetValue())
- }
- specOpts = append(specOpts, oci.WithEnv(env))
-
- securityContext := config.GetLinux().GetSecurityContext()
- labelOptions, err := toLabel(securityContext.GetSelinuxOptions())
- if err != nil {
- return nil, err
- }
- if len(labelOptions) == 0 {
- // Use pod level SELinux config
- if sandbox, err := c.sandboxStore.Get(sandboxID); err == nil {
- labelOptions, err = selinux.DupSecOpt(sandbox.ProcessLabel)
- if err != nil {
- return nil, err
- }
- }
- }
-
- processLabel, mountLabel, err := label.InitLabels(labelOptions)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to init selinux options %+v", securityContext.GetSelinuxOptions())
- }
- defer func() {
- if retErr != nil {
- _ = label.ReleaseLabel(processLabel)
- }
- }()
-
- specOpts = append(specOpts, customopts.WithMounts(c.os, config, extraMounts, mountLabel))
-
- if !c.config.DisableProcMount {
- // Apply masked paths if specified.
- // If the container is privileged, this will be cleared later on.
- specOpts = append(specOpts, oci.WithMaskedPaths(securityContext.GetMaskedPaths()))
-
- // Apply readonly paths if specified.
- // If the container is privileged, this will be cleared later on.
- specOpts = append(specOpts, oci.WithReadonlyPaths(securityContext.GetReadonlyPaths()))
- }
-
- if securityContext.GetPrivileged() {
- if !sandboxConfig.GetLinux().GetSecurityContext().GetPrivileged() {
- return nil, errors.New("no privileged container allowed in sandbox")
- }
- specOpts = append(specOpts, oci.WithPrivileged)
- if !ociRuntime.PrivilegedWithoutHostDevices {
- specOpts = append(specOpts, oci.WithHostDevices, oci.WithAllDevicesAllowed)
- } else {
- // add requested devices by the config as host devices are not automatically added
- specOpts = append(specOpts, customopts.WithDevices(c.os, config), customopts.WithCapabilities(securityContext))
- }
- } else { // not privileged
- specOpts = append(specOpts, customopts.WithDevices(c.os, config), customopts.WithCapabilities(securityContext))
- }
-
- // Clear all ambient capabilities. The implication of non-root + caps
- // is not clearly defined in Kubernetes.
- // See https://github.com/kubernetes/kubernetes/issues/56374
- // Keep docker's behavior for now.
- specOpts = append(specOpts,
- customopts.WithoutAmbientCaps,
- customopts.WithSelinuxLabels(processLabel, mountLabel),
- )
-
- // TODO: Figure out whether we should set no new privilege for sandbox container by default
- if securityContext.GetNoNewPrivs() {
- specOpts = append(specOpts, oci.WithNoNewPrivileges)
- }
- // TODO(random-liu): [P1] Set selinux options (privileged or not).
- if securityContext.GetReadonlyRootfs() {
- specOpts = append(specOpts, oci.WithRootFSReadonly())
- }
-
- if c.config.DisableCgroup {
- specOpts = append(specOpts, customopts.WithDisabledCgroups)
- } else {
- specOpts = append(specOpts, customopts.WithResources(config.GetLinux().GetResources(), c.config.TolerateMissingHugetlbController, c.config.DisableHugetlbController))
- if sandboxConfig.GetLinux().GetCgroupParent() != "" {
- cgroupsPath := getCgroupsPath(sandboxConfig.GetLinux().GetCgroupParent(), id)
- specOpts = append(specOpts, oci.WithCgroup(cgroupsPath))
- }
- }
-
- supplementalGroups := securityContext.GetSupplementalGroups()
-
- for pKey, pValue := range getPassthroughAnnotations(sandboxConfig.Annotations,
- ociRuntime.PodAnnotations) {
- specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
- }
-
- for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
- ociRuntime.ContainerAnnotations) {
- specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
- }
-
- specOpts = append(specOpts,
- customopts.WithOOMScoreAdj(config, c.config.RestrictOOMScoreAdj),
- customopts.WithPodNamespaces(securityContext, sandboxPid),
- customopts.WithSupplementalGroups(supplementalGroups),
- customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer),
- customopts.WithAnnotation(annotations.SandboxID, sandboxID),
- customopts.WithAnnotation(annotations.ContainerName, containerName),
- )
- // cgroupns is used for hiding /sys/fs/cgroup from containers.
- // For compatibility, cgroupns is not used when running in cgroup v1 mode or in privileged.
- // https://github.com/containers/libpod/issues/4363
- // https://github.com/kubernetes/enhancements/blob/0e409b47497e398b369c281074485c8de129694f/keps/sig-node/20191118-cgroups-v2.md#cgroup-namespace
- if cgroups.Mode() == cgroups.Unified && !securityContext.GetPrivileged() {
- specOpts = append(specOpts, oci.WithLinuxNamespace(
- runtimespec.LinuxNamespace{
- Type: runtimespec.CgroupNamespace,
- }))
- }
- return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
-}
-
-func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
- var specOpts []oci.SpecOpts
- securityContext := config.GetLinux().GetSecurityContext()
- // Set container username. This could only be done by containerd, because it needs
- // access to the container rootfs. Pass user name to containerd, and let it overwrite
- // the spec for us.
- userstr, err := generateUserString(
- securityContext.GetRunAsUsername(),
- securityContext.GetRunAsUser(),
- securityContext.GetRunAsGroup())
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate user string")
- }
- if userstr == "" {
- // Lastly, since no user override was passed via CRI try to set via OCI
- // Image
- userstr = imageConfig.User
- }
- if userstr != "" {
- specOpts = append(specOpts, oci.WithUser(userstr))
- }
-
- if securityContext.GetRunAsUsername() != "" {
- userstr = securityContext.GetRunAsUsername()
- } else {
- // Even if RunAsUser is not set, we still call `GetValue` to get uid 0.
- // Because it is still useful to get additional gids for uid 0.
- userstr = strconv.FormatInt(securityContext.GetRunAsUser().GetValue(), 10)
- }
- specOpts = append(specOpts, customopts.WithAdditionalGIDs(userstr))
-
- apparmorSpecOpts, err := generateApparmorSpecOpts(
- securityContext.GetApparmorProfile(),
- securityContext.GetPrivileged(),
- c.apparmorEnabled())
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate apparmor spec opts")
- }
- if apparmorSpecOpts != nil {
- specOpts = append(specOpts, apparmorSpecOpts)
- }
-
- seccompSpecOpts, err := c.generateSeccompSpecOpts(
- securityContext.GetSeccompProfilePath(),
- securityContext.GetPrivileged(),
- c.seccompEnabled())
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate seccomp spec opts")
- }
- if seccompSpecOpts != nil {
- specOpts = append(specOpts, seccompSpecOpts)
- }
- return specOpts, nil
-}
-
-// generateSeccompSpecOpts generates containerd SpecOpts for seccomp.
-func (c *criService) generateSeccompSpecOpts(seccompProf string, privileged, seccompEnabled bool) (oci.SpecOpts, error) {
- if privileged {
- // Do not set seccomp profile when container is privileged
- return nil, nil
- }
- if seccompProf == "" {
- seccompProf = c.config.UnsetSeccompProfile
- }
- // Set seccomp profile
- if seccompProf == runtimeDefault || seccompProf == dockerDefault {
- // use correct default profile (Eg. if not configured otherwise, the default is docker/default)
- seccompProf = seccompDefaultProfile
- }
- if !seccompEnabled {
- if seccompProf != "" && seccompProf != unconfinedProfile {
- return nil, errors.New("seccomp is not supported")
- }
- return nil, nil
- }
- switch seccompProf {
- case "", unconfinedProfile:
- // Do not set seccomp profile.
- return nil, nil
- case dockerDefault:
- // Note: WithDefaultProfile specOpts must be added after capabilities
- return seccomp.WithDefaultProfile(), nil
- default:
- // Require and Trim default profile name prefix
- if !strings.HasPrefix(seccompProf, profileNamePrefix) {
- return nil, errors.Errorf("invalid seccomp profile %q", seccompProf)
- }
- return seccomp.WithProfile(strings.TrimPrefix(seccompProf, profileNamePrefix)), nil
- }
-}
-
-// generateApparmorSpecOpts generates containerd SpecOpts for apparmor.
-func generateApparmorSpecOpts(apparmorProf string, privileged, apparmorEnabled bool) (oci.SpecOpts, error) {
- if !apparmorEnabled {
- // Should fail loudly if user try to specify apparmor profile
- // but we don't support it.
- if apparmorProf != "" && apparmorProf != unconfinedProfile {
- return nil, errors.New("apparmor is not supported")
- }
- return nil, nil
- }
- switch apparmorProf {
- // Based on kubernetes#51746, default apparmor profile should be applied
- // for when apparmor is not specified.
- case runtimeDefault, "":
- if privileged {
- // Do not set apparmor profile when container is privileged
- return nil, nil
- }
- // TODO (mikebrow): delete created apparmor default profile
- return apparmor.WithDefaultProfile(appArmorDefaultProfileName), nil
- case unconfinedProfile:
- return nil, nil
- default:
- // Require and Trim default profile name prefix
- if !strings.HasPrefix(apparmorProf, profileNamePrefix) {
- return nil, errors.Errorf("invalid apparmor profile %q", apparmorProf)
- }
- appArmorProfile := strings.TrimPrefix(apparmorProf, profileNamePrefix)
- if profileExists, err := appArmorProfileExists(appArmorProfile); !profileExists {
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate apparmor spec opts")
- }
- return nil, errors.Errorf("apparmor profile not found %s", appArmorProfile)
- }
- return apparmor.WithProfile(appArmorProfile), nil
- }
-}
-
-// appArmorProfileExists scans apparmor/profiles for the requested profile
-func appArmorProfileExists(profile string) (bool, error) {
- if profile == "" {
- return false, errors.New("nil apparmor profile is not supported")
- }
- profiles, err := os.Open("/sys/kernel/security/apparmor/profiles")
- if err != nil {
- return false, err
- }
- defer profiles.Close()
-
- rbuff := bufio.NewReader(profiles)
- for {
- line, err := rbuff.ReadString('\n')
- switch err {
- case nil:
- if strings.HasPrefix(line, profile+" (") {
- return true, nil
- }
- case io.EOF:
- return false, nil
- default:
- return false, err
- }
- }
-}
-
-// generateUserString generates valid user string based on OCI Image Spec
-// v1.0.0.
-//
-// CRI defines that the following combinations are valid:
-//
-// (none) -> ""
-// username -> username
-// username, uid -> username
-// username, uid, gid -> username:gid
-// username, gid -> username:gid
-// uid -> uid
-// uid, gid -> uid:gid
-// gid -> error
-//
-// TODO(random-liu): Add group name support in CRI.
-func generateUserString(username string, uid, gid *runtime.Int64Value) (string, error) {
- var userstr, groupstr string
- if uid != nil {
- userstr = strconv.FormatInt(uid.GetValue(), 10)
- }
- if username != "" {
- userstr = username
- }
- if gid != nil {
- groupstr = strconv.FormatInt(gid.GetValue(), 10)
- }
- if userstr == "" {
- if groupstr != "" {
- return "", errors.Errorf("user group %q is specified without user", groupstr)
- }
- return "", nil
- }
- if groupstr != "" {
- userstr = userstr + ":" + groupstr
- }
- return userstr, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_create_windows.go b/vendor/github.com/containerd/cri/pkg/server/container_create_windows.go
deleted file mode 100644
index 86a08d89e..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_create_windows.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// +build windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/containerd/containerd/oci"
- imagespec "github.com/opencontainers/image-spec/specs-go/v1"
- runtimespec "github.com/opencontainers/runtime-spec/specs-go"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/annotations"
- "github.com/containerd/cri/pkg/config"
- customopts "github.com/containerd/cri/pkg/containerd/opts"
-)
-
-// No container mounts for windows.
-func (c *criService) containerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
- return nil
-}
-
-func (c *criService) containerSpec(id string, sandboxID string, sandboxPid uint32, netNSPath string, containerName string,
- config *runtime.ContainerConfig, sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig,
- extraMounts []*runtime.Mount, ociRuntime config.Runtime) (*runtimespec.Spec, error) {
- specOpts := []oci.SpecOpts{
- customopts.WithProcessArgs(config, imageConfig),
- }
- if config.GetWorkingDir() != "" {
- specOpts = append(specOpts, oci.WithProcessCwd(config.GetWorkingDir()))
- } else if imageConfig.WorkingDir != "" {
- specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
- }
-
- if config.GetTty() {
- specOpts = append(specOpts, oci.WithTTY)
- }
-
- // Apply envs from image config first, so that envs from container config
- // can override them.
- env := imageConfig.Env
- for _, e := range config.GetEnvs() {
- env = append(env, e.GetKey()+"="+e.GetValue())
- }
- specOpts = append(specOpts, oci.WithEnv(env))
-
- specOpts = append(specOpts,
- // Clear the root location since hcsshim expects it.
- // NOTE: readonly rootfs doesn't work on windows.
- customopts.WithoutRoot,
- customopts.WithWindowsNetworkNamespace(netNSPath),
- oci.WithHostname(sandboxConfig.GetHostname()),
- )
-
- specOpts = append(specOpts, customopts.WithWindowsMounts(c.os, config, extraMounts))
-
- // Start with the image config user and override below if RunAsUsername is not "".
- username := imageConfig.User
-
- windowsConfig := config.GetWindows()
- if windowsConfig != nil {
- specOpts = append(specOpts, customopts.WithWindowsResources(windowsConfig.GetResources()))
- securityCtx := windowsConfig.GetSecurityContext()
- if securityCtx != nil {
- runAsUser := securityCtx.GetRunAsUsername()
- if runAsUser != "" {
- username = runAsUser
- }
- cs := securityCtx.GetCredentialSpec()
- if cs != "" {
- specOpts = append(specOpts, customopts.WithWindowsCredentialSpec(cs))
- }
- }
- }
-
- // There really isn't a good Windows way to verify that the username is available in the
- // image as early as here like there is for Linux. Later on in the stack hcsshim
- // will handle the behavior of erroring out if the user isn't available in the image
- // when trying to run the init process.
- specOpts = append(specOpts, oci.WithUser(username))
-
- for pKey, pValue := range getPassthroughAnnotations(sandboxConfig.Annotations,
- ociRuntime.PodAnnotations) {
- specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
- }
-
- for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
- ociRuntime.ContainerAnnotations) {
- specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
- }
-
- specOpts = append(specOpts,
- customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer),
- customopts.WithAnnotation(annotations.SandboxID, sandboxID),
- customopts.WithAnnotation(annotations.ContainerName, containerName),
- )
- return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
-}
-
-// No extra spec options needed for windows.
-func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
- return nil, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_exec.go b/vendor/github.com/containerd/cri/pkg/server/container_exec.go
deleted file mode 100644
index ae5498ff7..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_exec.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// Exec prepares a streaming endpoint to execute a command in the container, and returns the address.
-func (c *criService) Exec(ctx context.Context, r *runtime.ExecRequest) (*runtime.ExecResponse, error) {
- cntr, err := c.containerStore.Get(r.GetContainerId())
- if err != nil {
- return nil, errors.Wrapf(err, "failed to find container %q in store", r.GetContainerId())
- }
- state := cntr.Status.Get().State()
- if state != runtime.ContainerState_CONTAINER_RUNNING {
- return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
- }
- return c.streamServer.GetExec(r)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_execsync.go b/vendor/github.com/containerd/cri/pkg/server/container_execsync.go
deleted file mode 100644
index 1c019f651..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_execsync.go
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "bytes"
- "io"
- "syscall"
- "time"
-
- "github.com/containerd/containerd"
- containerdio "github.com/containerd/containerd/cio"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- "github.com/containerd/containerd/oci"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- "k8s.io/client-go/tools/remotecommand"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- cioutil "github.com/containerd/cri/pkg/ioutil"
- cio "github.com/containerd/cri/pkg/server/io"
- "github.com/containerd/cri/pkg/util"
-)
-
-// ExecSync executes a command in the container, and returns the stdout output.
-// If command exits with a non-zero exit code, an error is returned.
-func (c *criService) ExecSync(ctx context.Context, r *runtime.ExecSyncRequest) (*runtime.ExecSyncResponse, error) {
- var stdout, stderr bytes.Buffer
- exitCode, err := c.execInContainer(ctx, r.GetContainerId(), execOptions{
- cmd: r.GetCmd(),
- stdout: cioutil.NewNopWriteCloser(&stdout),
- stderr: cioutil.NewNopWriteCloser(&stderr),
- timeout: time.Duration(r.GetTimeout()) * time.Second,
- })
- if err != nil {
- return nil, errors.Wrap(err, "failed to exec in container")
- }
-
- return &runtime.ExecSyncResponse{
- Stdout: stdout.Bytes(),
- Stderr: stderr.Bytes(),
- ExitCode: int32(*exitCode),
- }, nil
-}
-
-// execOptions specifies how to execute command in container.
-type execOptions struct {
- cmd []string
- stdin io.Reader
- stdout io.WriteCloser
- stderr io.WriteCloser
- tty bool
- resize <-chan remotecommand.TerminalSize
- timeout time.Duration
-}
-
-func (c *criService) execInternal(ctx context.Context, container containerd.Container, id string, opts execOptions) (*uint32, error) {
- // Cancel the context before returning to ensure goroutines are stopped.
- // This is important, because if `Start` returns error, `Wait` will hang
- // forever unless we cancel the context.
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
-
- spec, err := container.Spec(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get container spec")
- }
- task, err := container.Task(ctx, nil)
- if err != nil {
- return nil, errors.Wrap(err, "failed to load task")
- }
- pspec := spec.Process
-
- pspec.Terminal = opts.tty
- if opts.tty {
- if err := oci.WithEnv([]string{"TERM=xterm"})(ctx, nil, nil, spec); err != nil {
- return nil, errors.Wrap(err, "add TERM env var to spec")
- }
- }
-
- pspec.Args = opts.cmd
-
- if opts.stdout == nil {
- opts.stdout = cio.NewDiscardLogger()
- }
- if opts.stderr == nil {
- opts.stderr = cio.NewDiscardLogger()
- }
- execID := util.GenerateID()
- log.G(ctx).Debugf("Generated exec id %q for container %q", execID, id)
- volatileRootDir := c.getVolatileContainerRootDir(id)
- var execIO *cio.ExecIO
- process, err := task.Exec(ctx, execID, pspec,
- func(id string) (containerdio.IO, error) {
- var err error
- execIO, err = cio.NewExecIO(id, volatileRootDir, opts.tty, opts.stdin != nil)
- return execIO, err
- },
- )
- if err != nil {
- return nil, errors.Wrapf(err, "failed to create exec %q", execID)
- }
- defer func() {
- deferCtx, deferCancel := ctrdutil.DeferContext()
- defer deferCancel()
- if _, err := process.Delete(deferCtx, containerd.WithProcessKill); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id)
- }
- }()
-
- exitCh, err := process.Wait(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to wait for process %q", execID)
- }
- if err := process.Start(ctx); err != nil {
- return nil, errors.Wrapf(err, "failed to start exec %q", execID)
- }
-
- handleResizing(ctx, opts.resize, func(size remotecommand.TerminalSize) {
- if err := process.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to resize process %q console for container %q", execID, id)
- }
- })
-
- attachDone := execIO.Attach(cio.AttachOptions{
- Stdin: opts.stdin,
- Stdout: opts.stdout,
- Stderr: opts.stderr,
- Tty: opts.tty,
- StdinOnce: true,
- CloseStdin: func() error {
- return process.CloseIO(ctx, containerd.WithStdinCloser)
- },
- })
-
- execCtx := ctx
- if opts.timeout > 0 {
- var execCtxCancel context.CancelFunc
- execCtx, execCtxCancel = context.WithTimeout(ctx, opts.timeout)
- defer execCtxCancel()
- }
-
- select {
- case <-execCtx.Done():
- // Ignore the not found error because the process may exit itself before killing.
- if err := process.Kill(ctx, syscall.SIGKILL); err != nil && !errdefs.IsNotFound(err) {
- return nil, errors.Wrapf(err, "failed to kill exec %q", execID)
- }
- // Wait for the process to be killed.
- exitRes := <-exitCh
- log.G(ctx).Infof("Timeout received while waiting for exec process kill %q code %d and error %v",
- execID, exitRes.ExitCode(), exitRes.Error())
- <-attachDone
- log.G(ctx).Debugf("Stream pipe for exec process %q done", execID)
- return nil, errors.Wrapf(execCtx.Err(), "timeout %v exceeded", opts.timeout)
- case exitRes := <-exitCh:
- code, _, err := exitRes.Result()
- log.G(ctx).Infof("Exec process %q exits with exit code %d and error %v", execID, code, err)
- if err != nil {
- return nil, errors.Wrapf(err, "failed while waiting for exec %q", execID)
- }
- <-attachDone
- log.G(ctx).Debugf("Stream pipe for exec process %q done", execID)
- return &code, nil
- }
-}
-
-// execInContainer executes a command inside the container synchronously, and
-// redirects stdio stream properly.
-// This function only returns when the exec process exits, this means that:
-// 1) As long as the exec process is running, the goroutine in the cri plugin
-// will be running and wait for the exit code;
-// 2) `kubectl exec -it` will hang until the exec process exits, even after io
-// is detached. This is different from dockershim, which leaves the exec process
-// running in background after io is detached.
-// https://github.com/kubernetes/kubernetes/blob/v1.15.0/pkg/kubelet/dockershim/exec.go#L127
-// For example, if the `kubectl exec -it` process is killed, IO will be closed. In
-// this case, the CRI plugin will still have a goroutine waiting for the exec process
-// to exit and log the exit code, but dockershim won't.
-func (c *criService) execInContainer(ctx context.Context, id string, opts execOptions) (*uint32, error) {
- // Get container from our container store.
- cntr, err := c.containerStore.Get(id)
-
- if err != nil {
- return nil, errors.Wrapf(err, "failed to find container %q in store", id)
- }
- id = cntr.ID
-
- state := cntr.Status.Get().State()
- if state != runtime.ContainerState_CONTAINER_RUNNING {
- return nil, errors.Errorf("container is in %s state", criContainerStateToString(state))
- }
-
- return c.execInternal(ctx, cntr.Container, id, opts)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_list.go b/vendor/github.com/containerd/cri/pkg/server/container_list.go
deleted file mode 100644
index c9e88d13d..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_list.go
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "golang.org/x/net/context"
-
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- containerstore "github.com/containerd/cri/pkg/store/container"
-)
-
-// ListContainers lists all containers matching the filter.
-func (c *criService) ListContainers(ctx context.Context, r *runtime.ListContainersRequest) (*runtime.ListContainersResponse, error) {
- // List all containers from store.
- containersInStore := c.containerStore.List()
-
- var containers []*runtime.Container
- for _, container := range containersInStore {
- containers = append(containers, toCRIContainer(container))
- }
-
- containers = c.filterCRIContainers(containers, r.GetFilter())
- return &runtime.ListContainersResponse{Containers: containers}, nil
-}
-
-// toCRIContainer converts internal container object into CRI container.
-func toCRIContainer(container containerstore.Container) *runtime.Container {
- status := container.Status.Get()
- return &runtime.Container{
- Id: container.ID,
- PodSandboxId: container.SandboxID,
- Metadata: container.Config.GetMetadata(),
- Image: container.Config.GetImage(),
- ImageRef: container.ImageRef,
- State: status.State(),
- CreatedAt: status.CreatedAt,
- Labels: container.Config.GetLabels(),
- Annotations: container.Config.GetAnnotations(),
- }
-}
-
-func (c *criService) normalizeContainerFilter(filter *runtime.ContainerFilter) {
- if cntr, err := c.containerStore.Get(filter.GetId()); err == nil {
- filter.Id = cntr.ID
- }
- if sb, err := c.sandboxStore.Get(filter.GetPodSandboxId()); err == nil {
- filter.PodSandboxId = sb.ID
- }
-}
-
-// filterCRIContainers filters CRIContainers.
-func (c *criService) filterCRIContainers(containers []*runtime.Container, filter *runtime.ContainerFilter) []*runtime.Container {
- if filter == nil {
- return containers
- }
-
- // The containerd cri plugin supports short ids so long as there is only one
- // match. So we do a lookup against the store here if a pod id has been
- // included in the filter.
- sb := filter.GetPodSandboxId()
- if sb != "" {
- sandbox, err := c.sandboxStore.Get(sb)
- if err == nil {
- sb = sandbox.ID
- }
- }
-
- c.normalizeContainerFilter(filter)
- filtered := []*runtime.Container{}
- for _, cntr := range containers {
- if filter.GetId() != "" && filter.GetId() != cntr.Id {
- continue
- }
- if sb != "" && sb != cntr.PodSandboxId {
- continue
- }
- if filter.GetState() != nil && filter.GetState().GetState() != cntr.State {
- continue
- }
- if filter.GetLabelSelector() != nil {
- match := true
- for k, v := range filter.GetLabelSelector() {
- got, ok := cntr.Labels[k]
- if !ok || got != v {
- match = false
- break
- }
- }
- if !match {
- continue
- }
- }
- filtered = append(filtered, cntr)
- }
-
- return filtered
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_log_reopen.go b/vendor/github.com/containerd/cri/pkg/server/container_log_reopen.go
deleted file mode 100644
index b15bb6238..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_log_reopen.go
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/pkg/errors"
- "golang.org/x/net/context"
-
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// ReopenContainerLog asks the cri plugin to reopen the stdout/stderr log file for the container.
-// This is often called after the log file has been rotated.
-func (c *criService) ReopenContainerLog(ctx context.Context, r *runtime.ReopenContainerLogRequest) (*runtime.ReopenContainerLogResponse, error) {
- container, err := c.containerStore.Get(r.GetContainerId())
- if err != nil {
- return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
- }
-
- if container.Status.Get().State() != runtime.ContainerState_CONTAINER_RUNNING {
- return nil, errors.New("container is not running")
- }
-
- // Create new container logger and replace the existing ones.
- stdoutWC, stderrWC, err := c.createContainerLoggers(container.LogPath, container.Config.GetTty())
- if err != nil {
- return nil, err
- }
- oldStdoutWC, oldStderrWC := container.IO.AddOutput("log", stdoutWC, stderrWC)
- if oldStdoutWC != nil {
- oldStdoutWC.Close()
- }
- if oldStderrWC != nil {
- oldStderrWC.Close()
- }
- return &runtime.ReopenContainerLogResponse{}, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_remove.go b/vendor/github.com/containerd/cri/pkg/server/container_remove.go
deleted file mode 100644
index 6426635dd..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_remove.go
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/store"
- containerstore "github.com/containerd/cri/pkg/store/container"
-)
-
-// RemoveContainer removes the container.
-func (c *criService) RemoveContainer(ctx context.Context, r *runtime.RemoveContainerRequest) (_ *runtime.RemoveContainerResponse, retErr error) {
- container, err := c.containerStore.Get(r.GetContainerId())
- if err != nil {
- if err != store.ErrNotExist {
- return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
- }
- // Do not return error if container metadata doesn't exist.
- log.G(ctx).Tracef("RemoveContainer called for container %q that does not exist", r.GetContainerId())
- return &runtime.RemoveContainerResponse{}, nil
- }
- id := container.ID
-
- // Forcibly stop the containers if they are in running or unknown state
- state := container.Status.Get().State()
- if state == runtime.ContainerState_CONTAINER_RUNNING ||
- state == runtime.ContainerState_CONTAINER_UNKNOWN {
- logrus.Infof("Forcibly stopping container %q", id)
- if err := c.stopContainer(ctx, container, 0); err != nil {
- return nil, errors.Wrapf(err, "failed to forcibly stop container %q", id)
- }
-
- }
-
- // Set removing state to prevent other start/remove operations against this container
- // while it's being removed.
- if err := setContainerRemoving(container); err != nil {
- return nil, errors.Wrapf(err, "failed to set removing state for container %q", id)
- }
- defer func() {
- if retErr != nil {
- // Reset removing if remove failed.
- if err := resetContainerRemoving(container); err != nil {
- log.G(ctx).WithError(err).Errorf("failed to reset removing state for container %q", id)
- }
- }
- }()
-
- // NOTE(random-liu): Docker set container to "Dead" state when start removing the
- // container so as to avoid start/restart the container again. However, for current
- // kubelet implementation, we'll never start a container once we decide to remove it,
- // so we don't need the "Dead" state for now.
-
- // Delete containerd container.
- if err := container.Container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
- if !errdefs.IsNotFound(err) {
- return nil, errors.Wrapf(err, "failed to delete containerd container %q", id)
- }
- log.G(ctx).Tracef("Remove called for containerd container %q that does not exist", id)
- }
-
- // Delete container checkpoint.
- if err := container.Delete(); err != nil {
- return nil, errors.Wrapf(err, "failed to delete container checkpoint for %q", id)
- }
-
- containerRootDir := c.getContainerRootDir(id)
- if err := ensureRemoveAll(ctx, containerRootDir); err != nil {
- return nil, errors.Wrapf(err, "failed to remove container root directory %q",
- containerRootDir)
- }
- volatileContainerRootDir := c.getVolatileContainerRootDir(id)
- if err := ensureRemoveAll(ctx, volatileContainerRootDir); err != nil {
- return nil, errors.Wrapf(err, "failed to remove volatile container root directory %q",
- volatileContainerRootDir)
- }
-
- c.containerStore.Delete(id)
-
- c.containerNameIndex.ReleaseByKey(id)
-
- return &runtime.RemoveContainerResponse{}, nil
-}
-
-// setContainerRemoving sets the container into removing state. In removing state, the
-// container will not be started or removed again.
-func setContainerRemoving(container containerstore.Container) error {
- return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
- // Do not remove container if it's still running or unknown.
- if status.State() == runtime.ContainerState_CONTAINER_RUNNING {
- return status, errors.New("container is still running, to stop first")
- }
- if status.State() == runtime.ContainerState_CONTAINER_UNKNOWN {
- return status, errors.New("container state is unknown, to stop first")
- }
- if status.Starting {
- return status, errors.New("container is in starting state, can't be removed")
- }
- if status.Removing {
- return status, errors.New("container is already in removing state")
- }
- status.Removing = true
- return status, nil
- })
-}
-
-// resetContainerRemoving resets the container removing state on remove failure. So
-// that we could remove the container again.
-func resetContainerRemoving(container containerstore.Container) error {
- return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
- status.Removing = false
- return status, nil
- })
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_start.go b/vendor/github.com/containerd/cri/pkg/server/container_start.go
deleted file mode 100644
index d00eb3d8e..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_start.go
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "io"
- "time"
-
- "github.com/containerd/containerd"
- containerdio "github.com/containerd/containerd/cio"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- "github.com/containerd/nri"
- v1 "github.com/containerd/nri/types/v1"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- cioutil "github.com/containerd/cri/pkg/ioutil"
- cio "github.com/containerd/cri/pkg/server/io"
- containerstore "github.com/containerd/cri/pkg/store/container"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-// StartContainer starts the container.
-func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (retRes *runtime.StartContainerResponse, retErr error) {
- cntr, err := c.containerStore.Get(r.GetContainerId())
- if err != nil {
- return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
- }
-
- id := cntr.ID
- meta := cntr.Metadata
- container := cntr.Container
- config := meta.Config
-
- // Set starting state to prevent other start/remove operations against this container
- // while it's being started.
- if err := setContainerStarting(cntr); err != nil {
- return nil, errors.Wrapf(err, "failed to set starting state for container %q", id)
- }
- defer func() {
- if retErr != nil {
- // Set container to exited if fail to start.
- if err := cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
- status.Pid = 0
- status.FinishedAt = time.Now().UnixNano()
- status.ExitCode = errorStartExitCode
- status.Reason = errorStartReason
- status.Message = retErr.Error()
- return status, nil
- }); err != nil {
- log.G(ctx).WithError(err).Errorf("failed to set start failure state for container %q", id)
- }
- }
- if err := resetContainerStarting(cntr); err != nil {
- log.G(ctx).WithError(err).Errorf("failed to reset starting state for container %q", id)
- }
- }()
-
- // Get sandbox config from sandbox store.
- sandbox, err := c.sandboxStore.Get(meta.SandboxID)
- if err != nil {
- return nil, errors.Wrapf(err, "sandbox %q not found", meta.SandboxID)
- }
- sandboxID := meta.SandboxID
- if sandbox.Status.Get().State != sandboxstore.StateReady {
- return nil, errors.Errorf("sandbox container %q is not running", sandboxID)
- }
-
- ioCreation := func(id string) (_ containerdio.IO, err error) {
- stdoutWC, stderrWC, err := c.createContainerLoggers(meta.LogPath, config.GetTty())
- if err != nil {
- return nil, errors.Wrap(err, "failed to create container loggers")
- }
- cntr.IO.AddOutput("log", stdoutWC, stderrWC)
- cntr.IO.Pipe()
- return cntr.IO, nil
- }
-
- ctrInfo, err := container.Info(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get container info")
- }
-
- taskOpts := c.taskOpts(ctrInfo.Runtime.Name)
- task, err := container.NewTask(ctx, ioCreation, taskOpts...)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create containerd task")
- }
- defer func() {
- if retErr != nil {
- deferCtx, deferCancel := ctrdutil.DeferContext()
- defer deferCancel()
- // It's possible that task is deleted by event monitor.
- if _, err := task.Delete(deferCtx, WithNRISandboxDelete(sandboxID), containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
- log.G(ctx).WithError(err).Errorf("Failed to delete containerd task %q", id)
- }
- }
- }()
-
- // wait is a long running background request, no timeout needed.
- exitCh, err := task.Wait(ctrdutil.NamespacedContext())
- if err != nil {
- return nil, errors.Wrap(err, "failed to wait for containerd task")
- }
- nric, err := nri.New()
- if err != nil {
- log.G(ctx).WithError(err).Error("unable to create nri client")
- }
- if nric != nil {
- nriSB := &nri.Sandbox{
- ID: sandboxID,
- }
- if _, err := nric.InvokeWithSandbox(ctx, task, v1.Create, nriSB); err != nil {
- return nil, errors.Wrap(err, "nri invoke")
- }
- }
-
- // Start containerd task.
- if err := task.Start(ctx); err != nil {
- return nil, errors.Wrapf(err, "failed to start containerd task %q", id)
- }
-
- // Update container start timestamp.
- if err := cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
- status.Pid = task.Pid()
- status.StartedAt = time.Now().UnixNano()
- return status, nil
- }); err != nil {
- return nil, errors.Wrapf(err, "failed to update container %q state", id)
- }
-
- // start the monitor after updating container state, this ensures that
- // event monitor receives the TaskExit event and update container state
- // after this.
- c.eventMonitor.startExitMonitor(context.Background(), id, task.Pid(), exitCh)
-
- return &runtime.StartContainerResponse{}, nil
-}
-
-// setContainerStarting sets the container into starting state. In starting state, the
-// container will not be removed or started again.
-func setContainerStarting(container containerstore.Container) error {
- return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
- // Return error if container is not in created state.
- if status.State() != runtime.ContainerState_CONTAINER_CREATED {
- return status, errors.Errorf("container is in %s state", criContainerStateToString(status.State()))
- }
- // Do not start the container when there is a removal in progress.
- if status.Removing {
- return status, errors.New("container is in removing state, can't be started")
- }
- if status.Starting {
- return status, errors.New("container is already in starting state")
- }
- status.Starting = true
- return status, nil
- })
-}
-
-// resetContainerStarting resets the container starting state on start failure. So
-// that we could remove the container later.
-func resetContainerStarting(container containerstore.Container) error {
- return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
- status.Starting = false
- return status, nil
- })
-}
-
-// createContainerLoggers creates container loggers and return write closer for stdout and stderr.
-func (c *criService) createContainerLoggers(logPath string, tty bool) (stdout io.WriteCloser, stderr io.WriteCloser, err error) {
- if logPath != "" {
- // Only generate container log when log path is specified.
- f, err := openLogFile(logPath)
- if err != nil {
- return nil, nil, errors.Wrap(err, "failed to create and open log file")
- }
- defer func() {
- if err != nil {
- f.Close()
- }
- }()
- var stdoutCh, stderrCh <-chan struct{}
- wc := cioutil.NewSerialWriteCloser(f)
- stdout, stdoutCh = cio.NewCRILogger(logPath, wc, cio.Stdout, c.config.MaxContainerLogLineSize)
- // Only redirect stderr when there is no tty.
- if !tty {
- stderr, stderrCh = cio.NewCRILogger(logPath, wc, cio.Stderr, c.config.MaxContainerLogLineSize)
- }
- go func() {
- if stdoutCh != nil {
- <-stdoutCh
- }
- if stderrCh != nil {
- <-stderrCh
- }
- logrus.Debugf("Finish redirecting log file %q, closing it", logPath)
- f.Close()
- }()
- } else {
- stdout = cio.NewDiscardLogger()
- stderr = cio.NewDiscardLogger()
- }
- return
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_stats.go b/vendor/github.com/containerd/cri/pkg/server/container_stats.go
deleted file mode 100644
index 22607cdd8..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_stats.go
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- tasks "github.com/containerd/containerd/api/services/tasks/v1"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// ContainerStats returns stats of the container. If the container does not
-// exist, the call returns an error.
-func (c *criService) ContainerStats(ctx context.Context, in *runtime.ContainerStatsRequest) (*runtime.ContainerStatsResponse, error) {
- cntr, err := c.containerStore.Get(in.GetContainerId())
- if err != nil {
- return nil, errors.Wrap(err, "failed to find container")
- }
- request := &tasks.MetricsRequest{Filters: []string{"id==" + cntr.ID}}
- resp, err := c.client.TaskService().Metrics(ctx, request)
- if err != nil {
- return nil, errors.Wrap(err, "failed to fetch metrics for task")
- }
- if len(resp.Metrics) != 1 {
- return nil, errors.Errorf("unexpected metrics response: %+v", resp.Metrics)
- }
-
- cs, err := c.containerMetrics(cntr.Metadata, resp.Metrics[0])
- if err != nil {
- return nil, errors.Wrap(err, "failed to decode container metrics")
- }
- return &runtime.ContainerStatsResponse{Stats: cs}, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_stats_list.go b/vendor/github.com/containerd/cri/pkg/server/container_stats_list.go
deleted file mode 100644
index 0a9be8741..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_stats_list.go
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- tasks "github.com/containerd/containerd/api/services/tasks/v1"
- "github.com/containerd/containerd/api/types"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- containerstore "github.com/containerd/cri/pkg/store/container"
-)
-
-// ListContainerStats returns stats of all running containers.
-func (c *criService) ListContainerStats(
- ctx context.Context,
- in *runtime.ListContainerStatsRequest,
-) (*runtime.ListContainerStatsResponse, error) {
- request, containers, err := c.buildTaskMetricsRequest(in)
- if err != nil {
- return nil, errors.Wrap(err, "failed to build metrics request")
- }
- resp, err := c.client.TaskService().Metrics(ctx, &request)
- if err != nil {
- return nil, errors.Wrap(err, "failed to fetch metrics for tasks")
- }
- criStats, err := c.toCRIContainerStats(resp.Metrics, containers)
- if err != nil {
- return nil, errors.Wrap(err, "failed to convert to cri containerd stats format")
- }
- return criStats, nil
-}
-
-func (c *criService) toCRIContainerStats(
- stats []*types.Metric,
- containers []containerstore.Container,
-) (*runtime.ListContainerStatsResponse, error) {
- statsMap := make(map[string]*types.Metric)
- for _, stat := range stats {
- statsMap[stat.ID] = stat
- }
- containerStats := new(runtime.ListContainerStatsResponse)
- for _, cntr := range containers {
- cs, err := c.containerMetrics(cntr.Metadata, statsMap[cntr.ID])
- if err != nil {
- return nil, errors.Wrapf(err, "failed to decode container metrics for %q", cntr.ID)
- }
- containerStats.Stats = append(containerStats.Stats, cs)
- }
- return containerStats, nil
-}
-
-func (c *criService) normalizeContainerStatsFilter(filter *runtime.ContainerStatsFilter) {
- if cntr, err := c.containerStore.Get(filter.GetId()); err == nil {
- filter.Id = cntr.ID
- }
- if sb, err := c.sandboxStore.Get(filter.GetPodSandboxId()); err == nil {
- filter.PodSandboxId = sb.ID
- }
-}
-
-// buildTaskMetricsRequest constructs a tasks.MetricsRequest based on
-// the information in the stats request and the containerStore
-func (c *criService) buildTaskMetricsRequest(
- r *runtime.ListContainerStatsRequest,
-) (tasks.MetricsRequest, []containerstore.Container, error) {
- var req tasks.MetricsRequest
- if r.GetFilter() == nil {
- return req, nil, nil
- }
- c.normalizeContainerStatsFilter(r.GetFilter())
- var containers []containerstore.Container
- for _, cntr := range c.containerStore.List() {
- if r.GetFilter().GetId() != "" && cntr.ID != r.GetFilter().GetId() {
- continue
- }
- if r.GetFilter().GetPodSandboxId() != "" && cntr.SandboxID != r.GetFilter().GetPodSandboxId() {
- continue
- }
- if r.GetFilter().GetLabelSelector() != nil &&
- !matchLabelSelector(r.GetFilter().GetLabelSelector(), cntr.Config.GetLabels()) {
- continue
- }
- containers = append(containers, cntr)
- req.Filters = append(req.Filters, "id=="+cntr.ID)
- }
- return req, containers, nil
-}
-
-func matchLabelSelector(selector, labels map[string]string) bool {
- for k, v := range selector {
- if val, ok := labels[k]; ok {
- if v != val {
- return false
- }
- } else {
- return false
- }
- }
- return true
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_stats_list_unix.go b/vendor/github.com/containerd/cri/pkg/server/container_stats_list_unix.go
deleted file mode 100644
index ad398bc7a..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_stats_list_unix.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// +build !windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "fmt"
-
- "github.com/containerd/containerd/api/types"
- v1 "github.com/containerd/containerd/metrics/types/v1"
- v2 "github.com/containerd/containerd/metrics/types/v2"
- "github.com/containerd/typeurl"
- "github.com/pkg/errors"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- containerstore "github.com/containerd/cri/pkg/store/container"
-)
-
-func (c *criService) containerMetrics(
- meta containerstore.Metadata,
- stats *types.Metric,
-) (*runtime.ContainerStats, error) {
- var cs runtime.ContainerStats
- var usedBytes, inodesUsed uint64
- sn, err := c.snapshotStore.Get(meta.ID)
- // If snapshotstore doesn't have cached snapshot information
- // set WritableLayer usage to zero
- if err == nil {
- usedBytes = sn.Size
- inodesUsed = sn.Inodes
- }
- cs.WritableLayer = &runtime.FilesystemUsage{
- Timestamp: sn.Timestamp,
- FsId: &runtime.FilesystemIdentifier{
- Mountpoint: c.imageFSPath,
- },
- UsedBytes: &runtime.UInt64Value{Value: usedBytes},
- InodesUsed: &runtime.UInt64Value{Value: inodesUsed},
- }
- cs.Attributes = &runtime.ContainerAttributes{
- Id: meta.ID,
- Metadata: meta.Config.GetMetadata(),
- Labels: meta.Config.GetLabels(),
- Annotations: meta.Config.GetAnnotations(),
- }
-
- if stats != nil {
- s, err := typeurl.UnmarshalAny(stats.Data)
- if err != nil {
- return nil, errors.Wrap(err, "failed to extract container metrics")
- }
- switch metrics := s.(type) {
- case *v1.Metrics:
- if metrics.CPU != nil && metrics.CPU.Usage != nil {
- cs.Cpu = &runtime.CpuUsage{
- Timestamp: stats.Timestamp.UnixNano(),
- UsageCoreNanoSeconds: &runtime.UInt64Value{Value: metrics.CPU.Usage.Total},
- }
- }
- if metrics.Memory != nil && metrics.Memory.Usage != nil {
- cs.Memory = &runtime.MemoryUsage{
- Timestamp: stats.Timestamp.UnixNano(),
- WorkingSetBytes: &runtime.UInt64Value{
- Value: getWorkingSet(metrics.Memory),
- },
- }
- }
- case *v2.Metrics:
- if metrics.CPU != nil {
- cs.Cpu = &runtime.CpuUsage{
- Timestamp: stats.Timestamp.UnixNano(),
- UsageCoreNanoSeconds: &runtime.UInt64Value{Value: metrics.CPU.UsageUsec * 1000},
- }
- }
- if metrics.Memory != nil {
- cs.Memory = &runtime.MemoryUsage{
- Timestamp: stats.Timestamp.UnixNano(),
- WorkingSetBytes: &runtime.UInt64Value{
- Value: getWorkingSetV2(metrics.Memory),
- },
- }
- }
- default:
- return &cs, errors.New(fmt.Sprintf("unxpected metrics type: %v", metrics))
- }
- }
-
- return &cs, nil
-}
-
-// getWorkingSet calculates workingset memory from cgroup memory stats.
-// The caller should make sure memory is not nil.
-// workingset = usage - total_inactive_file
-func getWorkingSet(memory *v1.MemoryStat) uint64 {
- if memory.Usage == nil {
- return 0
- }
- var workingSet uint64
- if memory.TotalInactiveFile < memory.Usage.Usage {
- workingSet = memory.Usage.Usage - memory.TotalInactiveFile
- }
- return workingSet
-}
-
-// getWorkingSetV2 calculates workingset memory from cgroupv2 memory stats.
-// The caller should make sure memory is not nil.
-// workingset = usage - inactive_file
-func getWorkingSetV2(memory *v2.MemoryStat) uint64 {
- var workingSet uint64
- if memory.InactiveFile < memory.Usage {
- workingSet = memory.Usage - memory.InactiveFile
- }
- return workingSet
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_stats_list_windows.go b/vendor/github.com/containerd/cri/pkg/server/container_stats_list_windows.go
deleted file mode 100644
index 4bd3b64c1..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_stats_list_windows.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// +build windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- wstats "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
- "github.com/containerd/containerd/api/types"
- "github.com/containerd/typeurl"
- "github.com/pkg/errors"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- containerstore "github.com/containerd/cri/pkg/store/container"
-)
-
-func (c *criService) containerMetrics(
- meta containerstore.Metadata,
- stats *types.Metric,
-) (*runtime.ContainerStats, error) {
- var cs runtime.ContainerStats
- var usedBytes, inodesUsed uint64
- sn, err := c.snapshotStore.Get(meta.ID)
- // If snapshotstore doesn't have cached snapshot information
- // set WritableLayer usage to zero
- if err == nil {
- usedBytes = sn.Size
- inodesUsed = sn.Inodes
- }
- cs.WritableLayer = &runtime.FilesystemUsage{
- Timestamp: sn.Timestamp,
- FsId: &runtime.FilesystemIdentifier{
- Mountpoint: c.imageFSPath,
- },
- UsedBytes: &runtime.UInt64Value{Value: usedBytes},
- InodesUsed: &runtime.UInt64Value{Value: inodesUsed},
- }
- cs.Attributes = &runtime.ContainerAttributes{
- Id: meta.ID,
- Metadata: meta.Config.GetMetadata(),
- Labels: meta.Config.GetLabels(),
- Annotations: meta.Config.GetAnnotations(),
- }
-
- if stats != nil {
- s, err := typeurl.UnmarshalAny(stats.Data)
- if err != nil {
- return nil, errors.Wrap(err, "failed to extract container metrics")
- }
- wstats := s.(*wstats.Statistics).GetWindows()
- if wstats == nil {
- return nil, errors.New("windows stats is empty")
- }
- if wstats.Processor != nil {
- cs.Cpu = &runtime.CpuUsage{
- Timestamp: wstats.Timestamp.UnixNano(),
- UsageCoreNanoSeconds: &runtime.UInt64Value{Value: wstats.Processor.TotalRuntimeNS},
- }
- }
- if wstats.Memory != nil {
- cs.Memory = &runtime.MemoryUsage{
- Timestamp: wstats.Timestamp.UnixNano(),
- WorkingSetBytes: &runtime.UInt64Value{
- Value: wstats.Memory.MemoryUsagePrivateWorkingSetBytes,
- },
- }
- }
- }
- return &cs, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_status.go b/vendor/github.com/containerd/cri/pkg/server/container_status.go
deleted file mode 100644
index aeeb76db3..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_status.go
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "encoding/json"
-
- runtimespec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/store"
- containerstore "github.com/containerd/cri/pkg/store/container"
-)
-
-// ContainerStatus inspects the container and returns the status.
-func (c *criService) ContainerStatus(ctx context.Context, r *runtime.ContainerStatusRequest) (*runtime.ContainerStatusResponse, error) {
- container, err := c.containerStore.Get(r.GetContainerId())
- if err != nil {
- return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
- }
-
- // TODO(random-liu): Clean up the following logic in CRI.
- // Current assumption:
- // * ImageSpec in container config is image ID.
- // * ImageSpec in container status is image tag.
- // * ImageRef in container status is repo digest.
- spec := container.Config.GetImage()
- imageRef := container.ImageRef
- image, err := c.imageStore.Get(imageRef)
- if err != nil {
- if err != store.ErrNotExist {
- return nil, errors.Wrapf(err, "failed to get image %q", imageRef)
- }
- } else {
- repoTags, repoDigests := parseImageReferences(image.References)
- if len(repoTags) > 0 {
- // Based on current behavior of dockershim, this field should be
- // image tag.
- spec = &runtime.ImageSpec{Image: repoTags[0]}
- }
- if len(repoDigests) > 0 {
- // Based on the CRI definition, this field will be consumed by user.
- imageRef = repoDigests[0]
- }
- }
- status := toCRIContainerStatus(container, spec, imageRef)
- if status.GetCreatedAt() == 0 {
- // CRI doesn't allow CreatedAt == 0.
- info, err := container.Container.Info(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get CreatedAt in %q state", status.State)
- }
- status.CreatedAt = info.CreatedAt.UnixNano()
- }
-
- info, err := toCRIContainerInfo(ctx, container, r.GetVerbose())
- if err != nil {
- return nil, errors.Wrap(err, "failed to get verbose container info")
- }
-
- return &runtime.ContainerStatusResponse{
- Status: status,
- Info: info,
- }, nil
-}
-
-// toCRIContainerStatus converts internal container object to CRI container status.
-func toCRIContainerStatus(container containerstore.Container, spec *runtime.ImageSpec, imageRef string) *runtime.ContainerStatus {
- meta := container.Metadata
- status := container.Status.Get()
- reason := status.Reason
- if status.State() == runtime.ContainerState_CONTAINER_EXITED && reason == "" {
- if status.ExitCode == 0 {
- reason = completeExitReason
- } else {
- reason = errorExitReason
- }
- }
-
- return &runtime.ContainerStatus{
- Id: meta.ID,
- Metadata: meta.Config.GetMetadata(),
- State: status.State(),
- CreatedAt: status.CreatedAt,
- StartedAt: status.StartedAt,
- FinishedAt: status.FinishedAt,
- ExitCode: status.ExitCode,
- Image: spec,
- ImageRef: imageRef,
- Reason: reason,
- Message: status.Message,
- Labels: meta.Config.GetLabels(),
- Annotations: meta.Config.GetAnnotations(),
- Mounts: meta.Config.GetMounts(),
- LogPath: meta.LogPath,
- }
-}
-
-// ContainerInfo is extra information for a container.
-type ContainerInfo struct {
- // TODO(random-liu): Add sandboxID in CRI container status.
- SandboxID string `json:"sandboxID"`
- Pid uint32 `json:"pid"`
- Removing bool `json:"removing"`
- SnapshotKey string `json:"snapshotKey"`
- Snapshotter string `json:"snapshotter"`
- RuntimeType string `json:"runtimeType"`
- RuntimeOptions interface{} `json:"runtimeOptions"`
- Config *runtime.ContainerConfig `json:"config"`
- RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"`
-}
-
-// toCRIContainerInfo converts internal container object information to CRI container status response info map.
-func toCRIContainerInfo(ctx context.Context, container containerstore.Container, verbose bool) (map[string]string, error) {
- if !verbose {
- return nil, nil
- }
-
- meta := container.Metadata
- status := container.Status.Get()
-
- // TODO(random-liu): Change CRI status info to use array instead of map.
- ci := &ContainerInfo{
- SandboxID: container.SandboxID,
- Pid: status.Pid,
- Removing: status.Removing,
- Config: meta.Config,
- }
-
- var err error
- ci.RuntimeSpec, err = container.Container.Spec(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get container runtime spec")
- }
-
- ctrInfo, err := container.Container.Info(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get container info")
- }
- ci.SnapshotKey = ctrInfo.SnapshotKey
- ci.Snapshotter = ctrInfo.Snapshotter
-
- runtimeOptions, err := getRuntimeOptions(ctrInfo)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get runtime options")
- }
- ci.RuntimeType = ctrInfo.Runtime.Name
- ci.RuntimeOptions = runtimeOptions
-
- infoBytes, err := json.Marshal(ci)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to marshal info %v", ci)
- }
- return map[string]string{
- "info": string(infoBytes),
- }, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_stop.go b/vendor/github.com/containerd/cri/pkg/server/container_stop.go
deleted file mode 100644
index 92075d6b6..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_stop.go
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "syscall"
- "time"
-
- "github.com/containerd/containerd"
- eventtypes "github.com/containerd/containerd/api/events"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- "github.com/containerd/cri/pkg/store"
- containerstore "github.com/containerd/cri/pkg/store/container"
-)
-
-// StopContainer stops a running container with a grace period (i.e., timeout).
-func (c *criService) StopContainer(ctx context.Context, r *runtime.StopContainerRequest) (*runtime.StopContainerResponse, error) {
- // Get container config from container store.
- container, err := c.containerStore.Get(r.GetContainerId())
- if err != nil {
- return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
- }
-
- if err := c.stopContainer(ctx, container, time.Duration(r.GetTimeout())*time.Second); err != nil {
- return nil, err
- }
-
- return &runtime.StopContainerResponse{}, nil
-}
-
-// stopContainer stops a container based on the container metadata.
-func (c *criService) stopContainer(ctx context.Context, container containerstore.Container, timeout time.Duration) error {
- id := container.ID
-
- // Return without error if container is not running. This makes sure that
- // stop only takes real action after the container is started.
- state := container.Status.Get().State()
- if state != runtime.ContainerState_CONTAINER_RUNNING &&
- state != runtime.ContainerState_CONTAINER_UNKNOWN {
- log.G(ctx).Infof("Container to stop %q must be in running or unknown state, current state %q",
- id, criContainerStateToString(state))
- return nil
- }
-
- task, err := container.Container.Task(ctx, nil)
- if err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrapf(err, "failed to get task for container %q", id)
- }
- // Don't return for unknown state, some cleanup needs to be done.
- if state == runtime.ContainerState_CONTAINER_UNKNOWN {
- return cleanupUnknownContainer(ctx, id, container)
- }
- return nil
- }
-
- // Handle unknown state.
- if state == runtime.ContainerState_CONTAINER_UNKNOWN {
- // Start an exit handler for containers in unknown state.
- waitCtx, waitCancel := context.WithCancel(ctrdutil.NamespacedContext())
- defer waitCancel()
- exitCh, err := task.Wait(waitCtx)
- if err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrapf(err, "failed to wait for task for %q", id)
- }
- return cleanupUnknownContainer(ctx, id, container)
- }
-
- exitCtx, exitCancel := context.WithCancel(context.Background())
- stopCh := c.eventMonitor.startExitMonitor(exitCtx, id, task.Pid(), exitCh)
- defer func() {
- exitCancel()
- // This ensures that exit monitor is stopped before
- // `Wait` is cancelled, so no exit event is generated
- // because of the `Wait` cancellation.
- <-stopCh
- }()
- }
-
- // We only need to kill the task. The event handler will Delete the
- // task from containerd after it handles the Exited event.
- if timeout > 0 {
- stopSignal := "SIGTERM"
- if container.StopSignal != "" {
- stopSignal = container.StopSignal
- } else {
- // The image may have been deleted, and the `StopSignal` field is
- // just introduced to handle that.
- // However, for containers created before the `StopSignal` field is
- // introduced, still try to get the stop signal from the image config.
- // If the image has been deleted, logging an error and using the
- // default SIGTERM is still better than returning error and leaving
- // the container unstoppable. (See issue #990)
- // TODO(random-liu): Remove this logic when containerd 1.2 is deprecated.
- image, err := c.imageStore.Get(container.ImageRef)
- if err != nil {
- if err != store.ErrNotExist {
- return errors.Wrapf(err, "failed to get image %q", container.ImageRef)
- }
- log.G(ctx).Warningf("Image %q not found, stop container with signal %q", container.ImageRef, stopSignal)
- } else {
- if image.ImageSpec.Config.StopSignal != "" {
- stopSignal = image.ImageSpec.Config.StopSignal
- }
- }
- }
- sig, err := containerd.ParseSignal(stopSignal)
- if err != nil {
- return errors.Wrapf(err, "failed to parse stop signal %q", stopSignal)
- }
- log.G(ctx).Infof("Stop container %q with signal %v", id, sig)
- if err = task.Kill(ctx, sig); err != nil && !errdefs.IsNotFound(err) {
- return errors.Wrapf(err, "failed to stop container %q", id)
- }
-
- sigTermCtx, sigTermCtxCancel := context.WithTimeout(ctx, timeout)
- defer sigTermCtxCancel()
- err = c.waitContainerStop(sigTermCtx, container)
- if err == nil {
- // Container stopped on first signal no need for SIGKILL
- return nil
- }
- // If the parent context was cancelled or exceeded return immediately
- if ctx.Err() != nil {
- return ctx.Err()
- }
- // sigTermCtx was exceeded. Send SIGKILL
- log.G(ctx).Debugf("Stop container %q with signal %v timed out", id, sig)
- }
-
- log.G(ctx).Infof("Kill container %q", id)
- if err = task.Kill(ctx, syscall.SIGKILL); err != nil && !errdefs.IsNotFound(err) {
- return errors.Wrapf(err, "failed to kill container %q", id)
- }
-
- // Wait for a fixed timeout until container stop is observed by event monitor.
- err = c.waitContainerStop(ctx, container)
- if err != nil {
- return errors.Wrapf(err, "an error occurs during waiting for container %q to be killed", id)
- }
- return nil
-}
-
-// waitContainerStop waits for container to be stopped until context is
-// cancelled or the context deadline is exceeded.
-func (c *criService) waitContainerStop(ctx context.Context, container containerstore.Container) error {
- select {
- case <-ctx.Done():
- return errors.Wrapf(ctx.Err(), "wait container %q", container.ID)
- case <-container.Stopped():
- return nil
- }
-}
-
-// cleanupUnknownContainer cleanup stopped container in unknown state.
-func cleanupUnknownContainer(ctx context.Context, id string, cntr containerstore.Container) error {
- // Reuse handleContainerExit to do the cleanup.
- return handleContainerExit(ctx, &eventtypes.TaskExit{
- ContainerID: id,
- ID: id,
- Pid: 0,
- ExitStatus: unknownExitCode,
- ExitedAt: time.Now(),
- }, cntr)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_update_resources_unix.go b/vendor/github.com/containerd/cri/pkg/server/container_update_resources_unix.go
deleted file mode 100644
index 23e0d409b..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_update_resources_unix.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// +build !windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- gocontext "context"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/containers"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- "github.com/containerd/typeurl"
- runtimespec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/containerd/opts"
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- containerstore "github.com/containerd/cri/pkg/store/container"
- "github.com/containerd/cri/pkg/util"
-)
-
-// UpdateContainerResources updates ContainerConfig of the container.
-func (c *criService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (retRes *runtime.UpdateContainerResourcesResponse, retErr error) {
- container, err := c.containerStore.Get(r.GetContainerId())
- if err != nil {
- return nil, errors.Wrap(err, "failed to find container")
- }
- // Update resources in status update transaction, so that:
- // 1) There won't be race condition with container start.
- // 2) There won't be concurrent resource update to the same container.
- if err := container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
- return status, c.updateContainerResources(ctx, container, r.GetLinux(), status)
- }); err != nil {
- return nil, errors.Wrap(err, "failed to update resources")
- }
- return &runtime.UpdateContainerResourcesResponse{}, nil
-}
-
-func (c *criService) updateContainerResources(ctx context.Context,
- cntr containerstore.Container,
- resources *runtime.LinuxContainerResources,
- status containerstore.Status) (retErr error) {
- id := cntr.ID
- // Do not update the container when there is a removal in progress.
- if status.Removing {
- return errors.Errorf("container %q is in removing state", id)
- }
-
- // Update container spec. If the container is not started yet, updating
- // spec makes sure that the resource limits are correct when start;
- // if the container is already started, updating spec is still required,
- // the spec will become our source of truth for resource limits.
- oldSpec, err := cntr.Container.Spec(ctx)
- if err != nil {
- return errors.Wrap(err, "failed to get container spec")
- }
- newSpec, err := updateOCILinuxResource(ctx, oldSpec, resources,
- c.config.TolerateMissingHugetlbController, c.config.DisableHugetlbController)
- if err != nil {
- return errors.Wrap(err, "failed to update resource in spec")
- }
-
- if err := updateContainerSpec(ctx, cntr.Container, newSpec); err != nil {
- return err
- }
- defer func() {
- if retErr != nil {
- deferCtx, deferCancel := ctrdutil.DeferContext()
- defer deferCancel()
- // Reset spec on error.
- if err := updateContainerSpec(deferCtx, cntr.Container, oldSpec); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to update spec %+v for container %q", oldSpec, id)
- }
- }
- }()
-
- // If container is not running, only update spec is enough, new resource
- // limit will be applied when container start.
- if status.State() != runtime.ContainerState_CONTAINER_RUNNING {
- return nil
- }
-
- task, err := cntr.Container.Task(ctx, nil)
- if err != nil {
- if errdefs.IsNotFound(err) {
- // Task exited already.
- return nil
- }
- return errors.Wrap(err, "failed to get task")
- }
- // newSpec.Linux won't be nil
- if err := task.Update(ctx, containerd.WithResources(newSpec.Linux.Resources)); err != nil {
- if errdefs.IsNotFound(err) {
- // Task exited already.
- return nil
- }
- return errors.Wrap(err, "failed to update resources")
- }
- return nil
-}
-
-// updateContainerSpec updates container spec.
-func updateContainerSpec(ctx context.Context, cntr containerd.Container, spec *runtimespec.Spec) error {
- any, err := typeurl.MarshalAny(spec)
- if err != nil {
- return errors.Wrapf(err, "failed to marshal spec %+v", spec)
- }
- if err := cntr.Update(ctx, func(ctx gocontext.Context, client *containerd.Client, c *containers.Container) error {
- c.Spec = any
- return nil
- }); err != nil {
- return errors.Wrap(err, "failed to update container spec")
- }
- return nil
-}
-
-// updateOCILinuxResource updates container resource limit.
-func updateOCILinuxResource(ctx context.Context, spec *runtimespec.Spec, new *runtime.LinuxContainerResources,
- tolerateMissingHugetlbController, disableHugetlbController bool) (*runtimespec.Spec, error) {
- // Copy to make sure old spec is not changed.
- var cloned runtimespec.Spec
- if err := util.DeepCopy(&cloned, spec); err != nil {
- return nil, errors.Wrap(err, "failed to deep copy")
- }
- if cloned.Linux == nil {
- cloned.Linux = &runtimespec.Linux{}
- }
- if err := opts.WithResources(new, tolerateMissingHugetlbController, disableHugetlbController)(ctx, nil, nil, &cloned); err != nil {
- return nil, errors.Wrap(err, "unable to set linux container resources")
- }
- return &cloned, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/container_update_resources_windows.go b/vendor/github.com/containerd/cri/pkg/server/container_update_resources_windows.go
deleted file mode 100644
index 72d0d459e..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/container_update_resources_windows.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// +build windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/containerd/containerd/errdefs"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// UpdateContainerResources updates ContainerConfig of the container.
-// TODO(windows): Figure out whether windows support this.
-func (c *criService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (*runtime.UpdateContainerResourcesResponse, error) {
- return nil, errdefs.ErrNotImplemented
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/events.go b/vendor/github.com/containerd/cri/pkg/server/events.go
deleted file mode 100644
index 8f35bf0bd..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/events.go
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "sync"
- "time"
-
- "github.com/containerd/containerd"
- eventtypes "github.com/containerd/containerd/api/events"
- containerdio "github.com/containerd/containerd/cio"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/events"
- "github.com/containerd/typeurl"
- gogotypes "github.com/gogo/protobuf/types"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "golang.org/x/net/context"
- "k8s.io/apimachinery/pkg/util/clock"
-
- "github.com/containerd/cri/pkg/constants"
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- "github.com/containerd/cri/pkg/store"
- containerstore "github.com/containerd/cri/pkg/store/container"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-const (
- backOffInitDuration = 1 * time.Second
- backOffMaxDuration = 5 * time.Minute
- backOffExpireCheckDuration = 1 * time.Second
-
- // handleEventTimeout is the timeout for handling 1 event. Event monitor
- // handles events in serial, if one event blocks the event monitor, no
- // other events can be handled.
- // Add a timeout for each event handling, events that timeout will be requeued and
- // handled again in the future.
- handleEventTimeout = 10 * time.Second
-
- exitChannelSize = 1024
-)
-
-// eventMonitor monitors containerd event and updates internal state correspondingly.
-// TODO(random-liu): Handle event for each container in a separate goroutine.
-type eventMonitor struct {
- c *criService
- ch <-chan *events.Envelope
- // exitCh receives container/sandbox exit events from exit monitors.
- exitCh chan *eventtypes.TaskExit
- errCh <-chan error
- ctx context.Context
- cancel context.CancelFunc
- backOff *backOff
-}
-
-type backOff struct {
- queuePool map[string]*backOffQueue
- // tickerMu is mutex used to protect the ticker.
- tickerMu sync.Mutex
- ticker *time.Ticker
- minDuration time.Duration
- maxDuration time.Duration
- checkDuration time.Duration
- clock clock.Clock
-}
-
-type backOffQueue struct {
- events []interface{}
- expireTime time.Time
- duration time.Duration
- clock clock.Clock
-}
-
-// Create new event monitor. New event monitor will start subscribing containerd event. All events
-// happen after it should be monitored.
-func newEventMonitor(c *criService) *eventMonitor {
- ctx, cancel := context.WithCancel(context.Background())
- return &eventMonitor{
- c: c,
- ctx: ctx,
- cancel: cancel,
- exitCh: make(chan *eventtypes.TaskExit, exitChannelSize),
- backOff: newBackOff(),
- }
-}
-
-// subscribe starts to subscribe containerd events.
-func (em *eventMonitor) subscribe(subscriber events.Subscriber) {
- // note: filters are any match, if you want any match but not in namespace foo
- // then you have to manually filter namespace foo
- filters := []string{
- `topic=="/tasks/oom"`,
- `topic~="/images/"`,
- }
- em.ch, em.errCh = subscriber.Subscribe(em.ctx, filters...)
-}
-
-// startExitMonitor starts an exit monitor for a given container/sandbox.
-func (em *eventMonitor) startExitMonitor(ctx context.Context, id string, pid uint32, exitCh <-chan containerd.ExitStatus) <-chan struct{} {
- stopCh := make(chan struct{})
- go func() {
- defer close(stopCh)
- select {
- case exitRes := <-exitCh:
- exitStatus, exitedAt, err := exitRes.Result()
- if err != nil {
- logrus.WithError(err).Errorf("Failed to get task exit status for %q", id)
- exitStatus = unknownExitCode
- exitedAt = time.Now()
- }
- em.exitCh <- &eventtypes.TaskExit{
- ContainerID: id,
- ID: id,
- Pid: pid,
- ExitStatus: exitStatus,
- ExitedAt: exitedAt,
- }
- case <-ctx.Done():
- }
- }()
- return stopCh
-}
-
-func convertEvent(e *gogotypes.Any) (string, interface{}, error) {
- id := ""
- evt, err := typeurl.UnmarshalAny(e)
- if err != nil {
- return "", nil, errors.Wrap(err, "failed to unmarshalany")
- }
-
- switch e := evt.(type) {
- case *eventtypes.TaskOOM:
- id = e.ContainerID
- case *eventtypes.ImageCreate:
- id = e.Name
- case *eventtypes.ImageUpdate:
- id = e.Name
- case *eventtypes.ImageDelete:
- id = e.Name
- default:
- return "", nil, errors.New("unsupported event")
- }
- return id, evt, nil
-}
-
-// start starts the event monitor which monitors and handles all subscribed events. It returns
-// an error channel for the caller to wait for stop errors from the event monitor.
-// start must be called after subscribe.
-func (em *eventMonitor) start() <-chan error {
- errCh := make(chan error)
- if em.ch == nil || em.errCh == nil {
- panic("event channel is nil")
- }
- backOffCheckCh := em.backOff.start()
- go func() {
- defer close(errCh)
- for {
- select {
- case e := <-em.exitCh:
- logrus.Debugf("Received exit event %+v", e)
- id := e.ID
- if em.backOff.isInBackOff(id) {
- logrus.Infof("Events for %q is in backoff, enqueue event %+v", id, e)
- em.backOff.enBackOff(id, e)
- break
- }
- if err := em.handleEvent(e); err != nil {
- logrus.WithError(err).Errorf("Failed to handle exit event %+v for %s", e, id)
- em.backOff.enBackOff(id, e)
- }
- case e := <-em.ch:
- logrus.Debugf("Received containerd event timestamp - %v, namespace - %q, topic - %q", e.Timestamp, e.Namespace, e.Topic)
- if e.Namespace != constants.K8sContainerdNamespace {
- logrus.Debugf("Ignoring events in namespace - %q", e.Namespace)
- break
- }
- id, evt, err := convertEvent(e.Event)
- if err != nil {
- logrus.WithError(err).Errorf("Failed to convert event %+v", e)
- break
- }
- if em.backOff.isInBackOff(id) {
- logrus.Infof("Events for %q is in backoff, enqueue event %+v", id, evt)
- em.backOff.enBackOff(id, evt)
- break
- }
- if err := em.handleEvent(evt); err != nil {
- logrus.WithError(err).Errorf("Failed to handle event %+v for %s", evt, id)
- em.backOff.enBackOff(id, evt)
- }
- case err := <-em.errCh:
- // Close errCh in defer directly if there is no error.
- if err != nil {
- logrus.WithError(err).Errorf("Failed to handle event stream")
- errCh <- err
- }
- return
- case <-backOffCheckCh:
- ids := em.backOff.getExpiredIDs()
- for _, id := range ids {
- queue := em.backOff.deBackOff(id)
- for i, any := range queue.events {
- if err := em.handleEvent(any); err != nil {
- logrus.WithError(err).Errorf("Failed to handle backOff event %+v for %s", any, id)
- em.backOff.reBackOff(id, queue.events[i:], queue.duration)
- break
- }
- }
- }
- }
- }
- }()
- return errCh
-}
-
-// stop stops the event monitor. It will close the event channel.
-// Once event monitor is stopped, it can't be started.
-func (em *eventMonitor) stop() {
- em.backOff.stop()
- em.cancel()
-}
-
-// handleEvent handles a containerd event.
-func (em *eventMonitor) handleEvent(any interface{}) error {
- ctx := ctrdutil.NamespacedContext()
- ctx, cancel := context.WithTimeout(ctx, handleEventTimeout)
- defer cancel()
-
- switch e := any.(type) {
- case *eventtypes.TaskExit:
- logrus.Infof("TaskExit event %+v", e)
- // Use ID instead of ContainerID to rule out TaskExit event for exec.
- cntr, err := em.c.containerStore.Get(e.ID)
- if err == nil {
- if err := handleContainerExit(ctx, e, cntr); err != nil {
- return errors.Wrap(err, "failed to handle container TaskExit event")
- }
- return nil
- } else if err != store.ErrNotExist {
- return errors.Wrap(err, "can't find container for TaskExit event")
- }
- sb, err := em.c.sandboxStore.Get(e.ID)
- if err == nil {
- if err := handleSandboxExit(ctx, e, sb); err != nil {
- return errors.Wrap(err, "failed to handle sandbox TaskExit event")
- }
- return nil
- } else if err != store.ErrNotExist {
- return errors.Wrap(err, "can't find sandbox for TaskExit event")
- }
- return nil
- case *eventtypes.TaskOOM:
- logrus.Infof("TaskOOM event %+v", e)
- // For TaskOOM, we only care which container it belongs to.
- cntr, err := em.c.containerStore.Get(e.ContainerID)
- if err != nil {
- if err != store.ErrNotExist {
- return errors.Wrap(err, "can't find container for TaskOOM event")
- }
- return nil
- }
- err = cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
- status.Reason = oomExitReason
- return status, nil
- })
- if err != nil {
- return errors.Wrap(err, "failed to update container status for TaskOOM event")
- }
- case *eventtypes.ImageCreate:
- logrus.Infof("ImageCreate event %+v", e)
- return em.c.updateImage(ctx, e.Name)
- case *eventtypes.ImageUpdate:
- logrus.Infof("ImageUpdate event %+v", e)
- return em.c.updateImage(ctx, e.Name)
- case *eventtypes.ImageDelete:
- logrus.Infof("ImageDelete event %+v", e)
- return em.c.updateImage(ctx, e.Name)
- }
-
- return nil
-}
-
-// handleContainerExit handles TaskExit event for container.
-func handleContainerExit(ctx context.Context, e *eventtypes.TaskExit, cntr containerstore.Container) error {
- // Attach container IO so that `Delete` could cleanup the stream properly.
- task, err := cntr.Container.Task(ctx,
- func(*containerdio.FIFOSet) (containerdio.IO, error) {
- // We can't directly return cntr.IO here, because
- // even if cntr.IO is nil, the cio.IO interface
- // is not.
- // See https://tour.golang.org/methods/12:
- // Note that an interface value that holds a nil
- // concrete value is itself non-nil.
- if cntr.IO != nil {
- return cntr.IO, nil
- }
- return nil, nil
- },
- )
- if err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrapf(err, "failed to load task for container")
- }
- } else {
- // TODO(random-liu): [P1] This may block the loop, we may want to spawn a worker
- if _, err = task.Delete(ctx, WithNRISandboxDelete(cntr.SandboxID), containerd.WithProcessKill); err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to stop container")
- }
- // Move on to make sure container status is updated.
- }
- }
- err = cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
- // If FinishedAt has been set (e.g. with start failure), keep as
- // it is.
- if status.FinishedAt != 0 {
- return status, nil
- }
- status.Pid = 0
- status.FinishedAt = e.ExitedAt.UnixNano()
- status.ExitCode = int32(e.ExitStatus)
- // Unknown state can only transit to EXITED state, so we need
- // to handle unknown state here.
- if status.Unknown {
- logrus.Debugf("Container %q transited from UNKNOWN to EXITED", cntr.ID)
- status.Unknown = false
- }
- return status, nil
- })
- if err != nil {
- return errors.Wrap(err, "failed to update container state")
- }
- // Using channel to propagate the information of container stop
- cntr.Stop()
- return nil
-}
-
-// handleSandboxExit handles TaskExit event for sandbox.
-func handleSandboxExit(ctx context.Context, e *eventtypes.TaskExit, sb sandboxstore.Sandbox) error {
- // No stream attached to sandbox container.
- task, err := sb.Container.Task(ctx, nil)
- if err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to load task for sandbox")
- }
- } else {
- // TODO(random-liu): [P1] This may block the loop, we may want to spawn a worker
- if _, err = task.Delete(ctx, WithNRISandboxDelete(sb.ID), containerd.WithProcessKill); err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to stop sandbox")
- }
- // Move on to make sure container status is updated.
- }
- }
- err = sb.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
- status.State = sandboxstore.StateNotReady
- status.Pid = 0
- return status, nil
- })
- if err != nil {
- return errors.Wrap(err, "failed to update sandbox state")
- }
- // Using channel to propagate the information of sandbox stop
- sb.Stop()
- return nil
-}
-
-func newBackOff() *backOff {
- return &backOff{
- queuePool: map[string]*backOffQueue{},
- minDuration: backOffInitDuration,
- maxDuration: backOffMaxDuration,
- checkDuration: backOffExpireCheckDuration,
- clock: clock.RealClock{},
- }
-}
-
-func (b *backOff) getExpiredIDs() []string {
- var ids []string
- for id, q := range b.queuePool {
- if q.isExpire() {
- ids = append(ids, id)
- }
- }
- return ids
-}
-
-func (b *backOff) isInBackOff(key string) bool {
- if _, ok := b.queuePool[key]; ok {
- return true
- }
- return false
-}
-
-// enBackOff start to backOff and put event to the tail of queue
-func (b *backOff) enBackOff(key string, evt interface{}) {
- if queue, ok := b.queuePool[key]; ok {
- queue.events = append(queue.events, evt)
- return
- }
- b.queuePool[key] = newBackOffQueue([]interface{}{evt}, b.minDuration, b.clock)
-}
-
-// enBackOff get out the whole queue
-func (b *backOff) deBackOff(key string) *backOffQueue {
- queue := b.queuePool[key]
- delete(b.queuePool, key)
- return queue
-}
-
-// enBackOff start to backOff again and put events to the queue
-func (b *backOff) reBackOff(key string, events []interface{}, oldDuration time.Duration) {
- duration := 2 * oldDuration
- if duration > b.maxDuration {
- duration = b.maxDuration
- }
- b.queuePool[key] = newBackOffQueue(events, duration, b.clock)
-}
-
-func (b *backOff) start() <-chan time.Time {
- b.tickerMu.Lock()
- defer b.tickerMu.Unlock()
- b.ticker = time.NewTicker(b.checkDuration)
- return b.ticker.C
-}
-
-func (b *backOff) stop() {
- b.tickerMu.Lock()
- defer b.tickerMu.Unlock()
- if b.ticker != nil {
- b.ticker.Stop()
- }
-}
-
-func newBackOffQueue(events []interface{}, init time.Duration, c clock.Clock) *backOffQueue {
- return &backOffQueue{
- events: events,
- duration: init,
- expireTime: c.Now().Add(init),
- clock: c,
- }
-}
-
-func (q *backOffQueue) isExpire() bool {
- // return time.Now >= expireTime
- return !q.clock.Now().Before(q.expireTime)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/helpers.go b/vendor/github.com/containerd/cri/pkg/server/helpers.go
deleted file mode 100644
index 34da9a254..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/helpers.go
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "fmt"
- "path"
- "path/filepath"
- "strconv"
- "strings"
-
- "github.com/BurntSushi/toml"
- runhcsoptions "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/containers"
- "github.com/containerd/containerd/plugin"
- "github.com/containerd/containerd/reference/docker"
- "github.com/containerd/containerd/runtime/linux/runctypes"
- runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
- "github.com/containerd/typeurl"
- imagedigest "github.com/opencontainers/go-digest"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- runtimeoptions "github.com/containerd/cri/pkg/api/runtimeoptions/v1"
- criconfig "github.com/containerd/cri/pkg/config"
- "github.com/containerd/cri/pkg/store"
- containerstore "github.com/containerd/cri/pkg/store/container"
- imagestore "github.com/containerd/cri/pkg/store/image"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-const (
- // errorStartReason is the exit reason when fails to start container.
- errorStartReason = "StartError"
- // errorStartExitCode is the exit code when fails to start container.
- // 128 is the same with Docker's behavior.
- // TODO(windows): Figure out what should be used for windows.
- errorStartExitCode = 128
- // completeExitReason is the exit reason when container exits with code 0.
- completeExitReason = "Completed"
- // errorExitReason is the exit reason when container exits with code non-zero.
- errorExitReason = "Error"
- // oomExitReason is the exit reason when process in container is oom killed.
- oomExitReason = "OOMKilled"
-
- // sandboxesDir contains all sandbox root. A sandbox root is the running
- // directory of the sandbox, all files created for the sandbox will be
- // placed under this directory.
- sandboxesDir = "sandboxes"
- // containersDir contains all container root.
- containersDir = "containers"
- // Delimiter used to construct container/sandbox names.
- nameDelimiter = "_"
-
- // criContainerdPrefix is common prefix for cri-containerd
- criContainerdPrefix = "io.cri-containerd"
- // containerKindLabel is a label key indicating container is sandbox container or application container
- containerKindLabel = criContainerdPrefix + ".kind"
- // containerKindSandbox is a label value indicating container is sandbox container
- containerKindSandbox = "sandbox"
- // containerKindContainer is a label value indicating container is application container
- containerKindContainer = "container"
- // imageLabelKey is the label key indicating the image is managed by cri plugin.
- imageLabelKey = criContainerdPrefix + ".image"
- // imageLabelValue is the label value indicating the image is managed by cri plugin.
- imageLabelValue = "managed"
- // sandboxMetadataExtension is an extension name that identify metadata of sandbox in CreateContainerRequest
- sandboxMetadataExtension = criContainerdPrefix + ".sandbox.metadata"
- // containerMetadataExtension is an extension name that identify metadata of container in CreateContainerRequest
- containerMetadataExtension = criContainerdPrefix + ".container.metadata"
-
- // defaultIfName is the default network interface for the pods
- defaultIfName = "eth0"
-
- // runtimeRunhcsV1 is the runtime type for runhcs.
- runtimeRunhcsV1 = "io.containerd.runhcs.v1"
-)
-
-// makeSandboxName generates sandbox name from sandbox metadata. The name
-// generated is unique as long as sandbox metadata is unique.
-func makeSandboxName(s *runtime.PodSandboxMetadata) string {
- return strings.Join([]string{
- s.Name, // 0
- s.Namespace, // 1
- s.Uid, // 2
- fmt.Sprintf("%d", s.Attempt), // 3
- }, nameDelimiter)
-}
-
-// makeContainerName generates container name from sandbox and container metadata.
-// The name generated is unique as long as the sandbox container combination is
-// unique.
-func makeContainerName(c *runtime.ContainerMetadata, s *runtime.PodSandboxMetadata) string {
- return strings.Join([]string{
- c.Name, // 0
- s.Name, // 1: pod name
- s.Namespace, // 2: pod namespace
- s.Uid, // 3: pod uid
- fmt.Sprintf("%d", c.Attempt), // 4
- }, nameDelimiter)
-}
-
-// getSandboxRootDir returns the root directory for managing sandbox files,
-// e.g. hosts files.
-func (c *criService) getSandboxRootDir(id string) string {
- return filepath.Join(c.config.RootDir, sandboxesDir, id)
-}
-
-// getVolatileSandboxRootDir returns the root directory for managing volatile sandbox files,
-// e.g. named pipes.
-func (c *criService) getVolatileSandboxRootDir(id string) string {
- return filepath.Join(c.config.StateDir, sandboxesDir, id)
-}
-
-// getContainerRootDir returns the root directory for managing container files,
-// e.g. state checkpoint.
-func (c *criService) getContainerRootDir(id string) string {
- return filepath.Join(c.config.RootDir, containersDir, id)
-}
-
-// getVolatileContainerRootDir returns the root directory for managing volatile container files,
-// e.g. named pipes.
-func (c *criService) getVolatileContainerRootDir(id string) string {
- return filepath.Join(c.config.StateDir, containersDir, id)
-}
-
-// criContainerStateToString formats CRI container state to string.
-func criContainerStateToString(state runtime.ContainerState) string {
- return runtime.ContainerState_name[int32(state)]
-}
-
-// getRepoDigestAngTag returns image repoDigest and repoTag of the named image reference.
-func getRepoDigestAndTag(namedRef docker.Named, digest imagedigest.Digest, schema1 bool) (string, string) {
- var repoTag, repoDigest string
- if _, ok := namedRef.(docker.NamedTagged); ok {
- repoTag = namedRef.String()
- }
- if _, ok := namedRef.(docker.Canonical); ok {
- repoDigest = namedRef.String()
- } else if !schema1 {
- // digest is not actual repo digest for schema1 image.
- repoDigest = namedRef.Name() + "@" + digest.String()
- }
- return repoDigest, repoTag
-}
-
-// localResolve resolves image reference locally and returns corresponding image metadata. It
-// returns store.ErrNotExist if the reference doesn't exist.
-func (c *criService) localResolve(refOrID string) (imagestore.Image, error) {
- getImageID := func(refOrId string) string {
- if _, err := imagedigest.Parse(refOrID); err == nil {
- return refOrID
- }
- return func(ref string) string {
- // ref is not image id, try to resolve it locally.
- // TODO(random-liu): Handle this error better for debugging.
- normalized, err := docker.ParseDockerRef(ref)
- if err != nil {
- return ""
- }
- id, err := c.imageStore.Resolve(normalized.String())
- if err != nil {
- return ""
- }
- return id
- }(refOrID)
- }
-
- imageID := getImageID(refOrID)
- if imageID == "" {
- // Try to treat ref as imageID
- imageID = refOrID
- }
- return c.imageStore.Get(imageID)
-}
-
-// toContainerdImage converts an image object in image store to containerd image handler.
-func (c *criService) toContainerdImage(ctx context.Context, image imagestore.Image) (containerd.Image, error) {
- // image should always have at least one reference.
- if len(image.References) == 0 {
- return nil, errors.Errorf("invalid image with no reference %q", image.ID)
- }
- return c.client.GetImage(ctx, image.References[0])
-}
-
-// getUserFromImage gets uid or user name of the image user.
-// If user is numeric, it will be treated as uid; or else, it is treated as user name.
-func getUserFromImage(user string) (*int64, string) {
- // return both empty if user is not specified in the image.
- if user == "" {
- return nil, ""
- }
- // split instances where the id may contain user:group
- user = strings.Split(user, ":")[0]
- // user could be either uid or user name. Try to interpret as numeric uid.
- uid, err := strconv.ParseInt(user, 10, 64)
- if err != nil {
- // If user is non numeric, assume it's user name.
- return nil, user
- }
- // If user is a numeric uid.
- return &uid, ""
-}
-
-// ensureImageExists returns corresponding metadata of the image reference, if image is not
-// pulled yet, the function will pull the image.
-func (c *criService) ensureImageExists(ctx context.Context, ref string, config *runtime.PodSandboxConfig) (*imagestore.Image, error) {
- image, err := c.localResolve(ref)
- if err != nil && err != store.ErrNotExist {
- return nil, errors.Wrapf(err, "failed to get image %q", ref)
- }
- if err == nil {
- return &image, nil
- }
- // Pull image to ensure the image exists
- resp, err := c.PullImage(ctx, &runtime.PullImageRequest{Image: &runtime.ImageSpec{Image: ref}, SandboxConfig: config})
- if err != nil {
- return nil, errors.Wrapf(err, "failed to pull image %q", ref)
- }
- imageID := resp.GetImageRef()
- newImage, err := c.imageStore.Get(imageID)
- if err != nil {
- // It's still possible that someone removed the image right after it is pulled.
- return nil, errors.Wrapf(err, "failed to get image %q after pulling", imageID)
- }
- return &newImage, nil
-}
-
-// isInCRIMounts checks whether a destination is in CRI mount list.
-func isInCRIMounts(dst string, mounts []*runtime.Mount) bool {
- for _, m := range mounts {
- if filepath.Clean(m.ContainerPath) == filepath.Clean(dst) {
- return true
- }
- }
- return false
-}
-
-// filterLabel returns a label filter. Use `%q` here because containerd
-// filter needs extra quote to work properly.
-func filterLabel(k, v string) string {
- return fmt.Sprintf("labels.%q==%q", k, v)
-}
-
-// buildLabel builds the labels from config to be passed to containerd
-func buildLabels(configLabels map[string]string, containerType string) map[string]string {
- labels := make(map[string]string)
- for k, v := range configLabels {
- labels[k] = v
- }
- labels[containerKindLabel] = containerType
- return labels
-}
-
-// toRuntimeAuthConfig converts cri plugin auth config to runtime auth config.
-func toRuntimeAuthConfig(a criconfig.AuthConfig) *runtime.AuthConfig {
- return &runtime.AuthConfig{
- Username: a.Username,
- Password: a.Password,
- Auth: a.Auth,
- IdentityToken: a.IdentityToken,
- }
-}
-
-// parseImageReferences parses a list of arbitrary image references and returns
-// the repotags and repodigests
-func parseImageReferences(refs []string) ([]string, []string) {
- var tags, digests []string
- for _, ref := range refs {
- parsed, err := docker.ParseAnyReference(ref)
- if err != nil {
- continue
- }
- if _, ok := parsed.(docker.Canonical); ok {
- digests = append(digests, parsed.String())
- } else if _, ok := parsed.(docker.Tagged); ok {
- tags = append(tags, parsed.String())
- }
- }
- return tags, digests
-}
-
-// generateRuntimeOptions generates runtime options from cri plugin config.
-func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{}, error) {
- if r.Options == nil {
- if r.Type != plugin.RuntimeLinuxV1 {
- return nil, nil
- }
- // This is a legacy config, generate runctypes.RuncOptions.
- return &runctypes.RuncOptions{
- Runtime: r.Engine,
- RuntimeRoot: r.Root,
- SystemdCgroup: c.SystemdCgroup,
- }, nil
- }
- options := getRuntimeOptionsType(r.Type)
- if err := toml.PrimitiveDecode(*r.Options, options); err != nil {
- return nil, err
- }
- return options, nil
-}
-
-// getRuntimeOptionsType gets empty runtime options by the runtime type name.
-func getRuntimeOptionsType(t string) interface{} {
- switch t {
- case plugin.RuntimeRuncV1:
- fallthrough
- case plugin.RuntimeRuncV2:
- return &runcoptions.Options{}
- case plugin.RuntimeLinuxV1:
- return &runctypes.RuncOptions{}
- case runtimeRunhcsV1:
- return &runhcsoptions.Options{}
- default:
- return &runtimeoptions.Options{}
- }
-}
-
-// getRuntimeOptions get runtime options from container metadata.
-func getRuntimeOptions(c containers.Container) (interface{}, error) {
- if c.Runtime.Options == nil {
- return nil, nil
- }
- opts, err := typeurl.UnmarshalAny(c.Runtime.Options)
- if err != nil {
- return nil, err
- }
- return opts, nil
-}
-
-const (
- // unknownExitCode is the exit code when exit reason is unknown.
- unknownExitCode = 255
- // unknownExitReason is the exit reason when exit reason is unknown.
- unknownExitReason = "Unknown"
-)
-
-// unknownContainerStatus returns the default container status when its status is unknown.
-func unknownContainerStatus() containerstore.Status {
- return containerstore.Status{
- CreatedAt: 0,
- StartedAt: 0,
- FinishedAt: 0,
- ExitCode: unknownExitCode,
- Reason: unknownExitReason,
- Unknown: true,
- }
-}
-
-// unknownSandboxStatus returns the default sandbox status when its status is unknown.
-func unknownSandboxStatus() sandboxstore.Status {
- return sandboxstore.Status{
- State: sandboxstore.StateUnknown,
- }
-}
-
-// getPassthroughAnnotations filters requested pod annotations by comparing
-// against permitted annotations for the given runtime.
-func getPassthroughAnnotations(podAnnotations map[string]string,
- runtimePodAnnotations []string) (passthroughAnnotations map[string]string) {
- passthroughAnnotations = make(map[string]string)
-
- for podAnnotationKey, podAnnotationValue := range podAnnotations {
- for _, pattern := range runtimePodAnnotations {
- // Use path.Match instead of filepath.Match here.
- // filepath.Match treated `\\` as path separator
- // on windows, which is not what we want.
- if ok, _ := path.Match(pattern, podAnnotationKey); ok {
- passthroughAnnotations[podAnnotationKey] = podAnnotationValue
- }
- }
- }
- return passthroughAnnotations
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/helpers_unix.go b/vendor/github.com/containerd/cri/pkg/server/helpers_unix.go
deleted file mode 100644
index 0c3a6652d..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/helpers_unix.go
+++ /dev/null
@@ -1,292 +0,0 @@
-// +build !windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "context"
- "fmt"
- "os"
- "path"
- "path/filepath"
- "regexp"
- "sort"
- "strings"
- "syscall"
- "time"
-
- "github.com/containerd/containerd/log"
- "github.com/containerd/containerd/mount"
- "github.com/containerd/cri/pkg/seccomp"
- "github.com/containerd/cri/pkg/seutil"
- runcapparmor "github.com/opencontainers/runc/libcontainer/apparmor"
- "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/selinux/go-selinux/label"
- "github.com/pkg/errors"
- "golang.org/x/sys/unix"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-const (
- // defaultSandboxOOMAdj is default omm adj for sandbox container. (kubernetes#47938).
- defaultSandboxOOMAdj = -998
- // defaultShmSize is the default size of the sandbox shm.
- defaultShmSize = int64(1024 * 1024 * 64)
- // relativeRootfsPath is the rootfs path relative to bundle path.
- relativeRootfsPath = "rootfs"
- // According to http://man7.org/linux/man-pages/man5/resolv.conf.5.html:
- // "The search list is currently limited to six domains with a total of 256 characters."
- maxDNSSearches = 6
- // devShm is the default path of /dev/shm.
- devShm = "/dev/shm"
- // etcHosts is the default path of /etc/hosts file.
- etcHosts = "/etc/hosts"
- // etcHostname is the default path of /etc/hostname file.
- etcHostname = "/etc/hostname"
- // resolvConfPath is the abs path of resolv.conf on host or container.
- resolvConfPath = "/etc/resolv.conf"
- // hostnameEnv is the key for HOSTNAME env.
- hostnameEnv = "HOSTNAME"
-)
-
-// getCgroupsPath generates container cgroups path.
-func getCgroupsPath(cgroupsParent, id string) string {
- base := path.Base(cgroupsParent)
- if strings.HasSuffix(base, ".slice") {
- // For a.slice/b.slice/c.slice, base is c.slice.
- // runc systemd cgroup path format is "slice:prefix:name".
- return strings.Join([]string{base, "cri-containerd", id}, ":")
- }
- return filepath.Join(cgroupsParent, id)
-}
-
-// getSandboxHostname returns the hostname file path inside the sandbox root directory.
-func (c *criService) getSandboxHostname(id string) string {
- return filepath.Join(c.getSandboxRootDir(id), "hostname")
-}
-
-// getSandboxHosts returns the hosts file path inside the sandbox root directory.
-func (c *criService) getSandboxHosts(id string) string {
- return filepath.Join(c.getSandboxRootDir(id), "hosts")
-}
-
-// getResolvPath returns resolv.conf filepath for specified sandbox.
-func (c *criService) getResolvPath(id string) string {
- return filepath.Join(c.getSandboxRootDir(id), "resolv.conf")
-}
-
-// getSandboxDevShm returns the shm file path inside the sandbox root directory.
-func (c *criService) getSandboxDevShm(id string) string {
- return filepath.Join(c.getVolatileSandboxRootDir(id), "shm")
-}
-
-func toLabel(selinuxOptions *runtime.SELinuxOption) ([]string, error) {
- var labels []string
-
- if selinuxOptions == nil {
- return nil, nil
- }
- if err := checkSelinuxLevel(selinuxOptions.Level); err != nil {
- return nil, err
- }
- if selinuxOptions.User != "" {
- labels = append(labels, "user:"+selinuxOptions.User)
- }
- if selinuxOptions.Role != "" {
- labels = append(labels, "role:"+selinuxOptions.Role)
- }
- if selinuxOptions.Type != "" {
- labels = append(labels, "type:"+selinuxOptions.Type)
- }
- if selinuxOptions.Level != "" {
- labels = append(labels, "level:"+selinuxOptions.Level)
- }
-
- return labels, nil
-}
-
-func initLabelsFromOpt(selinuxOpts *runtime.SELinuxOption) (string, string, error) {
- labels, err := toLabel(selinuxOpts)
- if err != nil {
- return "", "", err
- }
- return label.InitLabels(labels)
-}
-
-func checkSelinuxLevel(level string) error {
- if len(level) == 0 {
- return nil
- }
-
- matched, err := regexp.MatchString(`^s\d(-s\d)??(:c\d{1,4}(\.c\d{1,4})?(,c\d{1,4}(\.c\d{1,4})?)*)?$`, level)
- if err != nil {
- return errors.Wrapf(err, "the format of 'level' %q is not correct", level)
- }
- if !matched {
- return fmt.Errorf("the format of 'level' %q is not correct", level)
- }
- return nil
-}
-
-func (c *criService) apparmorEnabled() bool {
- return runcapparmor.IsEnabled() && !c.config.DisableApparmor
-}
-
-func (c *criService) seccompEnabled() bool {
- return seccomp.IsEnabled()
-}
-
-// openLogFile opens/creates a container log file.
-func openLogFile(path string) (*os.File, error) {
- return os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0640)
-}
-
-// unmountRecursive unmounts the target and all mounts underneath, starting with
-// the deepest mount first.
-func unmountRecursive(ctx context.Context, target string) error {
- mounts, err := mount.Self()
- if err != nil {
- return err
- }
-
- var toUnmount []string
- for _, m := range mounts {
- p, err := filepath.Rel(target, m.Mountpoint)
- if err != nil {
- return err
- }
- if !strings.HasPrefix(p, "..") {
- toUnmount = append(toUnmount, m.Mountpoint)
- }
- }
-
- // Make the deepest mount be first
- sort.Slice(toUnmount, func(i, j int) bool {
- return len(toUnmount[i]) > len(toUnmount[j])
- })
-
- for i, mountPath := range toUnmount {
- if err := mount.UnmountAll(mountPath, unix.MNT_DETACH); err != nil {
- if i == len(toUnmount)-1 { // last mount
- return err
- }
- // This is some submount, we can ignore this error for now, the final unmount will fail if this is a real problem
- log.G(ctx).WithError(err).Debugf("failed to unmount submount %s", mountPath)
- }
- }
- return nil
-}
-
-// ensureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
-// often be remedied.
-// Only use `ensureRemoveAll` if you really want to make every effort to remove
-// a directory.
-//
-// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
-// can be a race between reading directory entries and then actually attempting
-// to remove everything in the directory.
-// These types of errors do not need to be returned since it's ok for the dir to
-// be gone we can just retry the remove operation.
-//
-// This should not return a `os.ErrNotExist` kind of error under any circumstances
-func ensureRemoveAll(ctx context.Context, dir string) error {
- notExistErr := make(map[string]bool)
-
- // track retries
- exitOnErr := make(map[string]int)
- maxRetry := 50
-
- // Attempt to unmount anything beneath this dir first.
- if err := unmountRecursive(ctx, dir); err != nil {
- log.G(ctx).WithError(err).Debugf("failed to do initial unmount of %s", dir)
- }
-
- for {
- err := os.RemoveAll(dir)
- if err == nil {
- return nil
- }
-
- pe, ok := err.(*os.PathError)
- if !ok {
- return err
- }
-
- if os.IsNotExist(err) {
- if notExistErr[pe.Path] {
- return err
- }
- notExistErr[pe.Path] = true
-
- // There is a race where some subdir can be removed but after the
- // parent dir entries have been read.
- // So the path could be from `os.Remove(subdir)`
- // If the reported non-existent path is not the passed in `dir` we
- // should just retry, but otherwise return with no error.
- if pe.Path == dir {
- return nil
- }
- continue
- }
-
- if pe.Err != syscall.EBUSY {
- return err
- }
- if e := mount.Unmount(pe.Path, unix.MNT_DETACH); e != nil {
- return errors.Wrapf(e, "error while removing %s", dir)
- }
-
- if exitOnErr[pe.Path] == maxRetry {
- return err
- }
- exitOnErr[pe.Path]++
- time.Sleep(100 * time.Millisecond)
- }
-}
-
-var vmbasedRuntimes = []string{
- "io.containerd.kata",
-}
-
-func isVMBasedRuntime(runtimeType string) bool {
- for _, rt := range vmbasedRuntimes {
- if strings.Contains(runtimeType, rt) {
- return true
- }
- }
- return false
-}
-
-func modifyProcessLabel(runtimeType string, spec *specs.Spec) error {
- if !isVMBasedRuntime(runtimeType) {
- return nil
- }
- l, err := getKVMLabel(spec.Process.SelinuxLabel)
- if err != nil {
- return errors.Wrap(err, "failed to get selinux kvm label")
- }
- spec.Process.SelinuxLabel = l
- return nil
-}
-
-func getKVMLabel(l string) (string, error) {
- if !seutil.HasType("container_kvm_t") {
- return "", nil
- }
- return seutil.ChangeToKVM(l)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/helpers_windows.go b/vendor/github.com/containerd/cri/pkg/server/helpers_windows.go
deleted file mode 100644
index 5ce7104da..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/helpers_windows.go
+++ /dev/null
@@ -1,225 +0,0 @@
-// +build windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "context"
- "os"
- "path/filepath"
- "syscall"
- "time"
-
- "github.com/opencontainers/runtime-spec/specs-go"
-)
-
-// openLogFile opens/creates a container log file.
-// It specifies `FILE_SHARE_DELETE` option to make sure
-// log files can be rotated by kubelet.
-// TODO(windows): Use golang support after 1.14. (https://github.com/golang/go/issues/32088)
-func openLogFile(path string) (*os.File, error) {
- path = fixLongPath(path)
- if len(path) == 0 {
- return nil, syscall.ERROR_FILE_NOT_FOUND
- }
- pathp, err := syscall.UTF16PtrFromString(path)
- if err != nil {
- return nil, err
- }
- createmode := uint32(syscall.OPEN_ALWAYS)
- access := uint32(syscall.FILE_APPEND_DATA)
- sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
- h, err := syscall.CreateFile(pathp, access, sharemode, nil, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0)
- if err != nil {
- return nil, err
- }
- return os.NewFile(uintptr(h), path), nil
-}
-
-// Copyright (c) 2009 The Go Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// fixLongPath returns the extended-length (\\?\-prefixed) form of
-// path when needed, in order to avoid the default 260 character file
-// path limit imposed by Windows. If path is not easily converted to
-// the extended-length form (for example, if path is a relative path
-// or contains .. elements), or is short enough, fixLongPath returns
-// path unmodified.
-//
-// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
-//
-// This is copied from https://golang.org/src/path/filepath/path_windows.go.
-func fixLongPath(path string) string {
- // Do nothing (and don't allocate) if the path is "short".
- // Empirically (at least on the Windows Server 2013 builder),
- // the kernel is arbitrarily okay with < 248 bytes. That
- // matches what the docs above say:
- // "When using an API to create a directory, the specified
- // path cannot be so long that you cannot append an 8.3 file
- // name (that is, the directory name cannot exceed MAX_PATH
- // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
- //
- // The MSDN docs appear to say that a normal path that is 248 bytes long
- // will work; empirically the path must be less then 248 bytes long.
- if len(path) < 248 {
- // Don't fix. (This is how Go 1.7 and earlier worked,
- // not automatically generating the \\?\ form)
- return path
- }
-
- // The extended form begins with \\?\, as in
- // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
- // The extended form disables evaluation of . and .. path
- // elements and disables the interpretation of / as equivalent
- // to \. The conversion here rewrites / to \ and elides
- // . elements as well as trailing or duplicate separators. For
- // simplicity it avoids the conversion entirely for relative
- // paths or paths containing .. elements. For now,
- // \\server\share paths are not converted to
- // \\?\UNC\server\share paths because the rules for doing so
- // are less well-specified.
- if len(path) >= 2 && path[:2] == `\\` {
- // Don't canonicalize UNC paths.
- return path
- }
- if !filepath.IsAbs(path) {
- // Relative path
- return path
- }
-
- const prefix = `\\?`
-
- pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
- copy(pathbuf, prefix)
- n := len(path)
- r, w := 0, len(prefix)
- for r < n {
- switch {
- case os.IsPathSeparator(path[r]):
- // empty block
- r++
- case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
- // /./
- r++
- case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
- // /../ is currently unhandled
- return path
- default:
- pathbuf[w] = '\\'
- w++
- for ; r < n && !os.IsPathSeparator(path[r]); r++ {
- pathbuf[w] = path[r]
- w++
- }
- }
- }
- // A drive's root directory needs a trailing \
- if w == len(`\\?\c:`) {
- pathbuf[w] = '\\'
- w++
- }
- return string(pathbuf[:w])
-}
-
-// ensureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
-// often be remedied.
-// Only use `ensureRemoveAll` if you really want to make every effort to remove
-// a directory.
-//
-// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
-// can be a race between reading directory entries and then actually attempting
-// to remove everything in the directory.
-// These types of errors do not need to be returned since it's ok for the dir to
-// be gone we can just retry the remove operation.
-//
-// This should not return a `os.ErrNotExist` kind of error under any circumstances
-func ensureRemoveAll(_ context.Context, dir string) error {
- notExistErr := make(map[string]bool)
-
- // track retries
- exitOnErr := make(map[string]int)
- maxRetry := 50
-
- for {
- err := os.RemoveAll(dir)
- if err == nil {
- return nil
- }
-
- pe, ok := err.(*os.PathError)
- if !ok {
- return err
- }
-
- if os.IsNotExist(err) {
- if notExistErr[pe.Path] {
- return err
- }
- notExistErr[pe.Path] = true
-
- // There is a race where some subdir can be removed but after the
- // parent dir entries have been read.
- // So the path could be from `os.Remove(subdir)`
- // If the reported non-existent path is not the passed in `dir` we
- // should just retry, but otherwise return with no error.
- if pe.Path == dir {
- return nil
- }
- continue
- }
-
- if pe.Err != syscall.EBUSY {
- return err
- }
-
- if exitOnErr[pe.Path] == maxRetry {
- return err
- }
- exitOnErr[pe.Path]++
- time.Sleep(100 * time.Millisecond)
- }
-}
-
-func modifyProcessLabel(runtimeType string, spec *specs.Spec) error {
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/image_list.go b/vendor/github.com/containerd/cri/pkg/server/image_list.go
deleted file mode 100644
index dc6aeecc2..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/image_list.go
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// ListImages lists existing images.
-// TODO(random-liu): Add image list filters after CRI defines this more clear, and kubelet
-// actually needs it.
-func (c *criService) ListImages(ctx context.Context, r *runtime.ListImagesRequest) (*runtime.ListImagesResponse, error) {
- imagesInStore := c.imageStore.List()
-
- var images []*runtime.Image
- for _, image := range imagesInStore {
- // TODO(random-liu): [P0] Make sure corresponding snapshot exists. What if snapshot
- // doesn't exist?
- images = append(images, toCRIImage(image))
- }
-
- return &runtime.ListImagesResponse{Images: images}, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/image_pull.go b/vendor/github.com/containerd/cri/pkg/server/image_pull.go
deleted file mode 100644
index 8e2493613..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/image_pull.go
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "crypto/tls"
- "crypto/x509"
- "encoding/base64"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "net/url"
- "strings"
- "time"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/errdefs"
- containerdimages "github.com/containerd/containerd/images"
- "github.com/containerd/containerd/labels"
- "github.com/containerd/containerd/log"
- distribution "github.com/containerd/containerd/reference/docker"
- "github.com/containerd/containerd/remotes/docker"
- "github.com/containerd/imgcrypt"
- "github.com/containerd/imgcrypt/images/encryption"
- imagespec "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- criconfig "github.com/containerd/cri/pkg/config"
-)
-
-// For image management:
-// 1) We have an in-memory metadata index to:
-// a. Maintain ImageID -> RepoTags, ImageID -> RepoDigset relationships; ImageID
-// is the digest of image config, which conforms to oci image spec.
-// b. Cache constant and useful information such as image chainID, config etc.
-// c. An image will be added into the in-memory metadata only when it's successfully
-// pulled and unpacked.
-//
-// 2) We use containerd image metadata store and content store:
-// a. To resolve image reference (digest/tag) locally. During pulling image, we
-// normalize the image reference provided by user, and put it into image metadata
-// store with resolved descriptor. For the other operations, if image id is provided,
-// we'll access the in-memory metadata index directly; if image reference is
-// provided, we'll normalize it, resolve it in containerd image metadata store
-// to get the image id.
-// b. As the backup of in-memory metadata in 1). During startup, the in-memory
-// metadata could be re-constructed from image metadata store + content store.
-//
-// Several problems with current approach:
-// 1) An entry in containerd image metadata store doesn't mean a "READY" (successfully
-// pulled and unpacked) image. E.g. during pulling, the client gets killed. In that case,
-// if we saw an image without snapshots or with in-complete contents during startup,
-// should we re-pull the image? Or should we remove the entry?
-//
-// yanxuean: We can't delete image directly, because we don't know if the image
-// is pulled by us. There are resource leakage.
-//
-// 2) Containerd suggests user to add entry before pulling the image. However if
-// an error occurs during the pulling, should we remove the entry from metadata
-// store? Or should we leave it there until next startup (resource leakage)?
-//
-// 3) The cri plugin only exposes "READY" (successfully pulled and unpacked) images
-// to the user, which are maintained in the in-memory metadata index. However, it's
-// still possible that someone else removes the content or snapshot by-pass the cri plugin,
-// how do we detect that and update the in-memory metadata correspondingly? Always
-// check whether corresponding snapshot is ready when reporting image status?
-//
-// 4) Is the content important if we cached necessary information in-memory
-// after we pull the image? How to manage the disk usage of contents? If some
-// contents are missing but snapshots are ready, is the image still "READY"?
-
-// PullImage pulls an image with authentication config.
-func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (*runtime.PullImageResponse, error) {
- imageRef := r.GetImage().GetImage()
- namedRef, err := distribution.ParseDockerRef(imageRef)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to parse image reference %q", imageRef)
- }
- ref := namedRef.String()
- if ref != imageRef {
- log.G(ctx).Debugf("PullImage using normalized image ref: %q", ref)
- }
- var (
- resolver = docker.NewResolver(docker.ResolverOptions{
- Headers: c.config.Registry.Headers,
- Hosts: c.registryHosts(r.GetAuth()),
- })
- isSchema1 bool
- imageHandler containerdimages.HandlerFunc = func(_ context.Context,
- desc imagespec.Descriptor) ([]imagespec.Descriptor, error) {
- if desc.MediaType == containerdimages.MediaTypeDockerSchema1Manifest {
- isSchema1 = true
- }
- return nil, nil
- }
- )
-
- pullOpts := []containerd.RemoteOpt{
- containerd.WithSchema1Conversion,
- containerd.WithResolver(resolver),
- containerd.WithPullSnapshotter(c.config.ContainerdConfig.Snapshotter),
- containerd.WithPullUnpack,
- containerd.WithPullLabel(imageLabelKey, imageLabelValue),
- containerd.WithMaxConcurrentDownloads(c.config.MaxConcurrentDownloads),
- containerd.WithImageHandler(imageHandler),
- }
-
- pullOpts = append(pullOpts, c.encryptedImagesPullOpts()...)
- if !c.config.ContainerdConfig.DisableSnapshotAnnotations {
- pullOpts = append(pullOpts,
- containerd.WithImageHandlerWrapper(appendInfoHandlerWrapper(ref)))
- }
-
- if c.config.ContainerdConfig.DiscardUnpackedLayers {
- // Allows GC to clean layers up from the content store after unpacking
- pullOpts = append(pullOpts,
- containerd.WithChildLabelMap(containerdimages.ChildGCLabelsFilterLayers))
- }
-
- image, err := c.client.Pull(ctx, ref, pullOpts...)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to pull and unpack image %q", ref)
- }
-
- configDesc, err := image.Config(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "get image config descriptor")
- }
- imageID := configDesc.Digest.String()
-
- repoDigest, repoTag := getRepoDigestAndTag(namedRef, image.Target().Digest, isSchema1)
- for _, r := range []string{imageID, repoTag, repoDigest} {
- if r == "" {
- continue
- }
- if err := c.createImageReference(ctx, r, image.Target()); err != nil {
- return nil, errors.Wrapf(err, "failed to create image reference %q", r)
- }
- // Update image store to reflect the newest state in containerd.
- // No need to use `updateImage`, because the image reference must
- // have been managed by the cri plugin.
- if err := c.imageStore.Update(ctx, r); err != nil {
- return nil, errors.Wrapf(err, "failed to update image store %q", r)
- }
- }
-
- log.G(ctx).Debugf("Pulled image %q with image id %q, repo tag %q, repo digest %q", imageRef, imageID,
- repoTag, repoDigest)
- // NOTE(random-liu): the actual state in containerd is the source of truth, even we maintain
- // in-memory image store, it's only for in-memory indexing. The image could be removed
- // by someone else anytime, before/during/after we create the metadata. We should always
- // check the actual state in containerd before using the image or returning status of the
- // image.
- return &runtime.PullImageResponse{ImageRef: imageID}, nil
-}
-
-// ParseAuth parses AuthConfig and returns username and password/secret required by containerd.
-func ParseAuth(auth *runtime.AuthConfig, host string) (string, string, error) {
- if auth == nil {
- return "", "", nil
- }
- if auth.ServerAddress != "" {
- // Do not return the auth info when server address doesn't match.
- u, err := url.Parse(auth.ServerAddress)
- if err != nil {
- return "", "", errors.Wrap(err, "parse server address")
- }
- if host != u.Host {
- return "", "", nil
- }
- }
- if auth.Username != "" {
- return auth.Username, auth.Password, nil
- }
- if auth.IdentityToken != "" {
- return "", auth.IdentityToken, nil
- }
- if auth.Auth != "" {
- decLen := base64.StdEncoding.DecodedLen(len(auth.Auth))
- decoded := make([]byte, decLen)
- _, err := base64.StdEncoding.Decode(decoded, []byte(auth.Auth))
- if err != nil {
- return "", "", err
- }
- fields := strings.SplitN(string(decoded), ":", 2)
- if len(fields) != 2 {
- return "", "", errors.Errorf("invalid decoded auth: %q", decoded)
- }
- user, passwd := fields[0], fields[1]
- return user, strings.Trim(passwd, "\x00"), nil
- }
- // TODO(random-liu): Support RegistryToken.
- // An empty auth config is valid for anonymous registry
- return "", "", nil
-}
-
-// createImageReference creates image reference inside containerd image store.
-// Note that because create and update are not finished in one transaction, there could be race. E.g.
-// the image reference is deleted by someone else after create returns already exists, but before update
-// happens.
-func (c *criService) createImageReference(ctx context.Context, name string, desc imagespec.Descriptor) error {
- img := containerdimages.Image{
- Name: name,
- Target: desc,
- // Add a label to indicate that the image is managed by the cri plugin.
- Labels: map[string]string{imageLabelKey: imageLabelValue},
- }
- // TODO(random-liu): Figure out which is the more performant sequence create then update or
- // update then create.
- oldImg, err := c.client.ImageService().Create(ctx, img)
- if err == nil || !errdefs.IsAlreadyExists(err) {
- return err
- }
- if oldImg.Target.Digest == img.Target.Digest && oldImg.Labels[imageLabelKey] == imageLabelValue {
- return nil
- }
- _, err = c.client.ImageService().Update(ctx, img, "target", "labels")
- return err
-}
-
-// updateImage updates image store to reflect the newest state of an image reference
-// in containerd. If the reference is not managed by the cri plugin, the function also
-// generates necessary metadata for the image and make it managed.
-func (c *criService) updateImage(ctx context.Context, r string) error {
- img, err := c.client.GetImage(ctx, r)
- if err != nil && !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "get image by reference")
- }
- if err == nil && img.Labels()[imageLabelKey] != imageLabelValue {
- // Make sure the image has the image id as its unique
- // identifier that references the image in its lifetime.
- configDesc, err := img.Config(ctx)
- if err != nil {
- return errors.Wrap(err, "get image id")
- }
- id := configDesc.Digest.String()
- if err := c.createImageReference(ctx, id, img.Target()); err != nil {
- return errors.Wrapf(err, "create image id reference %q", id)
- }
- if err := c.imageStore.Update(ctx, id); err != nil {
- return errors.Wrapf(err, "update image store for %q", id)
- }
- // The image id is ready, add the label to mark the image as managed.
- if err := c.createImageReference(ctx, r, img.Target()); err != nil {
- return errors.Wrap(err, "create managed label")
- }
- }
- // If the image is not found, we should continue updating the cache,
- // so that the image can be removed from the cache.
- if err := c.imageStore.Update(ctx, r); err != nil {
- return errors.Wrapf(err, "update image store for %q", r)
- }
- return nil
-}
-
-// getTLSConfig returns a TLSConfig configured with a CA/Cert/Key specified by registryTLSConfig
-func (c *criService) getTLSConfig(registryTLSConfig criconfig.TLSConfig) (*tls.Config, error) {
- var (
- tlsConfig = &tls.Config{}
- cert tls.Certificate
- err error
- )
- if registryTLSConfig.CertFile != "" && registryTLSConfig.KeyFile == "" {
- return nil, errors.Errorf("cert file %q was specified, but no corresponding key file was specified", registryTLSConfig.CertFile)
- }
- if registryTLSConfig.CertFile == "" && registryTLSConfig.KeyFile != "" {
- return nil, errors.Errorf("key file %q was specified, but no corresponding cert file was specified", registryTLSConfig.KeyFile)
- }
- if registryTLSConfig.CertFile != "" && registryTLSConfig.KeyFile != "" {
- cert, err = tls.LoadX509KeyPair(registryTLSConfig.CertFile, registryTLSConfig.KeyFile)
- if err != nil {
- return nil, errors.Wrap(err, "failed to load cert file")
- }
- if len(cert.Certificate) != 0 {
- tlsConfig.Certificates = []tls.Certificate{cert}
- }
- tlsConfig.BuildNameToCertificate() // nolint:staticcheck
- }
-
- if registryTLSConfig.CAFile != "" {
- caCertPool, err := x509.SystemCertPool()
- if err != nil {
- return nil, errors.Wrap(err, "failed to get system cert pool")
- }
- caCert, err := ioutil.ReadFile(registryTLSConfig.CAFile)
- if err != nil {
- return nil, errors.Wrap(err, "failed to load CA file")
- }
- caCertPool.AppendCertsFromPEM(caCert)
- tlsConfig.RootCAs = caCertPool
- }
-
- tlsConfig.InsecureSkipVerify = registryTLSConfig.InsecureSkipVerify
- return tlsConfig, nil
-}
-
-// registryHosts is the registry hosts to be used by the resolver.
-func (c *criService) registryHosts(auth *runtime.AuthConfig) docker.RegistryHosts {
- return func(host string) ([]docker.RegistryHost, error) {
- var registries []docker.RegistryHost
-
- endpoints, err := c.registryEndpoints(host)
- if err != nil {
- return nil, errors.Wrap(err, "get registry endpoints")
- }
- for _, e := range endpoints {
- u, err := url.Parse(e)
- if err != nil {
- return nil, errors.Wrapf(err, "parse registry endpoint %q from mirrors", e)
- }
-
- var (
- transport = newTransport()
- client = &http.Client{Transport: transport}
- config = c.config.Registry.Configs[u.Host]
- )
-
- if config.TLS != nil {
- transport.TLSClientConfig, err = c.getTLSConfig(*config.TLS)
- if err != nil {
- return nil, errors.Wrapf(err, "get TLSConfig for registry %q", e)
- }
- }
-
- if auth == nil && config.Auth != nil {
- auth = toRuntimeAuthConfig(*config.Auth)
- }
-
- if u.Path == "" {
- u.Path = "/v2"
- }
-
- registries = append(registries, docker.RegistryHost{
- Client: client,
- Authorizer: docker.NewDockerAuthorizer(
- docker.WithAuthClient(client),
- docker.WithAuthCreds(func(host string) (string, string, error) {
- return ParseAuth(auth, host)
- })),
- Host: u.Host,
- Scheme: u.Scheme,
- Path: u.Path,
- Capabilities: docker.HostCapabilityResolve | docker.HostCapabilityPull,
- })
- }
- return registries, nil
- }
-}
-
-// defaultScheme returns the default scheme for a registry host.
-func defaultScheme(host string) string {
- if h, _, err := net.SplitHostPort(host); err == nil {
- host = h
- }
- if host == "localhost" || host == "127.0.0.1" || host == "::1" {
- return "http"
- }
- return "https"
-}
-
-// addDefaultScheme returns the endpoint with default scheme
-func addDefaultScheme(endpoint string) (string, error) {
- if strings.Contains(endpoint, "://") {
- return endpoint, nil
- }
- ue := "dummy://" + endpoint
- u, err := url.Parse(ue)
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("%s://%s", defaultScheme(u.Host), endpoint), nil
-}
-
-// registryEndpoints returns endpoints for a given host.
-// It adds default registry endpoint if it does not exist in the passed-in endpoint list.
-// It also supports wildcard host matching with `*`.
-func (c *criService) registryEndpoints(host string) ([]string, error) {
- var endpoints []string
- _, ok := c.config.Registry.Mirrors[host]
- if ok {
- endpoints = c.config.Registry.Mirrors[host].Endpoints
- } else {
- endpoints = c.config.Registry.Mirrors["*"].Endpoints
- }
- defaultHost, err := docker.DefaultHost(host)
- if err != nil {
- return nil, errors.Wrap(err, "get default host")
- }
- for i := range endpoints {
- en, err := addDefaultScheme(endpoints[i])
- if err != nil {
- return nil, errors.Wrap(err, "parse endpoint url")
- }
- endpoints[i] = en
- }
- for _, e := range endpoints {
- u, err := url.Parse(e)
- if err != nil {
- return nil, errors.Wrap(err, "parse endpoint url")
- }
- if u.Host == host {
- // Do not add default if the endpoint already exists.
- return endpoints, nil
- }
- }
- return append(endpoints, defaultScheme(defaultHost)+"://"+defaultHost), nil
-}
-
-// newTransport returns a new HTTP transport used to pull image.
-// TODO(random-liu): Create a library and share this code with `ctr`.
-func newTransport() *http.Transport {
- return &http.Transport{
- Proxy: http.ProxyFromEnvironment,
- DialContext: (&net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- FallbackDelay: 300 * time.Millisecond,
- }).DialContext,
- MaxIdleConns: 10,
- IdleConnTimeout: 30 * time.Second,
- TLSHandshakeTimeout: 10 * time.Second,
- ExpectContinueTimeout: 5 * time.Second,
- }
-}
-
-// encryptedImagesPullOpts returns the necessary list of pull options required
-// for decryption of encrypted images based on the cri decryption configuration.
-func (c *criService) encryptedImagesPullOpts() []containerd.RemoteOpt {
- if c.config.ImageDecryption.KeyModel == criconfig.KeyModelNode {
- ltdd := imgcrypt.Payload{}
- decUnpackOpt := encryption.WithUnpackConfigApplyOpts(encryption.WithDecryptedUnpack(<dd))
- opt := containerd.WithUnpackOpts([]containerd.UnpackOpt{decUnpackOpt})
- return []containerd.RemoteOpt{opt}
- }
- return nil
-}
-
-const (
- // targetRefLabel is a label which contains image reference and will be passed
- // to snapshotters.
- targetRefLabel = "containerd.io/snapshot/cri.image-ref"
- // targetDigestLabel is a label which contains layer digest and will be passed
- // to snapshotters.
- targetDigestLabel = "containerd.io/snapshot/cri.layer-digest"
- // targetImageLayersLabel is a label which contains layer digests contained in
- // the target image and will be passed to snapshotters for preparing layers in
- // parallel. Skipping some layers is allowed and only affects performance.
- targetImageLayersLabel = "containerd.io/snapshot/cri.image-layers"
-)
-
-// appendInfoHandlerWrapper makes a handler which appends some basic information
-// of images to each layer descriptor as annotations during unpack. These
-// annotations will be passed to snapshotters as labels. These labels will be
-// used mainly by stargz-based snapshotters for querying image contents from the
-// registry.
-func appendInfoHandlerWrapper(ref string) func(f containerdimages.Handler) containerdimages.Handler {
- return func(f containerdimages.Handler) containerdimages.Handler {
- return containerdimages.HandlerFunc(func(ctx context.Context, desc imagespec.Descriptor) ([]imagespec.Descriptor, error) {
- children, err := f.Handle(ctx, desc)
- if err != nil {
- return nil, err
- }
- switch desc.MediaType {
- case imagespec.MediaTypeImageManifest, containerdimages.MediaTypeDockerSchema2Manifest:
- for i := range children {
- c := &children[i]
- if containerdimages.IsLayerType(c.MediaType) {
- if c.Annotations == nil {
- c.Annotations = make(map[string]string)
- }
- c.Annotations[targetRefLabel] = ref
- c.Annotations[targetDigestLabel] = c.Digest.String()
- c.Annotations[targetImageLayersLabel] = getLayers(ctx, targetImageLayersLabel, children[i:], labels.Validate)
- }
- }
- }
- return children, nil
- })
- }
-}
-
-// getLayers returns comma-separated digests based on the passed list of
-// descriptors. The returned list contains as many digests as possible as well
-// as meets the label validation.
-func getLayers(ctx context.Context, key string, descs []imagespec.Descriptor, validate func(k, v string) error) (layers string) {
- var item string
- for _, l := range descs {
- if containerdimages.IsLayerType(l.MediaType) {
- item = l.Digest.String()
- if layers != "" {
- item = "," + item
- }
- // This avoids the label hits the size limitation.
- if err := validate(key, layers+item); err != nil {
- log.G(ctx).WithError(err).WithField("label", key).Debugf("%q is omitted in the layers list", l.Digest.String())
- break
- }
- layers += item
- }
- }
- return
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/image_remove.go b/vendor/github.com/containerd/cri/pkg/server/image_remove.go
deleted file mode 100644
index bcd02d758..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/image_remove.go
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/images"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/store"
-)
-
-// RemoveImage removes the image.
-// TODO(random-liu): Update CRI to pass image reference instead of ImageSpec. (See
-// kubernetes/kubernetes#46255)
-// TODO(random-liu): We should change CRI to distinguish image id and image spec.
-// Remove the whole image no matter the it's image id or reference. This is the
-// semantic defined in CRI now.
-func (c *criService) RemoveImage(ctx context.Context, r *runtime.RemoveImageRequest) (*runtime.RemoveImageResponse, error) {
- image, err := c.localResolve(r.GetImage().GetImage())
- if err != nil {
- if err == store.ErrNotExist {
- // return empty without error when image not found.
- return &runtime.RemoveImageResponse{}, nil
- }
- return nil, errors.Wrapf(err, "can not resolve %q locally", r.GetImage().GetImage())
- }
-
- // Remove all image references.
- for i, ref := range image.References {
- var opts []images.DeleteOpt
- if i == len(image.References)-1 {
- // Delete the last image reference synchronously to trigger garbage collection.
- // This is best effort. It is possible that the image reference is deleted by
- // someone else before this point.
- opts = []images.DeleteOpt{images.SynchronousDelete()}
- }
- err = c.client.ImageService().Delete(ctx, ref, opts...)
- if err == nil || errdefs.IsNotFound(err) {
- // Update image store to reflect the newest state in containerd.
- if err := c.imageStore.Update(ctx, ref); err != nil {
- return nil, errors.Wrapf(err, "failed to update image reference %q for %q", ref, image.ID)
- }
- continue
- }
- return nil, errors.Wrapf(err, "failed to delete image reference %q for %q", ref, image.ID)
- }
- return &runtime.RemoveImageResponse{}, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/image_status.go b/vendor/github.com/containerd/cri/pkg/server/image_status.go
deleted file mode 100644
index 5ada7b007..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/image_status.go
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "encoding/json"
-
- "github.com/containerd/containerd/log"
- imagespec "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/store"
- imagestore "github.com/containerd/cri/pkg/store/image"
-)
-
-// ImageStatus returns the status of the image, returns nil if the image isn't present.
-// TODO(random-liu): We should change CRI to distinguish image id and image spec. (See
-// kubernetes/kubernetes#46255)
-func (c *criService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (*runtime.ImageStatusResponse, error) {
- image, err := c.localResolve(r.GetImage().GetImage())
- if err != nil {
- if err == store.ErrNotExist {
- // return empty without error when image not found.
- return &runtime.ImageStatusResponse{}, nil
- }
- return nil, errors.Wrapf(err, "can not resolve %q locally", r.GetImage().GetImage())
- }
- // TODO(random-liu): [P0] Make sure corresponding snapshot exists. What if snapshot
- // doesn't exist?
-
- runtimeImage := toCRIImage(image)
- info, err := c.toCRIImageInfo(ctx, &image, r.GetVerbose())
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate image info")
- }
-
- return &runtime.ImageStatusResponse{
- Image: runtimeImage,
- Info: info,
- }, nil
-}
-
-// toCRIImage converts internal image object to CRI runtime.Image.
-func toCRIImage(image imagestore.Image) *runtime.Image {
- repoTags, repoDigests := parseImageReferences(image.References)
- runtimeImage := &runtime.Image{
- Id: image.ID,
- RepoTags: repoTags,
- RepoDigests: repoDigests,
- Size_: uint64(image.Size),
- }
- uid, username := getUserFromImage(image.ImageSpec.Config.User)
- if uid != nil {
- runtimeImage.Uid = &runtime.Int64Value{Value: *uid}
- }
- runtimeImage.Username = username
-
- return runtimeImage
-}
-
-// TODO (mikebrow): discuss moving this struct and / or constants for info map for some or all of these fields to CRI
-type verboseImageInfo struct {
- ChainID string `json:"chainID"`
- ImageSpec imagespec.Image `json:"imageSpec"`
-}
-
-// toCRIImageInfo converts internal image object information to CRI image status response info map.
-func (c *criService) toCRIImageInfo(ctx context.Context, image *imagestore.Image, verbose bool) (map[string]string, error) {
- if !verbose {
- return nil, nil
- }
-
- info := make(map[string]string)
-
- imi := &verboseImageInfo{
- ChainID: image.ChainID,
- ImageSpec: image.ImageSpec,
- }
-
- m, err := json.Marshal(imi)
- if err == nil {
- info["info"] = string(m)
- } else {
- log.G(ctx).WithError(err).Errorf("failed to marshal info %v", imi)
- info["info"] = err.Error()
- }
-
- return info, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/imagefs_info.go b/vendor/github.com/containerd/cri/pkg/server/imagefs_info.go
deleted file mode 100644
index 81dbd5cd0..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/imagefs_info.go
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "time"
-
- "golang.org/x/net/context"
-
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// ImageFsInfo returns information of the filesystem that is used to store images.
-// TODO(windows): Usage for windows is always 0 right now. Support this for windows.
-func (c *criService) ImageFsInfo(ctx context.Context, r *runtime.ImageFsInfoRequest) (*runtime.ImageFsInfoResponse, error) {
- snapshots := c.snapshotStore.List()
- timestamp := time.Now().UnixNano()
- var usedBytes, inodesUsed uint64
- for _, sn := range snapshots {
- // Use the oldest timestamp as the timestamp of imagefs info.
- if sn.Timestamp < timestamp {
- timestamp = sn.Timestamp
- }
- usedBytes += sn.Size
- inodesUsed += sn.Inodes
- }
- // TODO(random-liu): Handle content store
- return &runtime.ImageFsInfoResponse{
- ImageFilesystems: []*runtime.FilesystemUsage{
- {
- Timestamp: timestamp,
- FsId: &runtime.FilesystemIdentifier{Mountpoint: c.imageFSPath},
- UsedBytes: &runtime.UInt64Value{Value: usedBytes},
- InodesUsed: &runtime.UInt64Value{Value: inodesUsed},
- },
- },
- }, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/instrumented_service.go b/vendor/github.com/containerd/cri/pkg/server/instrumented_service.go
deleted file mode 100644
index 2c2528ab6..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/instrumented_service.go
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "errors"
-
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
-)
-
-// instrumentedService wraps service with containerd namespace and logs.
-type instrumentedService struct {
- c *criService
-}
-
-func newInstrumentedService(c *criService) grpcServices {
- return &instrumentedService{c: c}
-}
-
-// checkInitialized returns error if the server is not fully initialized.
-// GRPC service request handlers should return error before server is fully
-// initialized.
-// NOTE(random-liu): All following functions MUST check initialized at the beginning.
-func (in *instrumentedService) checkInitialized() error {
- if in.c.initialized.IsSet() {
- return nil
- }
- return errors.New("server is not initialized yet")
-}
-
-func (in *instrumentedService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (res *runtime.RunPodSandboxResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("RunPodsandbox for %+v", r.GetConfig().GetMetadata())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("RunPodSandbox for %+v failed, error", r.GetConfig().GetMetadata())
- } else {
- log.G(ctx).Infof("RunPodSandbox for %+v returns sandbox id %q", r.GetConfig().GetMetadata(), res.GetPodSandboxId())
- }
- }()
- res, err = in.c.RunPodSandbox(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ListPodSandbox(ctx context.Context, r *runtime.ListPodSandboxRequest) (res *runtime.ListPodSandboxResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("ListPodSandbox with filter %+v", r.GetFilter())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Error("ListPodSandbox failed")
- } else {
- log.G(ctx).Tracef("ListPodSandbox returns pod sandboxes %+v", res.GetItems())
- }
- }()
- res, err = in.c.ListPodSandbox(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) PodSandboxStatus(ctx context.Context, r *runtime.PodSandboxStatusRequest) (res *runtime.PodSandboxStatusResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("PodSandboxStatus for %q", r.GetPodSandboxId())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("PodSandboxStatus for %q failed", r.GetPodSandboxId())
- } else {
- log.G(ctx).Tracef("PodSandboxStatus for %q returns status %+v", r.GetPodSandboxId(), res.GetStatus())
- }
- }()
- res, err = in.c.PodSandboxStatus(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (_ *runtime.StopPodSandboxResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("StopPodSandbox for %q", r.GetPodSandboxId())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("StopPodSandbox for %q failed", r.GetPodSandboxId())
- } else {
- log.G(ctx).Infof("StopPodSandbox for %q returns successfully", r.GetPodSandboxId())
- }
- }()
- res, err := in.c.StopPodSandbox(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodSandboxRequest) (_ *runtime.RemovePodSandboxResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("RemovePodSandbox for %q", r.GetPodSandboxId())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("RemovePodSandbox for %q failed", r.GetPodSandboxId())
- } else {
- log.G(ctx).Infof("RemovePodSandbox %q returns successfully", r.GetPodSandboxId())
- }
- }()
- res, err := in.c.RemovePodSandbox(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (res *runtime.PortForwardResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("Portforward for %q port %v", r.GetPodSandboxId(), r.GetPort())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Portforward for %q failed", r.GetPodSandboxId())
- } else {
- log.G(ctx).Infof("Portforward for %q returns URL %q", r.GetPodSandboxId(), res.GetUrl())
- }
- }()
- res, err = in.c.PortForward(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) CreateContainer(ctx context.Context, r *runtime.CreateContainerRequest) (res *runtime.CreateContainerResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("CreateContainer within sandbox %q for container %+v",
- r.GetPodSandboxId(), r.GetConfig().GetMetadata())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("CreateContainer within sandbox %q for %+v failed",
- r.GetPodSandboxId(), r.GetConfig().GetMetadata())
- } else {
- log.G(ctx).Infof("CreateContainer within sandbox %q for %+v returns container id %q",
- r.GetPodSandboxId(), r.GetConfig().GetMetadata(), res.GetContainerId())
- }
- }()
- res, err = in.c.CreateContainer(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (_ *runtime.StartContainerResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("StartContainer for %q", r.GetContainerId())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("StartContainer for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Infof("StartContainer for %q returns successfully", r.GetContainerId())
- }
- }()
- res, err := in.c.StartContainer(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ListContainers(ctx context.Context, r *runtime.ListContainersRequest) (res *runtime.ListContainersResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("ListContainers with filter %+v", r.GetFilter())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("ListContainers with filter %+v failed", r.GetFilter())
- } else {
- log.G(ctx).Tracef("ListContainers with filter %+v returns containers %+v",
- r.GetFilter(), res.GetContainers())
- }
- }()
- res, err = in.c.ListContainers(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ContainerStatus(ctx context.Context, r *runtime.ContainerStatusRequest) (res *runtime.ContainerStatusResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("ContainerStatus for %q", r.GetContainerId())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("ContainerStatus for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Tracef("ContainerStatus for %q returns status %+v", r.GetContainerId(), res.GetStatus())
- }
- }()
- res, err = in.c.ContainerStatus(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) StopContainer(ctx context.Context, r *runtime.StopContainerRequest) (res *runtime.StopContainerResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("StopContainer for %q with timeout %d (s)", r.GetContainerId(), r.GetTimeout())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("StopContainer for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Infof("StopContainer for %q returns successfully", r.GetContainerId())
- }
- }()
- res, err = in.c.StopContainer(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) RemoveContainer(ctx context.Context, r *runtime.RemoveContainerRequest) (res *runtime.RemoveContainerResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("RemoveContainer for %q", r.GetContainerId())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("RemoveContainer for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Infof("RemoveContainer for %q returns successfully", r.GetContainerId())
- }
- }()
- res, err = in.c.RemoveContainer(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ExecSync(ctx context.Context, r *runtime.ExecSyncRequest) (res *runtime.ExecSyncResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("ExecSync for %q with command %+v and timeout %d (s)", r.GetContainerId(), r.GetCmd(), r.GetTimeout())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("ExecSync for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Infof("ExecSync for %q returns with exit code %d", r.GetContainerId(), res.GetExitCode())
- log.G(ctx).Debugf("ExecSync for %q outputs - stdout: %q, stderr: %q", r.GetContainerId(),
- res.GetStdout(), res.GetStderr())
- }
- }()
- res, err = in.c.ExecSync(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) Exec(ctx context.Context, r *runtime.ExecRequest) (res *runtime.ExecResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("Exec for %q with command %+v, tty %v and stdin %v",
- r.GetContainerId(), r.GetCmd(), r.GetTty(), r.GetStdin())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Exec for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Infof("Exec for %q returns URL %q", r.GetContainerId(), res.GetUrl())
- }
- }()
- res, err = in.c.Exec(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) Attach(ctx context.Context, r *runtime.AttachRequest) (res *runtime.AttachResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("Attach for %q with tty %v and stdin %v", r.GetContainerId(), r.GetTty(), r.GetStdin())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Attach for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Infof("Attach for %q returns URL %q", r.GetContainerId(), res.Url)
- }
- }()
- res, err = in.c.Attach(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) UpdateContainerResources(ctx context.Context, r *runtime.UpdateContainerResourcesRequest) (res *runtime.UpdateContainerResourcesResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("UpdateContainerResources for %q with %+v", r.GetContainerId(), r.GetLinux())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("UpdateContainerResources for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Infof("UpdateContainerResources for %q returns successfully", r.GetContainerId())
- }
- }()
- res, err = in.c.UpdateContainerResources(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (res *runtime.PullImageResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("PullImage %q", r.GetImage().GetImage())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("PullImage %q failed", r.GetImage().GetImage())
- } else {
- log.G(ctx).Infof("PullImage %q returns image reference %q",
- r.GetImage().GetImage(), res.GetImageRef())
- }
- }()
- res, err = in.c.PullImage(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ListImages(ctx context.Context, r *runtime.ListImagesRequest) (res *runtime.ListImagesResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("ListImages with filter %+v", r.GetFilter())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("ListImages with filter %+v failed", r.GetFilter())
- } else {
- log.G(ctx).Tracef("ListImages with filter %+v returns image list %+v",
- r.GetFilter(), res.GetImages())
- }
- }()
- res, err = in.c.ListImages(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (res *runtime.ImageStatusResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("ImageStatus for %q", r.GetImage().GetImage())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("ImageStatus for %q failed", r.GetImage().GetImage())
- } else {
- log.G(ctx).Tracef("ImageStatus for %q returns image status %+v",
- r.GetImage().GetImage(), res.GetImage())
- }
- }()
- res, err = in.c.ImageStatus(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) RemoveImage(ctx context.Context, r *runtime.RemoveImageRequest) (_ *runtime.RemoveImageResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Infof("RemoveImage %q", r.GetImage().GetImage())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("RemoveImage %q failed", r.GetImage().GetImage())
- } else {
- log.G(ctx).Infof("RemoveImage %q returns successfully", r.GetImage().GetImage())
- }
- }()
- res, err := in.c.RemoveImage(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ImageFsInfo(ctx context.Context, r *runtime.ImageFsInfoRequest) (res *runtime.ImageFsInfoResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Debugf("ImageFsInfo")
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Error("ImageFsInfo failed")
- } else {
- log.G(ctx).Debugf("ImageFsInfo returns filesystem info %+v", res.ImageFilesystems)
- }
- }()
- res, err = in.c.ImageFsInfo(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ContainerStats(ctx context.Context, r *runtime.ContainerStatsRequest) (res *runtime.ContainerStatsResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Debugf("ContainerStats for %q", r.GetContainerId())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("ContainerStats for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Debugf("ContainerStats for %q returns stats %+v", r.GetContainerId(), res.GetStats())
- }
- }()
- res, err = in.c.ContainerStats(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ListContainerStats(ctx context.Context, r *runtime.ListContainerStatsRequest) (res *runtime.ListContainerStatsResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("ListContainerStats with filter %+v", r.GetFilter())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Error("ListContainerStats failed")
- } else {
- log.G(ctx).Tracef("ListContainerStats returns stats %+v", res.GetStats())
- }
- }()
- res, err = in.c.ListContainerStats(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) Status(ctx context.Context, r *runtime.StatusRequest) (res *runtime.StatusResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("Status")
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Error("Status failed")
- } else {
- log.G(ctx).Tracef("Status returns status %+v", res.GetStatus())
- }
- }()
- res, err = in.c.Status(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) Version(ctx context.Context, r *runtime.VersionRequest) (res *runtime.VersionResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Tracef("Version with client side version %q", r.GetVersion())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Error("Version failed")
- } else {
- log.G(ctx).Tracef("Version returns %+v", res)
- }
- }()
- res, err = in.c.Version(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateRuntimeConfigRequest) (res *runtime.UpdateRuntimeConfigResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Debugf("UpdateRuntimeConfig with config %+v", r.GetRuntimeConfig())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Error("UpdateRuntimeConfig failed")
- } else {
- log.G(ctx).Debug("UpdateRuntimeConfig returns returns successfully")
- }
- }()
- res, err = in.c.UpdateRuntimeConfig(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
-
-func (in *instrumentedService) ReopenContainerLog(ctx context.Context, r *runtime.ReopenContainerLogRequest) (res *runtime.ReopenContainerLogResponse, err error) {
- if err := in.checkInitialized(); err != nil {
- return nil, err
- }
- log.G(ctx).Debugf("ReopenContainerLog for %q", r.GetContainerId())
- defer func() {
- if err != nil {
- log.G(ctx).WithError(err).Errorf("ReopenContainerLog for %q failed", r.GetContainerId())
- } else {
- log.G(ctx).Debugf("ReopenContainerLog for %q returns successfully", r.GetContainerId())
- }
- }()
- res, err = in.c.ReopenContainerLog(ctrdutil.WithNamespace(ctx), r)
- return res, errdefs.ToGRPC(err)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/io/container_io.go b/vendor/github.com/containerd/cri/pkg/server/io/container_io.go
deleted file mode 100644
index c66549ca5..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/io/container_io.go
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package io
-
-import (
- "errors"
- "io"
- "strings"
- "sync"
-
- "github.com/containerd/containerd/cio"
- "github.com/sirupsen/logrus"
-
- cioutil "github.com/containerd/cri/pkg/ioutil"
- "github.com/containerd/cri/pkg/util"
-)
-
-// streamKey generates a key for the stream.
-func streamKey(id, name string, stream StreamType) string {
- return strings.Join([]string{id, name, string(stream)}, "-")
-}
-
-// ContainerIO holds the container io.
-type ContainerIO struct {
- id string
-
- fifos *cio.FIFOSet
- *stdioPipes
-
- stdoutGroup *cioutil.WriterGroup
- stderrGroup *cioutil.WriterGroup
-
- closer *wgCloser
-}
-
-var _ cio.IO = &ContainerIO{}
-
-// ContainerIOOpts sets specific information to newly created ContainerIO.
-type ContainerIOOpts func(*ContainerIO) error
-
-// WithFIFOs specifies existing fifos for the container io.
-func WithFIFOs(fifos *cio.FIFOSet) ContainerIOOpts {
- return func(c *ContainerIO) error {
- c.fifos = fifos
- return nil
- }
-}
-
-// WithNewFIFOs creates new fifos for the container io.
-func WithNewFIFOs(root string, tty, stdin bool) ContainerIOOpts {
- return func(c *ContainerIO) error {
- fifos, err := newFifos(root, c.id, tty, stdin)
- if err != nil {
- return err
- }
- return WithFIFOs(fifos)(c)
- }
-}
-
-// NewContainerIO creates container io.
-func NewContainerIO(id string, opts ...ContainerIOOpts) (_ *ContainerIO, err error) {
- c := &ContainerIO{
- id: id,
- stdoutGroup: cioutil.NewWriterGroup(),
- stderrGroup: cioutil.NewWriterGroup(),
- }
- for _, opt := range opts {
- if err := opt(c); err != nil {
- return nil, err
- }
- }
- if c.fifos == nil {
- return nil, errors.New("fifos are not set")
- }
- // Create actual fifos.
- stdio, closer, err := newStdioPipes(c.fifos)
- if err != nil {
- return nil, err
- }
- c.stdioPipes = stdio
- c.closer = closer
- return c, nil
-}
-
-// Config returns io config.
-func (c *ContainerIO) Config() cio.Config {
- return c.fifos.Config
-}
-
-// Pipe creates container fifos and pipe container output
-// to output stream.
-func (c *ContainerIO) Pipe() {
- wg := c.closer.wg
- if c.stdout != nil {
- wg.Add(1)
- go func() {
- if _, err := io.Copy(c.stdoutGroup, c.stdout); err != nil {
- logrus.WithError(err).Errorf("Failed to pipe stdout of container %q", c.id)
- }
- c.stdout.Close()
- c.stdoutGroup.Close()
- wg.Done()
- logrus.Infof("Finish piping stdout of container %q", c.id)
- }()
- }
-
- if !c.fifos.Terminal && c.stderr != nil {
- wg.Add(1)
- go func() {
- if _, err := io.Copy(c.stderrGroup, c.stderr); err != nil {
- logrus.WithError(err).Errorf("Failed to pipe stderr of container %q", c.id)
- }
- c.stderr.Close()
- c.stderrGroup.Close()
- wg.Done()
- logrus.Infof("Finish piping stderr of container %q", c.id)
- }()
- }
-}
-
-// Attach attaches container stdio.
-// TODO(random-liu): Use pools.Copy in docker to reduce memory usage?
-func (c *ContainerIO) Attach(opts AttachOptions) {
- var wg sync.WaitGroup
- key := util.GenerateID()
- stdinKey := streamKey(c.id, "attach-"+key, Stdin)
- stdoutKey := streamKey(c.id, "attach-"+key, Stdout)
- stderrKey := streamKey(c.id, "attach-"+key, Stderr)
-
- var stdinStreamRC io.ReadCloser
- if c.stdin != nil && opts.Stdin != nil {
- // Create a wrapper of stdin which could be closed. Note that the
- // wrapper doesn't close the actual stdin, it only stops io.Copy.
- // The actual stdin will be closed by stream server.
- stdinStreamRC = cioutil.NewWrapReadCloser(opts.Stdin)
- wg.Add(1)
- go func() {
- if _, err := io.Copy(c.stdin, stdinStreamRC); err != nil {
- logrus.WithError(err).Errorf("Failed to pipe stdin for container attach %q", c.id)
- }
- logrus.Infof("Attach stream %q closed", stdinKey)
- if opts.StdinOnce && !opts.Tty {
- // Due to kubectl requirements and current docker behavior, when (opts.StdinOnce &&
- // opts.Tty) we have to close container stdin and keep stdout and stderr open until
- // container stops.
- c.stdin.Close()
- // Also closes the containerd side.
- if err := opts.CloseStdin(); err != nil {
- logrus.WithError(err).Errorf("Failed to close stdin for container %q", c.id)
- }
- } else {
- if opts.Stdout != nil {
- c.stdoutGroup.Remove(stdoutKey)
- }
- if opts.Stderr != nil {
- c.stderrGroup.Remove(stderrKey)
- }
- }
- wg.Done()
- }()
- }
-
- attachStream := func(key string, close <-chan struct{}) {
- <-close
- logrus.Infof("Attach stream %q closed", key)
- // Make sure stdin gets closed.
- if stdinStreamRC != nil {
- stdinStreamRC.Close()
- }
- wg.Done()
- }
-
- if opts.Stdout != nil {
- wg.Add(1)
- wc, close := cioutil.NewWriteCloseInformer(opts.Stdout)
- c.stdoutGroup.Add(stdoutKey, wc)
- go attachStream(stdoutKey, close)
- }
- if !opts.Tty && opts.Stderr != nil {
- wg.Add(1)
- wc, close := cioutil.NewWriteCloseInformer(opts.Stderr)
- c.stderrGroup.Add(stderrKey, wc)
- go attachStream(stderrKey, close)
- }
- wg.Wait()
-}
-
-// AddOutput adds new write closers to the container stream, and returns existing
-// write closers if there are any.
-func (c *ContainerIO) AddOutput(name string, stdout, stderr io.WriteCloser) (io.WriteCloser, io.WriteCloser) {
- var oldStdout, oldStderr io.WriteCloser
- if stdout != nil {
- key := streamKey(c.id, name, Stdout)
- oldStdout = c.stdoutGroup.Get(key)
- c.stdoutGroup.Add(key, stdout)
- }
- if stderr != nil {
- key := streamKey(c.id, name, Stderr)
- oldStderr = c.stderrGroup.Get(key)
- c.stderrGroup.Add(key, stderr)
- }
- return oldStdout, oldStderr
-}
-
-// Cancel cancels container io.
-func (c *ContainerIO) Cancel() {
- c.closer.Cancel()
-}
-
-// Wait waits container io to finish.
-func (c *ContainerIO) Wait() {
- c.closer.Wait()
-}
-
-// Close closes all FIFOs.
-func (c *ContainerIO) Close() error {
- c.closer.Close()
- if c.fifos != nil {
- return c.fifos.Close()
- }
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/io/exec_io.go b/vendor/github.com/containerd/cri/pkg/server/io/exec_io.go
deleted file mode 100644
index 4a695030d..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/io/exec_io.go
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package io
-
-import (
- "io"
- "sync"
-
- "github.com/containerd/containerd/cio"
- "github.com/sirupsen/logrus"
-
- cioutil "github.com/containerd/cri/pkg/ioutil"
-)
-
-// ExecIO holds the exec io.
-type ExecIO struct {
- id string
- fifos *cio.FIFOSet
- *stdioPipes
- closer *wgCloser
-}
-
-var _ cio.IO = &ExecIO{}
-
-// NewExecIO creates exec io.
-func NewExecIO(id, root string, tty, stdin bool) (*ExecIO, error) {
- fifos, err := newFifos(root, id, tty, stdin)
- if err != nil {
- return nil, err
- }
- stdio, closer, err := newStdioPipes(fifos)
- if err != nil {
- return nil, err
- }
- return &ExecIO{
- id: id,
- fifos: fifos,
- stdioPipes: stdio,
- closer: closer,
- }, nil
-}
-
-// Config returns io config.
-func (e *ExecIO) Config() cio.Config {
- return e.fifos.Config
-}
-
-// Attach attaches exec stdio. The logic is similar with container io attach.
-func (e *ExecIO) Attach(opts AttachOptions) <-chan struct{} {
- var wg sync.WaitGroup
- var stdinStreamRC io.ReadCloser
- if e.stdin != nil && opts.Stdin != nil {
- stdinStreamRC = cioutil.NewWrapReadCloser(opts.Stdin)
- wg.Add(1)
- go func() {
- if _, err := io.Copy(e.stdin, stdinStreamRC); err != nil {
- logrus.WithError(err).Errorf("Failed to redirect stdin for container exec %q", e.id)
- }
- logrus.Infof("Container exec %q stdin closed", e.id)
- if opts.StdinOnce && !opts.Tty {
- e.stdin.Close()
- if err := opts.CloseStdin(); err != nil {
- logrus.WithError(err).Errorf("Failed to close stdin for container exec %q", e.id)
- }
- } else {
- if e.stdout != nil {
- e.stdout.Close()
- }
- if e.stderr != nil {
- e.stderr.Close()
- }
- }
- wg.Done()
- }()
- }
-
- attachOutput := func(t StreamType, stream io.WriteCloser, out io.ReadCloser) {
- if _, err := io.Copy(stream, out); err != nil {
- logrus.WithError(err).Errorf("Failed to pipe %q for container exec %q", t, e.id)
- }
- out.Close()
- stream.Close()
- if stdinStreamRC != nil {
- stdinStreamRC.Close()
- }
- e.closer.wg.Done()
- wg.Done()
- logrus.Infof("Finish piping %q of container exec %q", t, e.id)
- }
-
- if opts.Stdout != nil {
- wg.Add(1)
- // Closer should wait for this routine to be over.
- e.closer.wg.Add(1)
- go attachOutput(Stdout, opts.Stdout, e.stdout)
- }
-
- if !opts.Tty && opts.Stderr != nil {
- wg.Add(1)
- // Closer should wait for this routine to be over.
- e.closer.wg.Add(1)
- go attachOutput(Stderr, opts.Stderr, e.stderr)
- }
-
- done := make(chan struct{})
- go func() {
- wg.Wait()
- close(done)
- }()
- return done
-}
-
-// Cancel cancels exec io.
-func (e *ExecIO) Cancel() {
- e.closer.Cancel()
-}
-
-// Wait waits exec io to finish.
-func (e *ExecIO) Wait() {
- e.closer.Wait()
-}
-
-// Close closes all FIFOs.
-func (e *ExecIO) Close() error {
- if e.closer != nil {
- e.closer.Close()
- }
- if e.fifos != nil {
- return e.fifos.Close()
- }
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/io/helpers.go b/vendor/github.com/containerd/cri/pkg/server/io/helpers.go
deleted file mode 100644
index 59d41411f..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/io/helpers.go
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package io
-
-import (
- "io"
- "os"
- "path/filepath"
- "sync"
- "syscall"
-
- "github.com/containerd/containerd/cio"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// AttachOptions specifies how to attach to a container.
-type AttachOptions struct {
- Stdin io.Reader
- Stdout io.WriteCloser
- Stderr io.WriteCloser
- Tty bool
- StdinOnce bool
- // CloseStdin is the function to close container stdin.
- CloseStdin func() error
-}
-
-// StreamType is the type of the stream, stdout/stderr.
-type StreamType string
-
-const (
- // Stdin stream type.
- Stdin StreamType = "stdin"
- // Stdout stream type.
- Stdout StreamType = StreamType(runtime.Stdout)
- // Stderr stream type.
- Stderr StreamType = StreamType(runtime.Stderr)
-)
-
-type wgCloser struct {
- ctx context.Context
- wg *sync.WaitGroup
- set []io.Closer
- cancel context.CancelFunc
-}
-
-func (g *wgCloser) Wait() {
- g.wg.Wait()
-}
-
-func (g *wgCloser) Close() {
- for _, f := range g.set {
- f.Close()
- }
-}
-
-func (g *wgCloser) Cancel() {
- g.cancel()
-}
-
-// newFifos creates fifos directory for a container.
-func newFifos(root, id string, tty, stdin bool) (*cio.FIFOSet, error) {
- root = filepath.Join(root, "io")
- if err := os.MkdirAll(root, 0700); err != nil {
- return nil, err
- }
- fifos, err := cio.NewFIFOSetInDir(root, id, tty)
- if err != nil {
- return nil, err
- }
- if !stdin {
- fifos.Stdin = ""
- }
- return fifos, nil
-}
-
-type stdioPipes struct {
- stdin io.WriteCloser
- stdout io.ReadCloser
- stderr io.ReadCloser
-}
-
-// newStdioPipes creates actual fifos for stdio.
-func newStdioPipes(fifos *cio.FIFOSet) (_ *stdioPipes, _ *wgCloser, err error) {
- var (
- f io.ReadWriteCloser
- set []io.Closer
- ctx, cancel = context.WithCancel(context.Background())
- p = &stdioPipes{}
- )
- defer func() {
- if err != nil {
- for _, f := range set {
- f.Close()
- }
- cancel()
- }
- }()
-
- if fifos.Stdin != "" {
- if f, err = openPipe(ctx, fifos.Stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
- return nil, nil, err
- }
- p.stdin = f
- set = append(set, f)
- }
-
- if fifos.Stdout != "" {
- if f, err = openPipe(ctx, fifos.Stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
- return nil, nil, err
- }
- p.stdout = f
- set = append(set, f)
- }
-
- if fifos.Stderr != "" {
- if f, err = openPipe(ctx, fifos.Stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
- return nil, nil, err
- }
- p.stderr = f
- set = append(set, f)
- }
-
- return p, &wgCloser{
- wg: &sync.WaitGroup{},
- set: set,
- ctx: ctx,
- cancel: cancel,
- }, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/io/helpers_unix.go b/vendor/github.com/containerd/cri/pkg/server/io/helpers_unix.go
deleted file mode 100644
index 2780b958a..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/io/helpers_unix.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// +build !windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package io
-
-import (
- "io"
- "os"
-
- "github.com/containerd/fifo"
- "golang.org/x/net/context"
-)
-
-func openPipe(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
- return fifo.OpenFifo(ctx, fn, flag, perm)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/io/helpers_windows.go b/vendor/github.com/containerd/cri/pkg/server/io/helpers_windows.go
deleted file mode 100644
index f64807edf..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/io/helpers_windows.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// +build windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package io
-
-import (
- "io"
- "net"
- "os"
- "sync"
-
- winio "github.com/Microsoft/go-winio"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
-)
-
-type pipe struct {
- l net.Listener
- con net.Conn
- conErr error
- conWg sync.WaitGroup
-}
-
-func openPipe(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
- l, err := winio.ListenPipe(fn, nil)
- if err != nil {
- return nil, err
- }
- p := &pipe{l: l}
- p.conWg.Add(1)
- go func() {
- defer p.conWg.Done()
- c, err := l.Accept()
- if err != nil {
- p.conErr = err
- return
- }
- p.con = c
- }()
- return p, nil
-}
-
-func (p *pipe) Write(b []byte) (int, error) {
- p.conWg.Wait()
- if p.conErr != nil {
- return 0, errors.Wrap(p.conErr, "connection error")
- }
- return p.con.Write(b)
-}
-
-func (p *pipe) Read(b []byte) (int, error) {
- p.conWg.Wait()
- if p.conErr != nil {
- return 0, errors.Wrap(p.conErr, "connection error")
- }
- return p.con.Read(b)
-}
-
-func (p *pipe) Close() error {
- p.l.Close()
- p.conWg.Wait()
- if p.con != nil {
- return p.con.Close()
- }
- return p.conErr
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/io/logger.go b/vendor/github.com/containerd/cri/pkg/server/io/logger.go
deleted file mode 100644
index f13b6f8bf..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/io/logger.go
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package io
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "time"
-
- "github.com/sirupsen/logrus"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- cioutil "github.com/containerd/cri/pkg/ioutil"
-)
-
-const (
- // delimiter used in CRI logging format.
- delimiter = ' '
- // eof is end-of-line.
- eol = '\n'
- // timestampFormat is the timestamp format used in CRI logging format.
- timestampFormat = time.RFC3339Nano
- // defaultBufSize is the default size of the read buffer in bytes.
- defaultBufSize = 4096
-)
-
-// NewDiscardLogger creates logger which discards all the input.
-func NewDiscardLogger() io.WriteCloser {
- return cioutil.NewNopWriteCloser(ioutil.Discard)
-}
-
-// NewCRILogger returns a write closer which redirect container log into
-// log file, and decorate the log line into CRI defined format. It also
-// returns a channel which indicates whether the logger is stopped.
-// maxLen is the max length limit of a line. A line longer than the
-// limit will be cut into multiple lines.
-func NewCRILogger(path string, w io.Writer, stream StreamType, maxLen int) (io.WriteCloser, <-chan struct{}) {
- logrus.Debugf("Start writing stream %q to log file %q", stream, path)
- prc, pwc := io.Pipe()
- stop := make(chan struct{})
- go func() {
- redirectLogs(path, prc, w, stream, maxLen)
- close(stop)
- }()
- return pwc, stop
-}
-
-// bufio.ReadLine in golang eats both read errors and tailing newlines
-// (See https://golang.org/pkg/bufio/#Reader.ReadLine). When reading
-// to io.EOF, it is impossible for the caller to figure out whether
-// there is a newline at the end, for example:
-// 1) When reading "CONTENT\n", it returns "CONTENT" without error;
-// 2) When reading "CONTENT", it also returns "CONTENT" without error.
-//
-// To differentiate these 2 cases, we need to write a readLine function
-// ourselves to not ignore the error.
-//
-// The code is similar with https://golang.org/src/bufio/bufio.go?s=9537:9604#L359.
-// The only difference is that it returns all errors from `ReadSlice`.
-//
-// readLine returns err != nil if and only if line does not end with a new line.
-func readLine(b *bufio.Reader) (line []byte, isPrefix bool, err error) {
- line, err = b.ReadSlice('\n')
- if err == bufio.ErrBufferFull {
- // Handle the case where "\r\n" straddles the buffer.
- if len(line) > 0 && line[len(line)-1] == '\r' {
- // Unread the last '\r'
- if err := b.UnreadByte(); err != nil {
- panic(fmt.Sprintf("invalid unread %v", err))
- }
- line = line[:len(line)-1]
- }
- return line, true, nil
- }
-
- if len(line) == 0 {
- if err != nil {
- line = nil
- }
- return
- }
-
- if line[len(line)-1] == '\n' {
- // "ReadSlice returns err != nil if and only if line does not end in delim"
- // (See https://golang.org/pkg/bufio/#Reader.ReadSlice).
- if err != nil {
- panic(fmt.Sprintf("full read with unexpected error %v", err))
- }
- drop := 1
- if len(line) > 1 && line[len(line)-2] == '\r' {
- drop = 2
- }
- line = line[:len(line)-drop]
- }
- return
-}
-
-func redirectLogs(path string, rc io.ReadCloser, w io.Writer, s StreamType, maxLen int) {
- defer rc.Close()
- var (
- stream = []byte(s)
- delimiter = []byte{delimiter}
- partial = []byte(runtime.LogTagPartial)
- full = []byte(runtime.LogTagFull)
- buf [][]byte
- length int
- bufSize = defaultBufSize
- )
- // Make sure bufSize <= maxLen
- if maxLen > 0 && maxLen < bufSize {
- bufSize = maxLen
- }
- r := bufio.NewReaderSize(rc, bufSize)
- writeLine := func(tag, line []byte) {
- timestamp := time.Now().AppendFormat(nil, timestampFormat)
- data := bytes.Join([][]byte{timestamp, stream, tag, line}, delimiter)
- data = append(data, eol)
- if _, err := w.Write(data); err != nil {
- logrus.WithError(err).Errorf("Fail to write %q log to log file %q", s, path)
- // Continue on write error to drain the container output.
- }
- }
- for {
- var stop bool
- newLine, isPrefix, err := readLine(r)
- // NOTE(random-liu): readLine can return actual content even if there is an error.
- if len(newLine) > 0 {
- // Buffer returned by ReadLine will change after
- // next read, copy it.
- l := make([]byte, len(newLine))
- copy(l, newLine)
- buf = append(buf, l)
- length += len(l)
- }
- if err != nil {
- if err == io.EOF {
- logrus.Debugf("Getting EOF from stream %q while redirecting to log file %q", s, path)
- } else {
- logrus.WithError(err).Errorf("An error occurred when redirecting stream %q to log file %q", s, path)
- }
- if length == 0 {
- // No content left to write, break.
- break
- }
- // Stop after writing the content left in buffer.
- stop = true
- }
- if maxLen > 0 && length > maxLen {
- exceedLen := length - maxLen
- last := buf[len(buf)-1]
- if exceedLen > len(last) {
- // exceedLen must <= len(last), or else the buffer
- // should have be written in the previous iteration.
- panic("exceed length should <= last buffer size")
- }
- buf[len(buf)-1] = last[:len(last)-exceedLen]
- writeLine(partial, bytes.Join(buf, nil))
- buf = [][]byte{last[len(last)-exceedLen:]}
- length = exceedLen
- }
- if isPrefix {
- continue
- }
- if stop {
- // readLine only returns error when the message doesn't
- // end with a newline, in that case it should be treated
- // as a partial line.
- writeLine(partial, bytes.Join(buf, nil))
- } else {
- writeLine(full, bytes.Join(buf, nil))
- }
- buf = nil
- length = 0
- if stop {
- break
- }
- }
- logrus.Debugf("Finish redirecting stream %q to log file %q", s, path)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/opts.go b/vendor/github.com/containerd/cri/pkg/server/opts.go
deleted file mode 100644
index 58520dbc3..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/opts.go
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "context"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/log"
- "github.com/containerd/nri"
- v1 "github.com/containerd/nri/types/v1"
-)
-
-// WithNRISandboxDelete calls delete for a sandbox'd task
-func WithNRISandboxDelete(sandboxID string) containerd.ProcessDeleteOpts {
- return func(ctx context.Context, p containerd.Process) error {
- task, ok := p.(containerd.Task)
- if !ok {
- return nil
- }
- nric, err := nri.New()
- if err != nil {
- log.G(ctx).WithError(err).Error("unable to create nri client")
- return nil
- }
- if nric == nil {
- return nil
- }
- sb := &nri.Sandbox{
- ID: sandboxID,
- }
- if _, err := nric.InvokeWithSandbox(ctx, task, v1.Delete, sb); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to delete nri for %q", task.ID())
- }
- return nil
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/restart.go b/vendor/github.com/containerd/cri/pkg/server/restart.go
deleted file mode 100644
index 2480bd5ea..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/restart.go
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
- goruntime "runtime"
- "time"
-
- "github.com/containerd/containerd"
- containerdio "github.com/containerd/containerd/cio"
- "github.com/containerd/containerd/errdefs"
- containerdimages "github.com/containerd/containerd/images"
- "github.com/containerd/containerd/log"
- "github.com/containerd/containerd/platforms"
- "github.com/containerd/typeurl"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- "github.com/containerd/cri/pkg/netns"
- cio "github.com/containerd/cri/pkg/server/io"
- containerstore "github.com/containerd/cri/pkg/store/container"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-// NOTE: The recovery logic has following assumption: when the cri plugin is down:
-// 1) Files (e.g. root directory, netns) and checkpoint maintained by the plugin MUST NOT be
-// touched. Or else, recovery logic for those containers/sandboxes may return error.
-// 2) Containerd containers may be deleted, but SHOULD NOT be added. Or else, recovery logic
-// for the newly added container/sandbox will return error, because there is no corresponding root
-// directory created.
-// 3) Containerd container tasks may exit or be stoppped, deleted. Even though current logic could
-// tolerant tasks being created or started, we prefer that not to happen.
-
-// recover recovers system state from containerd and status checkpoint.
-func (c *criService) recover(ctx context.Context) error {
- // Recover all sandboxes.
- sandboxes, err := c.client.Containers(ctx, filterLabel(containerKindLabel, containerKindSandbox))
- if err != nil {
- return errors.Wrap(err, "failed to list sandbox containers")
- }
- for _, sandbox := range sandboxes {
- sb, err := c.loadSandbox(ctx, sandbox)
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to load sandbox %q", sandbox.ID())
- continue
- }
- log.G(ctx).Debugf("Loaded sandbox %+v", sb)
- if err := c.sandboxStore.Add(sb); err != nil {
- return errors.Wrapf(err, "failed to add sandbox %q to store", sandbox.ID())
- }
- if err := c.sandboxNameIndex.Reserve(sb.Name, sb.ID); err != nil {
- return errors.Wrapf(err, "failed to reserve sandbox name %q", sb.Name)
- }
- }
-
- // Recover all containers.
- containers, err := c.client.Containers(ctx, filterLabel(containerKindLabel, containerKindContainer))
- if err != nil {
- return errors.Wrap(err, "failed to list containers")
- }
- for _, container := range containers {
- cntr, err := c.loadContainer(ctx, container)
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to load container %q", container.ID())
- continue
- }
- log.G(ctx).Debugf("Loaded container %+v", cntr)
- if err := c.containerStore.Add(cntr); err != nil {
- return errors.Wrapf(err, "failed to add container %q to store", container.ID())
- }
- if err := c.containerNameIndex.Reserve(cntr.Name, cntr.ID); err != nil {
- return errors.Wrapf(err, "failed to reserve container name %q", cntr.Name)
- }
- }
-
- // Recover all images.
- cImages, err := c.client.ListImages(ctx)
- if err != nil {
- return errors.Wrap(err, "failed to list images")
- }
- c.loadImages(ctx, cImages)
-
- // It's possible that containerd containers are deleted unexpectedly. In that case,
- // we can't even get metadata, we should cleanup orphaned sandbox/container directories
- // with best effort.
-
- // Cleanup orphaned sandbox and container directories without corresponding containerd container.
- for _, cleanup := range []struct {
- cntrs []containerd.Container
- base string
- errMsg string
- }{
- {
- cntrs: sandboxes,
- base: filepath.Join(c.config.RootDir, sandboxesDir),
- errMsg: "failed to cleanup orphaned sandbox directories",
- },
- {
- cntrs: sandboxes,
- base: filepath.Join(c.config.StateDir, sandboxesDir),
- errMsg: "failed to cleanup orphaned volatile sandbox directories",
- },
- {
- cntrs: containers,
- base: filepath.Join(c.config.RootDir, containersDir),
- errMsg: "failed to cleanup orphaned container directories",
- },
- {
- cntrs: containers,
- base: filepath.Join(c.config.StateDir, containersDir),
- errMsg: "failed to cleanup orphaned volatile container directories",
- },
- } {
- if err := cleanupOrphanedIDDirs(ctx, cleanup.cntrs, cleanup.base); err != nil {
- return errors.Wrap(err, cleanup.errMsg)
- }
- }
- return nil
-}
-
-// loadContainerTimeout is the default timeout for loading a container/sandbox.
-// One container/sandbox hangs (e.g. containerd#2438) should not affect other
-// containers/sandboxes.
-// Most CRI container/sandbox related operations are per container, the ones
-// which handle multiple containers at a time are:
-// * ListPodSandboxes: Don't talk with containerd services.
-// * ListContainers: Don't talk with containerd services.
-// * ListContainerStats: Not in critical code path, a default timeout will
-// be applied at CRI level.
-// * Recovery logic: We should set a time for each container/sandbox recovery.
-// * Event monitor: We should set a timeout for each container/sandbox event handling.
-const loadContainerTimeout = 10 * time.Second
-
-// loadContainer loads container from containerd and status checkpoint.
-func (c *criService) loadContainer(ctx context.Context, cntr containerd.Container) (containerstore.Container, error) {
- ctx, cancel := context.WithTimeout(ctx, loadContainerTimeout)
- defer cancel()
- id := cntr.ID()
- containerDir := c.getContainerRootDir(id)
- volatileContainerDir := c.getVolatileContainerRootDir(id)
- var container containerstore.Container
- // Load container metadata.
- exts, err := cntr.Extensions(ctx)
- if err != nil {
- return container, errors.Wrap(err, "failed to get container extensions")
- }
- ext, ok := exts[containerMetadataExtension]
- if !ok {
- return container, errors.Errorf("metadata extension %q not found", containerMetadataExtension)
- }
- data, err := typeurl.UnmarshalAny(&ext)
- if err != nil {
- return container, errors.Wrapf(err, "failed to unmarshal metadata extension %q", ext)
- }
- meta := data.(*containerstore.Metadata)
-
- // Load status from checkpoint.
- status, err := containerstore.LoadStatus(containerDir, id)
- if err != nil {
- log.G(ctx).WithError(err).Warnf("Failed to load container status for %q", id)
- status = unknownContainerStatus()
- }
-
- var containerIO *cio.ContainerIO
- err = func() error {
- // Load up-to-date status from containerd.
- t, err := cntr.Task(ctx, func(fifos *containerdio.FIFOSet) (_ containerdio.IO, err error) {
- stdoutWC, stderrWC, err := c.createContainerLoggers(meta.LogPath, meta.Config.GetTty())
- if err != nil {
- return nil, err
- }
- defer func() {
- if err != nil {
- if stdoutWC != nil {
- stdoutWC.Close()
- }
- if stderrWC != nil {
- stderrWC.Close()
- }
- }
- }()
- containerIO, err = cio.NewContainerIO(id,
- cio.WithFIFOs(fifos),
- )
- if err != nil {
- return nil, err
- }
- containerIO.AddOutput("log", stdoutWC, stderrWC)
- containerIO.Pipe()
- return containerIO, nil
- })
- if err != nil && !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to load task")
- }
- var s containerd.Status
- var notFound bool
- if errdefs.IsNotFound(err) {
- // Task is not found.
- notFound = true
- } else {
- // Task is found. Get task status.
- s, err = t.Status(ctx)
- if err != nil {
- // It's still possible that task is deleted during this window.
- if !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to get task status")
- }
- notFound = true
- }
- }
- if notFound {
- // Task is not created or has been deleted, use the checkpointed status
- // to generate container status.
- switch status.State() {
- case runtime.ContainerState_CONTAINER_CREATED:
- // NOTE: Another possibility is that we've tried to start the container, but
- // containerd got restarted during that. In that case, we still
- // treat the container as `CREATED`.
- containerIO, err = cio.NewContainerIO(id,
- cio.WithNewFIFOs(volatileContainerDir, meta.Config.GetTty(), meta.Config.GetStdin()),
- )
- if err != nil {
- return errors.Wrap(err, "failed to create container io")
- }
- case runtime.ContainerState_CONTAINER_RUNNING:
- // Container was in running state, but its task has been deleted,
- // set unknown exited state. Container io is not needed in this case.
- status.FinishedAt = time.Now().UnixNano()
- status.ExitCode = unknownExitCode
- status.Reason = unknownExitReason
- default:
- // Container is in exited/unknown state, return the status as it is.
- }
- } else {
- // Task status is found. Update container status based on the up-to-date task status.
- switch s.Status {
- case containerd.Created:
- // Task has been created, but not started yet. This could only happen if containerd
- // gets restarted during container start.
- // Container must be in `CREATED` state.
- if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to delete task")
- }
- if status.State() != runtime.ContainerState_CONTAINER_CREATED {
- return errors.Errorf("unexpected container state for created task: %q", status.State())
- }
- case containerd.Running:
- // Task is running. Container must be in `RUNNING` state, based on our assuption that
- // "task should not be started when containerd is down".
- switch status.State() {
- case runtime.ContainerState_CONTAINER_EXITED:
- return errors.Errorf("unexpected container state for running task: %q", status.State())
- case runtime.ContainerState_CONTAINER_RUNNING:
- default:
- // This may happen if containerd gets restarted after task is started, but
- // before status is checkpointed.
- status.StartedAt = time.Now().UnixNano()
- status.Pid = t.Pid()
- }
- // Wait for the task for exit monitor.
- // wait is a long running background request, no timeout needed.
- exitCh, err := t.Wait(ctrdutil.NamespacedContext())
- if err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to wait for task")
- }
- // Container was in running state, but its task has been deleted,
- // set unknown exited state.
- status.FinishedAt = time.Now().UnixNano()
- status.ExitCode = unknownExitCode
- status.Reason = unknownExitReason
- } else {
- // Start exit monitor.
- c.eventMonitor.startExitMonitor(context.Background(), id, status.Pid, exitCh)
- }
- case containerd.Stopped:
- // Task is stopped. Updata status and delete the task.
- if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to delete task")
- }
- status.FinishedAt = s.ExitTime.UnixNano()
- status.ExitCode = int32(s.ExitStatus)
- default:
- return errors.Errorf("unexpected task status %q", s.Status)
- }
- }
- return nil
- }()
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to load container status for %q", id)
- // Only set the unknown field in this case, because other fields may
- // contain useful information loaded from the checkpoint.
- status.Unknown = true
- }
- opts := []containerstore.Opts{
- containerstore.WithStatus(status, containerDir),
- containerstore.WithContainer(cntr),
- }
- // containerIO could be nil for container in unknown state.
- if containerIO != nil {
- opts = append(opts, containerstore.WithContainerIO(containerIO))
- }
- return containerstore.NewContainer(*meta, opts...)
-}
-
-// loadSandbox loads sandbox from containerd.
-func (c *criService) loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.Sandbox, error) {
- ctx, cancel := context.WithTimeout(ctx, loadContainerTimeout)
- defer cancel()
- var sandbox sandboxstore.Sandbox
- // Load sandbox metadata.
- exts, err := cntr.Extensions(ctx)
- if err != nil {
- return sandbox, errors.Wrap(err, "failed to get sandbox container extensions")
- }
- ext, ok := exts[sandboxMetadataExtension]
- if !ok {
- return sandbox, errors.Errorf("metadata extension %q not found", sandboxMetadataExtension)
- }
- data, err := typeurl.UnmarshalAny(&ext)
- if err != nil {
- return sandbox, errors.Wrapf(err, "failed to unmarshal metadata extension %q", ext)
- }
- meta := data.(*sandboxstore.Metadata)
-
- s, err := func() (sandboxstore.Status, error) {
- status := unknownSandboxStatus()
- // Load sandbox created timestamp.
- info, err := cntr.Info(ctx)
- if err != nil {
- return status, errors.Wrap(err, "failed to get sandbox container info")
- }
- status.CreatedAt = info.CreatedAt
-
- // Load sandbox state.
- t, err := cntr.Task(ctx, nil)
- if err != nil && !errdefs.IsNotFound(err) {
- return status, errors.Wrap(err, "failed to load task")
- }
- var taskStatus containerd.Status
- var notFound bool
- if errdefs.IsNotFound(err) {
- // Task is not found.
- notFound = true
- } else {
- // Task is found. Get task status.
- taskStatus, err = t.Status(ctx)
- if err != nil {
- // It's still possible that task is deleted during this window.
- if !errdefs.IsNotFound(err) {
- return status, errors.Wrap(err, "failed to get task status")
- }
- notFound = true
- }
- }
- if notFound {
- // Task does not exist, set sandbox state as NOTREADY.
- status.State = sandboxstore.StateNotReady
- } else {
- if taskStatus.Status == containerd.Running {
- // Wait for the task for sandbox monitor.
- // wait is a long running background request, no timeout needed.
- exitCh, err := t.Wait(ctrdutil.NamespacedContext())
- if err != nil {
- if !errdefs.IsNotFound(err) {
- return status, errors.Wrap(err, "failed to wait for task")
- }
- status.State = sandboxstore.StateNotReady
- } else {
- // Task is running, set sandbox state as READY.
- status.State = sandboxstore.StateReady
- status.Pid = t.Pid()
- c.eventMonitor.startExitMonitor(context.Background(), meta.ID, status.Pid, exitCh)
- }
- } else {
- // Task is not running. Delete the task and set sandbox state as NOTREADY.
- if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
- return status, errors.Wrap(err, "failed to delete task")
- }
- status.State = sandboxstore.StateNotReady
- }
- }
- return status, nil
- }()
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to load sandbox status for %q", cntr.ID())
- }
-
- sandbox = sandboxstore.NewSandbox(*meta, s)
- sandbox.Container = cntr
-
- // Load network namespace.
- if goruntime.GOOS != "windows" &&
- meta.Config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE {
- // Don't need to load netns for host network sandbox.
- return sandbox, nil
- }
- sandbox.NetNS = netns.LoadNetNS(meta.NetNSPath)
-
- // It doesn't matter whether task is running or not. If it is running, sandbox
- // status will be `READY`; if it is not running, sandbox status will be `NOT_READY`,
- // kubelet will stop the sandbox which will properly cleanup everything.
- return sandbox, nil
-}
-
-// loadImages loads images from containerd.
-func (c *criService) loadImages(ctx context.Context, cImages []containerd.Image) {
- snapshotter := c.config.ContainerdConfig.Snapshotter
- for _, i := range cImages {
- ok, _, _, _, err := containerdimages.Check(ctx, i.ContentStore(), i.Target(), platforms.Default())
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to check image content readiness for %q", i.Name())
- continue
- }
- if !ok {
- log.G(ctx).Warnf("The image content readiness for %q is not ok", i.Name())
- continue
- }
- // Checking existence of top-level snapshot for each image being recovered.
- unpacked, err := i.IsUnpacked(ctx, snapshotter)
- if err != nil {
- log.G(ctx).WithError(err).Warnf("Failed to check whether image is unpacked for image %s", i.Name())
- continue
- }
- if !unpacked {
- log.G(ctx).Warnf("The image %s is not unpacked.", i.Name())
- // TODO(random-liu): Consider whether we should try unpack here.
- }
- if err := c.updateImage(ctx, i.Name()); err != nil {
- log.G(ctx).WithError(err).Warnf("Failed to update reference for image %q", i.Name())
- continue
- }
- log.G(ctx).Debugf("Loaded image %q", i.Name())
- }
-}
-
-func cleanupOrphanedIDDirs(ctx context.Context, cntrs []containerd.Container, base string) error {
- // Cleanup orphaned id directories.
- dirs, err := ioutil.ReadDir(base)
- if err != nil && !os.IsNotExist(err) {
- return errors.Wrap(err, "failed to read base directory")
- }
- idsMap := make(map[string]containerd.Container)
- for _, cntr := range cntrs {
- idsMap[cntr.ID()] = cntr
- }
- for _, d := range dirs {
- if !d.IsDir() {
- log.G(ctx).Warnf("Invalid file %q found in base directory %q", d.Name(), base)
- continue
- }
- if _, ok := idsMap[d.Name()]; ok {
- // Do not remove id directory if corresponding container is found.
- continue
- }
- dir := filepath.Join(base, d.Name())
- if err := ensureRemoveAll(ctx, dir); err != nil {
- log.G(ctx).WithError(err).Warnf("Failed to remove id directory %q", dir)
- } else {
- log.G(ctx).Debugf("Cleanup orphaned id directory %q", dir)
- }
- }
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_list.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_list.go
deleted file mode 100644
index d2528b267..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_list.go
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-// ListPodSandbox returns a list of Sandbox.
-func (c *criService) ListPodSandbox(ctx context.Context, r *runtime.ListPodSandboxRequest) (*runtime.ListPodSandboxResponse, error) {
- // List all sandboxes from store.
- sandboxesInStore := c.sandboxStore.List()
- var sandboxes []*runtime.PodSandbox
- for _, sandboxInStore := range sandboxesInStore {
- sandboxes = append(sandboxes, toCRISandbox(
- sandboxInStore.Metadata,
- sandboxInStore.Status.Get(),
- ))
- }
-
- sandboxes = c.filterCRISandboxes(sandboxes, r.GetFilter())
- return &runtime.ListPodSandboxResponse{Items: sandboxes}, nil
-}
-
-// toCRISandbox converts sandbox metadata into CRI pod sandbox.
-func toCRISandbox(meta sandboxstore.Metadata, status sandboxstore.Status) *runtime.PodSandbox {
- // Set sandbox state to NOTREADY by default.
- state := runtime.PodSandboxState_SANDBOX_NOTREADY
- if status.State == sandboxstore.StateReady {
- state = runtime.PodSandboxState_SANDBOX_READY
- }
- return &runtime.PodSandbox{
- Id: meta.ID,
- Metadata: meta.Config.GetMetadata(),
- State: state,
- CreatedAt: status.CreatedAt.UnixNano(),
- Labels: meta.Config.GetLabels(),
- Annotations: meta.Config.GetAnnotations(),
- RuntimeHandler: meta.RuntimeHandler,
- }
-}
-
-func (c *criService) normalizePodSandboxFilter(filter *runtime.PodSandboxFilter) {
- if sb, err := c.sandboxStore.Get(filter.GetId()); err == nil {
- filter.Id = sb.ID
- }
-}
-
-// filterCRISandboxes filters CRISandboxes.
-func (c *criService) filterCRISandboxes(sandboxes []*runtime.PodSandbox, filter *runtime.PodSandboxFilter) []*runtime.PodSandbox {
- if filter == nil {
- return sandboxes
- }
-
- c.normalizePodSandboxFilter(filter)
- filtered := []*runtime.PodSandbox{}
- for _, s := range sandboxes {
- // Filter by id
- if filter.GetId() != "" && filter.GetId() != s.Id {
- continue
- }
- // Filter by state
- if filter.GetState() != nil && filter.GetState().GetState() != s.State {
- continue
- }
- // Filter by label
- if filter.GetLabelSelector() != nil {
- match := true
- for k, v := range filter.GetLabelSelector() {
- got, ok := s.Labels[k]
- if !ok || got != v {
- match = false
- break
- }
- }
- if !match {
- continue
- }
- }
- filtered = append(filtered, s)
- }
-
- return filtered
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward.go
deleted file mode 100644
index 6d382ba2b..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward.go
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-// PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
-func (c *criService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (retRes *runtime.PortForwardResponse, retErr error) {
- sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
- if err != nil {
- return nil, errors.Wrapf(err, "failed to find sandbox %q", r.GetPodSandboxId())
- }
- if sandbox.Status.Get().State != sandboxstore.StateReady {
- return nil, errors.New("sandbox container is not running")
- }
- // TODO(random-liu): Verify that ports are exposed.
- return c.streamServer.GetPortForward(r)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_unix.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_unix.go
deleted file mode 100644
index 5691c2b61..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_unix.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// +build !windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "fmt"
- "io"
- "net"
- "time"
-
- "github.com/containerd/containerd/log"
- "github.com/containernetworking/plugins/pkg/ns"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
-
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// portForward uses netns to enter the sandbox namespace, and forwards a stream inside the
-// the namespace to a specific port. It keeps forwarding until it exits or client disconnect.
-func (c *criService) portForward(ctx context.Context, id string, port int32, stream io.ReadWriteCloser) error {
- s, err := c.sandboxStore.Get(id)
- if err != nil {
- return errors.Wrapf(err, "failed to find sandbox %q in store", id)
- }
-
- var netNSDo func(func(ns.NetNS) error) error
- // netNSPath is the network namespace path for logging.
- var netNSPath string
- securityContext := s.Config.GetLinux().GetSecurityContext()
- hostNet := securityContext.GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE
- if !hostNet {
- if closed, err := s.NetNS.Closed(); err != nil {
- return errors.Wrapf(err, "failed to check netwok namespace closed for sandbox %q", id)
- } else if closed {
- return errors.Errorf("network namespace for sandbox %q is closed", id)
- }
- netNSDo = s.NetNS.Do
- netNSPath = s.NetNS.GetPath()
- } else {
- // Run the function directly for host network.
- netNSDo = func(do func(_ ns.NetNS) error) error {
- return do(nil)
- }
- netNSPath = "host"
- }
-
- log.G(ctx).Infof("Executing port forwarding in network namespace %q", netNSPath)
- err = netNSDo(func(_ ns.NetNS) error {
- defer stream.Close()
- // TODO: hardcoded to tcp4 because localhost resolves to ::1 by default if the system has IPv6 enabled.
- // Theoretically happy eyeballs will try IPv6 first and fallback to IPv4
- // but resolving localhost doesn't seem to return and IPv4 address, thus failing the connection.
- conn, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
- if err != nil {
- return errors.Wrapf(err, "failed to dial %d", port)
- }
- defer conn.Close()
-
- errCh := make(chan error, 2)
- // Copy from the the namespace port connection to the client stream
- go func() {
- log.G(ctx).Debugf("PortForward copying data from namespace %q port %d to the client stream", id, port)
- _, err := io.Copy(stream, conn)
- errCh <- err
- }()
-
- // Copy from the client stream to the namespace port connection
- go func() {
- log.G(ctx).Debugf("PortForward copying data from client stream to namespace %q port %d", id, port)
- _, err := io.Copy(conn, stream)
- errCh <- err
- }()
-
- // Wait until the first error is returned by one of the connections
- // we use errFwd to store the result of the port forwarding operation
- // if the context is cancelled close everything and return
- var errFwd error
- select {
- case errFwd = <-errCh:
- log.G(ctx).Debugf("PortForward stop forwarding in one direction in network namespace %q port %d: %v", id, port, errFwd)
- case <-ctx.Done():
- log.G(ctx).Debugf("PortForward cancelled in network namespace %q port %d: %v", id, port, ctx.Err())
- return ctx.Err()
- }
- // give a chance to terminate gracefully or timeout
- // after 1s
- // https://linux.die.net/man/1/socat
- const timeout = time.Second
- select {
- case e := <-errCh:
- if errFwd == nil {
- errFwd = e
- }
- log.G(ctx).Debugf("PortForward stopped forwarding in both directions in network namespace %q port %d: %v", id, port, e)
- case <-time.After(timeout):
- log.G(ctx).Debugf("PortForward timed out waiting to close the connection in network namespace %q port %d", id, port)
- case <-ctx.Done():
- log.G(ctx).Debugf("PortForward cancelled in network namespace %q port %d: %v", id, port, ctx.Err())
- errFwd = ctx.Err()
- }
-
- return errFwd
- })
-
- if err != nil {
- return errors.Wrapf(err, "failed to execute portforward in network namespace %q", netNSPath)
- }
- log.G(ctx).Infof("Finish port forwarding for %q port %d", id, port)
-
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_windows.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_windows.go
deleted file mode 100644
index 3c328a314..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_portforward_windows.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// +build windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "bytes"
- "fmt"
- "io"
-
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- "k8s.io/utils/exec"
-
- "github.com/containerd/cri/pkg/ioutil"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-func (c *criService) portForward(ctx context.Context, id string, port int32, stream io.ReadWriter) error {
- stdout := ioutil.NewNopWriteCloser(stream)
- stderrBuffer := new(bytes.Buffer)
- stderr := ioutil.NewNopWriteCloser(stderrBuffer)
- // localhost is resolved to 127.0.0.1 in ipv4, and ::1 in ipv6.
- // Explicitly using ipv4 IP address in here to avoid flakiness.
- cmd := []string{"wincat.exe", "127.0.0.1", fmt.Sprint(port)}
- err := c.execInSandbox(ctx, id, cmd, stream, stdout, stderr)
- if err != nil {
- return errors.Wrapf(err, "failed to execute port forward in sandbox: %s", stderrBuffer.String())
- }
- return nil
-}
-
-func (c *criService) execInSandbox(ctx context.Context, sandboxID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser) error {
- // Get sandbox from our sandbox store.
- sb, err := c.sandboxStore.Get(sandboxID)
- if err != nil {
- return errors.Wrapf(err, "failed to find sandbox %q in store", sandboxID)
- }
-
- // Check the sandbox state
- state := sb.Status.Get().State
- if state != sandboxstore.StateReady {
- return errors.Errorf("sandbox is in %s state", fmt.Sprint(state))
- }
-
- opts := execOptions{
- cmd: cmd,
- stdin: stdin,
- stdout: stdout,
- stderr: stderr,
- tty: false,
- resize: nil,
- }
- exitCode, err := c.execInternal(ctx, sb.Container, sandboxID, opts)
- if err != nil {
- return errors.Wrap(err, "failed to exec in sandbox")
- }
- if *exitCode == 0 {
- return nil
- }
- return &exec.CodeExitError{
- Err: errors.Errorf("error executing command %v, exit code %d", cmd, *exitCode),
- Code: int(*exitCode),
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_remove.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_remove.go
deleted file mode 100644
index 2c2deb2e0..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_remove.go
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/store"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-// RemovePodSandbox removes the sandbox. If there are running containers in the
-// sandbox, they should be forcibly removed.
-func (c *criService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodSandboxRequest) (*runtime.RemovePodSandboxResponse, error) {
- sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
- if err != nil {
- if err != store.ErrNotExist {
- return nil, errors.Wrapf(err, "an error occurred when try to find sandbox %q",
- r.GetPodSandboxId())
- }
- // Do not return error if the id doesn't exist.
- log.G(ctx).Tracef("RemovePodSandbox called for sandbox %q that does not exist",
- r.GetPodSandboxId())
- return &runtime.RemovePodSandboxResponse{}, nil
- }
- // Use the full sandbox id.
- id := sandbox.ID
-
- // Return error if sandbox container is still running or unknown.
- state := sandbox.Status.Get().State
- if state == sandboxstore.StateReady || state == sandboxstore.StateUnknown {
- logrus.Infof("Forcibly stopping sandbox %q", id)
- if err := c.stopPodSandbox(ctx, sandbox); err != nil {
- return nil, errors.Wrapf(err, "failed to forcibly stop sandbox %q", id)
- }
- }
-
- // Return error if sandbox network namespace is not closed yet.
- if sandbox.NetNS != nil {
- nsPath := sandbox.NetNS.GetPath()
- if closed, err := sandbox.NetNS.Closed(); err != nil {
- return nil, errors.Wrapf(err, "failed to check sandbox network namespace %q closed", nsPath)
- } else if !closed {
- return nil, errors.Errorf("sandbox network namespace %q is not fully closed", nsPath)
- }
- }
-
- // Remove all containers inside the sandbox.
- // NOTE(random-liu): container could still be created after this point, Kubelet should
- // not rely on this behavior.
- // TODO(random-liu): Introduce an intermediate state to avoid container creation after
- // this point.
- cntrs := c.containerStore.List()
- for _, cntr := range cntrs {
- if cntr.SandboxID != id {
- continue
- }
- _, err = c.RemoveContainer(ctx, &runtime.RemoveContainerRequest{ContainerId: cntr.ID})
- if err != nil {
- return nil, errors.Wrapf(err, "failed to remove container %q", cntr.ID)
- }
- }
-
- // Cleanup the sandbox root directories.
- sandboxRootDir := c.getSandboxRootDir(id)
- if err := ensureRemoveAll(ctx, sandboxRootDir); err != nil {
- return nil, errors.Wrapf(err, "failed to remove sandbox root directory %q",
- sandboxRootDir)
- }
- volatileSandboxRootDir := c.getVolatileSandboxRootDir(id)
- if err := ensureRemoveAll(ctx, volatileSandboxRootDir); err != nil {
- return nil, errors.Wrapf(err, "failed to remove volatile sandbox root directory %q",
- volatileSandboxRootDir)
- }
-
- // Delete sandbox container.
- if err := sandbox.Container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
- if !errdefs.IsNotFound(err) {
- return nil, errors.Wrapf(err, "failed to delete sandbox container %q", id)
- }
- log.G(ctx).Tracef("Remove called for sandbox container %q that does not exist", id)
- }
-
- // Remove sandbox from sandbox store. Note that once the sandbox is successfully
- // deleted:
- // 1) ListPodSandbox will not include this sandbox.
- // 2) PodSandboxStatus and StopPodSandbox will return error.
- // 3) On-going operations which have held the reference will not be affected.
- c.sandboxStore.Delete(id)
-
- // Release the sandbox name reserved for the sandbox.
- c.sandboxNameIndex.ReleaseByKey(id)
-
- return &runtime.RemovePodSandboxResponse{}, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go
deleted file mode 100644
index e0b207d7a..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go
+++ /dev/null
@@ -1,548 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "encoding/json"
- "math"
- goruntime "runtime"
- "strings"
-
- "github.com/containerd/containerd"
- containerdio "github.com/containerd/containerd/cio"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- cni "github.com/containerd/go-cni"
- "github.com/containerd/nri"
- v1 "github.com/containerd/nri/types/v1"
- "github.com/containerd/typeurl"
- "github.com/davecgh/go-spew/spew"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/annotations"
- criconfig "github.com/containerd/cri/pkg/config"
- customopts "github.com/containerd/cri/pkg/containerd/opts"
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- "github.com/containerd/cri/pkg/netns"
- "github.com/containerd/cri/pkg/server/bandwidth"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
- "github.com/containerd/cri/pkg/util"
- selinux "github.com/opencontainers/selinux/go-selinux"
-)
-
-func init() {
- typeurl.Register(&sandboxstore.Metadata{},
- "github.com/containerd/cri/pkg/store/sandbox", "Metadata")
-}
-
-// RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
-// the sandbox is in ready state.
-func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) {
- config := r.GetConfig()
- log.G(ctx).Debugf("Sandbox config %+v", config)
-
- // Generate unique id and name for the sandbox and reserve the name.
- id := util.GenerateID()
- metadata := config.GetMetadata()
- if metadata == nil {
- return nil, errors.New("sandbox config must include metadata")
- }
- name := makeSandboxName(metadata)
- log.G(ctx).Debugf("Generated id %q for sandbox %q", id, name)
- // Reserve the sandbox name to avoid concurrent `RunPodSandbox` request starting the
- // same sandbox.
- if err := c.sandboxNameIndex.Reserve(name, id); err != nil {
- return nil, errors.Wrapf(err, "failed to reserve sandbox name %q", name)
- }
- defer func() {
- // Release the name if the function returns with an error.
- if retErr != nil {
- c.sandboxNameIndex.ReleaseByName(name)
- }
- }()
-
- // Create initial internal sandbox object.
- sandbox := sandboxstore.NewSandbox(
- sandboxstore.Metadata{
- ID: id,
- Name: name,
- Config: config,
- RuntimeHandler: r.GetRuntimeHandler(),
- },
- sandboxstore.Status{
- State: sandboxstore.StateUnknown,
- },
- )
-
- // Ensure sandbox container image snapshot.
- image, err := c.ensureImageExists(ctx, c.config.SandboxImage, config)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get sandbox image %q", c.config.SandboxImage)
- }
- containerdImage, err := c.toContainerdImage(ctx, *image)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get image from containerd %q", image.ID)
- }
-
- ociRuntime, err := c.getSandboxRuntime(config, r.GetRuntimeHandler())
- if err != nil {
- return nil, errors.Wrap(err, "failed to get sandbox runtime")
- }
- log.G(ctx).Debugf("Use OCI %+v for sandbox %q", ociRuntime, id)
-
- podNetwork := true
- // Pod network is always needed on windows.
- if goruntime.GOOS != "windows" &&
- config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE {
- // Pod network is not needed on linux with host network.
- podNetwork = false
- }
- if podNetwork {
- // If it is not in host network namespace then create a namespace and set the sandbox
- // handle. NetNSPath in sandbox metadata and NetNS is non empty only for non host network
- // namespaces. If the pod is in host network namespace then both are empty and should not
- // be used.
- sandbox.NetNS, err = netns.NewNetNS()
- if err != nil {
- return nil, errors.Wrapf(err, "failed to create network namespace for sandbox %q", id)
- }
- sandbox.NetNSPath = sandbox.NetNS.GetPath()
- defer func() {
- if retErr != nil {
- // Teardown network if an error is returned.
- if err := c.teardownPodNetwork(ctx, sandbox); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
- }
-
- if err := sandbox.NetNS.Remove(); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to remove network namespace %s for sandbox %q", sandbox.NetNSPath, id)
- }
- sandbox.NetNSPath = ""
- }
- }()
-
- // Setup network for sandbox.
- // Certain VM based solutions like clear containers (Issue containerd/cri-containerd#524)
- // rely on the assumption that CRI shim will not be querying the network namespace to check the
- // network states such as IP.
- // In future runtime implementation should avoid relying on CRI shim implementation details.
- // In this case however caching the IP will add a subtle performance enhancement by avoiding
- // calls to network namespace of the pod to query the IP of the veth interface on every
- // SandboxStatus request.
- if err := c.setupPodNetwork(ctx, &sandbox); err != nil {
- return nil, errors.Wrapf(err, "failed to setup network for sandbox %q", id)
- }
- }
-
- // Create sandbox container.
- // NOTE: sandboxContainerSpec SHOULD NOT have side
- // effect, e.g. accessing/creating files, so that we can test
- // it safely.
- spec, err := c.sandboxContainerSpec(id, config, &image.ImageSpec.Config, sandbox.NetNSPath, ociRuntime.PodAnnotations)
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate sandbox container spec")
- }
- log.G(ctx).Debugf("Sandbox container %q spec: %#+v", id, spew.NewFormatter(spec))
- sandbox.ProcessLabel = spec.Process.SelinuxLabel
- defer func() {
- if retErr != nil {
- selinux.ReleaseLabel(sandbox.ProcessLabel)
- }
- }()
-
- // handle any KVM based runtime
- if err := modifyProcessLabel(ociRuntime.Type, spec); err != nil {
- return nil, err
- }
-
- if config.GetLinux().GetSecurityContext().GetPrivileged() {
- // If privileged don't set selinux label, but we still record the MCS label so that
- // the unused label can be freed later.
- spec.Process.SelinuxLabel = ""
- }
-
- // Generate spec options that will be applied to the spec later.
- specOpts, err := c.sandboxContainerSpecOpts(config, &image.ImageSpec.Config)
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate sanbdox container spec options")
- }
-
- sandboxLabels := buildLabels(config.Labels, containerKindSandbox)
-
- runtimeOpts, err := generateRuntimeOptions(ociRuntime, c.config)
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate runtime options")
- }
- opts := []containerd.NewContainerOpts{
- containerd.WithSnapshotter(c.config.ContainerdConfig.Snapshotter),
- customopts.WithNewSnapshot(id, containerdImage),
- containerd.WithSpec(spec, specOpts...),
- containerd.WithContainerLabels(sandboxLabels),
- containerd.WithContainerExtension(sandboxMetadataExtension, &sandbox.Metadata),
- containerd.WithRuntime(ociRuntime.Type, runtimeOpts)}
-
- container, err := c.client.NewContainer(ctx, id, opts...)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create containerd container")
- }
- defer func() {
- if retErr != nil {
- deferCtx, deferCancel := ctrdutil.DeferContext()
- defer deferCancel()
- if err := container.Delete(deferCtx, containerd.WithSnapshotCleanup); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to delete containerd container %q", id)
- }
- }
- }()
-
- // Create sandbox container root directories.
- sandboxRootDir := c.getSandboxRootDir(id)
- if err := c.os.MkdirAll(sandboxRootDir, 0755); err != nil {
- return nil, errors.Wrapf(err, "failed to create sandbox root directory %q",
- sandboxRootDir)
- }
- defer func() {
- if retErr != nil {
- // Cleanup the sandbox root directory.
- if err := c.os.RemoveAll(sandboxRootDir); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to remove sandbox root directory %q",
- sandboxRootDir)
- }
- }
- }()
- volatileSandboxRootDir := c.getVolatileSandboxRootDir(id)
- if err := c.os.MkdirAll(volatileSandboxRootDir, 0755); err != nil {
- return nil, errors.Wrapf(err, "failed to create volatile sandbox root directory %q",
- volatileSandboxRootDir)
- }
- defer func() {
- if retErr != nil {
- // Cleanup the volatile sandbox root directory.
- if err := c.os.RemoveAll(volatileSandboxRootDir); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to remove volatile sandbox root directory %q",
- volatileSandboxRootDir)
- }
- }
- }()
-
- // Setup files required for the sandbox.
- if err = c.setupSandboxFiles(id, config); err != nil {
- return nil, errors.Wrapf(err, "failed to setup sandbox files")
- }
- defer func() {
- if retErr != nil {
- if err = c.cleanupSandboxFiles(id, config); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to cleanup sandbox files in %q",
- sandboxRootDir)
- }
- }
- }()
-
- // Update sandbox created timestamp.
- info, err := container.Info(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get sandbox container info")
- }
-
- // Create sandbox task in containerd.
- log.G(ctx).Tracef("Create sandbox container (id=%q, name=%q).",
- id, name)
-
- taskOpts := c.taskOpts(ociRuntime.Type)
- // We don't need stdio for sandbox container.
- task, err := container.NewTask(ctx, containerdio.NullIO, taskOpts...)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create containerd task")
- }
- defer func() {
- if retErr != nil {
- deferCtx, deferCancel := ctrdutil.DeferContext()
- defer deferCancel()
- // Cleanup the sandbox container if an error is returned.
- if _, err := task.Delete(deferCtx, WithNRISandboxDelete(id), containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
- log.G(ctx).WithError(err).Errorf("Failed to delete sandbox container %q", id)
- }
- }
- }()
-
- // wait is a long running background request, no timeout needed.
- exitCh, err := task.Wait(ctrdutil.NamespacedContext())
- if err != nil {
- return nil, errors.Wrap(err, "failed to wait for sandbox container task")
- }
-
- nric, err := nri.New()
- if err != nil {
- return nil, errors.Wrap(err, "unable to create nri client")
- }
- if nric != nil {
- nriSB := &nri.Sandbox{
- ID: id,
- Labels: config.Labels,
- }
- if _, err := nric.InvokeWithSandbox(ctx, task, v1.Create, nriSB); err != nil {
- return nil, errors.Wrap(err, "nri invoke")
- }
- }
-
- if err := task.Start(ctx); err != nil {
- return nil, errors.Wrapf(err, "failed to start sandbox container task %q", id)
- }
-
- if err := sandbox.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
- // Set the pod sandbox as ready after successfully start sandbox container.
- status.Pid = task.Pid()
- status.State = sandboxstore.StateReady
- status.CreatedAt = info.CreatedAt
- return status, nil
- }); err != nil {
- return nil, errors.Wrap(err, "failed to update sandbox status")
- }
-
- // Add sandbox into sandbox store in INIT state.
- sandbox.Container = container
-
- if err := c.sandboxStore.Add(sandbox); err != nil {
- return nil, errors.Wrapf(err, "failed to add sandbox %+v into store", sandbox)
- }
-
- // start the monitor after adding sandbox into the store, this ensures
- // that sandbox is in the store, when event monitor receives the TaskExit event.
- //
- // TaskOOM from containerd may come before sandbox is added to store,
- // but we don't care about sandbox TaskOOM right now, so it is fine.
- c.eventMonitor.startExitMonitor(context.Background(), id, task.Pid(), exitCh)
-
- return &runtime.RunPodSandboxResponse{PodSandboxId: id}, nil
-}
-
-// setupPodNetwork setups up the network for a pod
-func (c *criService) setupPodNetwork(ctx context.Context, sandbox *sandboxstore.Sandbox) error {
- var (
- id = sandbox.ID
- config = sandbox.Config
- path = sandbox.NetNSPath
- )
- if c.netPlugin == nil {
- return errors.New("cni config not initialized")
- }
-
- opts, err := cniNamespaceOpts(id, config)
- if err != nil {
- return errors.Wrap(err, "get cni namespace options")
- }
-
- result, err := c.netPlugin.Setup(ctx, id, path, opts...)
- if err != nil {
- return err
- }
- logDebugCNIResult(ctx, id, result)
- // Check if the default interface has IP config
- if configs, ok := result.Interfaces[defaultIfName]; ok && len(configs.IPConfigs) > 0 {
- sandbox.IP, sandbox.AdditionalIPs = selectPodIPs(configs.IPConfigs)
- sandbox.CNIResult = result
- return nil
- }
- return errors.Errorf("failed to find network info for sandbox %q", id)
-}
-
-// cniNamespaceOpts get CNI namespace options from sandbox config.
-func cniNamespaceOpts(id string, config *runtime.PodSandboxConfig) ([]cni.NamespaceOpts, error) {
- opts := []cni.NamespaceOpts{
- cni.WithLabels(toCNILabels(id, config)),
- }
-
- portMappings := toCNIPortMappings(config.GetPortMappings())
- if len(portMappings) > 0 {
- opts = append(opts, cni.WithCapabilityPortMap(portMappings))
- }
-
- // Will return an error if the bandwidth limitation has the wrong unit
- // or an unreasonable value see validateBandwidthIsReasonable()
- bandWidth, err := toCNIBandWidth(config.Annotations)
- if err != nil {
- return nil, err
- }
- if bandWidth != nil {
- opts = append(opts, cni.WithCapabilityBandWidth(*bandWidth))
- }
-
- dns := toCNIDNS(config.GetDnsConfig())
- if dns != nil {
- opts = append(opts, cni.WithCapabilityDNS(*dns))
- }
-
- return opts, nil
-}
-
-// toCNILabels adds pod metadata into CNI labels.
-func toCNILabels(id string, config *runtime.PodSandboxConfig) map[string]string {
- return map[string]string{
- "K8S_POD_NAMESPACE": config.GetMetadata().GetNamespace(),
- "K8S_POD_NAME": config.GetMetadata().GetName(),
- "K8S_POD_INFRA_CONTAINER_ID": id,
- "IgnoreUnknown": "1",
- }
-}
-
-// toCNIBandWidth converts CRI annotations to CNI bandwidth.
-func toCNIBandWidth(annotations map[string]string) (*cni.BandWidth, error) {
- ingress, egress, err := bandwidth.ExtractPodBandwidthResources(annotations)
- if err != nil {
- return nil, errors.Wrap(err, "reading pod bandwidth annotations")
- }
-
- if ingress == nil && egress == nil {
- return nil, nil
- }
-
- bandWidth := &cni.BandWidth{}
-
- if ingress != nil {
- bandWidth.IngressRate = uint64(ingress.Value())
- bandWidth.IngressBurst = math.MaxUint32
- }
-
- if egress != nil {
- bandWidth.EgressRate = uint64(egress.Value())
- bandWidth.EgressBurst = math.MaxUint32
- }
-
- return bandWidth, nil
-}
-
-// toCNIPortMappings converts CRI port mappings to CNI.
-func toCNIPortMappings(criPortMappings []*runtime.PortMapping) []cni.PortMapping {
- var portMappings []cni.PortMapping
- for _, mapping := range criPortMappings {
- if mapping.HostPort <= 0 {
- continue
- }
- portMappings = append(portMappings, cni.PortMapping{
- HostPort: mapping.HostPort,
- ContainerPort: mapping.ContainerPort,
- Protocol: strings.ToLower(mapping.Protocol.String()),
- HostIP: mapping.HostIp,
- })
- }
- return portMappings
-}
-
-// toCNIDNS converts CRI DNSConfig to CNI.
-func toCNIDNS(dns *runtime.DNSConfig) *cni.DNS {
- if dns == nil {
- return nil
- }
- return &cni.DNS{
- Servers: dns.GetServers(),
- Searches: dns.GetSearches(),
- Options: dns.GetOptions(),
- }
-}
-
-// selectPodIPs select an ip from the ip list. It prefers ipv4 more than ipv6
-// and returns the additional ips
-// TODO(random-liu): Revisit the ip order in the ipv6 beta stage. (cri#1278)
-func selectPodIPs(ipConfigs []*cni.IPConfig) (string, []string) {
- var (
- additionalIPs []string
- ip string
- )
- for _, c := range ipConfigs {
- if c.IP.To4() != nil && ip == "" {
- ip = c.IP.String()
- } else {
- additionalIPs = append(additionalIPs, c.IP.String())
- }
- }
- if ip != "" {
- return ip, additionalIPs
- }
- if len(ipConfigs) == 1 {
- return additionalIPs[0], nil
- }
- return additionalIPs[0], additionalIPs[1:]
-}
-
-// untrustedWorkload returns true if the sandbox contains untrusted workload.
-func untrustedWorkload(config *runtime.PodSandboxConfig) bool {
- return config.GetAnnotations()[annotations.UntrustedWorkload] == "true"
-}
-
-// hostAccessingSandbox returns true if the sandbox configuration
-// requires additional host access for the sandbox.
-func hostAccessingSandbox(config *runtime.PodSandboxConfig) bool {
- securityContext := config.GetLinux().GetSecurityContext()
-
- namespaceOptions := securityContext.GetNamespaceOptions()
- if namespaceOptions.GetNetwork() == runtime.NamespaceMode_NODE ||
- namespaceOptions.GetPid() == runtime.NamespaceMode_NODE ||
- namespaceOptions.GetIpc() == runtime.NamespaceMode_NODE {
- return true
- }
-
- return false
-}
-
-// getSandboxRuntime returns the runtime configuration for sandbox.
-// If the sandbox contains untrusted workload, runtime for untrusted workload will be returned,
-// or else default runtime will be returned.
-func (c *criService) getSandboxRuntime(config *runtime.PodSandboxConfig, runtimeHandler string) (criconfig.Runtime, error) {
- if untrustedWorkload(config) {
- // If the untrusted annotation is provided, runtimeHandler MUST be empty.
- if runtimeHandler != "" && runtimeHandler != criconfig.RuntimeUntrusted {
- return criconfig.Runtime{}, errors.New("untrusted workload with explicit runtime handler is not allowed")
- }
-
- // If the untrusted workload is requesting access to the host/node, this request will fail.
- //
- // Note: If the workload is marked untrusted but requests privileged, this can be granted, as the
- // runtime may support this. For example, in a virtual-machine isolated runtime, privileged
- // is a supported option, granting the workload to access the entire guest VM instead of host.
- // TODO(windows): Deprecate this so that we don't need to handle it for windows.
- if hostAccessingSandbox(config) {
- return criconfig.Runtime{}, errors.New("untrusted workload with host access is not allowed")
- }
-
- runtimeHandler = criconfig.RuntimeUntrusted
- }
-
- if runtimeHandler == "" {
- runtimeHandler = c.config.ContainerdConfig.DefaultRuntimeName
- }
-
- handler, ok := c.config.ContainerdConfig.Runtimes[runtimeHandler]
- if !ok {
- return criconfig.Runtime{}, errors.Errorf("no runtime for %q is configured", runtimeHandler)
- }
- return handler, nil
-}
-
-func logDebugCNIResult(ctx context.Context, sandboxID string, result *cni.CNIResult) {
- if logrus.GetLevel() < logrus.DebugLevel {
- return
- }
- cniResult, err := json.Marshal(result)
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to marshal CNI result for sandbox %q: %v", sandboxID, err)
- return
- }
- log.G(ctx).Debugf("cni result for sandbox %q: %s", sandboxID, string(cniResult))
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_run_unix.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_run_unix.go
deleted file mode 100644
index ad0b85254..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_run_unix.go
+++ /dev/null
@@ -1,310 +0,0 @@
-// +build !windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "fmt"
- "os"
- "strings"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/oci"
- "github.com/containerd/containerd/plugin"
- imagespec "github.com/opencontainers/image-spec/specs-go/v1"
- runtimespec "github.com/opencontainers/runtime-spec/specs-go"
- selinux "github.com/opencontainers/selinux/go-selinux"
- "github.com/pkg/errors"
- "golang.org/x/sys/unix"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/annotations"
- customopts "github.com/containerd/cri/pkg/containerd/opts"
- osinterface "github.com/containerd/cri/pkg/os"
-)
-
-func (c *criService) sandboxContainerSpec(id string, config *runtime.PodSandboxConfig,
- imageConfig *imagespec.ImageConfig, nsPath string, runtimePodAnnotations []string) (_ *runtimespec.Spec, retErr error) {
- // Creates a spec Generator with the default spec.
- // TODO(random-liu): [P1] Compare the default settings with docker and containerd default.
- specOpts := []oci.SpecOpts{
- customopts.WithoutRunMount,
- customopts.WithoutDefaultSecuritySettings,
- customopts.WithRelativeRoot(relativeRootfsPath),
- oci.WithEnv(imageConfig.Env),
- oci.WithRootFSReadonly(),
- oci.WithHostname(config.GetHostname()),
- }
- if imageConfig.WorkingDir != "" {
- specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
- }
-
- if len(imageConfig.Entrypoint) == 0 && len(imageConfig.Cmd) == 0 {
- // Pause image must have entrypoint or cmd.
- return nil, errors.Errorf("invalid empty entrypoint and cmd in image config %+v", imageConfig)
- }
- specOpts = append(specOpts, oci.WithProcessArgs(append(imageConfig.Entrypoint, imageConfig.Cmd...)...))
-
- // Set cgroups parent.
- if c.config.DisableCgroup {
- specOpts = append(specOpts, customopts.WithDisabledCgroups)
- } else {
- if config.GetLinux().GetCgroupParent() != "" {
- cgroupsPath := getCgroupsPath(config.GetLinux().GetCgroupParent(), id)
- specOpts = append(specOpts, oci.WithCgroup(cgroupsPath))
- }
- }
-
- // When cgroup parent is not set, containerd-shim will create container in a child cgroup
- // of the cgroup itself is in.
- // TODO(random-liu): [P2] Set default cgroup path if cgroup parent is not specified.
-
- // Set namespace options.
- var (
- securityContext = config.GetLinux().GetSecurityContext()
- nsOptions = securityContext.GetNamespaceOptions()
- )
- if nsOptions.GetNetwork() == runtime.NamespaceMode_NODE {
- specOpts = append(specOpts, customopts.WithoutNamespace(runtimespec.NetworkNamespace))
- specOpts = append(specOpts, customopts.WithoutNamespace(runtimespec.UTSNamespace))
- } else {
- specOpts = append(specOpts, oci.WithLinuxNamespace(
- runtimespec.LinuxNamespace{
- Type: runtimespec.NetworkNamespace,
- Path: nsPath,
- }))
- }
- if nsOptions.GetPid() == runtime.NamespaceMode_NODE {
- specOpts = append(specOpts, customopts.WithoutNamespace(runtimespec.PIDNamespace))
- }
- if nsOptions.GetIpc() == runtime.NamespaceMode_NODE {
- specOpts = append(specOpts, customopts.WithoutNamespace(runtimespec.IPCNamespace))
- }
-
- // It's fine to generate the spec before the sandbox /dev/shm
- // is actually created.
- sandboxDevShm := c.getSandboxDevShm(id)
- if nsOptions.GetIpc() == runtime.NamespaceMode_NODE {
- sandboxDevShm = devShm
- }
- specOpts = append(specOpts, oci.WithMounts([]runtimespec.Mount{
- {
- Source: sandboxDevShm,
- Destination: devShm,
- Type: "bind",
- Options: []string{"rbind", "ro"},
- },
- // Add resolv.conf for katacontainers to setup the DNS of pod VM properly.
- {
- Source: c.getResolvPath(id),
- Destination: resolvConfPath,
- Type: "bind",
- Options: []string{"rbind", "ro"},
- },
- }))
-
- processLabel, mountLabel, err := initLabelsFromOpt(securityContext.GetSelinuxOptions())
- if err != nil {
- return nil, errors.Wrapf(err, "failed to init selinux options %+v", securityContext.GetSelinuxOptions())
- }
- defer func() {
- if retErr != nil {
- selinux.ReleaseLabel(processLabel)
- }
- }()
-
- supplementalGroups := securityContext.GetSupplementalGroups()
- specOpts = append(specOpts,
- customopts.WithSelinuxLabels(processLabel, mountLabel),
- customopts.WithSupplementalGroups(supplementalGroups),
- )
-
- // Add sysctls
- sysctls := config.GetLinux().GetSysctls()
- specOpts = append(specOpts, customopts.WithSysctls(sysctls))
-
- // Note: LinuxSandboxSecurityContext does not currently provide an apparmor profile
-
- if !c.config.DisableCgroup {
- specOpts = append(specOpts, customopts.WithDefaultSandboxShares)
- }
- specOpts = append(specOpts, customopts.WithPodOOMScoreAdj(int(defaultSandboxOOMAdj), c.config.RestrictOOMScoreAdj))
-
- for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
- runtimePodAnnotations) {
- specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
- }
-
- specOpts = append(specOpts,
- customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeSandbox),
- customopts.WithAnnotation(annotations.SandboxID, id),
- customopts.WithAnnotation(annotations.SandboxLogDir, config.GetLogDirectory()),
- )
-
- return c.runtimeSpec(id, "", specOpts...)
-}
-
-// sandboxContainerSpecOpts generates OCI spec options for
-// the sandbox container.
-func (c *criService) sandboxContainerSpecOpts(config *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
- var (
- securityContext = config.GetLinux().GetSecurityContext()
- specOpts []oci.SpecOpts
- )
- seccompSpecOpts, err := c.generateSeccompSpecOpts(
- securityContext.GetSeccompProfilePath(),
- securityContext.GetPrivileged(),
- c.seccompEnabled())
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate seccomp spec opts")
- }
- if seccompSpecOpts != nil {
- specOpts = append(specOpts, seccompSpecOpts)
- }
-
- userstr, err := generateUserString(
- "",
- securityContext.GetRunAsUser(),
- securityContext.GetRunAsGroup(),
- )
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate user string")
- }
- if userstr == "" {
- // Lastly, since no user override was passed via CRI try to set via OCI
- // Image
- userstr = imageConfig.User
- }
- if userstr != "" {
- specOpts = append(specOpts, oci.WithUser(userstr))
- }
- return specOpts, nil
-}
-
-// setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts,
-// /etc/resolv.conf and /etc/hostname.
-func (c *criService) setupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
- sandboxEtcHostname := c.getSandboxHostname(id)
- hostname := config.GetHostname()
- if hostname == "" {
- var err error
- hostname, err = c.os.Hostname()
- if err != nil {
- return errors.Wrap(err, "failed to get hostname")
- }
- }
- if err := c.os.WriteFile(sandboxEtcHostname, []byte(hostname+"\n"), 0644); err != nil {
- return errors.Wrapf(err, "failed to write hostname to %q", sandboxEtcHostname)
- }
-
- // TODO(random-liu): Consider whether we should maintain /etc/hosts and /etc/resolv.conf in kubelet.
- sandboxEtcHosts := c.getSandboxHosts(id)
- if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0644); err != nil {
- return errors.Wrapf(err, "failed to generate sandbox hosts file %q", sandboxEtcHosts)
- }
-
- // Set DNS options. Maintain a resolv.conf for the sandbox.
- var err error
- resolvContent := ""
- if dnsConfig := config.GetDnsConfig(); dnsConfig != nil {
- resolvContent, err = parseDNSOptions(dnsConfig.Servers, dnsConfig.Searches, dnsConfig.Options)
- if err != nil {
- return errors.Wrapf(err, "failed to parse sandbox DNSConfig %+v", dnsConfig)
- }
- }
- resolvPath := c.getResolvPath(id)
- if resolvContent == "" {
- // copy host's resolv.conf to resolvPath
- err = c.os.CopyFile(resolvConfPath, resolvPath, 0644)
- if err != nil {
- return errors.Wrapf(err, "failed to copy host's resolv.conf to %q", resolvPath)
- }
- } else {
- err = c.os.WriteFile(resolvPath, []byte(resolvContent), 0644)
- if err != nil {
- return errors.Wrapf(err, "failed to write resolv content to %q", resolvPath)
- }
- }
-
- // Setup sandbox /dev/shm.
- if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetIpc() == runtime.NamespaceMode_NODE {
- if _, err := c.os.Stat(devShm); err != nil {
- return errors.Wrapf(err, "host %q is not available for host ipc", devShm)
- }
- } else {
- sandboxDevShm := c.getSandboxDevShm(id)
- if err := c.os.MkdirAll(sandboxDevShm, 0700); err != nil {
- return errors.Wrap(err, "failed to create sandbox shm")
- }
- shmproperty := fmt.Sprintf("mode=1777,size=%d", defaultShmSize)
- if err := c.os.(osinterface.UNIX).Mount("shm", sandboxDevShm, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), shmproperty); err != nil {
- return errors.Wrap(err, "failed to mount sandbox shm")
- }
- }
-
- return nil
-}
-
-// parseDNSOptions parse DNS options into resolv.conf format content,
-// if none option is specified, will return empty with no error.
-func parseDNSOptions(servers, searches, options []string) (string, error) {
- resolvContent := ""
-
- if len(searches) > maxDNSSearches {
- return "", errors.Errorf("DNSOption.Searches has more than %d domains", maxDNSSearches)
- }
-
- if len(searches) > 0 {
- resolvContent += fmt.Sprintf("search %s\n", strings.Join(searches, " "))
- }
-
- if len(servers) > 0 {
- resolvContent += fmt.Sprintf("nameserver %s\n", strings.Join(servers, "\nnameserver "))
- }
-
- if len(options) > 0 {
- resolvContent += fmt.Sprintf("options %s\n", strings.Join(options, " "))
- }
-
- return resolvContent, nil
-}
-
-// cleanupSandboxFiles unmount some sandbox files, we rely on the removal of sandbox root directory to
-// remove these files. Unmount should *NOT* return error if the mount point is already unmounted.
-func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
- if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetIpc() != runtime.NamespaceMode_NODE {
- path, err := c.os.FollowSymlinkInScope(c.getSandboxDevShm(id), "/")
- if err != nil {
- return errors.Wrap(err, "failed to follow symlink")
- }
- if err := c.os.(osinterface.UNIX).Unmount(path); err != nil && !os.IsNotExist(err) {
- return errors.Wrapf(err, "failed to unmount %q", path)
- }
- }
- return nil
-}
-
-// taskOpts generates task options for a (sandbox) container.
-func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
- // TODO(random-liu): Remove this after shim v1 is deprecated.
- var taskOpts []containerd.NewTaskOpts
- if c.config.NoPivot && (runtimeType == plugin.RuntimeRuncV1 || runtimeType == plugin.RuntimeRuncV2) {
- taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
- }
- return taskOpts
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_run_windows.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_run_windows.go
deleted file mode 100644
index 85105c299..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_run_windows.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// +build windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/oci"
- imagespec "github.com/opencontainers/image-spec/specs-go/v1"
- runtimespec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/annotations"
- customopts "github.com/containerd/cri/pkg/containerd/opts"
-)
-
-func (c *criService) sandboxContainerSpec(id string, config *runtime.PodSandboxConfig,
- imageConfig *imagespec.ImageConfig, nsPath string, runtimePodAnnotations []string) (*runtimespec.Spec, error) {
- // Creates a spec Generator with the default spec.
- specOpts := []oci.SpecOpts{
- oci.WithEnv(imageConfig.Env),
- oci.WithHostname(config.GetHostname()),
- }
- if imageConfig.WorkingDir != "" {
- specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
- }
-
- if len(imageConfig.Entrypoint) == 0 && len(imageConfig.Cmd) == 0 {
- // Pause image must have entrypoint or cmd.
- return nil, errors.Errorf("invalid empty entrypoint and cmd in image config %+v", imageConfig)
- }
- specOpts = append(specOpts, oci.WithProcessArgs(append(imageConfig.Entrypoint, imageConfig.Cmd...)...))
-
- specOpts = append(specOpts,
- // Clear the root location since hcsshim expects it.
- // NOTE: readonly rootfs doesn't work on windows.
- customopts.WithoutRoot,
- customopts.WithWindowsNetworkNamespace(nsPath),
- )
-
- specOpts = append(specOpts, customopts.WithWindowsDefaultSandboxShares)
-
- for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
- runtimePodAnnotations) {
- specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
- }
-
- specOpts = append(specOpts,
- customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeSandbox),
- customopts.WithAnnotation(annotations.SandboxID, id),
- customopts.WithAnnotation(annotations.SandboxLogDir, config.GetLogDirectory()),
- )
-
- return c.runtimeSpec(id, "", specOpts...)
-}
-
-// No sandbox container spec options for windows yet.
-func (c *criService) sandboxContainerSpecOpts(config *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
- return nil, nil
-}
-
-// No sandbox files needed for windows.
-func (c *criService) setupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
- return nil
-}
-
-// No sandbox files needed for windows.
-func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
- return nil
-}
-
-// No task options needed for windows.
-func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_status.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_status.go
deleted file mode 100644
index 5644ab1be..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_status.go
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "encoding/json"
- goruntime "runtime"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/errdefs"
- cni "github.com/containerd/go-cni"
- runtimespec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-// PodSandboxStatus returns the status of the PodSandbox.
-func (c *criService) PodSandboxStatus(ctx context.Context, r *runtime.PodSandboxStatusRequest) (*runtime.PodSandboxStatusResponse, error) {
- sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
- if err != nil {
- return nil, errors.Wrap(err, "an error occurred when try to find sandbox")
- }
-
- ip, additionalIPs, err := c.getIPs(sandbox)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get sandbox ip")
- }
- status := toCRISandboxStatus(sandbox.Metadata, sandbox.Status.Get(), ip, additionalIPs)
- if status.GetCreatedAt() == 0 {
- // CRI doesn't allow CreatedAt == 0.
- info, err := sandbox.Container.Info(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get CreatedAt for sandbox container in %q state", status.State)
- }
- status.CreatedAt = info.CreatedAt.UnixNano()
- }
- if !r.GetVerbose() {
- return &runtime.PodSandboxStatusResponse{Status: status}, nil
- }
-
- // Generate verbose information.
- info, err := toCRISandboxInfo(ctx, sandbox)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get verbose sandbox container info")
- }
-
- return &runtime.PodSandboxStatusResponse{
- Status: status,
- Info: info,
- }, nil
-}
-
-func (c *criService) getIPs(sandbox sandboxstore.Sandbox) (string, []string, error) {
- config := sandbox.Config
-
- if goruntime.GOOS != "windows" &&
- config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE {
- // For sandboxes using the node network we are not
- // responsible for reporting the IP.
- return "", nil, nil
- }
-
- if closed, err := sandbox.NetNS.Closed(); err != nil {
- return "", nil, errors.Wrap(err, "check network namespace closed")
- } else if closed {
- return "", nil, nil
- }
-
- return sandbox.IP, sandbox.AdditionalIPs, nil
-}
-
-// toCRISandboxStatus converts sandbox metadata into CRI pod sandbox status.
-func toCRISandboxStatus(meta sandboxstore.Metadata, status sandboxstore.Status, ip string, additionalIPs []string) *runtime.PodSandboxStatus {
- // Set sandbox state to NOTREADY by default.
- state := runtime.PodSandboxState_SANDBOX_NOTREADY
- if status.State == sandboxstore.StateReady {
- state = runtime.PodSandboxState_SANDBOX_READY
- }
- nsOpts := meta.Config.GetLinux().GetSecurityContext().GetNamespaceOptions()
- var ips []*runtime.PodIP
- for _, additionalIP := range additionalIPs {
- ips = append(ips, &runtime.PodIP{Ip: additionalIP})
- }
- return &runtime.PodSandboxStatus{
- Id: meta.ID,
- Metadata: meta.Config.GetMetadata(),
- State: state,
- CreatedAt: status.CreatedAt.UnixNano(),
- Network: &runtime.PodSandboxNetworkStatus{
- Ip: ip,
- AdditionalIps: ips,
- },
- Linux: &runtime.LinuxPodSandboxStatus{
- Namespaces: &runtime.Namespace{
- Options: &runtime.NamespaceOption{
- Network: nsOpts.GetNetwork(),
- Pid: nsOpts.GetPid(),
- Ipc: nsOpts.GetIpc(),
- },
- },
- },
- Labels: meta.Config.GetLabels(),
- Annotations: meta.Config.GetAnnotations(),
- RuntimeHandler: meta.RuntimeHandler,
- }
-}
-
-// SandboxInfo is extra information for sandbox.
-// TODO (mikebrow): discuss predefining constants structures for some or all of these field names in CRI
-type SandboxInfo struct {
- Pid uint32 `json:"pid"`
- Status string `json:"processStatus"`
- NetNSClosed bool `json:"netNamespaceClosed"`
- Image string `json:"image"`
- SnapshotKey string `json:"snapshotKey"`
- Snapshotter string `json:"snapshotter"`
- // Note: a new field `RuntimeHandler` has been added into the CRI PodSandboxStatus struct, and
- // should be set. This `RuntimeHandler` field will be deprecated after containerd 1.3 (tracked
- // in https://github.com/containerd/cri/issues/1064).
- RuntimeHandler string `json:"runtimeHandler"` // see the Note above
- RuntimeType string `json:"runtimeType"`
- RuntimeOptions interface{} `json:"runtimeOptions"`
- Config *runtime.PodSandboxConfig `json:"config"`
- RuntimeSpec *runtimespec.Spec `json:"runtimeSpec"`
- CNIResult *cni.CNIResult `json:"cniResult"`
-}
-
-// toCRISandboxInfo converts internal container object information to CRI sandbox status response info map.
-func toCRISandboxInfo(ctx context.Context, sandbox sandboxstore.Sandbox) (map[string]string, error) {
- container := sandbox.Container
- task, err := container.Task(ctx, nil)
- if err != nil && !errdefs.IsNotFound(err) {
- return nil, errors.Wrap(err, "failed to get sandbox container task")
- }
-
- var processStatus containerd.ProcessStatus
- if task != nil {
- taskStatus, err := task.Status(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get task status")
- }
-
- processStatus = taskStatus.Status
- }
-
- si := &SandboxInfo{
- Pid: sandbox.Status.Get().Pid,
- RuntimeHandler: sandbox.RuntimeHandler,
- Status: string(processStatus),
- Config: sandbox.Config,
- CNIResult: sandbox.CNIResult,
- }
-
- if si.Status == "" {
- // If processStatus is empty, it means that the task is deleted. Apply "deleted"
- // status which does not exist in containerd.
- si.Status = "deleted"
- }
-
- if sandbox.NetNS != nil {
- // Add network closed information if sandbox is not using host network.
- closed, err := sandbox.NetNS.Closed()
- if err != nil {
- return nil, errors.Wrap(err, "failed to check network namespace closed")
- }
- si.NetNSClosed = closed
- }
-
- spec, err := container.Spec(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get sandbox container runtime spec")
- }
- si.RuntimeSpec = spec
-
- ctrInfo, err := container.Info(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get sandbox container info")
- }
- // Do not use config.SandboxImage because the configuration might
- // be changed during restart. It may not reflect the actual image
- // used by the sandbox container.
- si.Image = ctrInfo.Image
- si.SnapshotKey = ctrInfo.SnapshotKey
- si.Snapshotter = ctrInfo.Snapshotter
-
- runtimeOptions, err := getRuntimeOptions(ctrInfo)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get runtime options")
- }
- si.RuntimeType = ctrInfo.Runtime.Name
- si.RuntimeOptions = runtimeOptions
-
- infoBytes, err := json.Marshal(si)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to marshal info %v", si)
- }
- return map[string]string{
- "info": string(infoBytes),
- }, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/sandbox_stop.go b/vendor/github.com/containerd/cri/pkg/server/sandbox_stop.go
deleted file mode 100644
index 9b6e0a6ec..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/sandbox_stop.go
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "syscall"
- "time"
-
- eventtypes "github.com/containerd/containerd/api/events"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/log"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
-)
-
-// StopPodSandbox stops the sandbox. If there are any running containers in the
-// sandbox, they should be forcibly terminated.
-func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (*runtime.StopPodSandboxResponse, error) {
- sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
- if err != nil {
- return nil, errors.Wrapf(err, "an error occurred when try to find sandbox %q",
- r.GetPodSandboxId())
- }
-
- if err := c.stopPodSandbox(ctx, sandbox); err != nil {
- return nil, err
- }
-
- return &runtime.StopPodSandboxResponse{}, nil
-}
-
-func (c *criService) stopPodSandbox(ctx context.Context, sandbox sandboxstore.Sandbox) error {
- // Use the full sandbox id.
- id := sandbox.ID
-
- // Stop all containers inside the sandbox. This terminates the container forcibly,
- // and container may still be created, so production should not rely on this behavior.
- // TODO(random-liu): Introduce a state in sandbox to avoid future container creation.
- containers := c.containerStore.List()
- for _, container := range containers {
- if container.SandboxID != id {
- continue
- }
- // Forcibly stop the container. Do not use `StopContainer`, because it introduces a race
- // if a container is removed after list.
- if err := c.stopContainer(ctx, container, 0); err != nil {
- return errors.Wrapf(err, "failed to stop container %q", container.ID)
- }
- }
-
- if err := c.cleanupSandboxFiles(id, sandbox.Config); err != nil {
- return errors.Wrap(err, "failed to cleanup sandbox files")
- }
-
- // Only stop sandbox container when it's running or unknown.
- state := sandbox.Status.Get().State
- if state == sandboxstore.StateReady || state == sandboxstore.StateUnknown {
- if err := c.stopSandboxContainer(ctx, sandbox); err != nil {
- return errors.Wrapf(err, "failed to stop sandbox container %q in %q state", id, state)
- }
- }
-
- // Teardown network for sandbox.
- if sandbox.NetNS != nil {
- // Use empty netns path if netns is not available. This is defined in:
- // https://github.com/containernetworking/cni/blob/v0.7.0-alpha1/SPEC.md
- if closed, err := sandbox.NetNS.Closed(); err != nil {
- return errors.Wrap(err, "failed to check network namespace closed")
- } else if closed {
- sandbox.NetNSPath = ""
- }
- if err := c.teardownPodNetwork(ctx, sandbox); err != nil {
- return errors.Wrapf(err, "failed to destroy network for sandbox %q", id)
- }
- if err := sandbox.NetNS.Remove(); err != nil {
- return errors.Wrapf(err, "failed to remove network namespace for sandbox %q", id)
- }
- }
-
- log.G(ctx).Infof("TearDown network for sandbox %q successfully", id)
-
- return nil
-}
-
-// stopSandboxContainer kills the sandbox container.
-// `task.Delete` is not called here because it will be called when
-// the event monitor handles the `TaskExit` event.
-func (c *criService) stopSandboxContainer(ctx context.Context, sandbox sandboxstore.Sandbox) error {
- id := sandbox.ID
- container := sandbox.Container
- state := sandbox.Status.Get().State
- task, err := container.Task(ctx, nil)
- if err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to get sandbox container")
- }
- // Don't return for unknown state, some cleanup needs to be done.
- if state == sandboxstore.StateUnknown {
- return cleanupUnknownSandbox(ctx, id, sandbox)
- }
- return nil
- }
-
- // Handle unknown state.
- // The cleanup logic is the same with container unknown state.
- if state == sandboxstore.StateUnknown {
- // Start an exit handler for containers in unknown state.
- waitCtx, waitCancel := context.WithCancel(ctrdutil.NamespacedContext())
- defer waitCancel()
- exitCh, err := task.Wait(waitCtx)
- if err != nil {
- if !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to wait for task")
- }
- return cleanupUnknownSandbox(ctx, id, sandbox)
- }
-
- exitCtx, exitCancel := context.WithCancel(context.Background())
- stopCh := c.eventMonitor.startExitMonitor(exitCtx, id, task.Pid(), exitCh)
- defer func() {
- exitCancel()
- // This ensures that exit monitor is stopped before
- // `Wait` is cancelled, so no exit event is generated
- // because of the `Wait` cancellation.
- <-stopCh
- }()
- }
-
- // Kill the sandbox container.
- if err = task.Kill(ctx, syscall.SIGKILL); err != nil && !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "failed to kill sandbox container")
- }
-
- return c.waitSandboxStop(ctx, sandbox)
-}
-
-// waitSandboxStop waits for sandbox to be stopped until context is cancelled or
-// the context deadline is exceeded.
-func (c *criService) waitSandboxStop(ctx context.Context, sandbox sandboxstore.Sandbox) error {
- select {
- case <-ctx.Done():
- return errors.Wrapf(ctx.Err(), "wait sandbox container %q", sandbox.ID)
- case <-sandbox.Stopped():
- return nil
- }
-}
-
-// teardownPodNetwork removes the network from the pod
-func (c *criService) teardownPodNetwork(ctx context.Context, sandbox sandboxstore.Sandbox) error {
- if c.netPlugin == nil {
- return errors.New("cni config not initialized")
- }
-
- var (
- id = sandbox.ID
- path = sandbox.NetNSPath
- config = sandbox.Config
- )
- opts, err := cniNamespaceOpts(id, config)
- if err != nil {
- return errors.Wrap(err, "get cni namespace options")
- }
-
- return c.netPlugin.Remove(ctx, id, path, opts...)
-}
-
-// cleanupUnknownSandbox cleanup stopped sandbox in unknown state.
-func cleanupUnknownSandbox(ctx context.Context, id string, sandbox sandboxstore.Sandbox) error {
- // Reuse handleSandboxExit to do the cleanup.
- return handleSandboxExit(ctx, &eventtypes.TaskExit{
- ContainerID: id,
- ID: id,
- Pid: 0,
- ExitStatus: unknownExitCode,
- ExitedAt: time.Now(),
- }, sandbox)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/service.go b/vendor/github.com/containerd/cri/pkg/server/service.go
deleted file mode 100644
index 94e02591a..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/service.go
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "os"
- "path/filepath"
- "time"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/oci"
- "github.com/containerd/containerd/plugin"
- "github.com/containerd/cri/pkg/streaming"
- cni "github.com/containerd/go-cni"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "google.golang.org/grpc"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/store/label"
-
- "github.com/containerd/cri/pkg/atomic"
- criconfig "github.com/containerd/cri/pkg/config"
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- osinterface "github.com/containerd/cri/pkg/os"
- "github.com/containerd/cri/pkg/registrar"
- containerstore "github.com/containerd/cri/pkg/store/container"
- imagestore "github.com/containerd/cri/pkg/store/image"
- sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
- snapshotstore "github.com/containerd/cri/pkg/store/snapshot"
-)
-
-// grpcServices are all the grpc services provided by cri containerd.
-type grpcServices interface {
- runtime.RuntimeServiceServer
- runtime.ImageServiceServer
-}
-
-// CRIService is the interface implement CRI remote service server.
-type CRIService interface {
- Run() error
- // io.Closer is used by containerd to gracefully stop cri service.
- io.Closer
- plugin.Service
- grpcServices
-}
-
-// criService implements CRIService.
-type criService struct {
- // config contains all configurations.
- config criconfig.Config
- // imageFSPath is the path to image filesystem.
- imageFSPath string
- // os is an interface for all required os operations.
- os osinterface.OS
- // sandboxStore stores all resources associated with sandboxes.
- sandboxStore *sandboxstore.Store
- // sandboxNameIndex stores all sandbox names and make sure each name
- // is unique.
- sandboxNameIndex *registrar.Registrar
- // containerStore stores all resources associated with containers.
- containerStore *containerstore.Store
- // containerNameIndex stores all container names and make sure each
- // name is unique.
- containerNameIndex *registrar.Registrar
- // imageStore stores all resources associated with images.
- imageStore *imagestore.Store
- // snapshotStore stores information of all snapshots.
- snapshotStore *snapshotstore.Store
- // netPlugin is used to setup and teardown network when run/stop pod sandbox.
- netPlugin cni.CNI
- // client is an instance of the containerd client
- client *containerd.Client
- // streamServer is the streaming server serves container streaming request.
- streamServer streaming.Server
- // eventMonitor is the monitor monitors containerd events.
- eventMonitor *eventMonitor
- // initialized indicates whether the server is initialized. All GRPC services
- // should return error before the server is initialized.
- initialized atomic.Bool
- // cniNetConfMonitor is used to reload cni network conf if there is
- // any valid fs change events from cni network conf dir.
- cniNetConfMonitor *cniNetConfSyncer
- // baseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
- baseOCISpecs map[string]*oci.Spec
-}
-
-// NewCRIService returns a new instance of CRIService
-func NewCRIService(config criconfig.Config, client *containerd.Client) (CRIService, error) {
- var err error
- labels := label.NewStore()
- c := &criService{
- config: config,
- client: client,
- os: osinterface.RealOS{},
- sandboxStore: sandboxstore.NewStore(labels),
- containerStore: containerstore.NewStore(labels),
- imageStore: imagestore.NewStore(client),
- snapshotStore: snapshotstore.NewStore(),
- sandboxNameIndex: registrar.NewRegistrar(),
- containerNameIndex: registrar.NewRegistrar(),
- initialized: atomic.NewBool(false),
- }
-
- if client.SnapshotService(c.config.ContainerdConfig.Snapshotter) == nil {
- return nil, errors.Errorf("failed to find snapshotter %q", c.config.ContainerdConfig.Snapshotter)
- }
-
- c.imageFSPath = imageFSPath(config.ContainerdRootDir, config.ContainerdConfig.Snapshotter)
- logrus.Infof("Get image filesystem path %q", c.imageFSPath)
-
- if err := c.initPlatform(); err != nil {
- return nil, errors.Wrap(err, "initialize platform")
- }
-
- // prepare streaming server
- c.streamServer, err = newStreamServer(c, config.StreamServerAddress, config.StreamServerPort, config.StreamIdleTimeout)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create stream server")
- }
-
- c.eventMonitor = newEventMonitor(c)
-
- c.cniNetConfMonitor, err = newCNINetConfSyncer(c.config.NetworkPluginConfDir, c.netPlugin, c.cniLoadOptions())
- if err != nil {
- return nil, errors.Wrap(err, "failed to create cni conf monitor")
- }
-
- // Preload base OCI specs
- c.baseOCISpecs, err = loadBaseOCISpecs(&config)
- if err != nil {
- return nil, err
- }
-
- return c, nil
-}
-
-// Register registers all required services onto a specific grpc server.
-// This is used by containerd cri plugin.
-func (c *criService) Register(s *grpc.Server) error {
- return c.register(s)
-}
-
-// RegisterTCP register all required services onto a GRPC server on TCP.
-// This is used by containerd CRI plugin.
-func (c *criService) RegisterTCP(s *grpc.Server) error {
- if !c.config.DisableTCPService {
- return c.register(s)
- }
- return nil
-}
-
-// Run starts the CRI service.
-func (c *criService) Run() error {
- logrus.Info("Start subscribing containerd event")
- c.eventMonitor.subscribe(c.client)
-
- logrus.Infof("Start recovering state")
- if err := c.recover(ctrdutil.NamespacedContext()); err != nil {
- return errors.Wrap(err, "failed to recover state")
- }
-
- // Start event handler.
- logrus.Info("Start event monitor")
- eventMonitorErrCh := c.eventMonitor.start()
-
- // Start snapshot stats syncer, it doesn't need to be stopped.
- logrus.Info("Start snapshots syncer")
- snapshotsSyncer := newSnapshotsSyncer(
- c.snapshotStore,
- c.client.SnapshotService(c.config.ContainerdConfig.Snapshotter),
- time.Duration(c.config.StatsCollectPeriod)*time.Second,
- )
- snapshotsSyncer.start()
-
- // Start CNI network conf syncer
- logrus.Info("Start cni network conf syncer")
- cniNetConfMonitorErrCh := make(chan error, 1)
- go func() {
- defer close(cniNetConfMonitorErrCh)
- cniNetConfMonitorErrCh <- c.cniNetConfMonitor.syncLoop()
- }()
-
- // Start streaming server.
- logrus.Info("Start streaming server")
- streamServerErrCh := make(chan error)
- go func() {
- defer close(streamServerErrCh)
- if err := c.streamServer.Start(true); err != nil && err != http.ErrServerClosed {
- logrus.WithError(err).Error("Failed to start streaming server")
- streamServerErrCh <- err
- }
- }()
-
- // Set the server as initialized. GRPC services could start serving traffic.
- c.initialized.Set()
-
- var eventMonitorErr, streamServerErr, cniNetConfMonitorErr error
- // Stop the whole CRI service if any of the critical service exits.
- select {
- case eventMonitorErr = <-eventMonitorErrCh:
- case streamServerErr = <-streamServerErrCh:
- case cniNetConfMonitorErr = <-cniNetConfMonitorErrCh:
- }
- if err := c.Close(); err != nil {
- return errors.Wrap(err, "failed to stop cri service")
- }
- // If the error is set above, err from channel must be nil here, because
- // the channel is supposed to be closed. Or else, we wait and set it.
- if err := <-eventMonitorErrCh; err != nil {
- eventMonitorErr = err
- }
- logrus.Info("Event monitor stopped")
- // There is a race condition with http.Server.Serve.
- // When `Close` is called at the same time with `Serve`, `Close`
- // may finish first, and `Serve` may still block.
- // See https://github.com/golang/go/issues/20239.
- // Here we set a 2 second timeout for the stream server wait,
- // if it timeout, an error log is generated.
- // TODO(random-liu): Get rid of this after https://github.com/golang/go/issues/20239
- // is fixed.
- const streamServerStopTimeout = 2 * time.Second
- select {
- case err := <-streamServerErrCh:
- if err != nil {
- streamServerErr = err
- }
- logrus.Info("Stream server stopped")
- case <-time.After(streamServerStopTimeout):
- logrus.Errorf("Stream server is not stopped in %q", streamServerStopTimeout)
- }
- if eventMonitorErr != nil {
- return errors.Wrap(eventMonitorErr, "event monitor error")
- }
- if streamServerErr != nil {
- return errors.Wrap(streamServerErr, "stream server error")
- }
- if cniNetConfMonitorErr != nil {
- return errors.Wrap(cniNetConfMonitorErr, "cni network conf monitor error")
- }
- return nil
-}
-
-// Close stops the CRI service.
-// TODO(random-liu): Make close synchronous.
-func (c *criService) Close() error {
- logrus.Info("Stop CRI service")
- if err := c.cniNetConfMonitor.stop(); err != nil {
- logrus.WithError(err).Error("failed to stop cni network conf monitor")
- }
- c.eventMonitor.stop()
- if err := c.streamServer.Stop(); err != nil {
- return errors.Wrap(err, "failed to stop stream server")
- }
- return nil
-}
-
-func (c *criService) register(s *grpc.Server) error {
- instrumented := newInstrumentedService(c)
- runtime.RegisterRuntimeServiceServer(s, instrumented)
- runtime.RegisterImageServiceServer(s, instrumented)
- return nil
-}
-
-// imageFSPath returns containerd image filesystem path.
-// Note that if containerd changes directory layout, we also needs to change this.
-func imageFSPath(rootDir, snapshotter string) string {
- return filepath.Join(rootDir, fmt.Sprintf("%s.%s", plugin.SnapshotPlugin, snapshotter))
-}
-
-func loadOCISpec(filename string) (*oci.Spec, error) {
- file, err := os.Open(filename)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to open base OCI spec: %s", filename)
- }
- defer file.Close()
-
- spec := oci.Spec{}
- if err := json.NewDecoder(file).Decode(&spec); err != nil {
- return nil, errors.Wrap(err, "failed to parse base OCI spec file")
- }
-
- return &spec, nil
-}
-
-func loadBaseOCISpecs(config *criconfig.Config) (map[string]*oci.Spec, error) {
- specs := map[string]*oci.Spec{}
- for _, cfg := range config.Runtimes {
- if cfg.BaseRuntimeSpec == "" {
- continue
- }
-
- // Don't load same file twice
- if _, ok := specs[cfg.BaseRuntimeSpec]; ok {
- continue
- }
-
- spec, err := loadOCISpec(cfg.BaseRuntimeSpec)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to load base OCI spec from file: %s", cfg.BaseRuntimeSpec)
- }
-
- specs[cfg.BaseRuntimeSpec] = spec
- }
-
- return specs, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/service_unix.go b/vendor/github.com/containerd/cri/pkg/server/service_unix.go
deleted file mode 100644
index a1d9c9038..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/service_unix.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// +build !windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/containerd/containerd/sys"
- cni "github.com/containerd/go-cni"
- "github.com/opencontainers/selinux/go-selinux"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// networkAttachCount is the minimum number of networks the PodSandbox
-// attaches to
-const networkAttachCount = 2
-
-// initPlatform handles linux specific initialization for the CRI service.
-func (c *criService) initPlatform() error {
- var err error
-
- if sys.RunningInUserNS() {
- if !(c.config.DisableCgroup && !c.apparmorEnabled() && c.config.RestrictOOMScoreAdj) {
- logrus.Warn("Running containerd in a user namespace typically requires disable_cgroup, disable_apparmor, restrict_oom_score_adj set to be true")
- }
- }
-
- if c.config.EnableSelinux {
- if !selinux.GetEnabled() {
- logrus.Warn("Selinux is not supported")
- }
- if r := c.config.SelinuxCategoryRange; r > 0 {
- selinux.CategoryRange = uint32(r)
- }
- } else {
- selinux.SetDisabled()
- }
-
- // Pod needs to attach to at least loopback network and a non host network,
- // hence networkAttachCount is 2. If there are more network configs the
- // pod will be attached to all the networks but we will only use the ip
- // of the default network interface as the pod IP.
- c.netPlugin, err = cni.New(cni.WithMinNetworkCount(networkAttachCount),
- cni.WithPluginConfDir(c.config.NetworkPluginConfDir),
- cni.WithPluginMaxConfNum(c.config.NetworkPluginMaxConfNum),
- cni.WithPluginDir([]string{c.config.NetworkPluginBinDir}))
- if err != nil {
- return errors.Wrap(err, "failed to initialize cni")
- }
-
- return nil
-}
-
-// cniLoadOptions returns cni load options for the linux.
-func (c *criService) cniLoadOptions() []cni.CNIOpt {
- return []cni.CNIOpt{cni.WithLoNetwork, cni.WithDefaultConf}
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/service_windows.go b/vendor/github.com/containerd/cri/pkg/server/service_windows.go
deleted file mode 100644
index 5f764d452..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/service_windows.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// +build windows
-
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- cni "github.com/containerd/go-cni"
- "github.com/pkg/errors"
-)
-
-// windowsNetworkAttachCount is the minimum number of networks the PodSandbox
-// attaches to
-const windowsNetworkAttachCount = 1
-
-// initPlatform handles linux specific initialization for the CRI service.
-func (c *criService) initPlatform() error {
- var err error
- // For windows, the loopback network is added as default.
- // There is no need to explicitly add one hence networkAttachCount is 1.
- // If there are more network configs the pod will be attached to all the
- // networks but we will only use the ip of the default network interface
- // as the pod IP.
- c.netPlugin, err = cni.New(cni.WithMinNetworkCount(windowsNetworkAttachCount),
- cni.WithPluginConfDir(c.config.NetworkPluginConfDir),
- cni.WithPluginMaxConfNum(c.config.NetworkPluginMaxConfNum),
- cni.WithPluginDir([]string{c.config.NetworkPluginBinDir}))
- if err != nil {
- return errors.Wrap(err, "failed to initialize cni")
- }
-
- return nil
-}
-
-// cniLoadOptions returns cni load options for the windows.
-func (c *criService) cniLoadOptions() []cni.CNIOpt {
- return []cni.CNIOpt{cni.WithDefaultConf}
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/snapshots.go b/vendor/github.com/containerd/cri/pkg/server/snapshots.go
deleted file mode 100644
index 0c1670750..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/snapshots.go
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "context"
- "time"
-
- "github.com/containerd/containerd/errdefs"
- snapshot "github.com/containerd/containerd/snapshots"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- snapshotstore "github.com/containerd/cri/pkg/store/snapshot"
-)
-
-// snapshotsSyncer syncs snapshot stats periodically. imagefs info and container stats
-// should both use cached result here.
-// TODO(random-liu): Benchmark with high workload. We may need a statsSyncer instead if
-// benchmark result shows that container cpu/memory stats also need to be cached.
-type snapshotsSyncer struct {
- store *snapshotstore.Store
- snapshotter snapshot.Snapshotter
- syncPeriod time.Duration
-}
-
-// newSnapshotsSyncer creates a snapshot syncer.
-func newSnapshotsSyncer(store *snapshotstore.Store, snapshotter snapshot.Snapshotter,
- period time.Duration) *snapshotsSyncer {
- return &snapshotsSyncer{
- store: store,
- snapshotter: snapshotter,
- syncPeriod: period,
- }
-}
-
-// start starts the snapshots syncer. No stop function is needed because
-// the syncer doesn't update any persistent states, it's fine to let it
-// exit with the process.
-func (s *snapshotsSyncer) start() {
- tick := time.NewTicker(s.syncPeriod)
- go func() {
- defer tick.Stop()
- // TODO(random-liu): This is expensive. We should do benchmark to
- // check the resource usage and optimize this.
- for {
- if err := s.sync(); err != nil {
- logrus.WithError(err).Error("Failed to sync snapshot stats")
- }
- <-tick.C
- }
- }()
-}
-
-// sync updates all snapshots stats.
-func (s *snapshotsSyncer) sync() error {
- ctx := ctrdutil.NamespacedContext()
- start := time.Now().UnixNano()
- var snapshots []snapshot.Info
- // Do not call `Usage` directly in collect function, because
- // `Usage` takes time, we don't want `Walk` to hold read lock
- // of snapshot metadata store for too long time.
- // TODO(random-liu): Set timeout for the following 2 contexts.
- if err := s.snapshotter.Walk(ctx, func(ctx context.Context, info snapshot.Info) error {
- snapshots = append(snapshots, info)
- return nil
- }); err != nil {
- return errors.Wrap(err, "walk all snapshots failed")
- }
- for _, info := range snapshots {
- sn, err := s.store.Get(info.Name)
- if err == nil {
- // Only update timestamp for non-active snapshot.
- if sn.Kind == info.Kind && sn.Kind != snapshot.KindActive {
- sn.Timestamp = time.Now().UnixNano()
- s.store.Add(sn)
- continue
- }
- }
- // Get newest stats if the snapshot is new or active.
- sn = snapshotstore.Snapshot{
- Key: info.Name,
- Kind: info.Kind,
- Timestamp: time.Now().UnixNano(),
- }
- usage, err := s.snapshotter.Usage(ctx, info.Name)
- if err != nil {
- if !errdefs.IsNotFound(err) {
- logrus.WithError(err).Errorf("Failed to get usage for snapshot %q", info.Name)
- }
- continue
- }
- sn.Size = uint64(usage.Size)
- sn.Inodes = uint64(usage.Inodes)
- s.store.Add(sn)
- }
- for _, sn := range s.store.List() {
- if sn.Timestamp >= start {
- continue
- }
- // Delete the snapshot stats if it's not updated this time.
- s.store.Delete(sn.Key)
- }
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/status.go b/vendor/github.com/containerd/cri/pkg/server/status.go
deleted file mode 100644
index 4283a5190..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/status.go
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "encoding/json"
- "fmt"
- goruntime "runtime"
-
- "github.com/containerd/containerd/log"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// networkNotReadyReason is the reason reported when network is not ready.
-const networkNotReadyReason = "NetworkPluginNotReady"
-
-// Status returns the status of the runtime.
-func (c *criService) Status(ctx context.Context, r *runtime.StatusRequest) (*runtime.StatusResponse, error) {
- // As a containerd plugin, if CRI plugin is serving request,
- // containerd must be ready.
- runtimeCondition := &runtime.RuntimeCondition{
- Type: runtime.RuntimeReady,
- Status: true,
- }
- networkCondition := &runtime.RuntimeCondition{
- Type: runtime.NetworkReady,
- Status: true,
- }
- // Check the status of the cni initialization
- if err := c.netPlugin.Status(); err != nil {
- networkCondition.Status = false
- networkCondition.Reason = networkNotReadyReason
- networkCondition.Message = fmt.Sprintf("Network plugin returns error: %v", err)
- }
-
- resp := &runtime.StatusResponse{
- Status: &runtime.RuntimeStatus{Conditions: []*runtime.RuntimeCondition{
- runtimeCondition,
- networkCondition,
- }},
- }
- if r.Verbose {
- configByt, err := json.Marshal(c.config)
- if err != nil {
- return nil, err
- }
- resp.Info = make(map[string]string)
- resp.Info["config"] = string(configByt)
- versionByt, err := json.Marshal(goruntime.Version())
- if err != nil {
- return nil, err
- }
- resp.Info["golang"] = string(versionByt)
-
- cniConfig, err := json.Marshal(c.netPlugin.GetConfig())
- if err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to marshal CNI config %v", err)
- }
- resp.Info["cniconfig"] = string(cniConfig)
-
- lastCNILoadStatus := "OK"
- if lerr := c.cniNetConfMonitor.lastStatus(); lerr != nil {
- lastCNILoadStatus = lerr.Error()
- }
- resp.Info["lastCNILoadStatus"] = lastCNILoadStatus
- }
- return resp, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/streaming.go b/vendor/github.com/containerd/cri/pkg/server/streaming.go
deleted file mode 100644
index d0089cc89..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/streaming.go
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "context"
- "crypto/tls"
- "io"
- "math"
- "net"
- "os"
- "time"
-
- "github.com/pkg/errors"
- k8snet "k8s.io/apimachinery/pkg/util/net"
- "k8s.io/apimachinery/pkg/util/runtime"
- "k8s.io/client-go/tools/remotecommand"
- k8scert "k8s.io/client-go/util/cert"
- "k8s.io/utils/exec"
-
- ctrdutil "github.com/containerd/cri/pkg/containerd/util"
- "github.com/containerd/cri/pkg/streaming"
-)
-
-type streamListenerMode int
-
-const (
- x509KeyPairTLS streamListenerMode = iota
- selfSignTLS
- withoutTLS
-)
-
-func getStreamListenerMode(c *criService) (streamListenerMode, error) {
- if c.config.EnableTLSStreaming {
- if c.config.X509KeyPairStreaming.TLSCertFile != "" && c.config.X509KeyPairStreaming.TLSKeyFile != "" {
- return x509KeyPairTLS, nil
- }
- if c.config.X509KeyPairStreaming.TLSCertFile != "" && c.config.X509KeyPairStreaming.TLSKeyFile == "" {
- return -1, errors.New("must set X509KeyPairStreaming.TLSKeyFile")
- }
- if c.config.X509KeyPairStreaming.TLSCertFile == "" && c.config.X509KeyPairStreaming.TLSKeyFile != "" {
- return -1, errors.New("must set X509KeyPairStreaming.TLSCertFile")
- }
- return selfSignTLS, nil
- }
- if c.config.X509KeyPairStreaming.TLSCertFile != "" {
- return -1, errors.New("X509KeyPairStreaming.TLSCertFile is set but EnableTLSStreaming is not set")
- }
- if c.config.X509KeyPairStreaming.TLSKeyFile != "" {
- return -1, errors.New("X509KeyPairStreaming.TLSKeyFile is set but EnableTLSStreaming is not set")
- }
- return withoutTLS, nil
-}
-
-func newStreamServer(c *criService, addr, port, streamIdleTimeout string) (streaming.Server, error) {
- if addr == "" {
- a, err := k8snet.ResolveBindAddress(nil)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get stream server address")
- }
- addr = a.String()
- }
- config := streaming.DefaultConfig
- if streamIdleTimeout != "" {
- var err error
- config.StreamIdleTimeout, err = time.ParseDuration(streamIdleTimeout)
- if err != nil {
- return nil, errors.Wrap(err, "invalid stream idle timeout")
- }
- }
- config.Addr = net.JoinHostPort(addr, port)
- run := newStreamRuntime(c)
- tlsMode, err := getStreamListenerMode(c)
- if err != nil {
- return nil, errors.Wrapf(err, "invalid stream server configuration")
- }
- switch tlsMode {
- case x509KeyPairTLS:
- tlsCert, err := tls.LoadX509KeyPair(c.config.X509KeyPairStreaming.TLSCertFile, c.config.X509KeyPairStreaming.TLSKeyFile)
- if err != nil {
- return nil, errors.Wrap(err, "failed to load x509 key pair for stream server")
- }
- config.TLSConfig = &tls.Config{
- Certificates: []tls.Certificate{tlsCert},
- }
- return streaming.NewServer(config, run)
- case selfSignTLS:
- tlsCert, err := newTLSCert()
- if err != nil {
- return nil, errors.Wrap(err, "failed to generate tls certificate for stream server")
- }
- config.TLSConfig = &tls.Config{
- Certificates: []tls.Certificate{tlsCert},
- InsecureSkipVerify: true,
- }
- return streaming.NewServer(config, run)
- case withoutTLS:
- return streaming.NewServer(config, run)
- default:
- return nil, errors.New("invalid configuration for the stream listener")
- }
-}
-
-type streamRuntime struct {
- c *criService
-}
-
-func newStreamRuntime(c *criService) streaming.Runtime {
- return &streamRuntime{c: c}
-}
-
-// Exec executes a command inside the container. exec.ExitError is returned if the command
-// returns non-zero exit code.
-func (s *streamRuntime) Exec(containerID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser,
- tty bool, resize <-chan remotecommand.TerminalSize) error {
- exitCode, err := s.c.execInContainer(ctrdutil.NamespacedContext(), containerID, execOptions{
- cmd: cmd,
- stdin: stdin,
- stdout: stdout,
- stderr: stderr,
- tty: tty,
- resize: resize,
- })
- if err != nil {
- return errors.Wrap(err, "failed to exec in container")
- }
- if *exitCode == 0 {
- return nil
- }
- return &exec.CodeExitError{
- Err: errors.Errorf("error executing command %v, exit code %d", cmd, *exitCode),
- Code: int(*exitCode),
- }
-}
-
-func (s *streamRuntime) Attach(containerID string, in io.Reader, out, err io.WriteCloser, tty bool,
- resize <-chan remotecommand.TerminalSize) error {
- return s.c.attachContainer(ctrdutil.NamespacedContext(), containerID, in, out, err, tty, resize)
-}
-
-func (s *streamRuntime) PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error {
- if port <= 0 || port > math.MaxUint16 {
- return errors.Errorf("invalid port %d", port)
- }
- ctx := ctrdutil.NamespacedContext()
- return s.c.portForward(ctx, podSandboxID, port, stream)
-}
-
-// handleResizing spawns a goroutine that processes the resize channel, calling resizeFunc for each
-// remotecommand.TerminalSize received from the channel.
-func handleResizing(ctx context.Context, resize <-chan remotecommand.TerminalSize, resizeFunc func(size remotecommand.TerminalSize)) {
- if resize == nil {
- return
- }
-
- go func() {
- defer runtime.HandleCrash()
-
- for {
- select {
- case <-ctx.Done():
- return
- case size, ok := <-resize:
- if !ok {
- return
- }
- if size.Height < 1 || size.Width < 1 {
- continue
- }
- resizeFunc(size)
- }
- }
- }()
-}
-
-// newTLSCert returns a self CA signed tls.certificate.
-// TODO (mikebrow): replace / rewrite this function to support using CA
-// signing of the certificate. Requires a security plan for kubernetes regarding
-// CRI connections / streaming, etc. For example, kubernetes could configure or
-// require a CA service and pass a configuration down through CRI.
-func newTLSCert() (tls.Certificate, error) {
- fail := func(err error) (tls.Certificate, error) { return tls.Certificate{}, err }
-
- hostName, err := os.Hostname()
- if err != nil {
- return fail(errors.Wrap(err, "failed to get hostname"))
- }
-
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- return fail(errors.Wrap(err, "failed to get host IP addresses"))
- }
-
- var alternateIPs []net.IP
- var alternateDNS []string
- for _, addr := range addrs {
- var ip net.IP
-
- switch v := addr.(type) {
- case *net.IPNet:
- ip = v.IP
- case *net.IPAddr:
- ip = v.IP
- default:
- continue
- }
-
- alternateIPs = append(alternateIPs, ip)
- alternateDNS = append(alternateDNS, ip.String())
- }
-
- // Generate a self signed certificate key (CA is self)
- certPem, keyPem, err := k8scert.GenerateSelfSignedCertKey(hostName, alternateIPs, alternateDNS)
- if err != nil {
- return fail(errors.Wrap(err, "certificate key could not be created"))
- }
-
- // Load the tls certificate
- tlsCert, err := tls.X509KeyPair(certPem, keyPem)
- if err != nil {
- return fail(errors.Wrap(err, "certificate could not be loaded"))
- }
-
- return tlsCert, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/update_runtime_config.go b/vendor/github.com/containerd/cri/pkg/server/update_runtime_config.go
deleted file mode 100644
index 6c725e234..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/update_runtime_config.go
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "net"
- "os"
- "path/filepath"
- "strings"
- "text/template"
-
- "github.com/containerd/containerd/log"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// cniConfigTemplate contains the values containerd will overwrite
-// in the cni config template.
-type cniConfigTemplate struct {
- // PodCIDR is the cidr for pods on the node.
- PodCIDR string
- // PodCIDRRanges is the cidr ranges for pods on the node.
- PodCIDRRanges []string
- // Routes is a list of routes configured.
- Routes []string
-}
-
-const (
- // cniConfigFileName is the name of cni config file generated by containerd.
- cniConfigFileName = "10-containerd-net.conflist"
- // zeroCIDRv6 is the null route for IPv6.
- zeroCIDRv6 = "::/0"
- // zeroCIDRv4 is the null route for IPv4.
- zeroCIDRv4 = "0.0.0.0/0"
-)
-
-// UpdateRuntimeConfig updates the runtime config. Currently only handles podCIDR updates.
-func (c *criService) UpdateRuntimeConfig(ctx context.Context, r *runtime.UpdateRuntimeConfigRequest) (*runtime.UpdateRuntimeConfigResponse, error) {
- podCIDRs := r.GetRuntimeConfig().GetNetworkConfig().GetPodCidr()
- if podCIDRs == "" {
- return &runtime.UpdateRuntimeConfigResponse{}, nil
- }
- cidrs := strings.Split(podCIDRs, ",")
- for i := range cidrs {
- cidrs[i] = strings.TrimSpace(cidrs[i])
- }
- routes, err := getRoutes(cidrs)
- if err != nil {
- return nil, errors.Wrap(err, "get routes")
- }
-
- confTemplate := c.config.NetworkPluginConfTemplate
- if confTemplate == "" {
- log.G(ctx).Info("No cni config template is specified, wait for other system components to drop the config.")
- return &runtime.UpdateRuntimeConfigResponse{}, nil
- }
- if err := c.netPlugin.Status(); err == nil {
- log.G(ctx).Infof("Network plugin is ready, skip generating cni config from template %q", confTemplate)
- return &runtime.UpdateRuntimeConfigResponse{}, nil
- } else if err := c.netPlugin.Load(c.cniLoadOptions()...); err == nil {
- log.G(ctx).Infof("CNI config is successfully loaded, skip generating cni config from template %q", confTemplate)
- return &runtime.UpdateRuntimeConfigResponse{}, nil
- }
- log.G(ctx).Infof("Generating cni config from template %q", confTemplate)
- // generate cni config file from the template with updated pod cidr.
- t, err := template.ParseFiles(confTemplate)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to parse cni config template %q", confTemplate)
- }
- if err := os.MkdirAll(c.config.NetworkPluginConfDir, 0755); err != nil {
- return nil, errors.Wrapf(err, "failed to create cni config directory: %q", c.config.NetworkPluginConfDir)
- }
- confFile := filepath.Join(c.config.NetworkPluginConfDir, cniConfigFileName)
- f, err := os.OpenFile(confFile, os.O_WRONLY|os.O_CREATE, 0644)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to open cni config file %q", confFile)
- }
- defer f.Close()
- if err := t.Execute(f, cniConfigTemplate{
- PodCIDR: cidrs[0],
- PodCIDRRanges: cidrs,
- Routes: routes,
- }); err != nil {
- return nil, errors.Wrapf(err, "failed to generate cni config file %q", confFile)
- }
- return &runtime.UpdateRuntimeConfigResponse{}, nil
-}
-
-// getRoutes generates required routes for the passed in cidrs.
-func getRoutes(cidrs []string) ([]string, error) {
- var (
- routes []string
- hasV4, hasV6 bool
- )
- for _, c := range cidrs {
- _, cidr, err := net.ParseCIDR(c)
- if err != nil {
- return nil, err
- }
- if cidr.IP.To4() != nil {
- hasV4 = true
- } else {
- hasV6 = true
- }
- }
- if hasV4 {
- routes = append(routes, zeroCIDRv4)
- }
- if hasV6 {
- routes = append(routes, zeroCIDRv6)
- }
- return routes, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/server/version.go b/vendor/github.com/containerd/cri/pkg/server/version.go
deleted file mode 100644
index c1dea50c1..000000000
--- a/vendor/github.com/containerd/cri/pkg/server/version.go
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package server
-
-import (
- "github.com/containerd/containerd/version"
- "golang.org/x/net/context"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/constants"
-)
-
-const (
- containerName = "containerd"
- // kubeAPIVersion is the api version of kubernetes.
- // TODO(random-liu): Change this to actual CRI version.
- kubeAPIVersion = "0.1.0"
-)
-
-// Version returns the runtime name, runtime version and runtime API version.
-func (c *criService) Version(ctx context.Context, r *runtime.VersionRequest) (*runtime.VersionResponse, error) {
- return &runtime.VersionResponse{
- Version: kubeAPIVersion,
- RuntimeName: containerName,
- RuntimeVersion: version.Version,
- RuntimeApiVersion: constants.CRIVersion,
- }, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/seutil/seutil.go b/vendor/github.com/containerd/cri/pkg/seutil/seutil.go
deleted file mode 100644
index f453a7775..000000000
--- a/vendor/github.com/containerd/cri/pkg/seutil/seutil.go
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package seutil
-
-import (
- "bufio"
- "os"
-
- "github.com/opencontainers/selinux/go-selinux"
-)
-
-var seTypes map[string]struct{}
-
-const typePath = "/etc/selinux/targeted/contexts/customizable_types"
-
-func init() {
- seTypes = make(map[string]struct{})
- if !selinux.GetEnabled() {
- return
- }
- f, err := os.Open(typePath)
- if err != nil {
- return
- }
- defer f.Close()
- s := bufio.NewScanner(f)
- for s.Scan() {
- seTypes[s.Text()] = struct{}{}
- }
-}
-
-// HasType returns true if the underlying system has the
-// provided selinux type enabled.
-func HasType(name string) bool {
- _, ok := seTypes[name]
- return ok
-}
-
-// ChangeToKVM process label
-func ChangeToKVM(l string) (string, error) {
- if l == "" || !selinux.GetEnabled() {
- return "", nil
- }
- proc, _ := selinux.KVMContainerLabels()
- selinux.ReleaseLabel(proc)
-
- current, err := selinux.NewContext(l)
- if err != nil {
- return "", err
- }
- next, err := selinux.NewContext(proc)
- if err != nil {
- return "", err
- }
- current["type"] = next["type"]
- return current.Get(), nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/container/container.go b/vendor/github.com/containerd/cri/pkg/store/container/container.go
deleted file mode 100644
index 53c0745a5..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/container/container.go
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package container
-
-import (
- "sync"
-
- "github.com/containerd/containerd"
- "github.com/containerd/cri/pkg/store/label"
- "github.com/docker/docker/pkg/truncindex"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- cio "github.com/containerd/cri/pkg/server/io"
- "github.com/containerd/cri/pkg/store"
-)
-
-// Container contains all resources associated with the container. All methods to
-// mutate the internal state are thread-safe.
-type Container struct {
- // Metadata is the metadata of the container, it is **immutable** after created.
- Metadata
- // Status stores the status of the container.
- Status StatusStorage
- // Container is the containerd container client.
- Container containerd.Container
- // Container IO.
- // IO could only be nil when the container is in unknown state.
- IO *cio.ContainerIO
- // StopCh is used to propagate the stop information of the container.
- *store.StopCh
-}
-
-// Opts sets specific information to newly created Container.
-type Opts func(*Container) error
-
-// WithContainer adds the containerd Container to the internal data store.
-func WithContainer(cntr containerd.Container) Opts {
- return func(c *Container) error {
- c.Container = cntr
- return nil
- }
-}
-
-// WithContainerIO adds IO into the container.
-func WithContainerIO(io *cio.ContainerIO) Opts {
- return func(c *Container) error {
- c.IO = io
- return nil
- }
-}
-
-// WithStatus adds status to the container.
-func WithStatus(status Status, root string) Opts {
- return func(c *Container) error {
- s, err := StoreStatus(root, c.ID, status)
- if err != nil {
- return err
- }
- c.Status = s
- if s.Get().State() == runtime.ContainerState_CONTAINER_EXITED {
- c.Stop()
- }
- return nil
- }
-}
-
-// NewContainer creates an internally used container type.
-func NewContainer(metadata Metadata, opts ...Opts) (Container, error) {
- c := Container{
- Metadata: metadata,
- StopCh: store.NewStopCh(),
- }
- for _, o := range opts {
- if err := o(&c); err != nil {
- return Container{}, err
- }
- }
- return c, nil
-}
-
-// Delete deletes checkpoint for the container.
-func (c *Container) Delete() error {
- return c.Status.Delete()
-}
-
-// Store stores all Containers.
-type Store struct {
- lock sync.RWMutex
- containers map[string]Container
- idIndex *truncindex.TruncIndex
- labels *label.Store
-}
-
-// NewStore creates a container store.
-func NewStore(labels *label.Store) *Store {
- return &Store{
- containers: make(map[string]Container),
- idIndex: truncindex.NewTruncIndex([]string{}),
- labels: labels,
- }
-}
-
-// Add a container into the store. Returns store.ErrAlreadyExist if the
-// container already exists.
-func (s *Store) Add(c Container) error {
- s.lock.Lock()
- defer s.lock.Unlock()
- if _, ok := s.containers[c.ID]; ok {
- return store.ErrAlreadyExist
- }
- if err := s.labels.Reserve(c.ProcessLabel); err != nil {
- return err
- }
- if err := s.idIndex.Add(c.ID); err != nil {
- return err
- }
- s.containers[c.ID] = c
- return nil
-}
-
-// Get returns the container with specified id. Returns store.ErrNotExist
-// if the container doesn't exist.
-func (s *Store) Get(id string) (Container, error) {
- s.lock.RLock()
- defer s.lock.RUnlock()
- id, err := s.idIndex.Get(id)
- if err != nil {
- if err == truncindex.ErrNotExist {
- err = store.ErrNotExist
- }
- return Container{}, err
- }
- if c, ok := s.containers[id]; ok {
- return c, nil
- }
- return Container{}, store.ErrNotExist
-}
-
-// List lists all containers.
-func (s *Store) List() []Container {
- s.lock.RLock()
- defer s.lock.RUnlock()
- var containers []Container
- for _, c := range s.containers {
- containers = append(containers, c)
- }
- return containers
-}
-
-// Delete deletes the container from store with specified id.
-func (s *Store) Delete(id string) {
- s.lock.Lock()
- defer s.lock.Unlock()
- id, err := s.idIndex.Get(id)
- if err != nil {
- // Note: The idIndex.Delete and delete doesn't handle truncated index.
- // So we need to return if there are error.
- return
- }
- s.labels.Release(s.containers[id].ProcessLabel)
- s.idIndex.Delete(id) // nolint: errcheck
- delete(s.containers, id)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/container/fake_status.go b/vendor/github.com/containerd/cri/pkg/store/container/fake_status.go
deleted file mode 100644
index 756588152..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/container/fake_status.go
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package container
-
-import "sync"
-
-// WithFakeStatus adds fake status to the container.
-func WithFakeStatus(status Status) Opts {
- return func(c *Container) error {
- c.Status = &fakeStatusStorage{status: status}
- if status.FinishedAt != 0 {
- // Fake the TaskExit event
- c.Stop()
- }
- return nil
- }
-}
-
-// fakeStatusStorage is a fake status storage for testing.
-type fakeStatusStorage struct {
- sync.RWMutex
- status Status
-}
-
-func (f *fakeStatusStorage) Get() Status {
- f.RLock()
- defer f.RUnlock()
- return f.status
-}
-
-func (f *fakeStatusStorage) UpdateSync(u UpdateFunc) error {
- return f.Update(u)
-}
-
-func (f *fakeStatusStorage) Update(u UpdateFunc) error {
- f.Lock()
- defer f.Unlock()
- newStatus, err := u(f.status)
- if err != nil {
- return err
- }
- f.status = newStatus
- return nil
-}
-
-func (f *fakeStatusStorage) Delete() error {
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/container/metadata.go b/vendor/github.com/containerd/cri/pkg/store/container/metadata.go
deleted file mode 100644
index ff9b5f2a3..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/container/metadata.go
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package container
-
-import (
- "encoding/json"
-
- "github.com/pkg/errors"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// NOTE(random-liu):
-// 1) Metadata is immutable after created.
-// 2) Metadata is checkpointed as containerd container label.
-
-// metadataVersion is current version of container metadata.
-const metadataVersion = "v1" // nolint
-
-// versionedMetadata is the internal versioned container metadata.
-// nolint
-type versionedMetadata struct {
- // Version indicates the version of the versioned container metadata.
- Version string
- // Metadata's type is metadataInternal. If not there will be a recursive call in MarshalJSON.
- Metadata metadataInternal
-}
-
-// metadataInternal is for internal use.
-type metadataInternal Metadata
-
-// Metadata is the unversioned container metadata.
-type Metadata struct {
- // ID is the container id.
- ID string
- // Name is the container name.
- Name string
- // SandboxID is the sandbox id the container belongs to.
- SandboxID string
- // Config is the CRI container config.
- // NOTE(random-liu): Resource limits are updatable, the source
- // of truth for resource limits are in containerd.
- Config *runtime.ContainerConfig
- // ImageRef is the reference of image used by the container.
- ImageRef string
- // LogPath is the container log path.
- LogPath string
- // StopSignal is the system call signal that will be sent to the container to exit.
- // TODO(random-liu): Add integration test for stop signal.
- StopSignal string
- // ProcessLabel is the SELinux process label for the container
- ProcessLabel string
-}
-
-// MarshalJSON encodes Metadata into bytes in json format.
-func (c *Metadata) MarshalJSON() ([]byte, error) {
- return json.Marshal(&versionedMetadata{
- Version: metadataVersion,
- Metadata: metadataInternal(*c),
- })
-}
-
-// UnmarshalJSON decodes Metadata from bytes.
-func (c *Metadata) UnmarshalJSON(data []byte) error {
- versioned := &versionedMetadata{}
- if err := json.Unmarshal(data, versioned); err != nil {
- return err
- }
- // Handle old version after upgrade.
- switch versioned.Version {
- case metadataVersion:
- *c = Metadata(versioned.Metadata)
- return nil
- }
- return errors.Errorf("unsupported version: %q", versioned.Version)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/container/status.go b/vendor/github.com/containerd/cri/pkg/store/container/status.go
deleted file mode 100644
index 655f58806..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/container/status.go
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package container
-
-import (
- "encoding/json"
- "io/ioutil"
- "os"
- "path/filepath"
- "sync"
-
- "github.com/containerd/continuity"
- "github.com/pkg/errors"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// The container state machine in the CRI plugin:
-//
-// + +
-// | |
-// | Create | Load
-// | |
-// +----v----+ |
-// | | |
-// | CREATED <---------+-----------+
-// | | | |
-// +----+----- | |
-// | | |
-// | Start | |
-// | | |
-// +----v----+ | |
-// Exec +--------+ | | |
-// Attach | | RUNNING <---------+ |
-// LogReopen +--------> | | |
-// +----+----+ | |
-// | | |
-// | Stop/Exit | |
-// | | |
-// +----v----+ | |
-// | <---------+ +----v----+
-// | EXITED | | |
-// | <----------------+ UNKNOWN |
-// +----+----+ Stop | |
-// | +---------+
-// | Remove
-// v
-// DELETED
-
-// statusVersion is current version of container status.
-const statusVersion = "v1" // nolint
-
-// versionedStatus is the internal used versioned container status.
-// nolint
-type versionedStatus struct {
- // Version indicates the version of the versioned container status.
- Version string
- Status
-}
-
-// Status is the status of a container.
-type Status struct {
- // Pid is the init process id of the container.
- Pid uint32
- // CreatedAt is the created timestamp.
- CreatedAt int64
- // StartedAt is the started timestamp.
- StartedAt int64
- // FinishedAt is the finished timestamp.
- FinishedAt int64
- // ExitCode is the container exit code.
- ExitCode int32
- // CamelCase string explaining why container is in its current state.
- Reason string
- // Human-readable message indicating details about why container is in its
- // current state.
- Message string
- // Starting indicates that the container is in starting state.
- // This field doesn't need to be checkpointed.
- Starting bool `json:"-"`
- // Removing indicates that the container is in removing state.
- // This field doesn't need to be checkpointed.
- Removing bool `json:"-"`
- // Unknown indicates that the container status is not fully loaded.
- // This field doesn't need to be checkpointed.
- Unknown bool `json:"-"`
-}
-
-// State returns current state of the container based on the container status.
-func (s Status) State() runtime.ContainerState {
- if s.Unknown {
- return runtime.ContainerState_CONTAINER_UNKNOWN
- }
- if s.FinishedAt != 0 {
- return runtime.ContainerState_CONTAINER_EXITED
- }
- if s.StartedAt != 0 {
- return runtime.ContainerState_CONTAINER_RUNNING
- }
- if s.CreatedAt != 0 {
- return runtime.ContainerState_CONTAINER_CREATED
- }
- return runtime.ContainerState_CONTAINER_UNKNOWN
-}
-
-// encode encodes Status into bytes in json format.
-func (s *Status) encode() ([]byte, error) {
- return json.Marshal(&versionedStatus{
- Version: statusVersion,
- Status: *s,
- })
-}
-
-// decode decodes Status from bytes.
-func (s *Status) decode(data []byte) error {
- versioned := &versionedStatus{}
- if err := json.Unmarshal(data, versioned); err != nil {
- return err
- }
- // Handle old version after upgrade.
- switch versioned.Version {
- case statusVersion:
- *s = versioned.Status
- return nil
- }
- return errors.New("unsupported version")
-}
-
-// UpdateFunc is function used to update the container status. If there
-// is an error, the update will be rolled back.
-type UpdateFunc func(Status) (Status, error)
-
-// StatusStorage manages the container status with a storage backend.
-type StatusStorage interface {
- // Get a container status.
- Get() Status
- // UpdateSync updates the container status and the on disk checkpoint.
- // Note that the update MUST be applied in one transaction.
- UpdateSync(UpdateFunc) error
- // Update the container status. Note that the update MUST be applied
- // in one transaction.
- Update(UpdateFunc) error
- // Delete the container status.
- // Note:
- // * Delete should be idempotent.
- // * The status must be deleted in one trasaction.
- Delete() error
-}
-
-// StoreStatus creates the storage containing the passed in container status with the
-// specified id.
-// The status MUST be created in one transaction.
-func StoreStatus(root, id string, status Status) (StatusStorage, error) {
- data, err := status.encode()
- if err != nil {
- return nil, errors.Wrap(err, "failed to encode status")
- }
- path := filepath.Join(root, "status")
- if err := continuity.AtomicWriteFile(path, data, 0600); err != nil {
- return nil, errors.Wrapf(err, "failed to checkpoint status to %q", path)
- }
- return &statusStorage{
- path: path,
- status: status,
- }, nil
-}
-
-// LoadStatus loads container status from checkpoint. There shouldn't be threads
-// writing to the file during loading.
-func LoadStatus(root, id string) (Status, error) {
- path := filepath.Join(root, "status")
- data, err := ioutil.ReadFile(path)
- if err != nil {
- return Status{}, errors.Wrapf(err, "failed to read status from %q", path)
- }
- var status Status
- if err := status.decode(data); err != nil {
- return Status{}, errors.Wrapf(err, "failed to decode status %q", data)
- }
- return status, nil
-}
-
-type statusStorage struct {
- sync.RWMutex
- path string
- status Status
-}
-
-// Get a copy of container status.
-func (s *statusStorage) Get() Status {
- s.RLock()
- defer s.RUnlock()
- return s.status
-}
-
-// UpdateSync updates the container status and the on disk checkpoint.
-func (s *statusStorage) UpdateSync(u UpdateFunc) error {
- s.Lock()
- defer s.Unlock()
- newStatus, err := u(s.status)
- if err != nil {
- return err
- }
- data, err := newStatus.encode()
- if err != nil {
- return errors.Wrap(err, "failed to encode status")
- }
- if err := continuity.AtomicWriteFile(s.path, data, 0600); err != nil {
- return errors.Wrapf(err, "failed to checkpoint status to %q", s.path)
- }
- s.status = newStatus
- return nil
-}
-
-// Update the container status.
-func (s *statusStorage) Update(u UpdateFunc) error {
- s.Lock()
- defer s.Unlock()
- newStatus, err := u(s.status)
- if err != nil {
- return err
- }
- s.status = newStatus
- return nil
-}
-
-// Delete deletes the container status from disk atomically.
-func (s *statusStorage) Delete() error {
- temp := filepath.Dir(s.path) + ".del-" + filepath.Base(s.path)
- if err := os.Rename(s.path, temp); err != nil && !os.IsNotExist(err) {
- return err
- }
- return os.RemoveAll(temp)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/errors.go b/vendor/github.com/containerd/cri/pkg/store/errors.go
deleted file mode 100644
index d8398e45f..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/errors.go
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package store
-
-import "github.com/containerd/containerd/errdefs"
-
-var (
- // ErrAlreadyExist is the error returned when data added in the store
- // already exists.
- //
- // This error has been DEPRECATED and will be removed in 1.5. Please switch
- // usage directly to `errdefs.ErrAlreadyExists`.
- ErrAlreadyExist = errdefs.ErrAlreadyExists
- // ErrNotExist is the error returned when data is not in the store.
- //
- // This error has been DEPRECATED and will be removed in 1.5. Please switch
- // usage directly to `errdefs.ErrNotFound`.
- ErrNotExist = errdefs.ErrNotFound
-)
diff --git a/vendor/github.com/containerd/cri/pkg/store/image/fake_image.go b/vendor/github.com/containerd/cri/pkg/store/image/fake_image.go
deleted file mode 100644
index e1d6b7cd4..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/image/fake_image.go
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package image
-
-import "github.com/pkg/errors"
-
-// NewFakeStore returns an image store with predefined images.
-// Update is not allowed for this fake store.
-func NewFakeStore(images []Image) (*Store, error) {
- s := NewStore(nil)
- for _, i := range images {
- for _, ref := range i.References {
- s.refCache[ref] = i.ID
- }
- if err := s.store.add(i); err != nil {
- return nil, errors.Wrapf(err, "add image %+v", i)
- }
- }
- return s, nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/image/image.go b/vendor/github.com/containerd/cri/pkg/store/image/image.go
deleted file mode 100644
index 208d490db..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/image/image.go
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package image
-
-import (
- "context"
- "encoding/json"
- "sync"
-
- "github.com/containerd/containerd"
- "github.com/containerd/containerd/content"
- "github.com/containerd/containerd/errdefs"
- imagedigest "github.com/opencontainers/go-digest"
- "github.com/opencontainers/go-digest/digestset"
- imageidentity "github.com/opencontainers/image-spec/identity"
- imagespec "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
-
- storeutil "github.com/containerd/cri/pkg/store"
- "github.com/containerd/cri/pkg/util"
-)
-
-// Image contains all resources associated with the image. All fields
-// MUST not be mutated directly after created.
-type Image struct {
- // Id of the image. Normally the digest of image config.
- ID string
- // References are references to the image, e.g. RepoTag and RepoDigest.
- References []string
- // ChainID is the chainID of the image.
- ChainID string
- // Size is the compressed size of the image.
- Size int64
- // ImageSpec is the oci image structure which describes basic information about the image.
- ImageSpec imagespec.Image
-}
-
-// Store stores all images.
-type Store struct {
- lock sync.RWMutex
- // refCache is a containerd image reference to image id cache.
- refCache map[string]string
- // client is the containerd client.
- client *containerd.Client
- // store is the internal image store indexed by image id.
- store *store
-}
-
-// NewStore creates an image store.
-func NewStore(client *containerd.Client) *Store {
- return &Store{
- refCache: make(map[string]string),
- client: client,
- store: &store{
- images: make(map[string]Image),
- digestSet: digestset.NewSet(),
- },
- }
-}
-
-// Update updates cache for a reference.
-func (s *Store) Update(ctx context.Context, ref string) error {
- s.lock.Lock()
- defer s.lock.Unlock()
- i, err := s.client.GetImage(ctx, ref)
- if err != nil && !errdefs.IsNotFound(err) {
- return errors.Wrap(err, "get image from containerd")
- }
- var img *Image
- if err == nil {
- img, err = getImage(ctx, i)
- if err != nil {
- return errors.Wrap(err, "get image info from containerd")
- }
- }
- return s.update(ref, img)
-}
-
-// update updates the internal cache. img == nil means that
-// the image does not exist in containerd.
-func (s *Store) update(ref string, img *Image) error {
- oldID, oldExist := s.refCache[ref]
- if img == nil {
- // The image reference doesn't exist in containerd.
- if oldExist {
- // Remove the reference from the store.
- s.store.delete(oldID, ref)
- delete(s.refCache, ref)
- }
- return nil
- }
- if oldExist {
- if oldID == img.ID {
- return nil
- }
- // Updated. Remove tag from old image.
- s.store.delete(oldID, ref)
- }
- // New image. Add new image.
- s.refCache[ref] = img.ID
- return s.store.add(*img)
-}
-
-// getImage gets image information from containerd.
-func getImage(ctx context.Context, i containerd.Image) (*Image, error) {
- // Get image information.
- diffIDs, err := i.RootFS(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "get image diffIDs")
- }
- chainID := imageidentity.ChainID(diffIDs)
-
- size, err := i.Size(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "get image compressed resource size")
- }
-
- desc, err := i.Config(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "get image config descriptor")
- }
- id := desc.Digest.String()
-
- rb, err := content.ReadBlob(ctx, i.ContentStore(), desc)
- if err != nil {
- return nil, errors.Wrap(err, "read image config from content store")
- }
- var ociimage imagespec.Image
- if err := json.Unmarshal(rb, &ociimage); err != nil {
- return nil, errors.Wrapf(err, "unmarshal image config %s", rb)
- }
-
- return &Image{
- ID: id,
- References: []string{i.Name()},
- ChainID: chainID.String(),
- Size: size,
- ImageSpec: ociimage,
- }, nil
-}
-
-// Resolve resolves a image reference to image id.
-func (s *Store) Resolve(ref string) (string, error) {
- s.lock.RLock()
- defer s.lock.RUnlock()
- id, ok := s.refCache[ref]
- if !ok {
- return "", storeutil.ErrNotExist
- }
- return id, nil
-}
-
-// Get gets image metadata by image id. The id can be truncated.
-// Returns various validation errors if the image id is invalid.
-// Returns storeutil.ErrNotExist if the image doesn't exist.
-func (s *Store) Get(id string) (Image, error) {
- return s.store.get(id)
-}
-
-// List lists all images.
-func (s *Store) List() []Image {
- return s.store.list()
-}
-
-type store struct {
- lock sync.RWMutex
- images map[string]Image
- digestSet *digestset.Set
-}
-
-func (s *store) list() []Image {
- s.lock.RLock()
- defer s.lock.RUnlock()
- var images []Image
- for _, i := range s.images {
- images = append(images, i)
- }
- return images
-}
-
-func (s *store) add(img Image) error {
- s.lock.Lock()
- defer s.lock.Unlock()
- if _, err := s.digestSet.Lookup(img.ID); err != nil {
- if err != digestset.ErrDigestNotFound {
- return err
- }
- if err := s.digestSet.Add(imagedigest.Digest(img.ID)); err != nil {
- return err
- }
- }
-
- i, ok := s.images[img.ID]
- if !ok {
- // If the image doesn't exist, add it.
- s.images[img.ID] = img
- return nil
- }
- // Or else, merge the references.
- i.References = util.MergeStringSlices(i.References, img.References)
- s.images[img.ID] = i
- return nil
-}
-
-func (s *store) get(id string) (Image, error) {
- s.lock.RLock()
- defer s.lock.RUnlock()
- digest, err := s.digestSet.Lookup(id)
- if err != nil {
- if err == digestset.ErrDigestNotFound {
- err = storeutil.ErrNotExist
- }
- return Image{}, err
- }
- if i, ok := s.images[digest.String()]; ok {
- return i, nil
- }
- return Image{}, storeutil.ErrNotExist
-}
-
-func (s *store) delete(id, ref string) {
- s.lock.Lock()
- defer s.lock.Unlock()
- digest, err := s.digestSet.Lookup(id)
- if err != nil {
- // Note: The idIndex.Delete and delete doesn't handle truncated index.
- // So we need to return if there are error.
- return
- }
- i, ok := s.images[digest.String()]
- if !ok {
- return
- }
- i.References = util.SubtractStringSlice(i.References, ref)
- if len(i.References) != 0 {
- s.images[digest.String()] = i
- return
- }
- // Remove the image if it is not referenced any more.
- s.digestSet.Remove(digest) // nolint: errcheck
- delete(s.images, digest.String())
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/label/label.go b/vendor/github.com/containerd/cri/pkg/store/label/label.go
deleted file mode 100644
index c8c5ff924..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/label/label.go
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package label
-
-import (
- "sync"
-
- "github.com/opencontainers/selinux/go-selinux"
-)
-
-type Store struct {
- sync.Mutex
- levels map[string]int
- Releaser func(string)
- Reserver func(string)
-}
-
-func NewStore() *Store {
- return &Store{
- levels: map[string]int{},
- Releaser: selinux.ReleaseLabel,
- Reserver: selinux.ReserveLabel,
- }
-}
-
-func (s *Store) Reserve(label string) error {
- s.Lock()
- defer s.Unlock()
-
- context, err := selinux.NewContext(label)
- if err != nil {
- return err
- }
-
- level := context["level"]
- // no reason to count empty
- if level == "" {
- return nil
- }
-
- if _, ok := s.levels[level]; !ok {
- s.Reserver(label)
- }
-
- s.levels[level]++
- return nil
-}
-
-func (s *Store) Release(label string) {
- s.Lock()
- defer s.Unlock()
-
- context, err := selinux.NewContext(label)
- if err != nil {
- return
- }
-
- level := context["level"]
- if level == "" {
- return
- }
-
- count, ok := s.levels[level]
- if !ok {
- return
- }
- switch {
- case count == 1:
- s.Releaser(label)
- delete(s.levels, level)
- case count < 1:
- delete(s.levels, level)
- case count > 1:
- s.levels[level] = count - 1
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/sandbox/metadata.go b/vendor/github.com/containerd/cri/pkg/store/sandbox/metadata.go
deleted file mode 100644
index eb3aa8e83..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/sandbox/metadata.go
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package sandbox
-
-import (
- "encoding/json"
-
- cni "github.com/containerd/go-cni"
- "github.com/pkg/errors"
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// NOTE(random-liu):
-// 1) Metadata is immutable after created.
-// 2) Metadata is checkpointed as containerd container label.
-
-// metadataVersion is current version of sandbox metadata.
-const metadataVersion = "v1" // nolint
-
-// versionedMetadata is the internal versioned sandbox metadata.
-// nolint
-type versionedMetadata struct {
- // Version indicates the version of the versioned sandbox metadata.
- Version string
- // Metadata's type is metadataInternal. If not there will be a recursive call in MarshalJSON.
- Metadata metadataInternal
-}
-
-// metadataInternal is for internal use.
-type metadataInternal Metadata
-
-// Metadata is the unversioned sandbox metadata.
-type Metadata struct {
- // ID is the sandbox id.
- ID string
- // Name is the sandbox name.
- Name string
- // Config is the CRI sandbox config.
- Config *runtime.PodSandboxConfig
- // NetNSPath is the network namespace used by the sandbox.
- NetNSPath string
- // IP of Pod if it is attached to non host network
- IP string
- // AdditionalIPs of the Pod if it is attached to non host network
- AdditionalIPs []string
- // RuntimeHandler is the runtime handler name of the pod.
- RuntimeHandler string
- // CNIresult resulting configuration for attached network namespace interfaces
- CNIResult *cni.CNIResult
- // ProcessLabel is the SELinux process label for the container
- ProcessLabel string
-}
-
-// MarshalJSON encodes Metadata into bytes in json format.
-func (c *Metadata) MarshalJSON() ([]byte, error) {
- return json.Marshal(&versionedMetadata{
- Version: metadataVersion,
- Metadata: metadataInternal(*c),
- })
-}
-
-// UnmarshalJSON decodes Metadata from bytes.
-func (c *Metadata) UnmarshalJSON(data []byte) error {
- versioned := &versionedMetadata{}
- if err := json.Unmarshal(data, versioned); err != nil {
- return err
- }
- // Handle old version after upgrade.
- switch versioned.Version {
- case metadataVersion:
- *c = Metadata(versioned.Metadata)
- return nil
- }
- return errors.Errorf("unsupported version: %q", versioned.Version)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/sandbox/sandbox.go b/vendor/github.com/containerd/cri/pkg/store/sandbox/sandbox.go
deleted file mode 100644
index 223e88369..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/sandbox/sandbox.go
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package sandbox
-
-import (
- "sync"
-
- "github.com/containerd/containerd"
- "github.com/containerd/cri/pkg/store/label"
- "github.com/docker/docker/pkg/truncindex"
-
- "github.com/containerd/cri/pkg/netns"
- "github.com/containerd/cri/pkg/store"
-)
-
-// Sandbox contains all resources associated with the sandbox. All methods to
-// mutate the internal state are thread safe.
-type Sandbox struct {
- // Metadata is the metadata of the sandbox, it is immutable after created.
- Metadata
- // Status stores the status of the sandbox.
- Status StatusStorage
- // Container is the containerd sandbox container client.
- Container containerd.Container
- // CNI network namespace client.
- // For hostnetwork pod, this is always nil;
- // For non hostnetwork pod, this should never be nil.
- NetNS *netns.NetNS
- // StopCh is used to propagate the stop information of the sandbox.
- *store.StopCh
-}
-
-// NewSandbox creates an internally used sandbox type. This functions reminds
-// the caller that a sandbox must have a status.
-func NewSandbox(metadata Metadata, status Status) Sandbox {
- s := Sandbox{
- Metadata: metadata,
- Status: StoreStatus(status),
- StopCh: store.NewStopCh(),
- }
- if status.State == StateNotReady {
- s.Stop()
- }
- return s
-}
-
-// Store stores all sandboxes.
-type Store struct {
- lock sync.RWMutex
- sandboxes map[string]Sandbox
- idIndex *truncindex.TruncIndex
- labels *label.Store
-}
-
-// NewStore creates a sandbox store.
-func NewStore(labels *label.Store) *Store {
- return &Store{
- sandboxes: make(map[string]Sandbox),
- idIndex: truncindex.NewTruncIndex([]string{}),
- labels: labels,
- }
-}
-
-// Add a sandbox into the store.
-func (s *Store) Add(sb Sandbox) error {
- s.lock.Lock()
- defer s.lock.Unlock()
- if _, ok := s.sandboxes[sb.ID]; ok {
- return store.ErrAlreadyExist
- }
- if err := s.labels.Reserve(sb.ProcessLabel); err != nil {
- return err
- }
- if err := s.idIndex.Add(sb.ID); err != nil {
- return err
- }
- s.sandboxes[sb.ID] = sb
- return nil
-}
-
-// Get returns the sandbox with specified id.
-// Returns store.ErrNotExist if the sandbox doesn't exist.
-func (s *Store) Get(id string) (Sandbox, error) {
- s.lock.RLock()
- defer s.lock.RUnlock()
- id, err := s.idIndex.Get(id)
- if err != nil {
- if err == truncindex.ErrNotExist {
- err = store.ErrNotExist
- }
- return Sandbox{}, err
- }
- if sb, ok := s.sandboxes[id]; ok {
- return sb, nil
- }
- return Sandbox{}, store.ErrNotExist
-}
-
-// List lists all sandboxes.
-func (s *Store) List() []Sandbox {
- s.lock.RLock()
- defer s.lock.RUnlock()
- var sandboxes []Sandbox
- for _, sb := range s.sandboxes {
- sandboxes = append(sandboxes, sb)
- }
- return sandboxes
-}
-
-// Delete deletes the sandbox with specified id.
-func (s *Store) Delete(id string) {
- s.lock.Lock()
- defer s.lock.Unlock()
- id, err := s.idIndex.Get(id)
- if err != nil {
- // Note: The idIndex.Delete and delete doesn't handle truncated index.
- // So we need to return if there are error.
- return
- }
- s.labels.Release(s.sandboxes[id].ProcessLabel)
- s.idIndex.Delete(id) // nolint: errcheck
- delete(s.sandboxes, id)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/sandbox/status.go b/vendor/github.com/containerd/cri/pkg/store/sandbox/status.go
deleted file mode 100644
index e9198eb97..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/sandbox/status.go
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package sandbox
-
-import (
- "strconv"
- "sync"
- "time"
-
- runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-)
-
-// The sandbox state machine in the CRI plugin:
-// + +
-// | |
-// | Create(Run) | Load
-// | |
-// Start | |
-// (failed) | |
-// +------------------+ +-----------+
-// | | | |
-// | | | |
-// | | | |
-// | | Start(Run) | |
-// | | | |
-// | PortForward +----v----+ | |
-// | +------+ | | |
-// | | | READY <---------+ |
-// | +------> | | |
-// | +----+----+ | |
-// | | | |
-// | | Stop/Exit | |
-// | | | |
-// | +----v----+ | |
-// | | <---------+ +----v----+
-// | | NOTREADY| | |
-// | | <----------------+ UNKNOWN |
-// | +----+----+ Stop | |
-// | | +---------+
-// | | Remove
-// | v
-// +-------------> DELETED
-
-// State is the sandbox state we use in containerd/cri.
-// It includes unknown, which is internal states not defined in CRI.
-// The state mapping from internal states to CRI states:
-// * ready -> ready
-// * not ready -> not ready
-// * unknown -> not ready
-type State uint32
-
-const (
- // StateReady is ready state, it means sandbox container
- // is running.
- StateReady State = iota
- // StateNotReady is notready state, it ONLY means sandbox
- // container is not running.
- // StopPodSandbox should still be called for NOTREADY sandbox to
- // cleanup resources other than sandbox container, e.g. network namespace.
- // This is an assumption made in CRI.
- StateNotReady
- // StateUnknown is unknown state. Sandbox only goes
- // into unknown state when its status fails to be loaded.
- StateUnknown
-)
-
-// String returns the string representation of the state
-func (s State) String() string {
- switch s {
- case StateReady:
- return runtime.PodSandboxState_SANDBOX_READY.String()
- case StateNotReady:
- return runtime.PodSandboxState_SANDBOX_NOTREADY.String()
- case StateUnknown:
- // PodSandboxState doesn't have an unknown state, but State does, so return a string using the same convention
- return "SANDBOX_UNKNOWN"
- default:
- return "invalid sandbox state value: " + strconv.Itoa(int(s))
- }
-}
-
-// Status is the status of a sandbox.
-type Status struct {
- // Pid is the init process id of the sandbox container.
- Pid uint32
- // CreatedAt is the created timestamp.
- CreatedAt time.Time
- // State is the state of the sandbox.
- State State
-}
-
-// UpdateFunc is function used to update the sandbox status. If there
-// is an error, the update will be rolled back.
-type UpdateFunc func(Status) (Status, error)
-
-// StatusStorage manages the sandbox status.
-// The status storage for sandbox is different from container status storage,
-// because we don't checkpoint sandbox status. If we need checkpoint in the
-// future, we should combine this with container status storage.
-type StatusStorage interface {
- // Get a sandbox status.
- Get() Status
- // Update the sandbox status. Note that the update MUST be applied
- // in one transaction.
- Update(UpdateFunc) error
-}
-
-// StoreStatus creates the storage containing the passed in sandbox status with the
-// specified id.
-// The status MUST be created in one transaction.
-func StoreStatus(status Status) StatusStorage {
- return &statusStorage{status: status}
-}
-
-type statusStorage struct {
- sync.RWMutex
- status Status
-}
-
-// Get a copy of sandbox status.
-func (s *statusStorage) Get() Status {
- s.RLock()
- defer s.RUnlock()
- return s.status
-}
-
-// Update the sandbox status.
-func (s *statusStorage) Update(u UpdateFunc) error {
- s.Lock()
- defer s.Unlock()
- newStatus, err := u(s.status)
- if err != nil {
- return err
- }
- s.status = newStatus
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/snapshot/snapshot.go b/vendor/github.com/containerd/cri/pkg/store/snapshot/snapshot.go
deleted file mode 100644
index ce05f0e04..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/snapshot/snapshot.go
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package snapshot
-
-import (
- "sync"
-
- snapshot "github.com/containerd/containerd/snapshots"
-
- "github.com/containerd/cri/pkg/store"
-)
-
-// Snapshot contains the information about the snapshot.
-type Snapshot struct {
- // Key is the key of the snapshot
- Key string
- // Kind is the kind of the snapshot (active, committed, view)
- Kind snapshot.Kind
- // Size is the size of the snapshot in bytes.
- Size uint64
- // Inodes is the number of inodes used by the snapshot
- Inodes uint64
- // Timestamp is latest update time (in nanoseconds) of the snapshot
- // information.
- Timestamp int64
-}
-
-// Store stores all snapshots.
-type Store struct {
- lock sync.RWMutex
- snapshots map[string]Snapshot
-}
-
-// NewStore creates a snapshot store.
-func NewStore() *Store {
- return &Store{snapshots: make(map[string]Snapshot)}
-}
-
-// Add a snapshot into the store.
-func (s *Store) Add(snapshot Snapshot) {
- s.lock.Lock()
- defer s.lock.Unlock()
- s.snapshots[snapshot.Key] = snapshot
-}
-
-// Get returns the snapshot with specified key. Returns store.ErrNotExist if the
-// snapshot doesn't exist.
-func (s *Store) Get(key string) (Snapshot, error) {
- s.lock.RLock()
- defer s.lock.RUnlock()
- if sn, ok := s.snapshots[key]; ok {
- return sn, nil
- }
- return Snapshot{}, store.ErrNotExist
-}
-
-// List lists all snapshots.
-func (s *Store) List() []Snapshot {
- s.lock.RLock()
- defer s.lock.RUnlock()
- var snapshots []Snapshot
- for _, sn := range s.snapshots {
- snapshots = append(snapshots, sn)
- }
- return snapshots
-}
-
-// Delete deletes the snapshot with specified key.
-func (s *Store) Delete(key string) {
- s.lock.Lock()
- defer s.lock.Unlock()
- delete(s.snapshots, key)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/store/util.go b/vendor/github.com/containerd/cri/pkg/store/util.go
deleted file mode 100644
index 73626b1af..000000000
--- a/vendor/github.com/containerd/cri/pkg/store/util.go
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package store
-
-import "sync"
-
-// StopCh is used to propagate the stop information of a container.
-type StopCh struct {
- ch chan struct{}
- once sync.Once
-}
-
-// NewStopCh creates a stop channel. The channel is open by default.
-func NewStopCh() *StopCh {
- return &StopCh{ch: make(chan struct{})}
-}
-
-// Stop close stopCh of the container.
-func (s *StopCh) Stop() {
- s.once.Do(func() {
- close(s.ch)
- })
-}
-
-// Stopped return the stopCh of the container as a readonly channel.
-func (s *StopCh) Stopped() <-chan struct{} {
- return s.ch
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/errors.go b/vendor/github.com/containerd/cri/pkg/streaming/errors.go
deleted file mode 100644
index 874064054..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/errors.go
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package streaming
-
-import (
- "net/http"
- "strconv"
-
- "google.golang.org/grpc/codes"
- grpcstatus "google.golang.org/grpc/status"
-)
-
-// NewErrorStreamingDisabled creates an error for disabled streaming method.
-func NewErrorStreamingDisabled(method string) error {
- return grpcstatus.Errorf(codes.NotFound, "streaming method %s disabled", method)
-}
-
-// NewErrorTooManyInFlight creates an error for exceeding the maximum number of in-flight requests.
-func NewErrorTooManyInFlight() error {
- return grpcstatus.Error(codes.ResourceExhausted, "maximum number of in-flight requests exceeded")
-}
-
-// WriteError translates a CRI streaming error into an appropriate HTTP response.
-func WriteError(err error, w http.ResponseWriter) error {
- s, _ := grpcstatus.FromError(err)
- var status int
- switch s.Code() {
- case codes.NotFound:
- status = http.StatusNotFound
- case codes.ResourceExhausted:
- // We only expect to hit this if there is a DoS, so we just wait the full TTL.
- // If this is ever hit in steady-state operations, consider increasing the maxInFlight requests,
- // or plumbing through the time to next expiration.
- w.Header().Set("Retry-After", strconv.Itoa(int(cacheTTL.Seconds())))
- status = http.StatusTooManyRequests
- default:
- status = http.StatusInternalServerError
- }
- w.WriteHeader(status)
- _, writeErr := w.Write([]byte(err.Error()))
- return writeErr
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/portforward/constants.go b/vendor/github.com/containerd/cri/pkg/streaming/portforward/constants.go
deleted file mode 100644
index 217a0b114..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/portforward/constants.go
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2015 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Package portforward contains server-side logic for handling port forwarding requests.
-package portforward
-
-// ProtocolV1Name is the name of the subprotocol used for port forwarding.
-const ProtocolV1Name = "portforward.k8s.io"
-
-// SupportedProtocols are the supported port forwarding protocols.
-var SupportedProtocols = []string{ProtocolV1Name}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/portforward/httpstream.go b/vendor/github.com/containerd/cri/pkg/streaming/portforward/httpstream.go
deleted file mode 100644
index f961cdb6f..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/portforward/httpstream.go
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package portforward
-
-import (
- "errors"
- "fmt"
- "net/http"
- "strconv"
- "sync"
- "time"
-
- api "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/httpstream"
- "k8s.io/apimachinery/pkg/util/httpstream/spdy"
- utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-
- "k8s.io/klog/v2"
-)
-
-func handleHTTPStreams(req *http.Request, w http.ResponseWriter, portForwarder PortForwarder, podName string, uid types.UID, supportedPortForwardProtocols []string, idleTimeout, streamCreationTimeout time.Duration) error {
- _, err := httpstream.Handshake(req, w, supportedPortForwardProtocols)
- // negotiated protocol isn't currently used server side, but could be in the future
- if err != nil {
- // Handshake writes the error to the client
- return err
- }
- streamChan := make(chan httpstream.Stream, 1)
-
- klog.V(5).Infof("Upgrading port forward response")
- upgrader := spdy.NewResponseUpgrader()
- conn := upgrader.UpgradeResponse(w, req, httpStreamReceived(streamChan))
- if conn == nil {
- return errors.New("unable to upgrade httpstream connection")
- }
- defer conn.Close()
-
- klog.V(5).Infof("(conn=%p) setting port forwarding streaming connection idle timeout to %v", conn, idleTimeout)
- conn.SetIdleTimeout(idleTimeout)
-
- h := &httpStreamHandler{
- conn: conn,
- streamChan: streamChan,
- streamPairs: make(map[string]*httpStreamPair),
- streamCreationTimeout: streamCreationTimeout,
- pod: podName,
- uid: uid,
- forwarder: portForwarder,
- }
- h.run()
-
- return nil
-}
-
-// httpStreamReceived is the httpstream.NewStreamHandler for port
-// forward streams. It checks each stream's port and stream type headers,
-// rejecting any streams that with missing or invalid values. Each valid
-// stream is sent to the streams channel.
-func httpStreamReceived(streams chan httpstream.Stream) func(httpstream.Stream, <-chan struct{}) error {
- return func(stream httpstream.Stream, replySent <-chan struct{}) error {
- // make sure it has a valid port header
- portString := stream.Headers().Get(api.PortHeader)
- if len(portString) == 0 {
- return fmt.Errorf("%q header is required", api.PortHeader)
- }
- port, err := strconv.ParseUint(portString, 10, 16)
- if err != nil {
- return fmt.Errorf("unable to parse %q as a port: %v", portString, err)
- }
- if port < 1 {
- return fmt.Errorf("port %q must be > 0", portString)
- }
-
- // make sure it has a valid stream type header
- streamType := stream.Headers().Get(api.StreamType)
- if len(streamType) == 0 {
- return fmt.Errorf("%q header is required", api.StreamType)
- }
- if streamType != api.StreamTypeError && streamType != api.StreamTypeData {
- return fmt.Errorf("invalid stream type %q", streamType)
- }
-
- streams <- stream
- return nil
- }
-}
-
-// httpStreamHandler is capable of processing multiple port forward
-// requests over a single httpstream.Connection.
-type httpStreamHandler struct {
- conn httpstream.Connection
- streamChan chan httpstream.Stream
- streamPairsLock sync.RWMutex
- streamPairs map[string]*httpStreamPair
- streamCreationTimeout time.Duration
- pod string
- uid types.UID
- forwarder PortForwarder
-}
-
-// getStreamPair returns a httpStreamPair for requestID. This creates a
-// new pair if one does not yet exist for the requestID. The returned bool is
-// true if the pair was created.
-func (h *httpStreamHandler) getStreamPair(requestID string) (*httpStreamPair, bool) {
- h.streamPairsLock.Lock()
- defer h.streamPairsLock.Unlock()
-
- if p, ok := h.streamPairs[requestID]; ok {
- klog.V(5).Infof("(conn=%p, request=%s) found existing stream pair", h.conn, requestID)
- return p, false
- }
-
- klog.V(5).Infof("(conn=%p, request=%s) creating new stream pair", h.conn, requestID)
-
- p := newPortForwardPair(requestID)
- h.streamPairs[requestID] = p
-
- return p, true
-}
-
-// monitorStreamPair waits for the pair to receive both its error and data
-// streams, or for the timeout to expire (whichever happens first), and then
-// removes the pair.
-func (h *httpStreamHandler) monitorStreamPair(p *httpStreamPair, timeout <-chan time.Time) {
- select {
- case <-timeout:
- err := fmt.Errorf("(conn=%v, request=%s) timed out waiting for streams", h.conn, p.requestID)
- utilruntime.HandleError(err)
- p.printError(err.Error())
- case <-p.complete:
- klog.V(5).Infof("(conn=%v, request=%s) successfully received error and data streams", h.conn, p.requestID)
- }
- h.removeStreamPair(p.requestID)
-}
-
-// removeStreamPair removes the stream pair identified by requestID from streamPairs.
-func (h *httpStreamHandler) removeStreamPair(requestID string) {
- h.streamPairsLock.Lock()
- defer h.streamPairsLock.Unlock()
-
- delete(h.streamPairs, requestID)
-}
-
-// requestID returns the request id for stream.
-func (h *httpStreamHandler) requestID(stream httpstream.Stream) string {
- requestID := stream.Headers().Get(api.PortForwardRequestIDHeader)
- if len(requestID) == 0 {
- klog.V(5).Infof("(conn=%p) stream received without %s header", h.conn, api.PortForwardRequestIDHeader)
- // If we get here, it's because the connection came from an older client
- // that isn't generating the request id header
- // (https://github.com/kubernetes/kubernetes/blob/843134885e7e0b360eb5441e85b1410a8b1a7a0c/pkg/client/unversioned/portforward/portforward.go#L258-L287)
- //
- // This is a best-effort attempt at supporting older clients.
- //
- // When there aren't concurrent new forwarded connections, each connection
- // will have a pair of streams (data, error), and the stream IDs will be
- // consecutive odd numbers, e.g. 1 and 3 for the first connection. Convert
- // the stream ID into a pseudo-request id by taking the stream type and
- // using id = stream.Identifier() when the stream type is error,
- // and id = stream.Identifier() - 2 when it's data.
- //
- // NOTE: this only works when there are not concurrent new streams from
- // multiple forwarded connections; it's a best-effort attempt at supporting
- // old clients that don't generate request ids. If there are concurrent
- // new connections, it's possible that 1 connection gets streams whose IDs
- // are not consecutive (e.g. 5 and 9 instead of 5 and 7).
- streamType := stream.Headers().Get(api.StreamType)
- switch streamType {
- case api.StreamTypeError:
- requestID = strconv.Itoa(int(stream.Identifier()))
- case api.StreamTypeData:
- requestID = strconv.Itoa(int(stream.Identifier()) - 2)
- }
-
- klog.V(5).Infof("(conn=%p) automatically assigning request ID=%q from stream type=%s, stream ID=%d", h.conn, requestID, streamType, stream.Identifier())
- }
- return requestID
-}
-
-// run is the main loop for the httpStreamHandler. It processes new
-// streams, invoking portForward for each complete stream pair. The loop exits
-// when the httpstream.Connection is closed.
-func (h *httpStreamHandler) run() {
- klog.V(5).Infof("(conn=%p) waiting for port forward streams", h.conn)
-Loop:
- for {
- select {
- case <-h.conn.CloseChan():
- klog.V(5).Infof("(conn=%p) upgraded connection closed", h.conn)
- break Loop
- case stream := <-h.streamChan:
- requestID := h.requestID(stream)
- streamType := stream.Headers().Get(api.StreamType)
- klog.V(5).Infof("(conn=%p, request=%s) received new stream of type %s", h.conn, requestID, streamType)
-
- p, created := h.getStreamPair(requestID)
- if created {
- go h.monitorStreamPair(p, time.After(h.streamCreationTimeout))
- }
- if complete, err := p.add(stream); err != nil {
- msg := fmt.Sprintf("error processing stream for request %s: %v", requestID, err)
- utilruntime.HandleError(errors.New(msg))
- p.printError(msg)
- } else if complete {
- go h.portForward(p)
- }
- }
- }
-}
-
-// portForward invokes the httpStreamHandler's forwarder.PortForward
-// function for the given stream pair.
-func (h *httpStreamHandler) portForward(p *httpStreamPair) {
- defer p.dataStream.Close()
- defer p.errorStream.Close()
-
- portString := p.dataStream.Headers().Get(api.PortHeader)
- port, _ := strconv.ParseInt(portString, 10, 32)
-
- klog.V(5).Infof("(conn=%p, request=%s) invoking forwarder.PortForward for port %s", h.conn, p.requestID, portString)
- err := h.forwarder.PortForward(h.pod, h.uid, int32(port), p.dataStream)
- klog.V(5).Infof("(conn=%p, request=%s) done invoking forwarder.PortForward for port %s", h.conn, p.requestID, portString)
-
- if err != nil {
- msg := fmt.Errorf("error forwarding port %d to pod %s, uid %v: %v", port, h.pod, h.uid, err)
- utilruntime.HandleError(msg)
- fmt.Fprint(p.errorStream, msg.Error())
- }
-}
-
-// httpStreamPair represents the error and data streams for a port
-// forwarding request.
-type httpStreamPair struct {
- lock sync.RWMutex
- requestID string
- dataStream httpstream.Stream
- errorStream httpstream.Stream
- complete chan struct{}
-}
-
-// newPortForwardPair creates a new httpStreamPair.
-func newPortForwardPair(requestID string) *httpStreamPair {
- return &httpStreamPair{
- requestID: requestID,
- complete: make(chan struct{}),
- }
-}
-
-// add adds the stream to the httpStreamPair. If the pair already
-// contains a stream for the new stream's type, an error is returned. add
-// returns true if both the data and error streams for this pair have been
-// received.
-func (p *httpStreamPair) add(stream httpstream.Stream) (bool, error) {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- switch stream.Headers().Get(api.StreamType) {
- case api.StreamTypeError:
- if p.errorStream != nil {
- return false, errors.New("error stream already assigned")
- }
- p.errorStream = stream
- case api.StreamTypeData:
- if p.dataStream != nil {
- return false, errors.New("data stream already assigned")
- }
- p.dataStream = stream
- }
-
- complete := p.errorStream != nil && p.dataStream != nil
- if complete {
- close(p.complete)
- }
- return complete, nil
-}
-
-// printError writes s to p.errorStream if p.errorStream has been set.
-func (p *httpStreamPair) printError(s string) {
- p.lock.RLock()
- defer p.lock.RUnlock()
- if p.errorStream != nil {
- fmt.Fprint(p.errorStream, s)
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/portforward/portforward.go b/vendor/github.com/containerd/cri/pkg/streaming/portforward/portforward.go
deleted file mode 100644
index 89a39ea66..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/portforward/portforward.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package portforward
-
-import (
- "io"
- "net/http"
- "time"
-
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/runtime"
- "k8s.io/apiserver/pkg/util/wsstream"
-)
-
-// PortForwarder knows how to forward content from a data stream to/from a port
-// in a pod.
-type PortForwarder interface {
- // PortForwarder copies data between a data stream and a port in a pod.
- PortForward(name string, uid types.UID, port int32, stream io.ReadWriteCloser) error
-}
-
-// ServePortForward handles a port forwarding request. A single request is
-// kept alive as long as the client is still alive and the connection has not
-// been timed out due to idleness. This function handles multiple forwarded
-// connections; i.e., multiple `curl http://localhost:8888/` requests will be
-// handled by a single invocation of ServePortForward.
-func ServePortForward(w http.ResponseWriter, req *http.Request, portForwarder PortForwarder, podName string, uid types.UID, portForwardOptions *V4Options, idleTimeout time.Duration, streamCreationTimeout time.Duration, supportedProtocols []string) {
- var err error
- if wsstream.IsWebSocketRequest(req) {
- err = handleWebSocketStreams(req, w, portForwarder, podName, uid, portForwardOptions, supportedProtocols, idleTimeout, streamCreationTimeout)
- } else {
- err = handleHTTPStreams(req, w, portForwarder, podName, uid, supportedProtocols, idleTimeout, streamCreationTimeout)
- }
-
- if err != nil {
- runtime.HandleError(err)
- return
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/portforward/websocket.go b/vendor/github.com/containerd/cri/pkg/streaming/portforward/websocket.go
deleted file mode 100644
index 1b1c0151a..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/portforward/websocket.go
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package portforward
-
-import (
- "encoding/binary"
- "fmt"
- "io"
- "net/http"
- "strconv"
- "strings"
- "sync"
- "time"
-
- "k8s.io/klog/v2"
-
- api "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/runtime"
- "k8s.io/apiserver/pkg/server/httplog"
- "k8s.io/apiserver/pkg/util/wsstream"
-)
-
-const (
- dataChannel = iota
- errorChannel
-
- v4BinaryWebsocketProtocol = "v4." + wsstream.ChannelWebSocketProtocol
- v4Base64WebsocketProtocol = "v4." + wsstream.Base64ChannelWebSocketProtocol
-)
-
-// V4Options contains details about which streams are required for port
-// forwarding.
-// All fields included in V4Options need to be expressed explicitly in the
-// CRI (k8s.io/cri-api/pkg/apis/{version}/api.proto) PortForwardRequest.
-type V4Options struct {
- Ports []int32
-}
-
-// NewV4Options creates a new options from the Request.
-func NewV4Options(req *http.Request) (*V4Options, error) {
- if !wsstream.IsWebSocketRequest(req) {
- return &V4Options{}, nil
- }
-
- portStrings := req.URL.Query()[api.PortHeader]
- if len(portStrings) == 0 {
- return nil, fmt.Errorf("query parameter %q is required", api.PortHeader)
- }
-
- ports := make([]int32, 0, len(portStrings))
- for _, portString := range portStrings {
- if len(portString) == 0 {
- return nil, fmt.Errorf("query parameter %q cannot be empty", api.PortHeader)
- }
- for _, p := range strings.Split(portString, ",") {
- port, err := strconv.ParseUint(p, 10, 16)
- if err != nil {
- return nil, fmt.Errorf("unable to parse %q as a port: %v", portString, err)
- }
- if port < 1 {
- return nil, fmt.Errorf("port %q must be > 0", portString)
- }
- ports = append(ports, int32(port))
- }
- }
-
- return &V4Options{
- Ports: ports,
- }, nil
-}
-
-// BuildV4Options returns a V4Options based on the given information.
-func BuildV4Options(ports []int32) (*V4Options, error) {
- return &V4Options{Ports: ports}, nil
-}
-
-// handleWebSocketStreams handles requests to forward ports to a pod via
-// a PortForwarder. A pair of streams are created per port (DATA n,
-// ERROR n+1). The associated port is written to each stream as a unsigned 16
-// bit integer in little endian format.
-func handleWebSocketStreams(req *http.Request, w http.ResponseWriter, portForwarder PortForwarder, podName string, uid types.UID, opts *V4Options, supportedPortForwardProtocols []string, idleTimeout, streamCreationTimeout time.Duration) error {
- channels := make([]wsstream.ChannelType, 0, len(opts.Ports)*2)
- for i := 0; i < len(opts.Ports); i++ {
- channels = append(channels, wsstream.ReadWriteChannel, wsstream.WriteChannel)
- }
- conn := wsstream.NewConn(map[string]wsstream.ChannelProtocolConfig{
- "": {
- Binary: true,
- Channels: channels,
- },
- v4BinaryWebsocketProtocol: {
- Binary: true,
- Channels: channels,
- },
- v4Base64WebsocketProtocol: {
- Binary: false,
- Channels: channels,
- },
- })
- conn.SetIdleTimeout(idleTimeout)
- _, streams, err := conn.Open(httplog.Unlogged(req, w), req)
- if err != nil {
- err = fmt.Errorf("unable to upgrade websocket connection: %v", err)
- return err
- }
- defer conn.Close()
- streamPairs := make([]*websocketStreamPair, len(opts.Ports))
- for i := range streamPairs {
- streamPair := websocketStreamPair{
- port: opts.Ports[i],
- dataStream: streams[i*2+dataChannel],
- errorStream: streams[i*2+errorChannel],
- }
- streamPairs[i] = &streamPair
-
- portBytes := make([]byte, 2)
- // port is always positive so conversion is allowable
- binary.LittleEndian.PutUint16(portBytes, uint16(streamPair.port))
- streamPair.dataStream.Write(portBytes)
- streamPair.errorStream.Write(portBytes)
- }
- h := &websocketStreamHandler{
- conn: conn,
- streamPairs: streamPairs,
- pod: podName,
- uid: uid,
- forwarder: portForwarder,
- }
- h.run()
-
- return nil
-}
-
-// websocketStreamPair represents the error and data streams for a port
-// forwarding request.
-type websocketStreamPair struct {
- port int32
- dataStream io.ReadWriteCloser
- errorStream io.WriteCloser
-}
-
-// websocketStreamHandler is capable of processing a single port forward
-// request over a websocket connection
-type websocketStreamHandler struct {
- conn *wsstream.Conn
- streamPairs []*websocketStreamPair
- pod string
- uid types.UID
- forwarder PortForwarder
-}
-
-// run invokes the websocketStreamHandler's forwarder.PortForward
-// function for the given stream pair.
-func (h *websocketStreamHandler) run() {
- wg := sync.WaitGroup{}
- wg.Add(len(h.streamPairs))
-
- for _, pair := range h.streamPairs {
- p := pair
- go func() {
- defer wg.Done()
- h.portForward(p)
- }()
- }
-
- wg.Wait()
-}
-
-func (h *websocketStreamHandler) portForward(p *websocketStreamPair) {
- defer p.dataStream.Close()
- defer p.errorStream.Close()
-
- klog.V(5).Infof("(conn=%p) invoking forwarder.PortForward for port %d", h.conn, p.port)
- err := h.forwarder.PortForward(h.pod, h.uid, p.port, p.dataStream)
- klog.V(5).Infof("(conn=%p) done invoking forwarder.PortForward for port %d", h.conn, p.port)
-
- if err != nil {
- msg := fmt.Errorf("error forwarding port %d to pod %s, uid %v: %v", p.port, h.pod, h.uid, err)
- runtime.HandleError(msg)
- fmt.Fprint(p.errorStream, msg.Error())
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/attach.go b/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/attach.go
deleted file mode 100644
index 2b127bdf9..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/attach.go
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package remotecommand
-
-import (
- "fmt"
- "io"
- "net/http"
- "time"
-
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/runtime"
- "k8s.io/client-go/tools/remotecommand"
-)
-
-// Attacher knows how to attach to a running container in a pod.
-type Attacher interface {
- // AttachContainer attaches to the running container in the pod, copying data between in/out/err
- // and the container's stdin/stdout/stderr.
- AttachContainer(name string, uid types.UID, container string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error
-}
-
-// ServeAttach handles requests to attach to a container. After creating/receiving the required
-// streams, it delegates the actual attaching to attacher.
-func ServeAttach(w http.ResponseWriter, req *http.Request, attacher Attacher, podName string, uid types.UID, container string, streamOpts *Options, idleTimeout, streamCreationTimeout time.Duration, supportedProtocols []string) {
- ctx, ok := createStreams(req, w, streamOpts, supportedProtocols, idleTimeout, streamCreationTimeout)
- if !ok {
- // error is handled by createStreams
- return
- }
- defer ctx.conn.Close()
-
- err := attacher.AttachContainer(podName, uid, container, ctx.stdinStream, ctx.stdoutStream, ctx.stderrStream, ctx.tty, ctx.resizeChan)
- if err != nil {
- err = fmt.Errorf("error attaching to container: %v", err)
- runtime.HandleError(err)
- ctx.writeStatus(apierrors.NewInternalError(err))
- } else {
- ctx.writeStatus(&apierrors.StatusError{ErrStatus: metav1.Status{
- Status: metav1.StatusSuccess,
- }})
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/doc.go b/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/doc.go
deleted file mode 100644
index 6034cdc8e..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/doc.go
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Package remotecommand contains functions related to executing commands in and attaching to pods.
-package remotecommand
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/exec.go b/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/exec.go
deleted file mode 100644
index 5111521ba..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/exec.go
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package remotecommand
-
-import (
- "fmt"
- "io"
- "net/http"
- "time"
-
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
- remotecommandconsts "k8s.io/apimachinery/pkg/util/remotecommand"
- "k8s.io/apimachinery/pkg/util/runtime"
- "k8s.io/client-go/tools/remotecommand"
- utilexec "k8s.io/utils/exec"
-)
-
-// Executor knows how to execute a command in a container in a pod.
-type Executor interface {
- // ExecInContainer executes a command in a container in the pod, copying data
- // between in/out/err and the container's stdin/stdout/stderr.
- ExecInContainer(name string, uid types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error
-}
-
-// ServeExec handles requests to execute a command in a container. After
-// creating/receiving the required streams, it delegates the actual execution
-// to the executor.
-func ServeExec(w http.ResponseWriter, req *http.Request, executor Executor, podName string, uid types.UID, container string, cmd []string, streamOpts *Options, idleTimeout, streamCreationTimeout time.Duration, supportedProtocols []string) {
- ctx, ok := createStreams(req, w, streamOpts, supportedProtocols, idleTimeout, streamCreationTimeout)
- if !ok {
- // error is handled by createStreams
- return
- }
- defer ctx.conn.Close()
-
- err := executor.ExecInContainer(podName, uid, container, cmd, ctx.stdinStream, ctx.stdoutStream, ctx.stderrStream, ctx.tty, ctx.resizeChan, 0)
- if err != nil {
- if exitErr, ok := err.(utilexec.ExitError); ok && exitErr.Exited() {
- rc := exitErr.ExitStatus()
- ctx.writeStatus(&apierrors.StatusError{ErrStatus: metav1.Status{
- Status: metav1.StatusFailure,
- Reason: remotecommandconsts.NonZeroExitCodeReason,
- Details: &metav1.StatusDetails{
- Causes: []metav1.StatusCause{
- {
- Type: remotecommandconsts.ExitCodeCauseType,
- Message: fmt.Sprintf("%d", rc),
- },
- },
- },
- Message: fmt.Sprintf("command terminated with non-zero exit code: %v", exitErr),
- }})
- } else {
- err = fmt.Errorf("error executing command in container: %v", err)
- runtime.HandleError(err)
- ctx.writeStatus(apierrors.NewInternalError(err))
- }
- } else {
- ctx.writeStatus(&apierrors.StatusError{ErrStatus: metav1.Status{
- Status: metav1.StatusSuccess,
- }})
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/httpstream.go b/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/httpstream.go
deleted file mode 100644
index 0417a1a9e..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/httpstream.go
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package remotecommand
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "time"
-
- api "k8s.io/api/core/v1"
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/httpstream"
- "k8s.io/apimachinery/pkg/util/httpstream/spdy"
- remotecommandconsts "k8s.io/apimachinery/pkg/util/remotecommand"
- "k8s.io/apimachinery/pkg/util/runtime"
- "k8s.io/apiserver/pkg/util/wsstream"
- "k8s.io/client-go/tools/remotecommand"
-
- "k8s.io/klog/v2"
-)
-
-// Options contains details about which streams are required for
-// remote command execution.
-type Options struct {
- Stdin bool
- Stdout bool
- Stderr bool
- TTY bool
-}
-
-// NewOptions creates a new Options from the Request.
-func NewOptions(req *http.Request) (*Options, error) {
- tty := req.FormValue(api.ExecTTYParam) == "1"
- stdin := req.FormValue(api.ExecStdinParam) == "1"
- stdout := req.FormValue(api.ExecStdoutParam) == "1"
- stderr := req.FormValue(api.ExecStderrParam) == "1"
- if tty && stderr {
- // TODO: make this an error before we reach this method
- klog.V(4).Infof("Access to exec with tty and stderr is not supported, bypassing stderr")
- stderr = false
- }
-
- if !stdin && !stdout && !stderr {
- return nil, fmt.Errorf("you must specify at least 1 of stdin, stdout, stderr")
- }
-
- return &Options{
- Stdin: stdin,
- Stdout: stdout,
- Stderr: stderr,
- TTY: tty,
- }, nil
-}
-
-// context contains the connection and streams used when
-// forwarding an attach or execute session into a container.
-type context struct {
- conn io.Closer
- stdinStream io.ReadCloser
- stdoutStream io.WriteCloser
- stderrStream io.WriteCloser
- writeStatus func(status *apierrors.StatusError) error
- resizeStream io.ReadCloser
- resizeChan chan remotecommand.TerminalSize
- tty bool
-}
-
-// streamAndReply holds both a Stream and a channel that is closed when the stream's reply frame is
-// enqueued. Consumers can wait for replySent to be closed prior to proceeding, to ensure that the
-// replyFrame is enqueued before the connection's goaway frame is sent (e.g. if a stream was
-// received and right after, the connection gets closed).
-type streamAndReply struct {
- httpstream.Stream
- replySent <-chan struct{}
-}
-
-// waitStreamReply waits until either replySent or stop is closed. If replySent is closed, it sends
-// an empty struct to the notify channel.
-func waitStreamReply(replySent <-chan struct{}, notify chan<- struct{}, stop <-chan struct{}) {
- select {
- case <-replySent:
- notify <- struct{}{}
- case <-stop:
- }
-}
-
-func createStreams(req *http.Request, w http.ResponseWriter, opts *Options, supportedStreamProtocols []string, idleTimeout, streamCreationTimeout time.Duration) (*context, bool) {
- var ctx *context
- var ok bool
- if wsstream.IsWebSocketRequest(req) {
- ctx, ok = createWebSocketStreams(req, w, opts, idleTimeout)
- } else {
- ctx, ok = createHTTPStreamStreams(req, w, opts, supportedStreamProtocols, idleTimeout, streamCreationTimeout)
- }
- if !ok {
- return nil, false
- }
-
- if ctx.resizeStream != nil {
- ctx.resizeChan = make(chan remotecommand.TerminalSize)
- go handleResizeEvents(ctx.resizeStream, ctx.resizeChan)
- }
-
- return ctx, true
-}
-
-func createHTTPStreamStreams(req *http.Request, w http.ResponseWriter, opts *Options, supportedStreamProtocols []string, idleTimeout, streamCreationTimeout time.Duration) (*context, bool) {
- protocol, err := httpstream.Handshake(req, w, supportedStreamProtocols)
- if err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return nil, false
- }
-
- streamCh := make(chan streamAndReply)
-
- upgrader := spdy.NewResponseUpgrader()
- conn := upgrader.UpgradeResponse(w, req, func(stream httpstream.Stream, replySent <-chan struct{}) error {
- streamCh <- streamAndReply{Stream: stream, replySent: replySent}
- return nil
- })
- // from this point on, we can no longer call methods on response
- if conn == nil {
- // The upgrader is responsible for notifying the client of any errors that
- // occurred during upgrading. All we can do is return here at this point
- // if we weren't successful in upgrading.
- return nil, false
- }
-
- conn.SetIdleTimeout(idleTimeout)
-
- var handler protocolHandler
- switch protocol {
- case remotecommandconsts.StreamProtocolV4Name:
- handler = &v4ProtocolHandler{}
- case remotecommandconsts.StreamProtocolV3Name:
- handler = &v3ProtocolHandler{}
- case remotecommandconsts.StreamProtocolV2Name:
- handler = &v2ProtocolHandler{}
- case "":
- klog.V(4).Infof("Client did not request protocol negotiation. Falling back to %q", remotecommandconsts.StreamProtocolV1Name)
- fallthrough
- case remotecommandconsts.StreamProtocolV1Name:
- handler = &v1ProtocolHandler{}
- }
-
- // count the streams client asked for, starting with 1
- expectedStreams := 1
- if opts.Stdin {
- expectedStreams++
- }
- if opts.Stdout {
- expectedStreams++
- }
- if opts.Stderr {
- expectedStreams++
- }
- if opts.TTY && handler.supportsTerminalResizing() {
- expectedStreams++
- }
-
- expired := time.NewTimer(streamCreationTimeout)
- defer expired.Stop()
-
- ctx, err := handler.waitForStreams(streamCh, expectedStreams, expired.C)
- if err != nil {
- runtime.HandleError(err)
- return nil, false
- }
-
- ctx.conn = conn
- ctx.tty = opts.TTY
-
- return ctx, true
-}
-
-type protocolHandler interface {
- // waitForStreams waits for the expected streams or a timeout, returning a
- // remoteCommandContext if all the streams were received, or an error if not.
- waitForStreams(streams <-chan streamAndReply, expectedStreams int, expired <-chan time.Time) (*context, error)
- // supportsTerminalResizing returns true if the protocol handler supports terminal resizing
- supportsTerminalResizing() bool
-}
-
-// v4ProtocolHandler implements the V4 protocol version for streaming command execution. It only differs
-// in from v3 in the error stream format using an json-marshaled metav1.Status which carries
-// the process' exit code.
-type v4ProtocolHandler struct{}
-
-func (*v4ProtocolHandler) waitForStreams(streams <-chan streamAndReply, expectedStreams int, expired <-chan time.Time) (*context, error) {
- ctx := &context{}
- receivedStreams := 0
- replyChan := make(chan struct{})
- stop := make(chan struct{})
- defer close(stop)
-WaitForStreams:
- for {
- select {
- case stream := <-streams:
- streamType := stream.Headers().Get(api.StreamType)
- switch streamType {
- case api.StreamTypeError:
- ctx.writeStatus = v4WriteStatusFunc(stream) // write json errors
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStdin:
- ctx.stdinStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStdout:
- ctx.stdoutStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStderr:
- ctx.stderrStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeResize:
- ctx.resizeStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- default:
- runtime.HandleError(fmt.Errorf("unexpected stream type: %q", streamType))
- }
- case <-replyChan:
- receivedStreams++
- if receivedStreams == expectedStreams {
- break WaitForStreams
- }
- case <-expired:
- // TODO find a way to return the error to the user. Maybe use a separate
- // stream to report errors?
- return nil, errors.New("timed out waiting for client to create streams")
- }
- }
-
- return ctx, nil
-}
-
-// supportsTerminalResizing returns true because v4ProtocolHandler supports it
-func (*v4ProtocolHandler) supportsTerminalResizing() bool { return true }
-
-// v3ProtocolHandler implements the V3 protocol version for streaming command execution.
-type v3ProtocolHandler struct{}
-
-func (*v3ProtocolHandler) waitForStreams(streams <-chan streamAndReply, expectedStreams int, expired <-chan time.Time) (*context, error) {
- ctx := &context{}
- receivedStreams := 0
- replyChan := make(chan struct{})
- stop := make(chan struct{})
- defer close(stop)
-WaitForStreams:
- for {
- select {
- case stream := <-streams:
- streamType := stream.Headers().Get(api.StreamType)
- switch streamType {
- case api.StreamTypeError:
- ctx.writeStatus = v1WriteStatusFunc(stream)
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStdin:
- ctx.stdinStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStdout:
- ctx.stdoutStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStderr:
- ctx.stderrStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeResize:
- ctx.resizeStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- default:
- runtime.HandleError(fmt.Errorf("unexpected stream type: %q", streamType))
- }
- case <-replyChan:
- receivedStreams++
- if receivedStreams == expectedStreams {
- break WaitForStreams
- }
- case <-expired:
- // TODO find a way to return the error to the user. Maybe use a separate
- // stream to report errors?
- return nil, errors.New("timed out waiting for client to create streams")
- }
- }
-
- return ctx, nil
-}
-
-// supportsTerminalResizing returns true because v3ProtocolHandler supports it
-func (*v3ProtocolHandler) supportsTerminalResizing() bool { return true }
-
-// v2ProtocolHandler implements the V2 protocol version for streaming command execution.
-type v2ProtocolHandler struct{}
-
-func (*v2ProtocolHandler) waitForStreams(streams <-chan streamAndReply, expectedStreams int, expired <-chan time.Time) (*context, error) {
- ctx := &context{}
- receivedStreams := 0
- replyChan := make(chan struct{})
- stop := make(chan struct{})
- defer close(stop)
-WaitForStreams:
- for {
- select {
- case stream := <-streams:
- streamType := stream.Headers().Get(api.StreamType)
- switch streamType {
- case api.StreamTypeError:
- ctx.writeStatus = v1WriteStatusFunc(stream)
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStdin:
- ctx.stdinStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStdout:
- ctx.stdoutStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStderr:
- ctx.stderrStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- default:
- runtime.HandleError(fmt.Errorf("unexpected stream type: %q", streamType))
- }
- case <-replyChan:
- receivedStreams++
- if receivedStreams == expectedStreams {
- break WaitForStreams
- }
- case <-expired:
- // TODO find a way to return the error to the user. Maybe use a separate
- // stream to report errors?
- return nil, errors.New("timed out waiting for client to create streams")
- }
- }
-
- return ctx, nil
-}
-
-// supportsTerminalResizing returns false because v2ProtocolHandler doesn't support it.
-func (*v2ProtocolHandler) supportsTerminalResizing() bool { return false }
-
-// v1ProtocolHandler implements the V1 protocol version for streaming command execution.
-type v1ProtocolHandler struct{}
-
-func (*v1ProtocolHandler) waitForStreams(streams <-chan streamAndReply, expectedStreams int, expired <-chan time.Time) (*context, error) {
- ctx := &context{}
- receivedStreams := 0
- replyChan := make(chan struct{})
- stop := make(chan struct{})
- defer close(stop)
-WaitForStreams:
- for {
- select {
- case stream := <-streams:
- streamType := stream.Headers().Get(api.StreamType)
- switch streamType {
- case api.StreamTypeError:
- ctx.writeStatus = v1WriteStatusFunc(stream)
-
- // This defer statement shouldn't be here, but due to previous refactoring, it ended up in
- // here. This is what 1.0.x kubelets do, so we're retaining that behavior. This is fixed in
- // the v2ProtocolHandler.
- defer stream.Reset()
-
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStdin:
- ctx.stdinStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStdout:
- ctx.stdoutStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- case api.StreamTypeStderr:
- ctx.stderrStream = stream
- go waitStreamReply(stream.replySent, replyChan, stop)
- default:
- runtime.HandleError(fmt.Errorf("unexpected stream type: %q", streamType))
- }
- case <-replyChan:
- receivedStreams++
- if receivedStreams == expectedStreams {
- break WaitForStreams
- }
- case <-expired:
- // TODO find a way to return the error to the user. Maybe use a separate
- // stream to report errors?
- return nil, errors.New("timed out waiting for client to create streams")
- }
- }
-
- if ctx.stdinStream != nil {
- ctx.stdinStream.Close()
- }
-
- return ctx, nil
-}
-
-// supportsTerminalResizing returns false because v1ProtocolHandler doesn't support it.
-func (*v1ProtocolHandler) supportsTerminalResizing() bool { return false }
-
-func handleResizeEvents(stream io.Reader, channel chan<- remotecommand.TerminalSize) {
- defer runtime.HandleCrash()
- defer close(channel)
-
- decoder := json.NewDecoder(stream)
- for {
- size := remotecommand.TerminalSize{}
- if err := decoder.Decode(&size); err != nil {
- break
- }
- channel <- size
- }
-}
-
-func v1WriteStatusFunc(stream io.Writer) func(status *apierrors.StatusError) error {
- return func(status *apierrors.StatusError) error {
- if status.Status().Status == metav1.StatusSuccess {
- return nil // send error messages
- }
- _, err := stream.Write([]byte(status.Error()))
- return err
- }
-}
-
-// v4WriteStatusFunc returns a WriteStatusFunc that marshals a given api Status
-// as json in the error channel.
-func v4WriteStatusFunc(stream io.Writer) func(status *apierrors.StatusError) error {
- return func(status *apierrors.StatusError) error {
- bs, err := json.Marshal(status.Status())
- if err != nil {
- return err
- }
- _, err = stream.Write(bs)
- return err
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/websocket.go b/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/websocket.go
deleted file mode 100644
index 99c0af7c3..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/remotecommand/websocket.go
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package remotecommand
-
-import (
- "fmt"
- "net/http"
- "time"
-
- "k8s.io/apimachinery/pkg/util/runtime"
- "k8s.io/apiserver/pkg/server/httplog"
- "k8s.io/apiserver/pkg/util/wsstream"
-)
-
-const (
- stdinChannel = iota
- stdoutChannel
- stderrChannel
- errorChannel
- resizeChannel
-
- preV4BinaryWebsocketProtocol = wsstream.ChannelWebSocketProtocol
- preV4Base64WebsocketProtocol = wsstream.Base64ChannelWebSocketProtocol
- v4BinaryWebsocketProtocol = "v4." + wsstream.ChannelWebSocketProtocol
- v4Base64WebsocketProtocol = "v4." + wsstream.Base64ChannelWebSocketProtocol
-)
-
-// createChannels returns the standard channel types for a shell connection (STDIN 0, STDOUT 1, STDERR 2)
-// along with the approximate duplex value. It also creates the error (3) and resize (4) channels.
-func createChannels(opts *Options) []wsstream.ChannelType {
- // open the requested channels, and always open the error channel
- channels := make([]wsstream.ChannelType, 5)
- channels[stdinChannel] = readChannel(opts.Stdin)
- channels[stdoutChannel] = writeChannel(opts.Stdout)
- channels[stderrChannel] = writeChannel(opts.Stderr)
- channels[errorChannel] = wsstream.WriteChannel
- channels[resizeChannel] = wsstream.ReadChannel
- return channels
-}
-
-// readChannel returns wsstream.ReadChannel if real is true, or wsstream.IgnoreChannel.
-func readChannel(real bool) wsstream.ChannelType {
- if real {
- return wsstream.ReadChannel
- }
- return wsstream.IgnoreChannel
-}
-
-// writeChannel returns wsstream.WriteChannel if real is true, or wsstream.IgnoreChannel.
-func writeChannel(real bool) wsstream.ChannelType {
- if real {
- return wsstream.WriteChannel
- }
- return wsstream.IgnoreChannel
-}
-
-// createWebSocketStreams returns a context containing the websocket connection and
-// streams needed to perform an exec or an attach.
-func createWebSocketStreams(req *http.Request, w http.ResponseWriter, opts *Options, idleTimeout time.Duration) (*context, bool) {
- channels := createChannels(opts)
- conn := wsstream.NewConn(map[string]wsstream.ChannelProtocolConfig{
- "": {
- Binary: true,
- Channels: channels,
- },
- preV4BinaryWebsocketProtocol: {
- Binary: true,
- Channels: channels,
- },
- preV4Base64WebsocketProtocol: {
- Binary: false,
- Channels: channels,
- },
- v4BinaryWebsocketProtocol: {
- Binary: true,
- Channels: channels,
- },
- v4Base64WebsocketProtocol: {
- Binary: false,
- Channels: channels,
- },
- })
- conn.SetIdleTimeout(idleTimeout)
- negotiatedProtocol, streams, err := conn.Open(httplog.Unlogged(req, w), req)
- if err != nil {
- runtime.HandleError(fmt.Errorf("unable to upgrade websocket connection: %v", err))
- return nil, false
- }
-
- // Send an empty message to the lowest writable channel to notify the client the connection is established
- // TODO: make generic to SPDY and WebSockets and do it outside of this method?
- switch {
- case opts.Stdout:
- streams[stdoutChannel].Write([]byte{})
- case opts.Stderr:
- streams[stderrChannel].Write([]byte{})
- default:
- streams[errorChannel].Write([]byte{})
- }
-
- ctx := &context{
- conn: conn,
- stdinStream: streams[stdinChannel],
- stdoutStream: streams[stdoutChannel],
- stderrStream: streams[stderrChannel],
- tty: opts.TTY,
- resizeStream: streams[resizeChannel],
- }
-
- switch negotiatedProtocol {
- case v4BinaryWebsocketProtocol, v4Base64WebsocketProtocol:
- ctx.writeStatus = v4WriteStatusFunc(streams[errorChannel])
- default:
- ctx.writeStatus = v1WriteStatusFunc(streams[errorChannel])
- }
-
- return ctx, true
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/request_cache.go b/vendor/github.com/containerd/cri/pkg/streaming/request_cache.go
deleted file mode 100644
index 36d6921f6..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/request_cache.go
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package streaming
-
-import (
- "container/list"
- "crypto/rand"
- "encoding/base64"
- "fmt"
- "math"
- "sync"
- "time"
-
- "k8s.io/apimachinery/pkg/util/clock"
-)
-
-var (
- // cacheTTL is the timeout after which tokens become invalid.
- cacheTTL = 1 * time.Minute
- // maxInFlight is the maximum number of in-flight requests to allow.
- maxInFlight = 1000
- // tokenLen is the length of the random base64 encoded token identifying the request.
- tokenLen = 8
-)
-
-// requestCache caches streaming (exec/attach/port-forward) requests and generates a single-use
-// random token for their retrieval. The requestCache is used for building streaming URLs without
-// the need to encode every request parameter in the URL.
-type requestCache struct {
- // clock is used to obtain the current time
- clock clock.Clock
-
- // tokens maps the generate token to the request for fast retrieval.
- tokens map[string]*list.Element
- // ll maintains an age-ordered request list for faster garbage collection of expired requests.
- ll *list.List
-
- lock sync.Mutex
-}
-
-// Type representing an *ExecRequest, *AttachRequest, or *PortForwardRequest.
-type request interface{}
-
-type cacheEntry struct {
- token string
- req request
- expireTime time.Time
-}
-
-func newRequestCache() *requestCache {
- return &requestCache{
- clock: clock.RealClock{},
- ll: list.New(),
- tokens: make(map[string]*list.Element),
- }
-}
-
-// Insert the given request into the cache and returns the token used for fetching it out.
-func (c *requestCache) Insert(req request) (token string, err error) {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- // Remove expired entries.
- c.gc()
- // If the cache is full, reject the request.
- if c.ll.Len() == maxInFlight {
- return "", NewErrorTooManyInFlight()
- }
- token, err = c.uniqueToken()
- if err != nil {
- return "", err
- }
- ele := c.ll.PushFront(&cacheEntry{token, req, c.clock.Now().Add(cacheTTL)})
-
- c.tokens[token] = ele
- return token, nil
-}
-
-// Consume the token (remove it from the cache) and return the cached request, if found.
-func (c *requestCache) Consume(token string) (req request, found bool) {
- c.lock.Lock()
- defer c.lock.Unlock()
- ele, ok := c.tokens[token]
- if !ok {
- return nil, false
- }
- c.ll.Remove(ele)
- delete(c.tokens, token)
-
- entry := ele.Value.(*cacheEntry)
- if c.clock.Now().After(entry.expireTime) {
- // Entry already expired.
- return nil, false
- }
- return entry.req, true
-}
-
-// uniqueToken generates a random URL-safe token and ensures uniqueness.
-func (c *requestCache) uniqueToken() (string, error) {
- const maxTries = 10
- // Number of bytes to be tokenLen when base64 encoded.
- tokenSize := math.Ceil(float64(tokenLen) * 6 / 8)
- rawToken := make([]byte, int(tokenSize))
- for i := 0; i < maxTries; i++ {
- if _, err := rand.Read(rawToken); err != nil {
- return "", err
- }
- encoded := base64.RawURLEncoding.EncodeToString(rawToken)
- token := encoded[:tokenLen]
- // If it's unique, return it. Otherwise retry.
- if _, exists := c.tokens[encoded]; !exists {
- return token, nil
- }
- }
- return "", fmt.Errorf("failed to generate unique token")
-}
-
-// Must be write-locked prior to calling.
-func (c *requestCache) gc() {
- now := c.clock.Now()
- for c.ll.Len() > 0 {
- oldest := c.ll.Back()
- entry := oldest.Value.(*cacheEntry)
- if !now.After(entry.expireTime) {
- return
- }
-
- // Oldest value is expired; remove it.
- c.ll.Remove(oldest)
- delete(c.tokens, entry.token)
- }
-}
diff --git a/vendor/github.com/containerd/cri/pkg/streaming/server.go b/vendor/github.com/containerd/cri/pkg/streaming/server.go
deleted file mode 100644
index 589c9a8ca..000000000
--- a/vendor/github.com/containerd/cri/pkg/streaming/server.go
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/*
-Copyright 2016 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package streaming
-
-import (
- "crypto/tls"
- "errors"
- "io"
- "net"
- "net/http"
- "net/url"
- "path"
- "time"
-
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
-
- restful "github.com/emicklei/go-restful"
-
- "k8s.io/apimachinery/pkg/types"
- remotecommandconsts "k8s.io/apimachinery/pkg/util/remotecommand"
- "k8s.io/client-go/tools/remotecommand"
- runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
-
- "github.com/containerd/cri/pkg/streaming/portforward"
- remotecommandserver "github.com/containerd/cri/pkg/streaming/remotecommand"
-)
-
-// Server is the library interface to serve the stream requests.
-type Server interface {
- http.Handler
-
- // Get the serving URL for the requests.
- // Requests must not be nil. Responses may be nil iff an error is returned.
- GetExec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error)
- GetAttach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error)
- GetPortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error)
-
- // Start the server.
- // addr is the address to serve on (address:port) stayUp indicates whether the server should
- // listen until Stop() is called, or automatically stop after all expected connections are
- // closed. Calling Get{Exec,Attach,PortForward} increments the expected connection count.
- // Function does not return until the server is stopped.
- Start(stayUp bool) error
- // Stop the server, and terminate any open connections.
- Stop() error
-}
-
-// Runtime is the interface to execute the commands and provide the streams.
-type Runtime interface {
- Exec(containerID string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error
- Attach(containerID string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error
- PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error
-}
-
-// Config defines the options used for running the stream server.
-type Config struct {
- // The host:port address the server will listen on.
- Addr string
- // The optional base URL for constructing streaming URLs. If empty, the baseURL will be
- // constructed from the serve address.
- // Note that for port "0", the URL port will be set to actual port in use.
- BaseURL *url.URL
-
- // How long to leave idle connections open for.
- StreamIdleTimeout time.Duration
- // How long to wait for clients to create streams. Only used for SPDY streaming.
- StreamCreationTimeout time.Duration
-
- // The streaming protocols the server supports (understands and permits). See
- // k8s.io/kubernetes/pkg/kubelet/server/remotecommand/constants.go for available protocols.
- // Only used for SPDY streaming.
- SupportedRemoteCommandProtocols []string
-
- // The streaming protocols the server supports (understands and permits). See
- // k8s.io/kubernetes/pkg/kubelet/server/portforward/constants.go for available protocols.
- // Only used for SPDY streaming.
- SupportedPortForwardProtocols []string
-
- // The config for serving over TLS. If nil, TLS will not be used.
- TLSConfig *tls.Config
-}
-
-// DefaultConfig provides default values for server Config. The DefaultConfig is partial, so
-// some fields like Addr must still be provided.
-var DefaultConfig = Config{
- StreamIdleTimeout: 4 * time.Hour,
- StreamCreationTimeout: remotecommandconsts.DefaultStreamCreationTimeout,
- SupportedRemoteCommandProtocols: remotecommandconsts.SupportedStreamingProtocols,
- SupportedPortForwardProtocols: portforward.SupportedProtocols,
-}
-
-// NewServer creates a new Server for stream requests.
-// TODO(tallclair): Add auth(n/z) interface & handling.
-func NewServer(config Config, runtime Runtime) (Server, error) {
- s := &server{
- config: config,
- runtime: &criAdapter{runtime},
- cache: newRequestCache(),
- }
-
- if s.config.BaseURL == nil {
- s.config.BaseURL = &url.URL{
- Scheme: "http",
- Host: s.config.Addr,
- }
- if s.config.TLSConfig != nil {
- s.config.BaseURL.Scheme = "https"
- }
- }
-
- ws := &restful.WebService{}
- endpoints := []struct {
- path string
- handler restful.RouteFunction
- }{
- {"/exec/{token}", s.serveExec},
- {"/attach/{token}", s.serveAttach},
- {"/portforward/{token}", s.servePortForward},
- }
- // If serving relative to a base path, set that here.
- pathPrefix := path.Dir(s.config.BaseURL.Path)
- for _, e := range endpoints {
- for _, method := range []string{"GET", "POST"} {
- ws.Route(ws.
- Method(method).
- Path(path.Join(pathPrefix, e.path)).
- To(e.handler))
- }
- }
- handler := restful.NewContainer()
- handler.Add(ws)
- s.handler = handler
- s.server = &http.Server{
- Addr: s.config.Addr,
- Handler: s.handler,
- TLSConfig: s.config.TLSConfig,
- }
-
- return s, nil
-}
-
-type server struct {
- config Config
- runtime *criAdapter
- handler http.Handler
- cache *requestCache
- server *http.Server
-}
-
-func validateExecRequest(req *runtimeapi.ExecRequest) error {
- if req.ContainerId == "" {
- return status.Errorf(codes.InvalidArgument, "missing required container_id")
- }
- if req.Tty && req.Stderr {
- // If TTY is set, stderr cannot be true because multiplexing is not
- // supported.
- return status.Errorf(codes.InvalidArgument, "tty and stderr cannot both be true")
- }
- if !req.Stdin && !req.Stdout && !req.Stderr {
- return status.Errorf(codes.InvalidArgument, "one of stdin, stdout, or stderr must be set")
- }
- return nil
-}
-
-func (s *server) GetExec(req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) {
- if err := validateExecRequest(req); err != nil {
- return nil, err
- }
- token, err := s.cache.Insert(req)
- if err != nil {
- return nil, err
- }
- return &runtimeapi.ExecResponse{
- Url: s.buildURL("exec", token),
- }, nil
-}
-
-func validateAttachRequest(req *runtimeapi.AttachRequest) error {
- if req.ContainerId == "" {
- return status.Errorf(codes.InvalidArgument, "missing required container_id")
- }
- if req.Tty && req.Stderr {
- // If TTY is set, stderr cannot be true because multiplexing is not
- // supported.
- return status.Errorf(codes.InvalidArgument, "tty and stderr cannot both be true")
- }
- if !req.Stdin && !req.Stdout && !req.Stderr {
- return status.Errorf(codes.InvalidArgument, "one of stdin, stdout, and stderr must be set")
- }
- return nil
-}
-
-func (s *server) GetAttach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) {
- if err := validateAttachRequest(req); err != nil {
- return nil, err
- }
- token, err := s.cache.Insert(req)
- if err != nil {
- return nil, err
- }
- return &runtimeapi.AttachResponse{
- Url: s.buildURL("attach", token),
- }, nil
-}
-
-func (s *server) GetPortForward(req *runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error) {
- if req.PodSandboxId == "" {
- return nil, status.Errorf(codes.InvalidArgument, "missing required pod_sandbox_id")
- }
- token, err := s.cache.Insert(req)
- if err != nil {
- return nil, err
- }
- return &runtimeapi.PortForwardResponse{
- Url: s.buildURL("portforward", token),
- }, nil
-}
-
-func (s *server) Start(stayUp bool) error {
- if !stayUp {
- // TODO(tallclair): Implement this.
- return errors.New("stayUp=false is not yet implemented")
- }
-
- listener, err := net.Listen("tcp", s.config.Addr)
- if err != nil {
- return err
- }
- // Use the actual address as baseURL host. This handles the "0" port case.
- s.config.BaseURL.Host = listener.Addr().String()
- if s.config.TLSConfig != nil {
- return s.server.ServeTLS(listener, "", "") // Use certs from TLSConfig.
- }
- return s.server.Serve(listener)
-}
-
-func (s *server) Stop() error {
- return s.server.Close()
-}
-
-func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- s.handler.ServeHTTP(w, r)
-}
-
-func (s *server) buildURL(method, token string) string {
- return s.config.BaseURL.ResolveReference(&url.URL{
- Path: path.Join(method, token),
- }).String()
-}
-
-func (s *server) serveExec(req *restful.Request, resp *restful.Response) {
- token := req.PathParameter("token")
- cachedRequest, ok := s.cache.Consume(token)
- if !ok {
- http.NotFound(resp.ResponseWriter, req.Request)
- return
- }
- exec, ok := cachedRequest.(*runtimeapi.ExecRequest)
- if !ok {
- http.NotFound(resp.ResponseWriter, req.Request)
- return
- }
-
- streamOpts := &remotecommandserver.Options{
- Stdin: exec.Stdin,
- Stdout: exec.Stdout,
- Stderr: exec.Stderr,
- TTY: exec.Tty,
- }
-
- remotecommandserver.ServeExec(
- resp.ResponseWriter,
- req.Request,
- s.runtime,
- "", // unused: podName
- "", // unusued: podUID
- exec.ContainerId,
- exec.Cmd,
- streamOpts,
- s.config.StreamIdleTimeout,
- s.config.StreamCreationTimeout,
- s.config.SupportedRemoteCommandProtocols)
-}
-
-func (s *server) serveAttach(req *restful.Request, resp *restful.Response) {
- token := req.PathParameter("token")
- cachedRequest, ok := s.cache.Consume(token)
- if !ok {
- http.NotFound(resp.ResponseWriter, req.Request)
- return
- }
- attach, ok := cachedRequest.(*runtimeapi.AttachRequest)
- if !ok {
- http.NotFound(resp.ResponseWriter, req.Request)
- return
- }
-
- streamOpts := &remotecommandserver.Options{
- Stdin: attach.Stdin,
- Stdout: attach.Stdout,
- Stderr: attach.Stderr,
- TTY: attach.Tty,
- }
- remotecommandserver.ServeAttach(
- resp.ResponseWriter,
- req.Request,
- s.runtime,
- "", // unused: podName
- "", // unusued: podUID
- attach.ContainerId,
- streamOpts,
- s.config.StreamIdleTimeout,
- s.config.StreamCreationTimeout,
- s.config.SupportedRemoteCommandProtocols)
-}
-
-func (s *server) servePortForward(req *restful.Request, resp *restful.Response) {
- token := req.PathParameter("token")
- cachedRequest, ok := s.cache.Consume(token)
- if !ok {
- http.NotFound(resp.ResponseWriter, req.Request)
- return
- }
- pf, ok := cachedRequest.(*runtimeapi.PortForwardRequest)
- if !ok {
- http.NotFound(resp.ResponseWriter, req.Request)
- return
- }
-
- portForwardOptions, err := portforward.BuildV4Options(pf.Port)
- if err != nil {
- resp.WriteError(http.StatusBadRequest, err)
- return
- }
-
- portforward.ServePortForward(
- resp.ResponseWriter,
- req.Request,
- s.runtime,
- pf.PodSandboxId,
- "", // unused: podUID
- portForwardOptions,
- s.config.StreamIdleTimeout,
- s.config.StreamCreationTimeout,
- s.config.SupportedPortForwardProtocols)
-}
-
-// criAdapter wraps the Runtime functions to conform to the remotecommand interfaces.
-// The adapter binds the container ID to the container name argument, and the pod sandbox ID to the pod name.
-type criAdapter struct {
- Runtime
-}
-
-var _ remotecommandserver.Executor = &criAdapter{}
-var _ remotecommandserver.Attacher = &criAdapter{}
-var _ portforward.PortForwarder = &criAdapter{}
-
-func (a *criAdapter) ExecInContainer(podName string, podUID types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error {
- return a.Runtime.Exec(container, cmd, in, out, err, tty, resize)
-}
-
-func (a *criAdapter) AttachContainer(podName string, podUID types.UID, container string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
- return a.Runtime.Attach(container, in, out, err, tty, resize)
-}
-
-func (a *criAdapter) PortForward(podName string, podUID types.UID, port int32, stream io.ReadWriteCloser) error {
- return a.Runtime.PortForward(podName, port, stream)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/util/deep_copy.go b/vendor/github.com/containerd/cri/pkg/util/deep_copy.go
deleted file mode 100644
index d0e0bf37e..000000000
--- a/vendor/github.com/containerd/cri/pkg/util/deep_copy.go
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package util
-
-import (
- "encoding/json"
-
- "github.com/pkg/errors"
-)
-
-// DeepCopy makes a deep copy from src into dst.
-func DeepCopy(dst interface{}, src interface{}) error {
- if dst == nil {
- return errors.New("dst cannot be nil")
- }
- if src == nil {
- return errors.New("src cannot be nil")
- }
- bytes, err := json.Marshal(src)
- if err != nil {
- return errors.Wrap(err, "unable to marshal src")
- }
- err = json.Unmarshal(bytes, dst)
- if err != nil {
- return errors.Wrap(err, "unable to unmarshal into dst")
- }
- return nil
-}
diff --git a/vendor/github.com/containerd/cri/pkg/util/id.go b/vendor/github.com/containerd/cri/pkg/util/id.go
deleted file mode 100644
index 90f762ba7..000000000
--- a/vendor/github.com/containerd/cri/pkg/util/id.go
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package util
-
-import (
- "encoding/hex"
- "math/rand"
-)
-
-// GenerateID generates a random unique id.
-func GenerateID() string {
- b := make([]byte, 32)
- rand.Read(b)
- return hex.EncodeToString(b)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/util/image.go b/vendor/github.com/containerd/cri/pkg/util/image.go
deleted file mode 100644
index d3abcd31a..000000000
--- a/vendor/github.com/containerd/cri/pkg/util/image.go
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package util
-
-import (
- "github.com/containerd/containerd/reference/docker"
-)
-
-// NormalizeImageRef normalizes the image reference following the docker convention. This is added
-// mainly for backward compatibility.
-// The reference returned can only be either tagged or digested. For reference contains both tag
-// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@
-// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as
-// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa.
-//
-// Deprecated: use github.com/containerd/containerd/reference/docker.ParseDockerRef() instead
-func NormalizeImageRef(ref string) (docker.Named, error) {
- return docker.ParseDockerRef(ref)
-}
diff --git a/vendor/github.com/containerd/cri/pkg/util/strings.go b/vendor/github.com/containerd/cri/pkg/util/strings.go
deleted file mode 100644
index df1a5c91a..000000000
--- a/vendor/github.com/containerd/cri/pkg/util/strings.go
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package util
-
-import "strings"
-
-// InStringSlice checks whether a string is inside a string slice.
-// Comparison is case insensitive.
-func InStringSlice(ss []string, str string) bool {
- for _, s := range ss {
- if strings.EqualFold(s, str) {
- return true
- }
- }
- return false
-}
-
-// SubtractStringSlice subtracts string from string slice.
-// Comparison is case insensitive.
-func SubtractStringSlice(ss []string, str string) []string {
- var res []string
- for _, s := range ss {
- if strings.EqualFold(s, str) {
- continue
- }
- res = append(res, s)
- }
- return res
-}
-
-// MergeStringSlices merges 2 string slices into one and remove duplicated elements.
-func MergeStringSlices(a []string, b []string) []string {
- set := map[string]struct{}{}
- for _, s := range a {
- set[s] = struct{}{}
- }
- for _, s := range b {
- set[s] = struct{}{}
- }
- var ss []string
- for s := range set {
- ss = append(ss, s)
- }
- return ss
-}
diff --git a/vendor/github.com/containerd/cri/vendor.conf b/vendor/github.com/containerd/cri/vendor.conf
deleted file mode 100644
index 82139978f..000000000
--- a/vendor/github.com/containerd/cri/vendor.conf
+++ /dev/null
@@ -1,102 +0,0 @@
-# cri dependencies
-github.com/docker/docker 4634ce647cf2ce2c6031129ccd109e557244986f
-github.com/opencontainers/selinux v1.6.0
-github.com/tchap/go-patricia v2.2.6
-github.com/willf/bitset v1.1.11
-
-# containerd dependencies
-github.com/beorn7/perks v1.0.1
-github.com/BurntSushi/toml v0.3.1
-github.com/cespare/xxhash/v2 v2.1.1
-github.com/containerd/cgroups 318312a373405e5e91134d8063d04d59768a1bff
-github.com/containerd/console v1.0.0
-github.com/containerd/containerd v1.4.0
-github.com/containerd/continuity efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
-github.com/containerd/fifo f15a3290365b9d2627d189e619ab4008e0069caf
-github.com/containerd/go-runc 7016d3ce2328dd2cb1192b2076ebd565c4e8df0c
-github.com/containerd/nri 0afc7f031eaf9c7d9c1a381b7ab5462e89c998fc
-github.com/containerd/ttrpc v1.0.1
-github.com/containerd/typeurl v1.0.1
-github.com/coreos/go-systemd/v22 v22.1.0
-github.com/cpuguy83/go-md2man/v2 v2.0.0
-github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f
-github.com/docker/go-metrics v0.0.1
-github.com/docker/go-units v0.4.0
-github.com/godbus/dbus/v5 v5.0.3
-github.com/gogo/googleapis v1.3.2
-github.com/gogo/protobuf v1.3.1
-github.com/golang/protobuf v1.3.5
-github.com/google/uuid v1.1.1
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
-github.com/hashicorp/errwrap v1.0.0
-github.com/hashicorp/go-multierror v1.0.0
-github.com/hashicorp/golang-lru v0.5.3
-github.com/imdario/mergo v0.3.7
-github.com/konsorten/go-windows-terminal-sequences v1.0.3
-github.com/matttproud/golang_protobuf_extensions v1.0.1
-github.com/Microsoft/go-winio v0.4.14
-github.com/Microsoft/hcsshim v0.8.9
-github.com/opencontainers/go-digest v1.0.0
-github.com/opencontainers/image-spec v1.0.1
-github.com/opencontainers/runc v1.0.0-rc92
-github.com/opencontainers/runtime-spec 4d89ac9fbff6c455f46a5bb59c6b1bb7184a5e43 # v1.0.3-0.20200728170252-4d89ac9fbff6
-github.com/pkg/errors v0.9.1
-github.com/prometheus/client_golang v1.6.0
-github.com/prometheus/client_model v0.2.0
-github.com/prometheus/common v0.9.1
-github.com/prometheus/procfs v0.0.11
-github.com/russross/blackfriday/v2 v2.0.1
-github.com/shurcooL/sanitized_anchor_name v1.0.0
-github.com/sirupsen/logrus v1.6.0
-github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
-github.com/urfave/cli v1.22.1 # NOTE: urfave/cli must be <= v1.22.1 due to a regression: https://github.com/urfave/cli/issues/1092
-go.etcd.io/bbolt v1.3.5
-go.opencensus.io v0.22.0
-golang.org/x/net ab34263943818b32f575efc978a3d24e80b04bd7
-golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
-golang.org/x/sys ed371f2e16b4b305ee99df548828de367527b76b
-golang.org/x/text v0.3.3
-google.golang.org/genproto e50cd9704f63023d62cd06a1994b98227fc4d21a
-google.golang.org/grpc v1.27.1
-
-# cgroups dependencies
-github.com/cilium/ebpf 1c8d4c9ef7759622653a1d319284a44652333b28
-
-# kubernetes dependencies
-github.com/davecgh/go-spew v1.1.1
-github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
-github.com/emicklei/go-restful v2.9.5
-github.com/go-logr/logr v0.2.0
-github.com/google/gofuzz v1.1.0
-github.com/json-iterator/go v1.1.10
-github.com/modern-go/concurrent 1.0.3
-github.com/modern-go/reflect2 v1.0.1
-github.com/pmezard/go-difflib v1.0.0
-github.com/stretchr/testify v1.4.0
-golang.org/x/crypto 75b288015ac94e66e3d6715fb68a9b41bf046ec2
-golang.org/x/oauth2 858c2ad4c8b6c5d10852cb89079f6ca1c7309787
-golang.org/x/time 555d28b269f0569763d25dbe1a237ae74c6bcc82
-gopkg.in/inf.v0 v0.9.1
-gopkg.in/yaml.v2 v2.2.8
-k8s.io/api v0.19.0-rc.4
-k8s.io/apiserver v0.19.0-rc.4
-k8s.io/apimachinery v0.19.0-rc.4
-k8s.io/client-go v0.19.0-rc.4
-k8s.io/component-base v0.19.0-rc.4
-k8s.io/cri-api v0.19.0-rc.4
-k8s.io/klog/v2 v2.2.0
-k8s.io/utils 2df71ebbae66f39338aed4cd0bb82d2212ee33cc
-sigs.k8s.io/structured-merge-diff/v3 v3.0.0
-sigs.k8s.io/yaml v1.2.0
-
-# cni dependencies
-github.com/containerd/go-cni v1.0.1
-github.com/containernetworking/cni v0.8.0
-github.com/containernetworking/plugins v0.8.6
-github.com/fsnotify/fsnotify v1.4.9
-
-# image decrypt depedencies
-github.com/containerd/imgcrypt v1.0.1
-github.com/containers/ocicrypt v1.0.1
-github.com/fullsailor/pkcs7 8306686428a5fe132eac8cb7c4848af725098bd4
-gopkg.in/square/go-jose.v2 v2.3.1
diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/vendor/github.com/pmezard/go-difflib/LICENSE
new file mode 100644
index 000000000..c67dad612
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013, Patrick Mezard
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+ The names of its contributors may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/pmezard/go-difflib/README.md b/vendor/github.com/pmezard/go-difflib/README.md
new file mode 100644
index 000000000..e87f307ed
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/README.md
@@ -0,0 +1,50 @@
+go-difflib
+==========
+
+[](https://travis-ci.org/pmezard/go-difflib)
+[](https://godoc.org/github.com/pmezard/go-difflib/difflib)
+
+Go-difflib is a partial port of python 3 difflib package. Its main goal
+was to make unified and context diff available in pure Go, mostly for
+testing purposes.
+
+The following class and functions (and related tests) have be ported:
+
+* `SequenceMatcher`
+* `unified_diff()`
+* `context_diff()`
+
+## Installation
+
+```bash
+$ go get github.com/pmezard/go-difflib/difflib
+```
+
+### Quick Start
+
+Diffs are configured with Unified (or ContextDiff) structures, and can
+be output to an io.Writer or returned as a string.
+
+```Go
+diff := UnifiedDiff{
+ A: difflib.SplitLines("foo\nbar\n"),
+ B: difflib.SplitLines("foo\nbaz\n"),
+ FromFile: "Original",
+ ToFile: "Current",
+ Context: 3,
+}
+text, _ := GetUnifiedDiffString(diff)
+fmt.Printf(text)
+```
+
+would output:
+
+```
+--- Original
++++ Current
+@@ -1,3 +1,3 @@
+ foo
+-bar
++baz
+```
+
diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
new file mode 100644
index 000000000..003e99fad
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
@@ -0,0 +1,772 @@
+// Package difflib is a partial port of Python difflib module.
+//
+// It provides tools to compare sequences of strings and generate textual diffs.
+//
+// The following class and functions have been ported:
+//
+// - SequenceMatcher
+//
+// - unified_diff
+//
+// - context_diff
+//
+// Getting unified diffs was the main goal of the port. Keep in mind this code
+// is mostly suitable to output text differences in a human friendly way, there
+// are no guarantees generated diffs are consumable by patch(1).
+package difflib
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+)
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func calculateRatio(matches, length int) float64 {
+ if length > 0 {
+ return 2.0 * float64(matches) / float64(length)
+ }
+ return 1.0
+}
+
+type Match struct {
+ A int
+ B int
+ Size int
+}
+
+type OpCode struct {
+ Tag byte
+ I1 int
+ I2 int
+ J1 int
+ J2 int
+}
+
+// SequenceMatcher compares sequence of strings. The basic
+// algorithm predates, and is a little fancier than, an algorithm
+// published in the late 1980's by Ratcliff and Obershelp under the
+// hyperbolic name "gestalt pattern matching". The basic idea is to find
+// the longest contiguous matching subsequence that contains no "junk"
+// elements (R-O doesn't address junk). The same idea is then applied
+// recursively to the pieces of the sequences to the left and to the right
+// of the matching subsequence. This does not yield minimal edit
+// sequences, but does tend to yield matches that "look right" to people.
+//
+// SequenceMatcher tries to compute a "human-friendly diff" between two
+// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
+// longest *contiguous* & junk-free matching subsequence. That's what
+// catches peoples' eyes. The Windows(tm) windiff has another interesting
+// notion, pairing up elements that appear uniquely in each sequence.
+// That, and the method here, appear to yield more intuitive difference
+// reports than does diff. This method appears to be the least vulnerable
+// to synching up on blocks of "junk lines", though (like blank lines in
+// ordinary text files, or maybe "" lines in HTML files). That may be
+// because this is the only method of the 3 that has a *concept* of
+// "junk" .
+//
+// Timing: Basic R-O is cubic time worst case and quadratic time expected
+// case. SequenceMatcher is quadratic time for the worst case and has
+// expected-case behavior dependent in a complicated way on how many
+// elements the sequences have in common; best case time is linear.
+type SequenceMatcher struct {
+ a []string
+ b []string
+ b2j map[string][]int
+ IsJunk func(string) bool
+ autoJunk bool
+ bJunk map[string]struct{}
+ matchingBlocks []Match
+ fullBCount map[string]int
+ bPopular map[string]struct{}
+ opCodes []OpCode
+}
+
+func NewMatcher(a, b []string) *SequenceMatcher {
+ m := SequenceMatcher{autoJunk: true}
+ m.SetSeqs(a, b)
+ return &m
+}
+
+func NewMatcherWithJunk(a, b []string, autoJunk bool,
+ isJunk func(string) bool) *SequenceMatcher {
+
+ m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
+ m.SetSeqs(a, b)
+ return &m
+}
+
+// Set two sequences to be compared.
+func (m *SequenceMatcher) SetSeqs(a, b []string) {
+ m.SetSeq1(a)
+ m.SetSeq2(b)
+}
+
+// Set the first sequence to be compared. The second sequence to be compared is
+// not changed.
+//
+// SequenceMatcher computes and caches detailed information about the second
+// sequence, so if you want to compare one sequence S against many sequences,
+// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
+// sequences.
+//
+// See also SetSeqs() and SetSeq2().
+func (m *SequenceMatcher) SetSeq1(a []string) {
+ if &a == &m.a {
+ return
+ }
+ m.a = a
+ m.matchingBlocks = nil
+ m.opCodes = nil
+}
+
+// Set the second sequence to be compared. The first sequence to be compared is
+// not changed.
+func (m *SequenceMatcher) SetSeq2(b []string) {
+ if &b == &m.b {
+ return
+ }
+ m.b = b
+ m.matchingBlocks = nil
+ m.opCodes = nil
+ m.fullBCount = nil
+ m.chainB()
+}
+
+func (m *SequenceMatcher) chainB() {
+ // Populate line -> index mapping
+ b2j := map[string][]int{}
+ for i, s := range m.b {
+ indices := b2j[s]
+ indices = append(indices, i)
+ b2j[s] = indices
+ }
+
+ // Purge junk elements
+ m.bJunk = map[string]struct{}{}
+ if m.IsJunk != nil {
+ junk := m.bJunk
+ for s, _ := range b2j {
+ if m.IsJunk(s) {
+ junk[s] = struct{}{}
+ }
+ }
+ for s, _ := range junk {
+ delete(b2j, s)
+ }
+ }
+
+ // Purge remaining popular elements
+ popular := map[string]struct{}{}
+ n := len(m.b)
+ if m.autoJunk && n >= 200 {
+ ntest := n/100 + 1
+ for s, indices := range b2j {
+ if len(indices) > ntest {
+ popular[s] = struct{}{}
+ }
+ }
+ for s, _ := range popular {
+ delete(b2j, s)
+ }
+ }
+ m.bPopular = popular
+ m.b2j = b2j
+}
+
+func (m *SequenceMatcher) isBJunk(s string) bool {
+ _, ok := m.bJunk[s]
+ return ok
+}
+
+// Find longest matching block in a[alo:ahi] and b[blo:bhi].
+//
+// If IsJunk is not defined:
+//
+// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
+// alo <= i <= i+k <= ahi
+// blo <= j <= j+k <= bhi
+// and for all (i',j',k') meeting those conditions,
+// k >= k'
+// i <= i'
+// and if i == i', j <= j'
+//
+// In other words, of all maximal matching blocks, return one that
+// starts earliest in a, and of all those maximal matching blocks that
+// start earliest in a, return the one that starts earliest in b.
+//
+// If IsJunk is defined, first the longest matching block is
+// determined as above, but with the additional restriction that no
+// junk element appears in the block. Then that block is extended as
+// far as possible by matching (only) junk elements on both sides. So
+// the resulting block never matches on junk except as identical junk
+// happens to be adjacent to an "interesting" match.
+//
+// If no blocks match, return (alo, blo, 0).
+func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
+ // CAUTION: stripping common prefix or suffix would be incorrect.
+ // E.g.,
+ // ab
+ // acab
+ // Longest matching block is "ab", but if common prefix is
+ // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
+ // strip, so ends up claiming that ab is changed to acab by
+ // inserting "ca" in the middle. That's minimal but unintuitive:
+ // "it's obvious" that someone inserted "ac" at the front.
+ // Windiff ends up at the same place as diff, but by pairing up
+ // the unique 'b's and then matching the first two 'a's.
+ besti, bestj, bestsize := alo, blo, 0
+
+ // find longest junk-free match
+ // during an iteration of the loop, j2len[j] = length of longest
+ // junk-free match ending with a[i-1] and b[j]
+ j2len := map[int]int{}
+ for i := alo; i != ahi; i++ {
+ // look at all instances of a[i] in b; note that because
+ // b2j has no junk keys, the loop is skipped if a[i] is junk
+ newj2len := map[int]int{}
+ for _, j := range m.b2j[m.a[i]] {
+ // a[i] matches b[j]
+ if j < blo {
+ continue
+ }
+ if j >= bhi {
+ break
+ }
+ k := j2len[j-1] + 1
+ newj2len[j] = k
+ if k > bestsize {
+ besti, bestj, bestsize = i-k+1, j-k+1, k
+ }
+ }
+ j2len = newj2len
+ }
+
+ // Extend the best by non-junk elements on each end. In particular,
+ // "popular" non-junk elements aren't in b2j, which greatly speeds
+ // the inner loop above, but also means "the best" match so far
+ // doesn't contain any junk *or* popular non-junk elements.
+ for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
+ m.a[besti-1] == m.b[bestj-1] {
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ }
+ for besti+bestsize < ahi && bestj+bestsize < bhi &&
+ !m.isBJunk(m.b[bestj+bestsize]) &&
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
+ bestsize += 1
+ }
+
+ // Now that we have a wholly interesting match (albeit possibly
+ // empty!), we may as well suck up the matching junk on each
+ // side of it too. Can't think of a good reason not to, and it
+ // saves post-processing the (possibly considerable) expense of
+ // figuring out what to do with it. In the case of an empty
+ // interesting match, this is clearly the right thing to do,
+ // because no other kind of match is possible in the regions.
+ for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
+ m.a[besti-1] == m.b[bestj-1] {
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ }
+ for besti+bestsize < ahi && bestj+bestsize < bhi &&
+ m.isBJunk(m.b[bestj+bestsize]) &&
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
+ bestsize += 1
+ }
+
+ return Match{A: besti, B: bestj, Size: bestsize}
+}
+
+// Return list of triples describing matching subsequences.
+//
+// Each triple is of the form (i, j, n), and means that
+// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
+// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
+// adjacent triples in the list, and the second is not the last triple in the
+// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
+// adjacent equal blocks.
+//
+// The last triple is a dummy, (len(a), len(b), 0), and is the only
+// triple with n==0.
+func (m *SequenceMatcher) GetMatchingBlocks() []Match {
+ if m.matchingBlocks != nil {
+ return m.matchingBlocks
+ }
+
+ var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
+ matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
+ match := m.findLongestMatch(alo, ahi, blo, bhi)
+ i, j, k := match.A, match.B, match.Size
+ if match.Size > 0 {
+ if alo < i && blo < j {
+ matched = matchBlocks(alo, i, blo, j, matched)
+ }
+ matched = append(matched, match)
+ if i+k < ahi && j+k < bhi {
+ matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
+ }
+ }
+ return matched
+ }
+ matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
+
+ // It's possible that we have adjacent equal blocks in the
+ // matching_blocks list now.
+ nonAdjacent := []Match{}
+ i1, j1, k1 := 0, 0, 0
+ for _, b := range matched {
+ // Is this block adjacent to i1, j1, k1?
+ i2, j2, k2 := b.A, b.B, b.Size
+ if i1+k1 == i2 && j1+k1 == j2 {
+ // Yes, so collapse them -- this just increases the length of
+ // the first block by the length of the second, and the first
+ // block so lengthened remains the block to compare against.
+ k1 += k2
+ } else {
+ // Not adjacent. Remember the first block (k1==0 means it's
+ // the dummy we started with), and make the second block the
+ // new block to compare against.
+ if k1 > 0 {
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+ }
+ i1, j1, k1 = i2, j2, k2
+ }
+ }
+ if k1 > 0 {
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+ }
+
+ nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
+ m.matchingBlocks = nonAdjacent
+ return m.matchingBlocks
+}
+
+// Return list of 5-tuples describing how to turn a into b.
+//
+// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
+// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
+// tuple preceding it, and likewise for j1 == the previous j2.
+//
+// The tags are characters, with these meanings:
+//
+// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
+//
+// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
+//
+// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
+//
+// 'e' (equal): a[i1:i2] == b[j1:j2]
+func (m *SequenceMatcher) GetOpCodes() []OpCode {
+ if m.opCodes != nil {
+ return m.opCodes
+ }
+ i, j := 0, 0
+ matching := m.GetMatchingBlocks()
+ opCodes := make([]OpCode, 0, len(matching))
+ for _, m := range matching {
+ // invariant: we've pumped out correct diffs to change
+ // a[:i] into b[:j], and the next matching block is
+ // a[ai:ai+size] == b[bj:bj+size]. So we need to pump
+ // out a diff to change a[i:ai] into b[j:bj], pump out
+ // the matching block, and move (i,j) beyond the match
+ ai, bj, size := m.A, m.B, m.Size
+ tag := byte(0)
+ if i < ai && j < bj {
+ tag = 'r'
+ } else if i < ai {
+ tag = 'd'
+ } else if j < bj {
+ tag = 'i'
+ }
+ if tag > 0 {
+ opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
+ }
+ i, j = ai+size, bj+size
+ // the list of matching blocks is terminated by a
+ // sentinel with size 0
+ if size > 0 {
+ opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
+ }
+ }
+ m.opCodes = opCodes
+ return m.opCodes
+}
+
+// Isolate change clusters by eliminating ranges with no changes.
+//
+// Return a generator of groups with up to n lines of context.
+// Each group is in the same format as returned by GetOpCodes().
+func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
+ if n < 0 {
+ n = 3
+ }
+ codes := m.GetOpCodes()
+ if len(codes) == 0 {
+ codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
+ }
+ // Fixup leading and trailing groups if they show no changes.
+ if codes[0].Tag == 'e' {
+ c := codes[0]
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
+ }
+ if codes[len(codes)-1].Tag == 'e' {
+ c := codes[len(codes)-1]
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
+ }
+ nn := n + n
+ groups := [][]OpCode{}
+ group := []OpCode{}
+ for _, c := range codes {
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ // End the current group and start a new one whenever
+ // there is a large range with no changes.
+ if c.Tag == 'e' && i2-i1 > nn {
+ group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
+ j1, min(j2, j1+n)})
+ groups = append(groups, group)
+ group = []OpCode{}
+ i1, j1 = max(i1, i2-n), max(j1, j2-n)
+ }
+ group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
+ }
+ if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
+ groups = append(groups, group)
+ }
+ return groups
+}
+
+// Return a measure of the sequences' similarity (float in [0,1]).
+//
+// Where T is the total number of elements in both sequences, and
+// M is the number of matches, this is 2.0*M / T.
+// Note that this is 1 if the sequences are identical, and 0 if
+// they have nothing in common.
+//
+// .Ratio() is expensive to compute if you haven't already computed
+// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
+// want to try .QuickRatio() or .RealQuickRation() first to get an
+// upper bound.
+func (m *SequenceMatcher) Ratio() float64 {
+ matches := 0
+ for _, m := range m.GetMatchingBlocks() {
+ matches += m.Size
+ }
+ return calculateRatio(matches, len(m.a)+len(m.b))
+}
+
+// Return an upper bound on ratio() relatively quickly.
+//
+// This isn't defined beyond that it is an upper bound on .Ratio(), and
+// is faster to compute.
+func (m *SequenceMatcher) QuickRatio() float64 {
+ // viewing a and b as multisets, set matches to the cardinality
+ // of their intersection; this counts the number of matches
+ // without regard to order, so is clearly an upper bound
+ if m.fullBCount == nil {
+ m.fullBCount = map[string]int{}
+ for _, s := range m.b {
+ m.fullBCount[s] = m.fullBCount[s] + 1
+ }
+ }
+
+ // avail[x] is the number of times x appears in 'b' less the
+ // number of times we've seen it in 'a' so far ... kinda
+ avail := map[string]int{}
+ matches := 0
+ for _, s := range m.a {
+ n, ok := avail[s]
+ if !ok {
+ n = m.fullBCount[s]
+ }
+ avail[s] = n - 1
+ if n > 0 {
+ matches += 1
+ }
+ }
+ return calculateRatio(matches, len(m.a)+len(m.b))
+}
+
+// Return an upper bound on ratio() very quickly.
+//
+// This isn't defined beyond that it is an upper bound on .Ratio(), and
+// is faster to compute than either .Ratio() or .QuickRatio().
+func (m *SequenceMatcher) RealQuickRatio() float64 {
+ la, lb := len(m.a), len(m.b)
+ return calculateRatio(min(la, lb), la+lb)
+}
+
+// Convert range to the "ed" format
+func formatRangeUnified(start, stop int) string {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ beginning := start + 1 // lines start numbering with one
+ length := stop - start
+ if length == 1 {
+ return fmt.Sprintf("%d", beginning)
+ }
+ if length == 0 {
+ beginning -= 1 // empty ranges begin at line just before the range
+ }
+ return fmt.Sprintf("%d,%d", beginning, length)
+}
+
+// Unified diff parameters
+type UnifiedDiff struct {
+ A []string // First sequence lines
+ FromFile string // First file name
+ FromDate string // First file time
+ B []string // Second sequence lines
+ ToFile string // Second file name
+ ToDate string // Second file time
+ Eol string // Headers end of line, defaults to LF
+ Context int // Number of context lines
+}
+
+// Compare two sequences of lines; generate the delta as a unified diff.
+//
+// Unified diffs are a compact way of showing line changes and a few
+// lines of context. The number of context lines is set by 'n' which
+// defaults to three.
+//
+// By default, the diff control lines (those with ---, +++, or @@) are
+// created with a trailing newline. This is helpful so that inputs
+// created from file.readlines() result in diffs that are suitable for
+// file.writelines() since both the inputs and outputs have trailing
+// newlines.
+//
+// For inputs that do not have trailing newlines, set the lineterm
+// argument to "" so that the output will be uniformly newline free.
+//
+// The unidiff format normally has a header for filenames and modification
+// times. Any or all of these may be specified using strings for
+// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
+// The modification times are normally expressed in the ISO 8601 format.
+func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
+ buf := bufio.NewWriter(writer)
+ defer buf.Flush()
+ wf := func(format string, args ...interface{}) error {
+ _, err := buf.WriteString(fmt.Sprintf(format, args...))
+ return err
+ }
+ ws := func(s string) error {
+ _, err := buf.WriteString(s)
+ return err
+ }
+
+ if len(diff.Eol) == 0 {
+ diff.Eol = "\n"
+ }
+
+ started := false
+ m := NewMatcher(diff.A, diff.B)
+ for _, g := range m.GetGroupedOpCodes(diff.Context) {
+ if !started {
+ started = true
+ fromDate := ""
+ if len(diff.FromDate) > 0 {
+ fromDate = "\t" + diff.FromDate
+ }
+ toDate := ""
+ if len(diff.ToDate) > 0 {
+ toDate = "\t" + diff.ToDate
+ }
+ if diff.FromFile != "" || diff.ToFile != "" {
+ err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
+ if err != nil {
+ return err
+ }
+ err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ first, last := g[0], g[len(g)-1]
+ range1 := formatRangeUnified(first.I1, last.I2)
+ range2 := formatRangeUnified(first.J1, last.J2)
+ if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
+ return err
+ }
+ for _, c := range g {
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ if c.Tag == 'e' {
+ for _, line := range diff.A[i1:i2] {
+ if err := ws(" " + line); err != nil {
+ return err
+ }
+ }
+ continue
+ }
+ if c.Tag == 'r' || c.Tag == 'd' {
+ for _, line := range diff.A[i1:i2] {
+ if err := ws("-" + line); err != nil {
+ return err
+ }
+ }
+ }
+ if c.Tag == 'r' || c.Tag == 'i' {
+ for _, line := range diff.B[j1:j2] {
+ if err := ws("+" + line); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// Like WriteUnifiedDiff but returns the diff a string.
+func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
+ w := &bytes.Buffer{}
+ err := WriteUnifiedDiff(w, diff)
+ return string(w.Bytes()), err
+}
+
+// Convert range to the "ed" format.
+func formatRangeContext(start, stop int) string {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ beginning := start + 1 // lines start numbering with one
+ length := stop - start
+ if length == 0 {
+ beginning -= 1 // empty ranges begin at line just before the range
+ }
+ if length <= 1 {
+ return fmt.Sprintf("%d", beginning)
+ }
+ return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
+}
+
+type ContextDiff UnifiedDiff
+
+// Compare two sequences of lines; generate the delta as a context diff.
+//
+// Context diffs are a compact way of showing line changes and a few
+// lines of context. The number of context lines is set by diff.Context
+// which defaults to three.
+//
+// By default, the diff control lines (those with *** or ---) are
+// created with a trailing newline.
+//
+// For inputs that do not have trailing newlines, set the diff.Eol
+// argument to "" so that the output will be uniformly newline free.
+//
+// The context diff format normally has a header for filenames and
+// modification times. Any or all of these may be specified using
+// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
+// The modification times are normally expressed in the ISO 8601 format.
+// If not specified, the strings default to blanks.
+func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
+ buf := bufio.NewWriter(writer)
+ defer buf.Flush()
+ var diffErr error
+ wf := func(format string, args ...interface{}) {
+ _, err := buf.WriteString(fmt.Sprintf(format, args...))
+ if diffErr == nil && err != nil {
+ diffErr = err
+ }
+ }
+ ws := func(s string) {
+ _, err := buf.WriteString(s)
+ if diffErr == nil && err != nil {
+ diffErr = err
+ }
+ }
+
+ if len(diff.Eol) == 0 {
+ diff.Eol = "\n"
+ }
+
+ prefix := map[byte]string{
+ 'i': "+ ",
+ 'd': "- ",
+ 'r': "! ",
+ 'e': " ",
+ }
+
+ started := false
+ m := NewMatcher(diff.A, diff.B)
+ for _, g := range m.GetGroupedOpCodes(diff.Context) {
+ if !started {
+ started = true
+ fromDate := ""
+ if len(diff.FromDate) > 0 {
+ fromDate = "\t" + diff.FromDate
+ }
+ toDate := ""
+ if len(diff.ToDate) > 0 {
+ toDate = "\t" + diff.ToDate
+ }
+ if diff.FromFile != "" || diff.ToFile != "" {
+ wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
+ wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
+ }
+ }
+
+ first, last := g[0], g[len(g)-1]
+ ws("***************" + diff.Eol)
+
+ range1 := formatRangeContext(first.I1, last.I2)
+ wf("*** %s ****%s", range1, diff.Eol)
+ for _, c := range g {
+ if c.Tag == 'r' || c.Tag == 'd' {
+ for _, cc := range g {
+ if cc.Tag == 'i' {
+ continue
+ }
+ for _, line := range diff.A[cc.I1:cc.I2] {
+ ws(prefix[cc.Tag] + line)
+ }
+ }
+ break
+ }
+ }
+
+ range2 := formatRangeContext(first.J1, last.J2)
+ wf("--- %s ----%s", range2, diff.Eol)
+ for _, c := range g {
+ if c.Tag == 'r' || c.Tag == 'i' {
+ for _, cc := range g {
+ if cc.Tag == 'd' {
+ continue
+ }
+ for _, line := range diff.B[cc.J1:cc.J2] {
+ ws(prefix[cc.Tag] + line)
+ }
+ }
+ break
+ }
+ }
+ }
+ return diffErr
+}
+
+// Like WriteContextDiff but returns the diff a string.
+func GetContextDiffString(diff ContextDiff) (string, error) {
+ w := &bytes.Buffer{}
+ err := WriteContextDiff(w, diff)
+ return string(w.Bytes()), err
+}
+
+// Split a string on "\n" while preserving them. The output can be used
+// as input for UnifiedDiff and ContextDiff structures.
+func SplitLines(s string) []string {
+ lines := strings.SplitAfter(s, "\n")
+ lines[len(lines)-1] += "\n"
+ return lines
+}
diff --git a/vendor/github.com/stretchr/testify/LICENSE b/vendor/github.com/stretchr/testify/LICENSE
new file mode 100644
index 000000000..f38ec5956
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/stretchr/testify/README.md b/vendor/github.com/stretchr/testify/README.md
new file mode 100644
index 000000000..ef0197e8a
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/README.md
@@ -0,0 +1,342 @@
+Testify - Thou Shalt Write Tests
+================================
+
+[](https://travis-ci.org/stretchr/testify) [](https://goreportcard.com/report/github.com/stretchr/testify) [](https://godoc.org/github.com/stretchr/testify)
+
+Go code (golang) set of packages that provide many tools for testifying that your code will behave as you intend.
+
+Features include:
+
+ * [Easy assertions](#assert-package)
+ * [Mocking](#mock-package)
+ * [Testing suite interfaces and functions](#suite-package)
+
+Get started:
+
+ * Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date)
+ * For an introduction to writing test code in Go, see http://golang.org/doc/code.html#Testing
+ * Check out the API Documentation http://godoc.org/github.com/stretchr/testify
+ * To make your testing life easier, check out our other project, [gorc](http://github.com/stretchr/gorc)
+ * A little about [Test-Driven Development (TDD)](http://en.wikipedia.org/wiki/Test-driven_development)
+
+
+
+[`assert`](http://godoc.org/github.com/stretchr/testify/assert "API documentation") package
+-------------------------------------------------------------------------------------------
+
+The `assert` package provides some helpful methods that allow you to write better test code in Go.
+
+ * Prints friendly, easy to read failure descriptions
+ * Allows for very readable code
+ * Optionally annotate each assertion with a message
+
+See it in action:
+
+```go
+package yours
+
+import (
+ "testing"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSomething(t *testing.T) {
+
+ // assert equality
+ assert.Equal(t, 123, 123, "they should be equal")
+
+ // assert inequality
+ assert.NotEqual(t, 123, 456, "they should not be equal")
+
+ // assert for nil (good for errors)
+ assert.Nil(t, object)
+
+ // assert for not nil (good when you expect something)
+ if assert.NotNil(t, object) {
+
+ // now we know that object isn't nil, we are safe to make
+ // further assertions without causing any errors
+ assert.Equal(t, "Something", object.Value)
+
+ }
+
+}
+```
+
+ * Every assert func takes the `testing.T` object as the first argument. This is how it writes the errors out through the normal `go test` capabilities.
+ * Every assert func returns a bool indicating whether the assertion was successful or not, this is useful for if you want to go on making further assertions under certain conditions.
+
+if you assert many times, use the below:
+
+```go
+package yours
+
+import (
+ "testing"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSomething(t *testing.T) {
+ assert := assert.New(t)
+
+ // assert equality
+ assert.Equal(123, 123, "they should be equal")
+
+ // assert inequality
+ assert.NotEqual(123, 456, "they should not be equal")
+
+ // assert for nil (good for errors)
+ assert.Nil(object)
+
+ // assert for not nil (good when you expect something)
+ if assert.NotNil(object) {
+
+ // now we know that object isn't nil, we are safe to make
+ // further assertions without causing any errors
+ assert.Equal("Something", object.Value)
+ }
+}
+```
+
+[`require`](http://godoc.org/github.com/stretchr/testify/require "API documentation") package
+---------------------------------------------------------------------------------------------
+
+The `require` package provides same global functions as the `assert` package, but instead of returning a boolean result they terminate current test.
+
+See [t.FailNow](http://golang.org/pkg/testing/#T.FailNow) for details.
+
+[`mock`](http://godoc.org/github.com/stretchr/testify/mock "API documentation") package
+----------------------------------------------------------------------------------------
+
+The `mock` package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code.
+
+An example test function that tests a piece of code that relies on an external object `testObj`, can setup expectations (testify) and assert that they indeed happened:
+
+```go
+package yours
+
+import (
+ "testing"
+ "github.com/stretchr/testify/mock"
+)
+
+/*
+ Test objects
+*/
+
+// MyMockedObject is a mocked object that implements an interface
+// that describes an object that the code I am testing relies on.
+type MyMockedObject struct{
+ mock.Mock
+}
+
+// DoSomething is a method on MyMockedObject that implements some interface
+// and just records the activity, and returns what the Mock object tells it to.
+//
+// In the real object, this method would do something useful, but since this
+// is a mocked object - we're just going to stub it out.
+//
+// NOTE: This method is not being tested here, code that uses this object is.
+func (m *MyMockedObject) DoSomething(number int) (bool, error) {
+
+ args := m.Called(number)
+ return args.Bool(0), args.Error(1)
+
+}
+
+/*
+ Actual test functions
+*/
+
+// TestSomething is an example of how to use our test object to
+// make assertions about some target code we are testing.
+func TestSomething(t *testing.T) {
+
+ // create an instance of our test object
+ testObj := new(MyMockedObject)
+
+ // setup expectations
+ testObj.On("DoSomething", 123).Return(true, nil)
+
+ // call the code we are testing
+ targetFuncThatDoesSomethingWithObj(testObj)
+
+ // assert that the expectations were met
+ testObj.AssertExpectations(t)
+
+
+}
+
+// TestSomethingElse is a second example of how to use our test object to
+// make assertions about some target code we are testing.
+// This time using a placeholder. Placeholders might be used when the
+// data being passed in is normally dynamically generated and cannot be
+// predicted beforehand (eg. containing hashes that are time sensitive)
+func TestSomethingElse(t *testing.T) {
+
+ // create an instance of our test object
+ testObj := new(MyMockedObject)
+
+ // setup expectations with a placeholder in the argument list
+ testObj.On("DoSomething", mock.Anything).Return(true, nil)
+
+ // call the code we are testing
+ targetFuncThatDoesSomethingWithObj(testObj)
+
+ // assert that the expectations were met
+ testObj.AssertExpectations(t)
+
+
+}
+```
+
+For more information on how to write mock code, check out the [API documentation for the `mock` package](http://godoc.org/github.com/stretchr/testify/mock).
+
+You can use the [mockery tool](http://github.com/vektra/mockery) to autogenerate the mock code against an interface as well, making using mocks much quicker.
+
+[`suite`](http://godoc.org/github.com/stretchr/testify/suite "API documentation") package
+-----------------------------------------------------------------------------------------
+
+The `suite` package provides functionality that you might be used to from more common object oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal.
+
+An example suite is shown below:
+
+```go
+// Basic imports
+import (
+ "testing"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+)
+
+// Define the suite, and absorb the built-in basic suite
+// functionality from testify - including a T() method which
+// returns the current testing context
+type ExampleTestSuite struct {
+ suite.Suite
+ VariableThatShouldStartAtFive int
+}
+
+// Make sure that VariableThatShouldStartAtFive is set to five
+// before each test
+func (suite *ExampleTestSuite) SetupTest() {
+ suite.VariableThatShouldStartAtFive = 5
+}
+
+// All methods that begin with "Test" are run as tests within a
+// suite.
+func (suite *ExampleTestSuite) TestExample() {
+ assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
+}
+
+// In order for 'go test' to run this suite, we need to create
+// a normal test function and pass our suite to suite.Run
+func TestExampleTestSuite(t *testing.T) {
+ suite.Run(t, new(ExampleTestSuite))
+}
+```
+
+For a more complete example, using all of the functionality provided by the suite package, look at our [example testing suite](https://github.com/stretchr/testify/blob/master/suite/suite_test.go)
+
+For more information on writing suites, check out the [API documentation for the `suite` package](http://godoc.org/github.com/stretchr/testify/suite).
+
+`Suite` object has assertion methods:
+
+```go
+// Basic imports
+import (
+ "testing"
+ "github.com/stretchr/testify/suite"
+)
+
+// Define the suite, and absorb the built-in basic suite
+// functionality from testify - including assertion methods.
+type ExampleTestSuite struct {
+ suite.Suite
+ VariableThatShouldStartAtFive int
+}
+
+// Make sure that VariableThatShouldStartAtFive is set to five
+// before each test
+func (suite *ExampleTestSuite) SetupTest() {
+ suite.VariableThatShouldStartAtFive = 5
+}
+
+// All methods that begin with "Test" are run as tests within a
+// suite.
+func (suite *ExampleTestSuite) TestExample() {
+ suite.Equal(suite.VariableThatShouldStartAtFive, 5)
+}
+
+// In order for 'go test' to run this suite, we need to create
+// a normal test function and pass our suite to suite.Run
+func TestExampleTestSuite(t *testing.T) {
+ suite.Run(t, new(ExampleTestSuite))
+}
+```
+
+------
+
+Installation
+============
+
+To install Testify, use `go get`:
+
+ go get github.com/stretchr/testify
+
+This will then make the following packages available to you:
+
+ github.com/stretchr/testify/assert
+ github.com/stretchr/testify/require
+ github.com/stretchr/testify/mock
+ github.com/stretchr/testify/suite
+ github.com/stretchr/testify/http (deprecated)
+
+Import the `testify/assert` package into your code using this template:
+
+```go
+package yours
+
+import (
+ "testing"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSomething(t *testing.T) {
+
+ assert.True(t, true, "True is true!")
+
+}
+```
+
+------
+
+Staying up to date
+==================
+
+To update Testify to the latest version, use `go get -u github.com/stretchr/testify`.
+
+------
+
+Supported go versions
+==================
+
+We support the three major Go versions, which are 1.9, 1.10, and 1.11 at the moment.
+
+------
+
+Contributing
+============
+
+Please feel free to submit issues, fork the repository and send pull requests!
+
+When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it.
+
+Code generation is used. Look for `CODE GENERATED AUTOMATICALLY` at the top of some files. Run `go generate ./...` to update generated files.
+
+------
+
+License
+=======
+
+This project is licensed under the terms of the MIT license.
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go
new file mode 100644
index 000000000..e0364e9e7
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go
@@ -0,0 +1,566 @@
+/*
+* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
+* THIS FILE MUST NOT BE EDITED BY HAND
+ */
+
+package assert
+
+import (
+ http "net/http"
+ url "net/url"
+ time "time"
+)
+
+// Conditionf uses a Comparison to assert a complex condition.
+func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Condition(t, comp, append([]interface{}{msg}, args...)...)
+}
+
+// Containsf asserts that the specified string, list(array, slice...) or map contains the
+// specified substring or element.
+//
+// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
+// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
+// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
+func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Contains(t, s, contains, append([]interface{}{msg}, args...)...)
+}
+
+// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
+func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return DirExists(t, path, append([]interface{}{msg}, args...)...)
+}
+
+// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
+// the number of appearances of each of them in both lists should match.
+//
+// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
+func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
+}
+
+// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// assert.Emptyf(t, obj, "error message %s", "formatted")
+func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Empty(t, object, append([]interface{}{msg}, args...)...)
+}
+
+// Equalf asserts that two objects are equal.
+//
+// assert.Equalf(t, 123, 123, "error message %s", "formatted")
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses). Function equality
+// cannot be determined and will always fail.
+func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Equal(t, expected, actual, append([]interface{}{msg}, args...)...)
+}
+
+// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+// actualObj, err := SomeFunction()
+// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
+func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
+}
+
+// EqualValuesf asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
+func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
+}
+
+// Errorf asserts that a function returned an error (i.e. not `nil`).
+//
+// actualObj, err := SomeFunction()
+// if assert.Errorf(t, err, "error message %s", "formatted") {
+// assert.Equal(t, expectedErrorf, err)
+// }
+func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Error(t, err, append([]interface{}{msg}, args...)...)
+}
+
+// Eventuallyf asserts that given condition will be met in waitFor time,
+// periodically checking target function each tick.
+//
+// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
+func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
+}
+
+// Exactlyf asserts that two objects are equal in value and type.
+//
+// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
+func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...)
+}
+
+// Failf reports a failure through
+func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Fail(t, failureMessage, append([]interface{}{msg}, args...)...)
+}
+
+// FailNowf fails test
+func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...)
+}
+
+// Falsef asserts that the specified value is false.
+//
+// assert.Falsef(t, myBool, "error message %s", "formatted")
+func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return False(t, value, append([]interface{}{msg}, args...)...)
+}
+
+// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
+func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return FileExists(t, path, append([]interface{}{msg}, args...)...)
+}
+
+// Greaterf asserts that the first element is greater than the second
+//
+// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
+// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1))
+// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
+func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Greater(t, e1, e2, append([]interface{}{msg}, args...)...)
+}
+
+// GreaterOrEqualf asserts that the first element is greater than or equal to the second
+//
+// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
+// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
+// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
+// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
+func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
+}
+
+// HTTPBodyContainsf asserts that a specified handler returns a
+// body that contains a string.
+//
+// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
+}
+
+// HTTPBodyNotContainsf asserts that a specified handler returns a
+// body that does not contain a string.
+//
+// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
+}
+
+// HTTPErrorf asserts that a specified handler returns an error status code.
+//
+// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
+func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
+}
+
+// HTTPRedirectf asserts that a specified handler returns a redirect status code.
+//
+// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
+func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
+}
+
+// HTTPSuccessf asserts that a specified handler returns a success status code.
+//
+// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
+}
+
+// Implementsf asserts that an object is implemented by the specified interface.
+//
+// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
+func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...)
+}
+
+// InDeltaf asserts that the two numerals are within delta of each other.
+//
+// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
+func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
+}
+
+// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
+func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
+}
+
+// InDeltaSlicef is the same as InDelta, except it compares two slices.
+func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
+}
+
+// InEpsilonf asserts that expected and actual have a relative error less than epsilon
+func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
+}
+
+// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
+func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
+}
+
+// IsTypef asserts that the specified objects are of the same type.
+func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...)
+}
+
+// JSONEqf asserts that two JSON strings are equivalent.
+//
+// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
+func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
+}
+
+// YAMLEqf asserts that two YAML strings are equivalent.
+func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...)
+}
+
+// Lenf asserts that the specified object has specific length.
+// Lenf also fails if the object has a type that len() not accept.
+//
+// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
+func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Len(t, object, length, append([]interface{}{msg}, args...)...)
+}
+
+// Lessf asserts that the first element is less than the second
+//
+// assert.Lessf(t, 1, 2, "error message %s", "formatted")
+// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2))
+// assert.Lessf(t, "a", "b", "error message %s", "formatted")
+func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Less(t, e1, e2, append([]interface{}{msg}, args...)...)
+}
+
+// LessOrEqualf asserts that the first element is less than or equal to the second
+//
+// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
+// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
+// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
+// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
+func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
+}
+
+// Nilf asserts that the specified object is nil.
+//
+// assert.Nilf(t, err, "error message %s", "formatted")
+func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Nil(t, object, append([]interface{}{msg}, args...)...)
+}
+
+// NoErrorf asserts that a function returned no error (i.e. `nil`).
+//
+// actualObj, err := SomeFunction()
+// if assert.NoErrorf(t, err, "error message %s", "formatted") {
+// assert.Equal(t, expectedObj, actualObj)
+// }
+func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NoError(t, err, append([]interface{}{msg}, args...)...)
+}
+
+// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
+// specified substring or element.
+//
+// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
+// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
+// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
+func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
+}
+
+// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
+// assert.Equal(t, "two", obj[1])
+// }
+func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotEmpty(t, object, append([]interface{}{msg}, args...)...)
+}
+
+// NotEqualf asserts that the specified values are NOT equal.
+//
+// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses).
+func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
+}
+
+// NotNilf asserts that the specified object is not nil.
+//
+// assert.NotNilf(t, err, "error message %s", "formatted")
+func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotNil(t, object, append([]interface{}{msg}, args...)...)
+}
+
+// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
+func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotPanics(t, f, append([]interface{}{msg}, args...)...)
+}
+
+// NotRegexpf asserts that a specified regexp does not match a string.
+//
+// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
+// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
+func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...)
+}
+
+// NotSubsetf asserts that the specified list(array, slice...) contains not all
+// elements given in the specified subset(array, slice...).
+//
+// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
+func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...)
+}
+
+// NotZerof asserts that i is not the zero value for its type.
+func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotZero(t, i, append([]interface{}{msg}, args...)...)
+}
+
+// Panicsf asserts that the code inside the specified PanicTestFunc panics.
+//
+// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
+func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Panics(t, f, append([]interface{}{msg}, args...)...)
+}
+
+// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
+// the recovered panic value equals the expected panic value.
+//
+// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
+func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
+}
+
+// Regexpf asserts that a specified regexp matches a string.
+//
+// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
+// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
+func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
+}
+
+// Samef asserts that two pointers reference the same object.
+//
+// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
+//
+// Both arguments must be pointer variables. Pointer variable sameness is
+// determined based on the equality of both type and value.
+func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Same(t, expected, actual, append([]interface{}{msg}, args...)...)
+}
+
+// Subsetf asserts that the specified list(array, slice...) contains all
+// elements given in the specified subset(array, slice...).
+//
+// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
+func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Subset(t, list, subset, append([]interface{}{msg}, args...)...)
+}
+
+// Truef asserts that the specified value is true.
+//
+// assert.Truef(t, myBool, "error message %s", "formatted")
+func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return True(t, value, append([]interface{}{msg}, args...)...)
+}
+
+// WithinDurationf asserts that the two times are within duration delta of each other.
+//
+// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
+func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
+}
+
+// Zerof asserts that i is the zero value for its type.
+func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Zero(t, i, append([]interface{}{msg}, args...)...)
+}
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go
new file mode 100644
index 000000000..26830403a
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go
@@ -0,0 +1,1120 @@
+/*
+* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
+* THIS FILE MUST NOT BE EDITED BY HAND
+ */
+
+package assert
+
+import (
+ http "net/http"
+ url "net/url"
+ time "time"
+)
+
+// Condition uses a Comparison to assert a complex condition.
+func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Condition(a.t, comp, msgAndArgs...)
+}
+
+// Conditionf uses a Comparison to assert a complex condition.
+func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Conditionf(a.t, comp, msg, args...)
+}
+
+// Contains asserts that the specified string, list(array, slice...) or map contains the
+// specified substring or element.
+//
+// a.Contains("Hello World", "World")
+// a.Contains(["Hello", "World"], "World")
+// a.Contains({"Hello": "World"}, "Hello")
+func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Contains(a.t, s, contains, msgAndArgs...)
+}
+
+// Containsf asserts that the specified string, list(array, slice...) or map contains the
+// specified substring or element.
+//
+// a.Containsf("Hello World", "World", "error message %s", "formatted")
+// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted")
+// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted")
+func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Containsf(a.t, s, contains, msg, args...)
+}
+
+// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
+func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return DirExists(a.t, path, msgAndArgs...)
+}
+
+// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
+func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return DirExistsf(a.t, path, msg, args...)
+}
+
+// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
+// the number of appearances of each of them in both lists should match.
+//
+// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2])
+func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return ElementsMatch(a.t, listA, listB, msgAndArgs...)
+}
+
+// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
+// the number of appearances of each of them in both lists should match.
+//
+// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
+func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return ElementsMatchf(a.t, listA, listB, msg, args...)
+}
+
+// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// a.Empty(obj)
+func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Empty(a.t, object, msgAndArgs...)
+}
+
+// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// a.Emptyf(obj, "error message %s", "formatted")
+func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Emptyf(a.t, object, msg, args...)
+}
+
+// Equal asserts that two objects are equal.
+//
+// a.Equal(123, 123)
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses). Function equality
+// cannot be determined and will always fail.
+func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Equal(a.t, expected, actual, msgAndArgs...)
+}
+
+// EqualError asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+// actualObj, err := SomeFunction()
+// a.EqualError(err, expectedErrorString)
+func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return EqualError(a.t, theError, errString, msgAndArgs...)
+}
+
+// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+// actualObj, err := SomeFunction()
+// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted")
+func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return EqualErrorf(a.t, theError, errString, msg, args...)
+}
+
+// EqualValues asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+// a.EqualValues(uint32(123), int32(123))
+func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return EqualValues(a.t, expected, actual, msgAndArgs...)
+}
+
+// EqualValuesf asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
+func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return EqualValuesf(a.t, expected, actual, msg, args...)
+}
+
+// Equalf asserts that two objects are equal.
+//
+// a.Equalf(123, 123, "error message %s", "formatted")
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses). Function equality
+// cannot be determined and will always fail.
+func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Equalf(a.t, expected, actual, msg, args...)
+}
+
+// Error asserts that a function returned an error (i.e. not `nil`).
+//
+// actualObj, err := SomeFunction()
+// if a.Error(err) {
+// assert.Equal(t, expectedError, err)
+// }
+func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Error(a.t, err, msgAndArgs...)
+}
+
+// Errorf asserts that a function returned an error (i.e. not `nil`).
+//
+// actualObj, err := SomeFunction()
+// if a.Errorf(err, "error message %s", "formatted") {
+// assert.Equal(t, expectedErrorf, err)
+// }
+func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Errorf(a.t, err, msg, args...)
+}
+
+// Eventually asserts that given condition will be met in waitFor time,
+// periodically checking target function each tick.
+//
+// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond)
+func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
+}
+
+// Eventuallyf asserts that given condition will be met in waitFor time,
+// periodically checking target function each tick.
+//
+// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
+func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Eventuallyf(a.t, condition, waitFor, tick, msg, args...)
+}
+
+// Exactly asserts that two objects are equal in value and type.
+//
+// a.Exactly(int32(123), int64(123))
+func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Exactly(a.t, expected, actual, msgAndArgs...)
+}
+
+// Exactlyf asserts that two objects are equal in value and type.
+//
+// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
+func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Exactlyf(a.t, expected, actual, msg, args...)
+}
+
+// Fail reports a failure through
+func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Fail(a.t, failureMessage, msgAndArgs...)
+}
+
+// FailNow fails test
+func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return FailNow(a.t, failureMessage, msgAndArgs...)
+}
+
+// FailNowf fails test
+func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return FailNowf(a.t, failureMessage, msg, args...)
+}
+
+// Failf reports a failure through
+func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Failf(a.t, failureMessage, msg, args...)
+}
+
+// False asserts that the specified value is false.
+//
+// a.False(myBool)
+func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return False(a.t, value, msgAndArgs...)
+}
+
+// Falsef asserts that the specified value is false.
+//
+// a.Falsef(myBool, "error message %s", "formatted")
+func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Falsef(a.t, value, msg, args...)
+}
+
+// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
+func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return FileExists(a.t, path, msgAndArgs...)
+}
+
+// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
+func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return FileExistsf(a.t, path, msg, args...)
+}
+
+// Greater asserts that the first element is greater than the second
+//
+// a.Greater(2, 1)
+// a.Greater(float64(2), float64(1))
+// a.Greater("b", "a")
+func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Greater(a.t, e1, e2, msgAndArgs...)
+}
+
+// GreaterOrEqual asserts that the first element is greater than or equal to the second
+//
+// a.GreaterOrEqual(2, 1)
+// a.GreaterOrEqual(2, 2)
+// a.GreaterOrEqual("b", "a")
+// a.GreaterOrEqual("b", "b")
+func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return GreaterOrEqual(a.t, e1, e2, msgAndArgs...)
+}
+
+// GreaterOrEqualf asserts that the first element is greater than or equal to the second
+//
+// a.GreaterOrEqualf(2, 1, "error message %s", "formatted")
+// a.GreaterOrEqualf(2, 2, "error message %s", "formatted")
+// a.GreaterOrEqualf("b", "a", "error message %s", "formatted")
+// a.GreaterOrEqualf("b", "b", "error message %s", "formatted")
+func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return GreaterOrEqualf(a.t, e1, e2, msg, args...)
+}
+
+// Greaterf asserts that the first element is greater than the second
+//
+// a.Greaterf(2, 1, "error message %s", "formatted")
+// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1))
+// a.Greaterf("b", "a", "error message %s", "formatted")
+func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Greaterf(a.t, e1, e2, msg, args...)
+}
+
+// HTTPBodyContains asserts that a specified handler returns a
+// body that contains a string.
+//
+// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...)
+}
+
+// HTTPBodyContainsf asserts that a specified handler returns a
+// body that contains a string.
+//
+// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...)
+}
+
+// HTTPBodyNotContains asserts that a specified handler returns a
+// body that does not contain a string.
+//
+// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...)
+}
+
+// HTTPBodyNotContainsf asserts that a specified handler returns a
+// body that does not contain a string.
+//
+// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...)
+}
+
+// HTTPError asserts that a specified handler returns an error status code.
+//
+// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPError(a.t, handler, method, url, values, msgAndArgs...)
+}
+
+// HTTPErrorf asserts that a specified handler returns an error status code.
+//
+// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
+func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPErrorf(a.t, handler, method, url, values, msg, args...)
+}
+
+// HTTPRedirect asserts that a specified handler returns a redirect status code.
+//
+// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...)
+}
+
+// HTTPRedirectf asserts that a specified handler returns a redirect status code.
+//
+// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
+func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
+}
+
+// HTTPSuccess asserts that a specified handler returns a success status code.
+//
+// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...)
+}
+
+// HTTPSuccessf asserts that a specified handler returns a success status code.
+//
+// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return HTTPSuccessf(a.t, handler, method, url, values, msg, args...)
+}
+
+// Implements asserts that an object is implemented by the specified interface.
+//
+// a.Implements((*MyInterface)(nil), new(MyObject))
+func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Implements(a.t, interfaceObject, object, msgAndArgs...)
+}
+
+// Implementsf asserts that an object is implemented by the specified interface.
+//
+// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
+func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Implementsf(a.t, interfaceObject, object, msg, args...)
+}
+
+// InDelta asserts that the two numerals are within delta of each other.
+//
+// a.InDelta(math.Pi, (22 / 7.0), 0.01)
+func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDelta(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
+func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
+func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...)
+}
+
+// InDeltaSlice is the same as InDelta, except it compares two slices.
+func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// InDeltaSlicef is the same as InDelta, except it compares two slices.
+func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDeltaSlicef(a.t, expected, actual, delta, msg, args...)
+}
+
+// InDeltaf asserts that the two numerals are within delta of each other.
+//
+// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
+func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InDeltaf(a.t, expected, actual, delta, msg, args...)
+}
+
+// InEpsilon asserts that expected and actual have a relative error less than epsilon
+func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
+}
+
+// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
+func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...)
+}
+
+// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
+func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...)
+}
+
+// InEpsilonf asserts that expected and actual have a relative error less than epsilon
+func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
+}
+
+// IsType asserts that the specified objects are of the same type.
+func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return IsType(a.t, expectedType, object, msgAndArgs...)
+}
+
+// IsTypef asserts that the specified objects are of the same type.
+func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return IsTypef(a.t, expectedType, object, msg, args...)
+}
+
+// JSONEq asserts that two JSON strings are equivalent.
+//
+// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
+func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return JSONEq(a.t, expected, actual, msgAndArgs...)
+}
+
+// JSONEqf asserts that two JSON strings are equivalent.
+//
+// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
+func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return JSONEqf(a.t, expected, actual, msg, args...)
+}
+
+// YAMLEq asserts that two YAML strings are equivalent.
+func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return YAMLEq(a.t, expected, actual, msgAndArgs...)
+}
+
+// YAMLEqf asserts that two YAML strings are equivalent.
+func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return YAMLEqf(a.t, expected, actual, msg, args...)
+}
+
+// Len asserts that the specified object has specific length.
+// Len also fails if the object has a type that len() not accept.
+//
+// a.Len(mySlice, 3)
+func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Len(a.t, object, length, msgAndArgs...)
+}
+
+// Lenf asserts that the specified object has specific length.
+// Lenf also fails if the object has a type that len() not accept.
+//
+// a.Lenf(mySlice, 3, "error message %s", "formatted")
+func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Lenf(a.t, object, length, msg, args...)
+}
+
+// Less asserts that the first element is less than the second
+//
+// a.Less(1, 2)
+// a.Less(float64(1), float64(2))
+// a.Less("a", "b")
+func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Less(a.t, e1, e2, msgAndArgs...)
+}
+
+// LessOrEqual asserts that the first element is less than or equal to the second
+//
+// a.LessOrEqual(1, 2)
+// a.LessOrEqual(2, 2)
+// a.LessOrEqual("a", "b")
+// a.LessOrEqual("b", "b")
+func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return LessOrEqual(a.t, e1, e2, msgAndArgs...)
+}
+
+// LessOrEqualf asserts that the first element is less than or equal to the second
+//
+// a.LessOrEqualf(1, 2, "error message %s", "formatted")
+// a.LessOrEqualf(2, 2, "error message %s", "formatted")
+// a.LessOrEqualf("a", "b", "error message %s", "formatted")
+// a.LessOrEqualf("b", "b", "error message %s", "formatted")
+func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return LessOrEqualf(a.t, e1, e2, msg, args...)
+}
+
+// Lessf asserts that the first element is less than the second
+//
+// a.Lessf(1, 2, "error message %s", "formatted")
+// a.Lessf(float64(1, "error message %s", "formatted"), float64(2))
+// a.Lessf("a", "b", "error message %s", "formatted")
+func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Lessf(a.t, e1, e2, msg, args...)
+}
+
+// Nil asserts that the specified object is nil.
+//
+// a.Nil(err)
+func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Nil(a.t, object, msgAndArgs...)
+}
+
+// Nilf asserts that the specified object is nil.
+//
+// a.Nilf(err, "error message %s", "formatted")
+func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Nilf(a.t, object, msg, args...)
+}
+
+// NoError asserts that a function returned no error (i.e. `nil`).
+//
+// actualObj, err := SomeFunction()
+// if a.NoError(err) {
+// assert.Equal(t, expectedObj, actualObj)
+// }
+func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NoError(a.t, err, msgAndArgs...)
+}
+
+// NoErrorf asserts that a function returned no error (i.e. `nil`).
+//
+// actualObj, err := SomeFunction()
+// if a.NoErrorf(err, "error message %s", "formatted") {
+// assert.Equal(t, expectedObj, actualObj)
+// }
+func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NoErrorf(a.t, err, msg, args...)
+}
+
+// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
+// specified substring or element.
+//
+// a.NotContains("Hello World", "Earth")
+// a.NotContains(["Hello", "World"], "Earth")
+// a.NotContains({"Hello": "World"}, "Earth")
+func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotContains(a.t, s, contains, msgAndArgs...)
+}
+
+// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
+// specified substring or element.
+//
+// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted")
+// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted")
+// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted")
+func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotContainsf(a.t, s, contains, msg, args...)
+}
+
+// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if a.NotEmpty(obj) {
+// assert.Equal(t, "two", obj[1])
+// }
+func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotEmpty(a.t, object, msgAndArgs...)
+}
+
+// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if a.NotEmptyf(obj, "error message %s", "formatted") {
+// assert.Equal(t, "two", obj[1])
+// }
+func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotEmptyf(a.t, object, msg, args...)
+}
+
+// NotEqual asserts that the specified values are NOT equal.
+//
+// a.NotEqual(obj1, obj2)
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses).
+func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotEqual(a.t, expected, actual, msgAndArgs...)
+}
+
+// NotEqualf asserts that the specified values are NOT equal.
+//
+// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses).
+func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotEqualf(a.t, expected, actual, msg, args...)
+}
+
+// NotNil asserts that the specified object is not nil.
+//
+// a.NotNil(err)
+func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotNil(a.t, object, msgAndArgs...)
+}
+
+// NotNilf asserts that the specified object is not nil.
+//
+// a.NotNilf(err, "error message %s", "formatted")
+func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotNilf(a.t, object, msg, args...)
+}
+
+// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+// a.NotPanics(func(){ RemainCalm() })
+func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotPanics(a.t, f, msgAndArgs...)
+}
+
+// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted")
+func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotPanicsf(a.t, f, msg, args...)
+}
+
+// NotRegexp asserts that a specified regexp does not match a string.
+//
+// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
+// a.NotRegexp("^start", "it's not starting")
+func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotRegexp(a.t, rx, str, msgAndArgs...)
+}
+
+// NotRegexpf asserts that a specified regexp does not match a string.
+//
+// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
+// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
+func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotRegexpf(a.t, rx, str, msg, args...)
+}
+
+// NotSubset asserts that the specified list(array, slice...) contains not all
+// elements given in the specified subset(array, slice...).
+//
+// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
+func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotSubset(a.t, list, subset, msgAndArgs...)
+}
+
+// NotSubsetf asserts that the specified list(array, slice...) contains not all
+// elements given in the specified subset(array, slice...).
+//
+// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
+func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotSubsetf(a.t, list, subset, msg, args...)
+}
+
+// NotZero asserts that i is not the zero value for its type.
+func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotZero(a.t, i, msgAndArgs...)
+}
+
+// NotZerof asserts that i is not the zero value for its type.
+func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return NotZerof(a.t, i, msg, args...)
+}
+
+// Panics asserts that the code inside the specified PanicTestFunc panics.
+//
+// a.Panics(func(){ GoCrazy() })
+func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Panics(a.t, f, msgAndArgs...)
+}
+
+// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
+// the recovered panic value equals the expected panic value.
+//
+// a.PanicsWithValue("crazy error", func(){ GoCrazy() })
+func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return PanicsWithValue(a.t, expected, f, msgAndArgs...)
+}
+
+// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
+// the recovered panic value equals the expected panic value.
+//
+// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
+func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return PanicsWithValuef(a.t, expected, f, msg, args...)
+}
+
+// Panicsf asserts that the code inside the specified PanicTestFunc panics.
+//
+// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted")
+func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Panicsf(a.t, f, msg, args...)
+}
+
+// Regexp asserts that a specified regexp matches a string.
+//
+// a.Regexp(regexp.MustCompile("start"), "it's starting")
+// a.Regexp("start...$", "it's not starting")
+func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Regexp(a.t, rx, str, msgAndArgs...)
+}
+
+// Regexpf asserts that a specified regexp matches a string.
+//
+// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
+// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
+func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Regexpf(a.t, rx, str, msg, args...)
+}
+
+// Same asserts that two pointers reference the same object.
+//
+// a.Same(ptr1, ptr2)
+//
+// Both arguments must be pointer variables. Pointer variable sameness is
+// determined based on the equality of both type and value.
+func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Same(a.t, expected, actual, msgAndArgs...)
+}
+
+// Samef asserts that two pointers reference the same object.
+//
+// a.Samef(ptr1, ptr2, "error message %s", "formatted")
+//
+// Both arguments must be pointer variables. Pointer variable sameness is
+// determined based on the equality of both type and value.
+func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Samef(a.t, expected, actual, msg, args...)
+}
+
+// Subset asserts that the specified list(array, slice...) contains all
+// elements given in the specified subset(array, slice...).
+//
+// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
+func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Subset(a.t, list, subset, msgAndArgs...)
+}
+
+// Subsetf asserts that the specified list(array, slice...) contains all
+// elements given in the specified subset(array, slice...).
+//
+// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
+func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Subsetf(a.t, list, subset, msg, args...)
+}
+
+// True asserts that the specified value is true.
+//
+// a.True(myBool)
+func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return True(a.t, value, msgAndArgs...)
+}
+
+// Truef asserts that the specified value is true.
+//
+// a.Truef(myBool, "error message %s", "formatted")
+func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Truef(a.t, value, msg, args...)
+}
+
+// WithinDuration asserts that the two times are within duration delta of each other.
+//
+// a.WithinDuration(time.Now(), time.Now(), 10*time.Second)
+func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// WithinDurationf asserts that the two times are within duration delta of each other.
+//
+// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
+func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return WithinDurationf(a.t, expected, actual, delta, msg, args...)
+}
+
+// Zero asserts that i is the zero value for its type.
+func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Zero(a.t, i, msgAndArgs...)
+}
+
+// Zerof asserts that i is the zero value for its type.
+func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return Zerof(a.t, i, msg, args...)
+}
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go
new file mode 100644
index 000000000..15a486ca6
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go
@@ -0,0 +1,309 @@
+package assert
+
+import (
+ "fmt"
+ "reflect"
+)
+
+func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
+ switch kind {
+ case reflect.Int:
+ {
+ intobj1 := obj1.(int)
+ intobj2 := obj2.(int)
+ if intobj1 > intobj2 {
+ return -1, true
+ }
+ if intobj1 == intobj2 {
+ return 0, true
+ }
+ if intobj1 < intobj2 {
+ return 1, true
+ }
+ }
+ case reflect.Int8:
+ {
+ int8obj1 := obj1.(int8)
+ int8obj2 := obj2.(int8)
+ if int8obj1 > int8obj2 {
+ return -1, true
+ }
+ if int8obj1 == int8obj2 {
+ return 0, true
+ }
+ if int8obj1 < int8obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Int16:
+ {
+ int16obj1 := obj1.(int16)
+ int16obj2 := obj2.(int16)
+ if int16obj1 > int16obj2 {
+ return -1, true
+ }
+ if int16obj1 == int16obj2 {
+ return 0, true
+ }
+ if int16obj1 < int16obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Int32:
+ {
+ int32obj1 := obj1.(int32)
+ int32obj2 := obj2.(int32)
+ if int32obj1 > int32obj2 {
+ return -1, true
+ }
+ if int32obj1 == int32obj2 {
+ return 0, true
+ }
+ if int32obj1 < int32obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Int64:
+ {
+ int64obj1 := obj1.(int64)
+ int64obj2 := obj2.(int64)
+ if int64obj1 > int64obj2 {
+ return -1, true
+ }
+ if int64obj1 == int64obj2 {
+ return 0, true
+ }
+ if int64obj1 < int64obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Uint:
+ {
+ uintobj1 := obj1.(uint)
+ uintobj2 := obj2.(uint)
+ if uintobj1 > uintobj2 {
+ return -1, true
+ }
+ if uintobj1 == uintobj2 {
+ return 0, true
+ }
+ if uintobj1 < uintobj2 {
+ return 1, true
+ }
+ }
+ case reflect.Uint8:
+ {
+ uint8obj1 := obj1.(uint8)
+ uint8obj2 := obj2.(uint8)
+ if uint8obj1 > uint8obj2 {
+ return -1, true
+ }
+ if uint8obj1 == uint8obj2 {
+ return 0, true
+ }
+ if uint8obj1 < uint8obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Uint16:
+ {
+ uint16obj1 := obj1.(uint16)
+ uint16obj2 := obj2.(uint16)
+ if uint16obj1 > uint16obj2 {
+ return -1, true
+ }
+ if uint16obj1 == uint16obj2 {
+ return 0, true
+ }
+ if uint16obj1 < uint16obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Uint32:
+ {
+ uint32obj1 := obj1.(uint32)
+ uint32obj2 := obj2.(uint32)
+ if uint32obj1 > uint32obj2 {
+ return -1, true
+ }
+ if uint32obj1 == uint32obj2 {
+ return 0, true
+ }
+ if uint32obj1 < uint32obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Uint64:
+ {
+ uint64obj1 := obj1.(uint64)
+ uint64obj2 := obj2.(uint64)
+ if uint64obj1 > uint64obj2 {
+ return -1, true
+ }
+ if uint64obj1 == uint64obj2 {
+ return 0, true
+ }
+ if uint64obj1 < uint64obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Float32:
+ {
+ float32obj1 := obj1.(float32)
+ float32obj2 := obj2.(float32)
+ if float32obj1 > float32obj2 {
+ return -1, true
+ }
+ if float32obj1 == float32obj2 {
+ return 0, true
+ }
+ if float32obj1 < float32obj2 {
+ return 1, true
+ }
+ }
+ case reflect.Float64:
+ {
+ float64obj1 := obj1.(float64)
+ float64obj2 := obj2.(float64)
+ if float64obj1 > float64obj2 {
+ return -1, true
+ }
+ if float64obj1 == float64obj2 {
+ return 0, true
+ }
+ if float64obj1 < float64obj2 {
+ return 1, true
+ }
+ }
+ case reflect.String:
+ {
+ stringobj1 := obj1.(string)
+ stringobj2 := obj2.(string)
+ if stringobj1 > stringobj2 {
+ return -1, true
+ }
+ if stringobj1 == stringobj2 {
+ return 0, true
+ }
+ if stringobj1 < stringobj2 {
+ return 1, true
+ }
+ }
+ }
+
+ return 0, false
+}
+
+// Greater asserts that the first element is greater than the second
+//
+// assert.Greater(t, 2, 1)
+// assert.Greater(t, float64(2), float64(1))
+// assert.Greater(t, "b", "a")
+func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ e1Kind := reflect.ValueOf(e1).Kind()
+ e2Kind := reflect.ValueOf(e2).Kind()
+ if e1Kind != e2Kind {
+ return Fail(t, "Elements should be the same type", msgAndArgs...)
+ }
+
+ res, isComparable := compare(e1, e2, e1Kind)
+ if !isComparable {
+ return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
+ }
+
+ if res != -1 {
+ return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...)
+ }
+
+ return true
+}
+
+// GreaterOrEqual asserts that the first element is greater than or equal to the second
+//
+// assert.GreaterOrEqual(t, 2, 1)
+// assert.GreaterOrEqual(t, 2, 2)
+// assert.GreaterOrEqual(t, "b", "a")
+// assert.GreaterOrEqual(t, "b", "b")
+func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ e1Kind := reflect.ValueOf(e1).Kind()
+ e2Kind := reflect.ValueOf(e2).Kind()
+ if e1Kind != e2Kind {
+ return Fail(t, "Elements should be the same type", msgAndArgs...)
+ }
+
+ res, isComparable := compare(e1, e2, e1Kind)
+ if !isComparable {
+ return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
+ }
+
+ if res != -1 && res != 0 {
+ return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...)
+ }
+
+ return true
+}
+
+// Less asserts that the first element is less than the second
+//
+// assert.Less(t, 1, 2)
+// assert.Less(t, float64(1), float64(2))
+// assert.Less(t, "a", "b")
+func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ e1Kind := reflect.ValueOf(e1).Kind()
+ e2Kind := reflect.ValueOf(e2).Kind()
+ if e1Kind != e2Kind {
+ return Fail(t, "Elements should be the same type", msgAndArgs...)
+ }
+
+ res, isComparable := compare(e1, e2, e1Kind)
+ if !isComparable {
+ return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
+ }
+
+ if res != 1 {
+ return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...)
+ }
+
+ return true
+}
+
+// LessOrEqual asserts that the first element is less than or equal to the second
+//
+// assert.LessOrEqual(t, 1, 2)
+// assert.LessOrEqual(t, 2, 2)
+// assert.LessOrEqual(t, "a", "b")
+// assert.LessOrEqual(t, "b", "b")
+func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ e1Kind := reflect.ValueOf(e1).Kind()
+ e2Kind := reflect.ValueOf(e2).Kind()
+ if e1Kind != e2Kind {
+ return Fail(t, "Elements should be the same type", msgAndArgs...)
+ }
+
+ res, isComparable := compare(e1, e2, e1Kind)
+ if !isComparable {
+ return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
+ }
+
+ if res != 1 && res != 0 {
+ return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...)
+ }
+
+ return true
+}
diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go
new file mode 100644
index 000000000..044da8b01
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/assert/assertions.go
@@ -0,0 +1,1498 @@
+package assert
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "os"
+ "reflect"
+ "regexp"
+ "runtime"
+ "strings"
+ "time"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/davecgh/go-spew/spew"
+ "github.com/pmezard/go-difflib/difflib"
+ yaml "gopkg.in/yaml.v2"
+)
+
+//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl
+
+// TestingT is an interface wrapper around *testing.T
+type TestingT interface {
+ Errorf(format string, args ...interface{})
+}
+
+// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
+// for table driven tests.
+type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool
+
+// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
+// for table driven tests.
+type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool
+
+// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
+// for table driven tests.
+type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
+
+// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful
+// for table driven tests.
+type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
+
+// Comparison a custom function that returns true on success and false on failure
+type Comparison func() (success bool)
+
+/*
+ Helper functions
+*/
+
+// ObjectsAreEqual determines if two objects are considered equal.
+//
+// This function does no assertion of any kind.
+func ObjectsAreEqual(expected, actual interface{}) bool {
+ if expected == nil || actual == nil {
+ return expected == actual
+ }
+
+ exp, ok := expected.([]byte)
+ if !ok {
+ return reflect.DeepEqual(expected, actual)
+ }
+
+ act, ok := actual.([]byte)
+ if !ok {
+ return false
+ }
+ if exp == nil || act == nil {
+ return exp == nil && act == nil
+ }
+ return bytes.Equal(exp, act)
+}
+
+// ObjectsAreEqualValues gets whether two objects are equal, or if their
+// values are equal.
+func ObjectsAreEqualValues(expected, actual interface{}) bool {
+ if ObjectsAreEqual(expected, actual) {
+ return true
+ }
+
+ actualType := reflect.TypeOf(actual)
+ if actualType == nil {
+ return false
+ }
+ expectedValue := reflect.ValueOf(expected)
+ if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
+ // Attempt comparison after type conversion
+ return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual)
+ }
+
+ return false
+}
+
+/* CallerInfo is necessary because the assert functions use the testing object
+internally, causing it to print the file:line of the assert method, rather than where
+the problem actually occurred in calling code.*/
+
+// CallerInfo returns an array of strings containing the file and line number
+// of each stack frame leading from the current test to the assert call that
+// failed.
+func CallerInfo() []string {
+
+ pc := uintptr(0)
+ file := ""
+ line := 0
+ ok := false
+ name := ""
+
+ callers := []string{}
+ for i := 0; ; i++ {
+ pc, file, line, ok = runtime.Caller(i)
+ if !ok {
+ // The breaks below failed to terminate the loop, and we ran off the
+ // end of the call stack.
+ break
+ }
+
+ // This is a huge edge case, but it will panic if this is the case, see #180
+ if file == "" {
+ break
+ }
+
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ break
+ }
+ name = f.Name()
+
+ // testing.tRunner is the standard library function that calls
+ // tests. Subtests are called directly by tRunner, without going through
+ // the Test/Benchmark/Example function that contains the t.Run calls, so
+ // with subtests we should break when we hit tRunner, without adding it
+ // to the list of callers.
+ if name == "testing.tRunner" {
+ break
+ }
+
+ parts := strings.Split(file, "/")
+ file = parts[len(parts)-1]
+ if len(parts) > 1 {
+ dir := parts[len(parts)-2]
+ if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
+ callers = append(callers, fmt.Sprintf("%s:%d", file, line))
+ }
+ }
+
+ // Drop the package
+ segments := strings.Split(name, ".")
+ name = segments[len(segments)-1]
+ if isTest(name, "Test") ||
+ isTest(name, "Benchmark") ||
+ isTest(name, "Example") {
+ break
+ }
+ }
+
+ return callers
+}
+
+// Stolen from the `go test` tool.
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+func isTest(name, prefix string) bool {
+ if !strings.HasPrefix(name, prefix) {
+ return false
+ }
+ if len(name) == len(prefix) { // "Test" is ok
+ return true
+ }
+ rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
+ return !unicode.IsLower(rune)
+}
+
+func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
+ if len(msgAndArgs) == 0 || msgAndArgs == nil {
+ return ""
+ }
+ if len(msgAndArgs) == 1 {
+ msg := msgAndArgs[0]
+ if msgAsStr, ok := msg.(string); ok {
+ return msgAsStr
+ }
+ return fmt.Sprintf("%+v", msg)
+ }
+ if len(msgAndArgs) > 1 {
+ return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
+ }
+ return ""
+}
+
+// Aligns the provided message so that all lines after the first line start at the same location as the first line.
+// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab).
+// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the
+// basis on which the alignment occurs).
+func indentMessageLines(message string, longestLabelLen int) string {
+ outBuf := new(bytes.Buffer)
+
+ for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
+ // no need to align first line because it starts at the correct location (after the label)
+ if i != 0 {
+ // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
+ outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
+ }
+ outBuf.WriteString(scanner.Text())
+ }
+
+ return outBuf.String()
+}
+
+type failNower interface {
+ FailNow()
+}
+
+// FailNow fails test
+func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ Fail(t, failureMessage, msgAndArgs...)
+
+ // We cannot extend TestingT with FailNow() and
+ // maintain backwards compatibility, so we fallback
+ // to panicking when FailNow is not available in
+ // TestingT.
+ // See issue #263
+
+ if t, ok := t.(failNower); ok {
+ t.FailNow()
+ } else {
+ panic("test failed and t is missing `FailNow()`")
+ }
+ return false
+}
+
+// Fail reports a failure through
+func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ content := []labeledContent{
+ {"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")},
+ {"Error", failureMessage},
+ }
+
+ // Add test name if the Go version supports it
+ if n, ok := t.(interface {
+ Name() string
+ }); ok {
+ content = append(content, labeledContent{"Test", n.Name()})
+ }
+
+ message := messageFromMsgAndArgs(msgAndArgs...)
+ if len(message) > 0 {
+ content = append(content, labeledContent{"Messages", message})
+ }
+
+ t.Errorf("\n%s", ""+labeledOutput(content...))
+
+ return false
+}
+
+type labeledContent struct {
+ label string
+ content string
+}
+
+// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
+//
+// \t{{label}}:{{align_spaces}}\t{{content}}\n
+//
+// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
+// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
+// alignment is achieved, "\t{{content}}\n" is added for the output.
+//
+// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line.
+func labeledOutput(content ...labeledContent) string {
+ longestLabel := 0
+ for _, v := range content {
+ if len(v.label) > longestLabel {
+ longestLabel = len(v.label)
+ }
+ }
+ var output string
+ for _, v := range content {
+ output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n"
+ }
+ return output
+}
+
+// Implements asserts that an object is implemented by the specified interface.
+//
+// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
+func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ interfaceType := reflect.TypeOf(interfaceObject).Elem()
+
+ if object == nil {
+ return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...)
+ }
+ if !reflect.TypeOf(object).Implements(interfaceType) {
+ return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...)
+ }
+
+ return true
+}
+
+// IsType asserts that the specified objects are of the same type.
+func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {
+ return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...)
+ }
+
+ return true
+}
+
+// Equal asserts that two objects are equal.
+//
+// assert.Equal(t, 123, 123)
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses). Function equality
+// cannot be determined and will always fail.
+func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if err := validateEqualArgs(expected, actual); err != nil {
+ return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)",
+ expected, actual, err), msgAndArgs...)
+ }
+
+ if !ObjectsAreEqual(expected, actual) {
+ diff := diff(expected, actual)
+ expected, actual = formatUnequalValues(expected, actual)
+ return Fail(t, fmt.Sprintf("Not equal: \n"+
+ "expected: %s\n"+
+ "actual : %s%s", expected, actual, diff), msgAndArgs...)
+ }
+
+ return true
+
+}
+
+// Same asserts that two pointers reference the same object.
+//
+// assert.Same(t, ptr1, ptr2)
+//
+// Both arguments must be pointer variables. Pointer variable sameness is
+// determined based on the equality of both type and value.
+func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual)
+ if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr {
+ return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...)
+ }
+
+ expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual)
+ if expectedType != actualType {
+ return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v",
+ expectedType, actualType), msgAndArgs...)
+ }
+
+ if expected != actual {
+ return Fail(t, fmt.Sprintf("Not same: \n"+
+ "expected: %p %#v\n"+
+ "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...)
+ }
+
+ return true
+}
+
+// formatUnequalValues takes two values of arbitrary types and returns string
+// representations appropriate to be presented to the user.
+//
+// If the values are not of like type, the returned strings will be prefixed
+// with the type name, and the value will be enclosed in parenthesis similar
+// to a type conversion in the Go grammar.
+func formatUnequalValues(expected, actual interface{}) (e string, a string) {
+ if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
+ return fmt.Sprintf("%T(%#v)", expected, expected),
+ fmt.Sprintf("%T(%#v)", actual, actual)
+ }
+
+ return fmt.Sprintf("%#v", expected),
+ fmt.Sprintf("%#v", actual)
+}
+
+// EqualValues asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+// assert.EqualValues(t, uint32(123), int32(123))
+func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ if !ObjectsAreEqualValues(expected, actual) {
+ diff := diff(expected, actual)
+ expected, actual = formatUnequalValues(expected, actual)
+ return Fail(t, fmt.Sprintf("Not equal: \n"+
+ "expected: %s\n"+
+ "actual : %s%s", expected, actual, diff), msgAndArgs...)
+ }
+
+ return true
+
+}
+
+// Exactly asserts that two objects are equal in value and type.
+//
+// assert.Exactly(t, int32(123), int64(123))
+func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ aType := reflect.TypeOf(expected)
+ bType := reflect.TypeOf(actual)
+
+ if aType != bType {
+ return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
+ }
+
+ return Equal(t, expected, actual, msgAndArgs...)
+
+}
+
+// NotNil asserts that the specified object is not nil.
+//
+// assert.NotNil(t, err)
+func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if !isNil(object) {
+ return true
+ }
+ return Fail(t, "Expected value not to be nil.", msgAndArgs...)
+}
+
+// containsKind checks if a specified kind in the slice of kinds.
+func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
+ for i := 0; i < len(kinds); i++ {
+ if kind == kinds[i] {
+ return true
+ }
+ }
+
+ return false
+}
+
+// isNil checks if a specified object is nil or not, without Failing.
+func isNil(object interface{}) bool {
+ if object == nil {
+ return true
+ }
+
+ value := reflect.ValueOf(object)
+ kind := value.Kind()
+ isNilableKind := containsKind(
+ []reflect.Kind{
+ reflect.Chan, reflect.Func,
+ reflect.Interface, reflect.Map,
+ reflect.Ptr, reflect.Slice},
+ kind)
+
+ if isNilableKind && value.IsNil() {
+ return true
+ }
+
+ return false
+}
+
+// Nil asserts that the specified object is nil.
+//
+// assert.Nil(t, err)
+func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if isNil(object) {
+ return true
+ }
+ return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
+}
+
+// isEmpty gets whether the specified object is considered empty or not.
+func isEmpty(object interface{}) bool {
+
+ // get nil case out of the way
+ if object == nil {
+ return true
+ }
+
+ objValue := reflect.ValueOf(object)
+
+ switch objValue.Kind() {
+ // collection types are empty when they have no element
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
+ return objValue.Len() == 0
+ // pointers are empty if nil or if the value they point to is empty
+ case reflect.Ptr:
+ if objValue.IsNil() {
+ return true
+ }
+ deref := objValue.Elem().Interface()
+ return isEmpty(deref)
+ // for all other types, compare against the zero value
+ default:
+ zero := reflect.Zero(objValue.Type())
+ return reflect.DeepEqual(object, zero.Interface())
+ }
+}
+
+// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// assert.Empty(t, obj)
+func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ pass := isEmpty(object)
+ if !pass {
+ Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
+ }
+
+ return pass
+
+}
+
+// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if assert.NotEmpty(t, obj) {
+// assert.Equal(t, "two", obj[1])
+// }
+func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ pass := !isEmpty(object)
+ if !pass {
+ Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
+ }
+
+ return pass
+
+}
+
+// getLen try to get length of object.
+// return (false, 0) if impossible.
+func getLen(x interface{}) (ok bool, length int) {
+ v := reflect.ValueOf(x)
+ defer func() {
+ if e := recover(); e != nil {
+ ok = false
+ }
+ }()
+ return true, v.Len()
+}
+
+// Len asserts that the specified object has specific length.
+// Len also fails if the object has a type that len() not accept.
+//
+// assert.Len(t, mySlice, 3)
+func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ ok, l := getLen(object)
+ if !ok {
+ return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...)
+ }
+
+ if l != length {
+ return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
+ }
+ return true
+}
+
+// True asserts that the specified value is true.
+//
+// assert.True(t, myBool)
+func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if h, ok := t.(interface {
+ Helper()
+ }); ok {
+ h.Helper()
+ }
+
+ if value != true {
+ return Fail(t, "Should be true", msgAndArgs...)
+ }
+
+ return true
+
+}
+
+// False asserts that the specified value is false.
+//
+// assert.False(t, myBool)
+func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ if value != false {
+ return Fail(t, "Should be false", msgAndArgs...)
+ }
+
+ return true
+
+}
+
+// NotEqual asserts that the specified values are NOT equal.
+//
+// assert.NotEqual(t, obj1, obj2)
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses).
+func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if err := validateEqualArgs(expected, actual); err != nil {
+ return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)",
+ expected, actual, err), msgAndArgs...)
+ }
+
+ if ObjectsAreEqual(expected, actual) {
+ return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
+ }
+
+ return true
+
+}
+
+// containsElement try loop over the list check if the list includes the element.
+// return (false, false) if impossible.
+// return (true, false) if element was not found.
+// return (true, true) if element was found.
+func includeElement(list interface{}, element interface{}) (ok, found bool) {
+
+ listValue := reflect.ValueOf(list)
+ listKind := reflect.TypeOf(list).Kind()
+ defer func() {
+ if e := recover(); e != nil {
+ ok = false
+ found = false
+ }
+ }()
+
+ if listKind == reflect.String {
+ elementValue := reflect.ValueOf(element)
+ return true, strings.Contains(listValue.String(), elementValue.String())
+ }
+
+ if listKind == reflect.Map {
+ mapKeys := listValue.MapKeys()
+ for i := 0; i < len(mapKeys); i++ {
+ if ObjectsAreEqual(mapKeys[i].Interface(), element) {
+ return true, true
+ }
+ }
+ return true, false
+ }
+
+ for i := 0; i < listValue.Len(); i++ {
+ if ObjectsAreEqual(listValue.Index(i).Interface(), element) {
+ return true, true
+ }
+ }
+ return true, false
+
+}
+
+// Contains asserts that the specified string, list(array, slice...) or map contains the
+// specified substring or element.
+//
+// assert.Contains(t, "Hello World", "World")
+// assert.Contains(t, ["Hello", "World"], "World")
+// assert.Contains(t, {"Hello": "World"}, "Hello")
+func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ ok, found := includeElement(s, contains)
+ if !ok {
+ return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
+ }
+ if !found {
+ return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...)
+ }
+
+ return true
+
+}
+
+// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
+// specified substring or element.
+//
+// assert.NotContains(t, "Hello World", "Earth")
+// assert.NotContains(t, ["Hello", "World"], "Earth")
+// assert.NotContains(t, {"Hello": "World"}, "Earth")
+func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ ok, found := includeElement(s, contains)
+ if !ok {
+ return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
+ }
+ if found {
+ return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...)
+ }
+
+ return true
+
+}
+
+// Subset asserts that the specified list(array, slice...) contains all
+// elements given in the specified subset(array, slice...).
+//
+// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
+func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if subset == nil {
+ return true // we consider nil to be equal to the nil set
+ }
+
+ subsetValue := reflect.ValueOf(subset)
+ defer func() {
+ if e := recover(); e != nil {
+ ok = false
+ }
+ }()
+
+ listKind := reflect.TypeOf(list).Kind()
+ subsetKind := reflect.TypeOf(subset).Kind()
+
+ if listKind != reflect.Array && listKind != reflect.Slice {
+ return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
+ }
+
+ if subsetKind != reflect.Array && subsetKind != reflect.Slice {
+ return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
+ }
+
+ for i := 0; i < subsetValue.Len(); i++ {
+ element := subsetValue.Index(i).Interface()
+ ok, found := includeElement(list, element)
+ if !ok {
+ return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
+ }
+ if !found {
+ return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
+ }
+ }
+
+ return true
+}
+
+// NotSubset asserts that the specified list(array, slice...) contains not all
+// elements given in the specified subset(array, slice...).
+//
+// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
+func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if subset == nil {
+ return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
+ }
+
+ subsetValue := reflect.ValueOf(subset)
+ defer func() {
+ if e := recover(); e != nil {
+ ok = false
+ }
+ }()
+
+ listKind := reflect.TypeOf(list).Kind()
+ subsetKind := reflect.TypeOf(subset).Kind()
+
+ if listKind != reflect.Array && listKind != reflect.Slice {
+ return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
+ }
+
+ if subsetKind != reflect.Array && subsetKind != reflect.Slice {
+ return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
+ }
+
+ for i := 0; i < subsetValue.Len(); i++ {
+ element := subsetValue.Index(i).Interface()
+ ok, found := includeElement(list, element)
+ if !ok {
+ return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
+ }
+ if !found {
+ return true
+ }
+ }
+
+ return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
+}
+
+// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
+// the number of appearances of each of them in both lists should match.
+//
+// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
+func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if isEmpty(listA) && isEmpty(listB) {
+ return true
+ }
+
+ aKind := reflect.TypeOf(listA).Kind()
+ bKind := reflect.TypeOf(listB).Kind()
+
+ if aKind != reflect.Array && aKind != reflect.Slice {
+ return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
+ }
+
+ if bKind != reflect.Array && bKind != reflect.Slice {
+ return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
+ }
+
+ aValue := reflect.ValueOf(listA)
+ bValue := reflect.ValueOf(listB)
+
+ aLen := aValue.Len()
+ bLen := bValue.Len()
+
+ if aLen != bLen {
+ return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
+ }
+
+ // Mark indexes in bValue that we already used
+ visited := make([]bool, bLen)
+ for i := 0; i < aLen; i++ {
+ element := aValue.Index(i).Interface()
+ found := false
+ for j := 0; j < bLen; j++ {
+ if visited[j] {
+ continue
+ }
+ if ObjectsAreEqual(bValue.Index(j).Interface(), element) {
+ visited[j] = true
+ found = true
+ break
+ }
+ }
+ if !found {
+ return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
+ }
+ }
+
+ return true
+}
+
+// Condition uses a Comparison to assert a complex condition.
+func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ result := comp()
+ if !result {
+ Fail(t, "Condition failed!", msgAndArgs...)
+ }
+ return result
+}
+
+// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics
+// methods, and represents a simple func that takes no arguments, and returns nothing.
+type PanicTestFunc func()
+
+// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
+func didPanic(f PanicTestFunc) (bool, interface{}) {
+
+ didPanic := false
+ var message interface{}
+ func() {
+
+ defer func() {
+ if message = recover(); message != nil {
+ didPanic = true
+ }
+ }()
+
+ // call the target function
+ f()
+
+ }()
+
+ return didPanic, message
+
+}
+
+// Panics asserts that the code inside the specified PanicTestFunc panics.
+//
+// assert.Panics(t, func(){ GoCrazy() })
+func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
+ return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
+ }
+
+ return true
+}
+
+// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
+// the recovered panic value equals the expected panic value.
+//
+// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
+func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ funcDidPanic, panicValue := didPanic(f)
+ if !funcDidPanic {
+ return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
+ }
+ if panicValue != expected {
+ return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...)
+ }
+
+ return true
+}
+
+// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+// assert.NotPanics(t, func(){ RemainCalm() })
+func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
+ return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...)
+ }
+
+ return true
+}
+
+// WithinDuration asserts that the two times are within duration delta of each other.
+//
+// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
+func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ dt := expected.Sub(actual)
+ if dt < -delta || dt > delta {
+ return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
+ }
+
+ return true
+}
+
+func toFloat(x interface{}) (float64, bool) {
+ var xf float64
+ xok := true
+
+ switch xn := x.(type) {
+ case uint8:
+ xf = float64(xn)
+ case uint16:
+ xf = float64(xn)
+ case uint32:
+ xf = float64(xn)
+ case uint64:
+ xf = float64(xn)
+ case int:
+ xf = float64(xn)
+ case int8:
+ xf = float64(xn)
+ case int16:
+ xf = float64(xn)
+ case int32:
+ xf = float64(xn)
+ case int64:
+ xf = float64(xn)
+ case float32:
+ xf = float64(xn)
+ case float64:
+ xf = float64(xn)
+ case time.Duration:
+ xf = float64(xn)
+ default:
+ xok = false
+ }
+
+ return xf, xok
+}
+
+// InDelta asserts that the two numerals are within delta of each other.
+//
+// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
+func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ af, aok := toFloat(expected)
+ bf, bok := toFloat(actual)
+
+ if !aok || !bok {
+ return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...)
+ }
+
+ if math.IsNaN(af) {
+ return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...)
+ }
+
+ if math.IsNaN(bf) {
+ return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...)
+ }
+
+ dt := af - bf
+ if dt < -delta || dt > delta {
+ return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
+ }
+
+ return true
+}
+
+// InDeltaSlice is the same as InDelta, except it compares two slices.
+func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if expected == nil || actual == nil ||
+ reflect.TypeOf(actual).Kind() != reflect.Slice ||
+ reflect.TypeOf(expected).Kind() != reflect.Slice {
+ return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
+ }
+
+ actualSlice := reflect.ValueOf(actual)
+ expectedSlice := reflect.ValueOf(expected)
+
+ for i := 0; i < actualSlice.Len(); i++ {
+ result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta, msgAndArgs...)
+ if !result {
+ return result
+ }
+ }
+
+ return true
+}
+
+// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
+func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if expected == nil || actual == nil ||
+ reflect.TypeOf(actual).Kind() != reflect.Map ||
+ reflect.TypeOf(expected).Kind() != reflect.Map {
+ return Fail(t, "Arguments must be maps", msgAndArgs...)
+ }
+
+ expectedMap := reflect.ValueOf(expected)
+ actualMap := reflect.ValueOf(actual)
+
+ if expectedMap.Len() != actualMap.Len() {
+ return Fail(t, "Arguments must have the same number of keys", msgAndArgs...)
+ }
+
+ for _, k := range expectedMap.MapKeys() {
+ ev := expectedMap.MapIndex(k)
+ av := actualMap.MapIndex(k)
+
+ if !ev.IsValid() {
+ return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...)
+ }
+
+ if !av.IsValid() {
+ return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...)
+ }
+
+ if !InDelta(
+ t,
+ ev.Interface(),
+ av.Interface(),
+ delta,
+ msgAndArgs...,
+ ) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func calcRelativeError(expected, actual interface{}) (float64, error) {
+ af, aok := toFloat(expected)
+ if !aok {
+ return 0, fmt.Errorf("expected value %q cannot be converted to float", expected)
+ }
+ if af == 0 {
+ return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
+ }
+ bf, bok := toFloat(actual)
+ if !bok {
+ return 0, fmt.Errorf("actual value %q cannot be converted to float", actual)
+ }
+
+ return math.Abs(af-bf) / math.Abs(af), nil
+}
+
+// InEpsilon asserts that expected and actual have a relative error less than epsilon
+func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ actualEpsilon, err := calcRelativeError(expected, actual)
+ if err != nil {
+ return Fail(t, err.Error(), msgAndArgs...)
+ }
+ if actualEpsilon > epsilon {
+ return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+
+ " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...)
+ }
+
+ return true
+}
+
+// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
+func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if expected == nil || actual == nil ||
+ reflect.TypeOf(actual).Kind() != reflect.Slice ||
+ reflect.TypeOf(expected).Kind() != reflect.Slice {
+ return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
+ }
+
+ actualSlice := reflect.ValueOf(actual)
+ expectedSlice := reflect.ValueOf(expected)
+
+ for i := 0; i < actualSlice.Len(); i++ {
+ result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon)
+ if !result {
+ return result
+ }
+ }
+
+ return true
+}
+
+/*
+ Errors
+*/
+
+// NoError asserts that a function returned no error (i.e. `nil`).
+//
+// actualObj, err := SomeFunction()
+// if assert.NoError(t, err) {
+// assert.Equal(t, expectedObj, actualObj)
+// }
+func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if err != nil {
+ return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
+ }
+
+ return true
+}
+
+// Error asserts that a function returned an error (i.e. not `nil`).
+//
+// actualObj, err := SomeFunction()
+// if assert.Error(t, err) {
+// assert.Equal(t, expectedError, err)
+// }
+func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ if err == nil {
+ return Fail(t, "An error is expected but got nil.", msgAndArgs...)
+ }
+
+ return true
+}
+
+// EqualError asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+// actualObj, err := SomeFunction()
+// assert.EqualError(t, err, expectedErrorString)
+func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if !Error(t, theError, msgAndArgs...) {
+ return false
+ }
+ expected := errString
+ actual := theError.Error()
+ // don't need to use deep equals here, we know they are both strings
+ if expected != actual {
+ return Fail(t, fmt.Sprintf("Error message not equal:\n"+
+ "expected: %q\n"+
+ "actual : %q", expected, actual), msgAndArgs...)
+ }
+ return true
+}
+
+// matchRegexp return true if a specified regexp matches a string.
+func matchRegexp(rx interface{}, str interface{}) bool {
+
+ var r *regexp.Regexp
+ if rr, ok := rx.(*regexp.Regexp); ok {
+ r = rr
+ } else {
+ r = regexp.MustCompile(fmt.Sprint(rx))
+ }
+
+ return (r.FindStringIndex(fmt.Sprint(str)) != nil)
+
+}
+
+// Regexp asserts that a specified regexp matches a string.
+//
+// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
+// assert.Regexp(t, "start...$", "it's not starting")
+func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ match := matchRegexp(rx, str)
+
+ if !match {
+ Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...)
+ }
+
+ return match
+}
+
+// NotRegexp asserts that a specified regexp does not match a string.
+//
+// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
+// assert.NotRegexp(t, "^start", "it's not starting")
+func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ match := matchRegexp(rx, str)
+
+ if match {
+ Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...)
+ }
+
+ return !match
+
+}
+
+// Zero asserts that i is the zero value for its type.
+func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
+ return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
+ }
+ return true
+}
+
+// NotZero asserts that i is not the zero value for its type.
+func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
+ return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
+ }
+ return true
+}
+
+// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
+func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ info, err := os.Lstat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
+ }
+ return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
+ }
+ if info.IsDir() {
+ return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...)
+ }
+ return true
+}
+
+// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
+func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ info, err := os.Lstat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
+ }
+ return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
+ }
+ if !info.IsDir() {
+ return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...)
+ }
+ return true
+}
+
+// JSONEq asserts that two JSON strings are equivalent.
+//
+// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
+func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ var expectedJSONAsInterface, actualJSONAsInterface interface{}
+
+ if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil {
+ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...)
+ }
+
+ if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil {
+ return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...)
+ }
+
+ return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...)
+}
+
+// YAMLEq asserts that two YAML strings are equivalent.
+func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ var expectedYAMLAsInterface, actualYAMLAsInterface interface{}
+
+ if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil {
+ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...)
+ }
+
+ if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil {
+ return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...)
+ }
+
+ return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...)
+}
+
+func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
+ t := reflect.TypeOf(v)
+ k := t.Kind()
+
+ if k == reflect.Ptr {
+ t = t.Elem()
+ k = t.Kind()
+ }
+ return t, k
+}
+
+// diff returns a diff of both values as long as both are of the same type and
+// are a struct, map, slice, array or string. Otherwise it returns an empty string.
+func diff(expected interface{}, actual interface{}) string {
+ if expected == nil || actual == nil {
+ return ""
+ }
+
+ et, ek := typeAndKind(expected)
+ at, _ := typeAndKind(actual)
+
+ if et != at {
+ return ""
+ }
+
+ if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String {
+ return ""
+ }
+
+ var e, a string
+ if et != reflect.TypeOf("") {
+ e = spewConfig.Sdump(expected)
+ a = spewConfig.Sdump(actual)
+ } else {
+ e = reflect.ValueOf(expected).String()
+ a = reflect.ValueOf(actual).String()
+ }
+
+ diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
+ A: difflib.SplitLines(e),
+ B: difflib.SplitLines(a),
+ FromFile: "Expected",
+ FromDate: "",
+ ToFile: "Actual",
+ ToDate: "",
+ Context: 1,
+ })
+
+ return "\n\nDiff:\n" + diff
+}
+
+// validateEqualArgs checks whether provided arguments can be safely used in the
+// Equal/NotEqual functions.
+func validateEqualArgs(expected, actual interface{}) error {
+ if isFunction(expected) || isFunction(actual) {
+ return errors.New("cannot take func type as argument")
+ }
+ return nil
+}
+
+func isFunction(arg interface{}) bool {
+ if arg == nil {
+ return false
+ }
+ return reflect.TypeOf(arg).Kind() == reflect.Func
+}
+
+var spewConfig = spew.ConfigState{
+ Indent: " ",
+ DisablePointerAddresses: true,
+ DisableCapacities: true,
+ SortKeys: true,
+}
+
+type tHelper interface {
+ Helper()
+}
+
+// Eventually asserts that given condition will be met in waitFor time,
+// periodically checking target function each tick.
+//
+// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
+func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+
+ timer := time.NewTimer(waitFor)
+ ticker := time.NewTicker(tick)
+ checkPassed := make(chan bool)
+ defer timer.Stop()
+ defer ticker.Stop()
+ defer close(checkPassed)
+ for {
+ select {
+ case <-timer.C:
+ return Fail(t, "Condition never satisfied", msgAndArgs...)
+ case result := <-checkPassed:
+ if result {
+ return true
+ }
+ case <-ticker.C:
+ go func() {
+ checkPassed <- condition()
+ }()
+ }
+ }
+}
diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go
new file mode 100644
index 000000000..c9dccc4d6
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/assert/doc.go
@@ -0,0 +1,45 @@
+// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
+//
+// Example Usage
+//
+// The following is a complete example using assert in a standard test function:
+// import (
+// "testing"
+// "github.com/stretchr/testify/assert"
+// )
+//
+// func TestSomething(t *testing.T) {
+//
+// var a string = "Hello"
+// var b string = "Hello"
+//
+// assert.Equal(t, a, b, "The two words should be the same.")
+//
+// }
+//
+// if you assert many times, use the format below:
+//
+// import (
+// "testing"
+// "github.com/stretchr/testify/assert"
+// )
+//
+// func TestSomething(t *testing.T) {
+// assert := assert.New(t)
+//
+// var a string = "Hello"
+// var b string = "Hello"
+//
+// assert.Equal(a, b, "The two words should be the same.")
+// }
+//
+// Assertions
+//
+// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
+// All assertion functions take, as the first argument, the `*testing.T` object provided by the
+// testing framework. This allows the assertion funcs to write the failings and other details to
+// the correct place.
+//
+// Every assertion function also takes an optional string message as the final argument,
+// allowing custom error messages to be appended to the message the assertion method outputs.
+package assert
diff --git a/vendor/github.com/stretchr/testify/assert/errors.go b/vendor/github.com/stretchr/testify/assert/errors.go
new file mode 100644
index 000000000..ac9dc9d1d
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/assert/errors.go
@@ -0,0 +1,10 @@
+package assert
+
+import (
+ "errors"
+)
+
+// AnError is an error instance useful for testing. If the code does not care
+// about error specifics, and only needs to return the error for example, this
+// error should be used to make the test code more readable.
+var AnError = errors.New("assert.AnError general error for testing")
diff --git a/vendor/github.com/stretchr/testify/assert/forward_assertions.go b/vendor/github.com/stretchr/testify/assert/forward_assertions.go
new file mode 100644
index 000000000..9ad56851d
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/assert/forward_assertions.go
@@ -0,0 +1,16 @@
+package assert
+
+// Assertions provides assertion methods around the
+// TestingT interface.
+type Assertions struct {
+ t TestingT
+}
+
+// New makes a new Assertions object for the specified TestingT.
+func New(t TestingT) *Assertions {
+ return &Assertions{
+ t: t,
+ }
+}
+
+//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs
diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go
new file mode 100644
index 000000000..df46fa777
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go
@@ -0,0 +1,143 @@
+package assert
+
+import (
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "strings"
+)
+
+// httpCode is a helper that returns HTTP code of the response. It returns -1 and
+// an error if building a new request fails.
+func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) {
+ w := httptest.NewRecorder()
+ req, err := http.NewRequest(method, url, nil)
+ if err != nil {
+ return -1, err
+ }
+ req.URL.RawQuery = values.Encode()
+ handler(w, req)
+ return w.Code, nil
+}
+
+// HTTPSuccess asserts that a specified handler returns a success status code.
+//
+// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ code, err := httpCode(handler, method, url, values)
+ if err != nil {
+ Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
+ return false
+ }
+
+ isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent
+ if !isSuccessCode {
+ Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code))
+ }
+
+ return isSuccessCode
+}
+
+// HTTPRedirect asserts that a specified handler returns a redirect status code.
+//
+// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ code, err := httpCode(handler, method, url, values)
+ if err != nil {
+ Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
+ return false
+ }
+
+ isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
+ if !isRedirectCode {
+ Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code))
+ }
+
+ return isRedirectCode
+}
+
+// HTTPError asserts that a specified handler returns an error status code.
+//
+// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ code, err := httpCode(handler, method, url, values)
+ if err != nil {
+ Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
+ return false
+ }
+
+ isErrorCode := code >= http.StatusBadRequest
+ if !isErrorCode {
+ Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code))
+ }
+
+ return isErrorCode
+}
+
+// HTTPBody is a helper that returns HTTP body of the response. It returns
+// empty string if building a new request fails.
+func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
+ w := httptest.NewRecorder()
+ req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
+ if err != nil {
+ return ""
+ }
+ handler(w, req)
+ return w.Body.String()
+}
+
+// HTTPBodyContains asserts that a specified handler returns a
+// body that contains a string.
+//
+// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ body := HTTPBody(handler, method, url, values)
+
+ contains := strings.Contains(body, fmt.Sprint(str))
+ if !contains {
+ Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
+ }
+
+ return contains
+}
+
+// HTTPBodyNotContains asserts that a specified handler returns a
+// body that does not contain a string.
+//
+// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ body := HTTPBody(handler, method, url, values)
+
+ contains := strings.Contains(body, fmt.Sprint(str))
+ if contains {
+ Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
+ }
+
+ return !contains
+}
diff --git a/vendor/github.com/stretchr/testify/go.mod b/vendor/github.com/stretchr/testify/go.mod
new file mode 100644
index 000000000..50536488c
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/go.mod
@@ -0,0 +1,8 @@
+module github.com/stretchr/testify
+
+require (
+ github.com/davecgh/go-spew v1.1.0
+ github.com/pmezard/go-difflib v1.0.0
+ github.com/stretchr/objx v0.1.0
+ gopkg.in/yaml.v2 v2.2.2
+)
diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go
new file mode 100644
index 000000000..169de3922
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/require/doc.go
@@ -0,0 +1,28 @@
+// Package require implements the same assertions as the `assert` package but
+// stops test execution when a test fails.
+//
+// Example Usage
+//
+// The following is a complete example using require in a standard test function:
+// import (
+// "testing"
+// "github.com/stretchr/testify/require"
+// )
+//
+// func TestSomething(t *testing.T) {
+//
+// var a string = "Hello"
+// var b string = "Hello"
+//
+// require.Equal(t, a, b, "The two words should be the same.")
+//
+// }
+//
+// Assertions
+//
+// The `require` package have same global functions as in the `assert` package,
+// but instead of returning a boolean result they call `t.FailNow()`.
+//
+// Every assertion function also takes an optional string message as the final argument,
+// allowing custom error messages to be appended to the message the assertion method outputs.
+package require
diff --git a/vendor/github.com/stretchr/testify/require/forward_requirements.go b/vendor/github.com/stretchr/testify/require/forward_requirements.go
new file mode 100644
index 000000000..ac71d4058
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/require/forward_requirements.go
@@ -0,0 +1,16 @@
+package require
+
+// Assertions provides assertion methods around the
+// TestingT interface.
+type Assertions struct {
+ t TestingT
+}
+
+// New makes a new Assertions object for the specified TestingT.
+func New(t TestingT) *Assertions {
+ return &Assertions{
+ t: t,
+ }
+}
+
+//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl -include-format-funcs
diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go
new file mode 100644
index 000000000..c5903f5db
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/require/require.go
@@ -0,0 +1,1433 @@
+/*
+* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
+* THIS FILE MUST NOT BE EDITED BY HAND
+ */
+
+package require
+
+import (
+ assert "github.com/stretchr/testify/assert"
+ http "net/http"
+ url "net/url"
+ time "time"
+)
+
+// Condition uses a Comparison to assert a complex condition.
+func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Condition(t, comp, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Conditionf uses a Comparison to assert a complex condition.
+func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Conditionf(t, comp, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Contains asserts that the specified string, list(array, slice...) or map contains the
+// specified substring or element.
+//
+// assert.Contains(t, "Hello World", "World")
+// assert.Contains(t, ["Hello", "World"], "World")
+// assert.Contains(t, {"Hello": "World"}, "Hello")
+func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Contains(t, s, contains, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Containsf asserts that the specified string, list(array, slice...) or map contains the
+// specified substring or element.
+//
+// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
+// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
+// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
+func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Containsf(t, s, contains, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
+func DirExists(t TestingT, path string, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.DirExists(t, path, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
+func DirExistsf(t TestingT, path string, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.DirExistsf(t, path, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
+// the number of appearances of each of them in both lists should match.
+//
+// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
+func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.ElementsMatch(t, listA, listB, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
+// the number of appearances of each of them in both lists should match.
+//
+// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
+func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.ElementsMatchf(t, listA, listB, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// assert.Empty(t, obj)
+func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Empty(t, object, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// assert.Emptyf(t, obj, "error message %s", "formatted")
+func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Emptyf(t, object, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Equal asserts that two objects are equal.
+//
+// assert.Equal(t, 123, 123)
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses). Function equality
+// cannot be determined and will always fail.
+func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Equal(t, expected, actual, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// EqualError asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+// actualObj, err := SomeFunction()
+// assert.EqualError(t, err, expectedErrorString)
+func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.EqualError(t, theError, errString, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+// actualObj, err := SomeFunction()
+// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
+func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.EqualErrorf(t, theError, errString, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// EqualValues asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+// assert.EqualValues(t, uint32(123), int32(123))
+func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.EqualValues(t, expected, actual, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// EqualValuesf asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
+func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.EqualValuesf(t, expected, actual, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Equalf asserts that two objects are equal.
+//
+// assert.Equalf(t, 123, 123, "error message %s", "formatted")
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses). Function equality
+// cannot be determined and will always fail.
+func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Equalf(t, expected, actual, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Error asserts that a function returned an error (i.e. not `nil`).
+//
+// actualObj, err := SomeFunction()
+// if assert.Error(t, err) {
+// assert.Equal(t, expectedError, err)
+// }
+func Error(t TestingT, err error, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Error(t, err, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Errorf asserts that a function returned an error (i.e. not `nil`).
+//
+// actualObj, err := SomeFunction()
+// if assert.Errorf(t, err, "error message %s", "formatted") {
+// assert.Equal(t, expectedErrorf, err)
+// }
+func Errorf(t TestingT, err error, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Errorf(t, err, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Eventually asserts that given condition will be met in waitFor time,
+// periodically checking target function each tick.
+//
+// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
+func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
+ if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) {
+ return
+ }
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ t.FailNow()
+}
+
+// Eventuallyf asserts that given condition will be met in waitFor time,
+// periodically checking target function each tick.
+//
+// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
+func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
+ if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) {
+ return
+ }
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ t.FailNow()
+}
+
+// Exactly asserts that two objects are equal in value and type.
+//
+// assert.Exactly(t, int32(123), int64(123))
+func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Exactly(t, expected, actual, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Exactlyf asserts that two objects are equal in value and type.
+//
+// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
+func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Exactlyf(t, expected, actual, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Fail reports a failure through
+func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Fail(t, failureMessage, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// FailNow fails test
+func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.FailNow(t, failureMessage, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// FailNowf fails test
+func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.FailNowf(t, failureMessage, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Failf reports a failure through
+func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Failf(t, failureMessage, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// False asserts that the specified value is false.
+//
+// assert.False(t, myBool)
+func False(t TestingT, value bool, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.False(t, value, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Falsef asserts that the specified value is false.
+//
+// assert.Falsef(t, myBool, "error message %s", "formatted")
+func Falsef(t TestingT, value bool, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Falsef(t, value, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
+func FileExists(t TestingT, path string, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.FileExists(t, path, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
+func FileExistsf(t TestingT, path string, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.FileExistsf(t, path, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Greater asserts that the first element is greater than the second
+//
+// assert.Greater(t, 2, 1)
+// assert.Greater(t, float64(2), float64(1))
+// assert.Greater(t, "b", "a")
+func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Greater(t, e1, e2, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// GreaterOrEqual asserts that the first element is greater than or equal to the second
+//
+// assert.GreaterOrEqual(t, 2, 1)
+// assert.GreaterOrEqual(t, 2, 2)
+// assert.GreaterOrEqual(t, "b", "a")
+// assert.GreaterOrEqual(t, "b", "b")
+func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.GreaterOrEqual(t, e1, e2, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// GreaterOrEqualf asserts that the first element is greater than or equal to the second
+//
+// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
+// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
+// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
+// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
+func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.GreaterOrEqualf(t, e1, e2, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Greaterf asserts that the first element is greater than the second
+//
+// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
+// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1))
+// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
+func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Greaterf(t, e1, e2, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPBodyContains asserts that a specified handler returns a
+// body that contains a string.
+//
+// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPBodyContainsf asserts that a specified handler returns a
+// body that contains a string.
+//
+// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPBodyNotContains asserts that a specified handler returns a
+// body that does not contain a string.
+//
+// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPBodyNotContainsf asserts that a specified handler returns a
+// body that does not contain a string.
+//
+// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPError asserts that a specified handler returns an error status code.
+//
+// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPErrorf asserts that a specified handler returns an error status code.
+//
+// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
+func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPRedirect asserts that a specified handler returns a redirect status code.
+//
+// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPRedirectf asserts that a specified handler returns a redirect status code.
+//
+// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
+func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPSuccess asserts that a specified handler returns a success status code.
+//
+// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// HTTPSuccessf asserts that a specified handler returns a success status code.
+//
+// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Implements asserts that an object is implemented by the specified interface.
+//
+// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
+func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Implements(t, interfaceObject, object, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Implementsf asserts that an object is implemented by the specified interface.
+//
+// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
+func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Implementsf(t, interfaceObject, object, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InDelta asserts that the two numerals are within delta of each other.
+//
+// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
+func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InDelta(t, expected, actual, delta, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
+func InDeltaMapValues(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
+func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InDeltaSlice is the same as InDelta, except it compares two slices.
+func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InDeltaSlicef is the same as InDelta, except it compares two slices.
+func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InDeltaf asserts that the two numerals are within delta of each other.
+//
+// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
+func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InDeltaf(t, expected, actual, delta, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InEpsilon asserts that expected and actual have a relative error less than epsilon
+func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
+func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
+func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// InEpsilonf asserts that expected and actual have a relative error less than epsilon
+func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// IsType asserts that the specified objects are of the same type.
+func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.IsType(t, expectedType, object, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// IsTypef asserts that the specified objects are of the same type.
+func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.IsTypef(t, expectedType, object, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// JSONEq asserts that two JSON strings are equivalent.
+//
+// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
+func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.JSONEq(t, expected, actual, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// JSONEqf asserts that two JSON strings are equivalent.
+//
+// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
+func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.JSONEqf(t, expected, actual, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// YAMLEq asserts that two YAML strings are equivalent.
+func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.YAMLEq(t, expected, actual, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// YAMLEqf asserts that two YAML strings are equivalent.
+func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.YAMLEqf(t, expected, actual, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Len asserts that the specified object has specific length.
+// Len also fails if the object has a type that len() not accept.
+//
+// assert.Len(t, mySlice, 3)
+func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Len(t, object, length, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Lenf asserts that the specified object has specific length.
+// Lenf also fails if the object has a type that len() not accept.
+//
+// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
+func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Lenf(t, object, length, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Less asserts that the first element is less than the second
+//
+// assert.Less(t, 1, 2)
+// assert.Less(t, float64(1), float64(2))
+// assert.Less(t, "a", "b")
+func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Less(t, e1, e2, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// LessOrEqual asserts that the first element is less than or equal to the second
+//
+// assert.LessOrEqual(t, 1, 2)
+// assert.LessOrEqual(t, 2, 2)
+// assert.LessOrEqual(t, "a", "b")
+// assert.LessOrEqual(t, "b", "b")
+func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.LessOrEqual(t, e1, e2, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// LessOrEqualf asserts that the first element is less than or equal to the second
+//
+// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
+// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
+// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
+// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
+func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.LessOrEqualf(t, e1, e2, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Lessf asserts that the first element is less than the second
+//
+// assert.Lessf(t, 1, 2, "error message %s", "formatted")
+// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2))
+// assert.Lessf(t, "a", "b", "error message %s", "formatted")
+func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Lessf(t, e1, e2, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Nil asserts that the specified object is nil.
+//
+// assert.Nil(t, err)
+func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Nil(t, object, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Nilf asserts that the specified object is nil.
+//
+// assert.Nilf(t, err, "error message %s", "formatted")
+func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Nilf(t, object, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NoError asserts that a function returned no error (i.e. `nil`).
+//
+// actualObj, err := SomeFunction()
+// if assert.NoError(t, err) {
+// assert.Equal(t, expectedObj, actualObj)
+// }
+func NoError(t TestingT, err error, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NoError(t, err, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NoErrorf asserts that a function returned no error (i.e. `nil`).
+//
+// actualObj, err := SomeFunction()
+// if assert.NoErrorf(t, err, "error message %s", "formatted") {
+// assert.Equal(t, expectedObj, actualObj)
+// }
+func NoErrorf(t TestingT, err error, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NoErrorf(t, err, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
+// specified substring or element.
+//
+// assert.NotContains(t, "Hello World", "Earth")
+// assert.NotContains(t, ["Hello", "World"], "Earth")
+// assert.NotContains(t, {"Hello": "World"}, "Earth")
+func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotContains(t, s, contains, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
+// specified substring or element.
+//
+// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
+// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
+// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
+func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotContainsf(t, s, contains, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if assert.NotEmpty(t, obj) {
+// assert.Equal(t, "two", obj[1])
+// }
+func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotEmpty(t, object, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
+// assert.Equal(t, "two", obj[1])
+// }
+func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotEmptyf(t, object, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotEqual asserts that the specified values are NOT equal.
+//
+// assert.NotEqual(t, obj1, obj2)
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses).
+func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotEqual(t, expected, actual, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotEqualf asserts that the specified values are NOT equal.
+//
+// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses).
+func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotEqualf(t, expected, actual, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotNil asserts that the specified object is not nil.
+//
+// assert.NotNil(t, err)
+func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotNil(t, object, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotNilf asserts that the specified object is not nil.
+//
+// assert.NotNilf(t, err, "error message %s", "formatted")
+func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotNilf(t, object, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+// assert.NotPanics(t, func(){ RemainCalm() })
+func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotPanics(t, f, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
+func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotPanicsf(t, f, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotRegexp asserts that a specified regexp does not match a string.
+//
+// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
+// assert.NotRegexp(t, "^start", "it's not starting")
+func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotRegexp(t, rx, str, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotRegexpf asserts that a specified regexp does not match a string.
+//
+// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
+// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
+func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotRegexpf(t, rx, str, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotSubset asserts that the specified list(array, slice...) contains not all
+// elements given in the specified subset(array, slice...).
+//
+// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
+func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotSubset(t, list, subset, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotSubsetf asserts that the specified list(array, slice...) contains not all
+// elements given in the specified subset(array, slice...).
+//
+// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
+func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotSubsetf(t, list, subset, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotZero asserts that i is not the zero value for its type.
+func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotZero(t, i, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// NotZerof asserts that i is not the zero value for its type.
+func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.NotZerof(t, i, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Panics asserts that the code inside the specified PanicTestFunc panics.
+//
+// assert.Panics(t, func(){ GoCrazy() })
+func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Panics(t, f, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
+// the recovered panic value equals the expected panic value.
+//
+// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
+func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.PanicsWithValue(t, expected, f, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
+// the recovered panic value equals the expected panic value.
+//
+// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
+func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.PanicsWithValuef(t, expected, f, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Panicsf asserts that the code inside the specified PanicTestFunc panics.
+//
+// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
+func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Panicsf(t, f, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Regexp asserts that a specified regexp matches a string.
+//
+// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
+// assert.Regexp(t, "start...$", "it's not starting")
+func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Regexp(t, rx, str, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Regexpf asserts that a specified regexp matches a string.
+//
+// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
+// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
+func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Regexpf(t, rx, str, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Same asserts that two pointers reference the same object.
+//
+// assert.Same(t, ptr1, ptr2)
+//
+// Both arguments must be pointer variables. Pointer variable sameness is
+// determined based on the equality of both type and value.
+func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Same(t, expected, actual, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Samef asserts that two pointers reference the same object.
+//
+// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
+//
+// Both arguments must be pointer variables. Pointer variable sameness is
+// determined based on the equality of both type and value.
+func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Samef(t, expected, actual, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Subset asserts that the specified list(array, slice...) contains all
+// elements given in the specified subset(array, slice...).
+//
+// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
+func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Subset(t, list, subset, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Subsetf asserts that the specified list(array, slice...) contains all
+// elements given in the specified subset(array, slice...).
+//
+// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
+func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Subsetf(t, list, subset, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// True asserts that the specified value is true.
+//
+// assert.True(t, myBool)
+func True(t TestingT, value bool, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.True(t, value, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Truef asserts that the specified value is true.
+//
+// assert.Truef(t, myBool, "error message %s", "formatted")
+func Truef(t TestingT, value bool, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Truef(t, value, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// WithinDuration asserts that the two times are within duration delta of each other.
+//
+// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
+func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// WithinDurationf asserts that the two times are within duration delta of each other.
+//
+// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
+func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.WithinDurationf(t, expected, actual, delta, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Zero asserts that i is the zero value for its type.
+func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Zero(t, i, msgAndArgs...) {
+ return
+ }
+ t.FailNow()
+}
+
+// Zerof asserts that i is the zero value for its type.
+func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ if assert.Zerof(t, i, msg, args...) {
+ return
+ }
+ t.FailNow()
+}
diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go
new file mode 100644
index 000000000..804fae035
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/require/require_forward.go
@@ -0,0 +1,1121 @@
+/*
+* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
+* THIS FILE MUST NOT BE EDITED BY HAND
+ */
+
+package require
+
+import (
+ assert "github.com/stretchr/testify/assert"
+ http "net/http"
+ url "net/url"
+ time "time"
+)
+
+// Condition uses a Comparison to assert a complex condition.
+func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Condition(a.t, comp, msgAndArgs...)
+}
+
+// Conditionf uses a Comparison to assert a complex condition.
+func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Conditionf(a.t, comp, msg, args...)
+}
+
+// Contains asserts that the specified string, list(array, slice...) or map contains the
+// specified substring or element.
+//
+// a.Contains("Hello World", "World")
+// a.Contains(["Hello", "World"], "World")
+// a.Contains({"Hello": "World"}, "Hello")
+func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Contains(a.t, s, contains, msgAndArgs...)
+}
+
+// Containsf asserts that the specified string, list(array, slice...) or map contains the
+// specified substring or element.
+//
+// a.Containsf("Hello World", "World", "error message %s", "formatted")
+// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted")
+// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted")
+func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Containsf(a.t, s, contains, msg, args...)
+}
+
+// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
+func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ DirExists(a.t, path, msgAndArgs...)
+}
+
+// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
+func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ DirExistsf(a.t, path, msg, args...)
+}
+
+// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
+// the number of appearances of each of them in both lists should match.
+//
+// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2])
+func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ ElementsMatch(a.t, listA, listB, msgAndArgs...)
+}
+
+// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
+// the number of appearances of each of them in both lists should match.
+//
+// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
+func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ ElementsMatchf(a.t, listA, listB, msg, args...)
+}
+
+// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// a.Empty(obj)
+func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Empty(a.t, object, msgAndArgs...)
+}
+
+// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// a.Emptyf(obj, "error message %s", "formatted")
+func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Emptyf(a.t, object, msg, args...)
+}
+
+// Equal asserts that two objects are equal.
+//
+// a.Equal(123, 123)
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses). Function equality
+// cannot be determined and will always fail.
+func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Equal(a.t, expected, actual, msgAndArgs...)
+}
+
+// EqualError asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+// actualObj, err := SomeFunction()
+// a.EqualError(err, expectedErrorString)
+func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ EqualError(a.t, theError, errString, msgAndArgs...)
+}
+
+// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+// actualObj, err := SomeFunction()
+// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted")
+func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ EqualErrorf(a.t, theError, errString, msg, args...)
+}
+
+// EqualValues asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+// a.EqualValues(uint32(123), int32(123))
+func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ EqualValues(a.t, expected, actual, msgAndArgs...)
+}
+
+// EqualValuesf asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
+func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ EqualValuesf(a.t, expected, actual, msg, args...)
+}
+
+// Equalf asserts that two objects are equal.
+//
+// a.Equalf(123, 123, "error message %s", "formatted")
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses). Function equality
+// cannot be determined and will always fail.
+func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Equalf(a.t, expected, actual, msg, args...)
+}
+
+// Error asserts that a function returned an error (i.e. not `nil`).
+//
+// actualObj, err := SomeFunction()
+// if a.Error(err) {
+// assert.Equal(t, expectedError, err)
+// }
+func (a *Assertions) Error(err error, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Error(a.t, err, msgAndArgs...)
+}
+
+// Errorf asserts that a function returned an error (i.e. not `nil`).
+//
+// actualObj, err := SomeFunction()
+// if a.Errorf(err, "error message %s", "formatted") {
+// assert.Equal(t, expectedErrorf, err)
+// }
+func (a *Assertions) Errorf(err error, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Errorf(a.t, err, msg, args...)
+}
+
+// Eventually asserts that given condition will be met in waitFor time,
+// periodically checking target function each tick.
+//
+// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond)
+func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
+}
+
+// Eventuallyf asserts that given condition will be met in waitFor time,
+// periodically checking target function each tick.
+//
+// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
+func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Eventuallyf(a.t, condition, waitFor, tick, msg, args...)
+}
+
+// Exactly asserts that two objects are equal in value and type.
+//
+// a.Exactly(int32(123), int64(123))
+func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Exactly(a.t, expected, actual, msgAndArgs...)
+}
+
+// Exactlyf asserts that two objects are equal in value and type.
+//
+// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
+func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Exactlyf(a.t, expected, actual, msg, args...)
+}
+
+// Fail reports a failure through
+func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Fail(a.t, failureMessage, msgAndArgs...)
+}
+
+// FailNow fails test
+func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ FailNow(a.t, failureMessage, msgAndArgs...)
+}
+
+// FailNowf fails test
+func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ FailNowf(a.t, failureMessage, msg, args...)
+}
+
+// Failf reports a failure through
+func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Failf(a.t, failureMessage, msg, args...)
+}
+
+// False asserts that the specified value is false.
+//
+// a.False(myBool)
+func (a *Assertions) False(value bool, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ False(a.t, value, msgAndArgs...)
+}
+
+// Falsef asserts that the specified value is false.
+//
+// a.Falsef(myBool, "error message %s", "formatted")
+func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Falsef(a.t, value, msg, args...)
+}
+
+// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
+func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ FileExists(a.t, path, msgAndArgs...)
+}
+
+// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
+func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ FileExistsf(a.t, path, msg, args...)
+}
+
+// Greater asserts that the first element is greater than the second
+//
+// a.Greater(2, 1)
+// a.Greater(float64(2), float64(1))
+// a.Greater("b", "a")
+func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Greater(a.t, e1, e2, msgAndArgs...)
+}
+
+// GreaterOrEqual asserts that the first element is greater than or equal to the second
+//
+// a.GreaterOrEqual(2, 1)
+// a.GreaterOrEqual(2, 2)
+// a.GreaterOrEqual("b", "a")
+// a.GreaterOrEqual("b", "b")
+func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ GreaterOrEqual(a.t, e1, e2, msgAndArgs...)
+}
+
+// GreaterOrEqualf asserts that the first element is greater than or equal to the second
+//
+// a.GreaterOrEqualf(2, 1, "error message %s", "formatted")
+// a.GreaterOrEqualf(2, 2, "error message %s", "formatted")
+// a.GreaterOrEqualf("b", "a", "error message %s", "formatted")
+// a.GreaterOrEqualf("b", "b", "error message %s", "formatted")
+func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ GreaterOrEqualf(a.t, e1, e2, msg, args...)
+}
+
+// Greaterf asserts that the first element is greater than the second
+//
+// a.Greaterf(2, 1, "error message %s", "formatted")
+// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1))
+// a.Greaterf("b", "a", "error message %s", "formatted")
+func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Greaterf(a.t, e1, e2, msg, args...)
+}
+
+// HTTPBodyContains asserts that a specified handler returns a
+// body that contains a string.
+//
+// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...)
+}
+
+// HTTPBodyContainsf asserts that a specified handler returns a
+// body that contains a string.
+//
+// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...)
+}
+
+// HTTPBodyNotContains asserts that a specified handler returns a
+// body that does not contain a string.
+//
+// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...)
+}
+
+// HTTPBodyNotContainsf asserts that a specified handler returns a
+// body that does not contain a string.
+//
+// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...)
+}
+
+// HTTPError asserts that a specified handler returns an error status code.
+//
+// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPError(a.t, handler, method, url, values, msgAndArgs...)
+}
+
+// HTTPErrorf asserts that a specified handler returns an error status code.
+//
+// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
+func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPErrorf(a.t, handler, method, url, values, msg, args...)
+}
+
+// HTTPRedirect asserts that a specified handler returns a redirect status code.
+//
+// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...)
+}
+
+// HTTPRedirectf asserts that a specified handler returns a redirect status code.
+//
+// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
+func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
+}
+
+// HTTPSuccess asserts that a specified handler returns a success status code.
+//
+// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...)
+}
+
+// HTTPSuccessf asserts that a specified handler returns a success status code.
+//
+// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ HTTPSuccessf(a.t, handler, method, url, values, msg, args...)
+}
+
+// Implements asserts that an object is implemented by the specified interface.
+//
+// a.Implements((*MyInterface)(nil), new(MyObject))
+func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Implements(a.t, interfaceObject, object, msgAndArgs...)
+}
+
+// Implementsf asserts that an object is implemented by the specified interface.
+//
+// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
+func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Implementsf(a.t, interfaceObject, object, msg, args...)
+}
+
+// InDelta asserts that the two numerals are within delta of each other.
+//
+// a.InDelta(math.Pi, (22 / 7.0), 0.01)
+func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InDelta(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
+func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
+func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...)
+}
+
+// InDeltaSlice is the same as InDelta, except it compares two slices.
+func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// InDeltaSlicef is the same as InDelta, except it compares two slices.
+func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InDeltaSlicef(a.t, expected, actual, delta, msg, args...)
+}
+
+// InDeltaf asserts that the two numerals are within delta of each other.
+//
+// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
+func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InDeltaf(a.t, expected, actual, delta, msg, args...)
+}
+
+// InEpsilon asserts that expected and actual have a relative error less than epsilon
+func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
+}
+
+// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
+func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...)
+}
+
+// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
+func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...)
+}
+
+// InEpsilonf asserts that expected and actual have a relative error less than epsilon
+func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
+}
+
+// IsType asserts that the specified objects are of the same type.
+func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ IsType(a.t, expectedType, object, msgAndArgs...)
+}
+
+// IsTypef asserts that the specified objects are of the same type.
+func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ IsTypef(a.t, expectedType, object, msg, args...)
+}
+
+// JSONEq asserts that two JSON strings are equivalent.
+//
+// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
+func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ JSONEq(a.t, expected, actual, msgAndArgs...)
+}
+
+// JSONEqf asserts that two JSON strings are equivalent.
+//
+// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
+func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ JSONEqf(a.t, expected, actual, msg, args...)
+}
+
+// YAMLEq asserts that two YAML strings are equivalent.
+func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ YAMLEq(a.t, expected, actual, msgAndArgs...)
+}
+
+// YAMLEqf asserts that two YAML strings are equivalent.
+func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ YAMLEqf(a.t, expected, actual, msg, args...)
+}
+
+// Len asserts that the specified object has specific length.
+// Len also fails if the object has a type that len() not accept.
+//
+// a.Len(mySlice, 3)
+func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Len(a.t, object, length, msgAndArgs...)
+}
+
+// Lenf asserts that the specified object has specific length.
+// Lenf also fails if the object has a type that len() not accept.
+//
+// a.Lenf(mySlice, 3, "error message %s", "formatted")
+func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Lenf(a.t, object, length, msg, args...)
+}
+
+// Less asserts that the first element is less than the second
+//
+// a.Less(1, 2)
+// a.Less(float64(1), float64(2))
+// a.Less("a", "b")
+func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Less(a.t, e1, e2, msgAndArgs...)
+}
+
+// LessOrEqual asserts that the first element is less than or equal to the second
+//
+// a.LessOrEqual(1, 2)
+// a.LessOrEqual(2, 2)
+// a.LessOrEqual("a", "b")
+// a.LessOrEqual("b", "b")
+func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ LessOrEqual(a.t, e1, e2, msgAndArgs...)
+}
+
+// LessOrEqualf asserts that the first element is less than or equal to the second
+//
+// a.LessOrEqualf(1, 2, "error message %s", "formatted")
+// a.LessOrEqualf(2, 2, "error message %s", "formatted")
+// a.LessOrEqualf("a", "b", "error message %s", "formatted")
+// a.LessOrEqualf("b", "b", "error message %s", "formatted")
+func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ LessOrEqualf(a.t, e1, e2, msg, args...)
+}
+
+// Lessf asserts that the first element is less than the second
+//
+// a.Lessf(1, 2, "error message %s", "formatted")
+// a.Lessf(float64(1, "error message %s", "formatted"), float64(2))
+// a.Lessf("a", "b", "error message %s", "formatted")
+func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Lessf(a.t, e1, e2, msg, args...)
+}
+
+// Nil asserts that the specified object is nil.
+//
+// a.Nil(err)
+func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Nil(a.t, object, msgAndArgs...)
+}
+
+// Nilf asserts that the specified object is nil.
+//
+// a.Nilf(err, "error message %s", "formatted")
+func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Nilf(a.t, object, msg, args...)
+}
+
+// NoError asserts that a function returned no error (i.e. `nil`).
+//
+// actualObj, err := SomeFunction()
+// if a.NoError(err) {
+// assert.Equal(t, expectedObj, actualObj)
+// }
+func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NoError(a.t, err, msgAndArgs...)
+}
+
+// NoErrorf asserts that a function returned no error (i.e. `nil`).
+//
+// actualObj, err := SomeFunction()
+// if a.NoErrorf(err, "error message %s", "formatted") {
+// assert.Equal(t, expectedObj, actualObj)
+// }
+func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NoErrorf(a.t, err, msg, args...)
+}
+
+// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
+// specified substring or element.
+//
+// a.NotContains("Hello World", "Earth")
+// a.NotContains(["Hello", "World"], "Earth")
+// a.NotContains({"Hello": "World"}, "Earth")
+func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotContains(a.t, s, contains, msgAndArgs...)
+}
+
+// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
+// specified substring or element.
+//
+// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted")
+// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted")
+// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted")
+func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotContainsf(a.t, s, contains, msg, args...)
+}
+
+// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if a.NotEmpty(obj) {
+// assert.Equal(t, "two", obj[1])
+// }
+func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotEmpty(a.t, object, msgAndArgs...)
+}
+
+// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if a.NotEmptyf(obj, "error message %s", "formatted") {
+// assert.Equal(t, "two", obj[1])
+// }
+func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotEmptyf(a.t, object, msg, args...)
+}
+
+// NotEqual asserts that the specified values are NOT equal.
+//
+// a.NotEqual(obj1, obj2)
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses).
+func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotEqual(a.t, expected, actual, msgAndArgs...)
+}
+
+// NotEqualf asserts that the specified values are NOT equal.
+//
+// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
+//
+// Pointer variable equality is determined based on the equality of the
+// referenced values (as opposed to the memory addresses).
+func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotEqualf(a.t, expected, actual, msg, args...)
+}
+
+// NotNil asserts that the specified object is not nil.
+//
+// a.NotNil(err)
+func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotNil(a.t, object, msgAndArgs...)
+}
+
+// NotNilf asserts that the specified object is not nil.
+//
+// a.NotNilf(err, "error message %s", "formatted")
+func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotNilf(a.t, object, msg, args...)
+}
+
+// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+// a.NotPanics(func(){ RemainCalm() })
+func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotPanics(a.t, f, msgAndArgs...)
+}
+
+// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted")
+func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotPanicsf(a.t, f, msg, args...)
+}
+
+// NotRegexp asserts that a specified regexp does not match a string.
+//
+// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
+// a.NotRegexp("^start", "it's not starting")
+func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotRegexp(a.t, rx, str, msgAndArgs...)
+}
+
+// NotRegexpf asserts that a specified regexp does not match a string.
+//
+// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
+// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
+func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotRegexpf(a.t, rx, str, msg, args...)
+}
+
+// NotSubset asserts that the specified list(array, slice...) contains not all
+// elements given in the specified subset(array, slice...).
+//
+// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
+func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotSubset(a.t, list, subset, msgAndArgs...)
+}
+
+// NotSubsetf asserts that the specified list(array, slice...) contains not all
+// elements given in the specified subset(array, slice...).
+//
+// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
+func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotSubsetf(a.t, list, subset, msg, args...)
+}
+
+// NotZero asserts that i is not the zero value for its type.
+func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotZero(a.t, i, msgAndArgs...)
+}
+
+// NotZerof asserts that i is not the zero value for its type.
+func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ NotZerof(a.t, i, msg, args...)
+}
+
+// Panics asserts that the code inside the specified PanicTestFunc panics.
+//
+// a.Panics(func(){ GoCrazy() })
+func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Panics(a.t, f, msgAndArgs...)
+}
+
+// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
+// the recovered panic value equals the expected panic value.
+//
+// a.PanicsWithValue("crazy error", func(){ GoCrazy() })
+func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ PanicsWithValue(a.t, expected, f, msgAndArgs...)
+}
+
+// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
+// the recovered panic value equals the expected panic value.
+//
+// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
+func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ PanicsWithValuef(a.t, expected, f, msg, args...)
+}
+
+// Panicsf asserts that the code inside the specified PanicTestFunc panics.
+//
+// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted")
+func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Panicsf(a.t, f, msg, args...)
+}
+
+// Regexp asserts that a specified regexp matches a string.
+//
+// a.Regexp(regexp.MustCompile("start"), "it's starting")
+// a.Regexp("start...$", "it's not starting")
+func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Regexp(a.t, rx, str, msgAndArgs...)
+}
+
+// Regexpf asserts that a specified regexp matches a string.
+//
+// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
+// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
+func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Regexpf(a.t, rx, str, msg, args...)
+}
+
+// Same asserts that two pointers reference the same object.
+//
+// a.Same(ptr1, ptr2)
+//
+// Both arguments must be pointer variables. Pointer variable sameness is
+// determined based on the equality of both type and value.
+func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Same(a.t, expected, actual, msgAndArgs...)
+}
+
+// Samef asserts that two pointers reference the same object.
+//
+// a.Samef(ptr1, ptr2, "error message %s", "formatted")
+//
+// Both arguments must be pointer variables. Pointer variable sameness is
+// determined based on the equality of both type and value.
+func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Samef(a.t, expected, actual, msg, args...)
+}
+
+// Subset asserts that the specified list(array, slice...) contains all
+// elements given in the specified subset(array, slice...).
+//
+// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
+func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Subset(a.t, list, subset, msgAndArgs...)
+}
+
+// Subsetf asserts that the specified list(array, slice...) contains all
+// elements given in the specified subset(array, slice...).
+//
+// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
+func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Subsetf(a.t, list, subset, msg, args...)
+}
+
+// True asserts that the specified value is true.
+//
+// a.True(myBool)
+func (a *Assertions) True(value bool, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ True(a.t, value, msgAndArgs...)
+}
+
+// Truef asserts that the specified value is true.
+//
+// a.Truef(myBool, "error message %s", "formatted")
+func (a *Assertions) Truef(value bool, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Truef(a.t, value, msg, args...)
+}
+
+// WithinDuration asserts that the two times are within duration delta of each other.
+//
+// a.WithinDuration(time.Now(), time.Now(), 10*time.Second)
+func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// WithinDurationf asserts that the two times are within duration delta of each other.
+//
+// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
+func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ WithinDurationf(a.t, expected, actual, delta, msg, args...)
+}
+
+// Zero asserts that i is the zero value for its type.
+func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Zero(a.t, i, msgAndArgs...)
+}
+
+// Zerof asserts that i is the zero value for its type.
+func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ Zerof(a.t, i, msg, args...)
+}
diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go
new file mode 100644
index 000000000..6b85c5ece
--- /dev/null
+++ b/vendor/github.com/stretchr/testify/require/requirements.go
@@ -0,0 +1,29 @@
+package require
+
+// TestingT is an interface wrapper around *testing.T
+type TestingT interface {
+ Errorf(format string, args ...interface{})
+ FailNow()
+}
+
+type tHelper interface {
+ Helper()
+}
+
+// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
+// for table driven tests.
+type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{})
+
+// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
+// for table driven tests.
+type ValueAssertionFunc func(TestingT, interface{}, ...interface{})
+
+// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
+// for table driven tests.
+type BoolAssertionFunc func(TestingT, bool, ...interface{})
+
+// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful
+// for table driven tests.
+type ErrorAssertionFunc func(TestingT, error, ...interface{})
+
+//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl -include-format-funcs
diff --git a/vendor/github.com/containerd/cri/LICENSE b/vendor/k8s.io/component-base/LICENSE
similarity index 99%
rename from vendor/github.com/containerd/cri/LICENSE
rename to vendor/k8s.io/component-base/LICENSE
index 8dada3eda..d64569567 100644
--- a/vendor/github.com/containerd/cri/LICENSE
+++ b/vendor/k8s.io/component-base/LICENSE
@@ -1,3 +1,4 @@
+
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -178,7 +179,7 @@
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
+ boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
@@ -186,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright {yyyy} {name of copyright owner}
+ Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/vendor/k8s.io/component-base/README.md b/vendor/k8s.io/component-base/README.md
new file mode 100644
index 000000000..0b6d35c64
--- /dev/null
+++ b/vendor/k8s.io/component-base/README.md
@@ -0,0 +1,34 @@
+## component-base
+
+## Purpose
+
+Implement KEP 32: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cluster-lifecycle/wgs/0032-create-a-k8s-io-component-repo.md
+
+The proposal is essentially about refactoring the Kubernetes core package structure in a way that all core components may share common code around:
+ - ComponentConfig implementation
+ - flag and command handling
+ - HTTPS serving
+ - delegated authn/z
+ - logging.
+
+## Compatibility
+
+There are *NO compatibility guarantees* for this repository, yet. It is in direct support of Kubernetes, so branches
+will track Kubernetes and be compatible with that repo. As we more cleanly separate the layers, we will review the
+compatibility guarantee. We have a goal to make this easier to use in the future.
+
+
+## Where does it come from?
+
+This repository is synced from https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/component-base.
+Code changes are made in that location, merged into `k8s.io/kubernetes` and later synced here.
+
+## Things you should *NOT* do
+
+ 1. Directly modify any files in this repo. Those are driven from `k8s.io/kubernetes/staging/src/k8s.io/component-base`.
+ 2. Expect compatibility. This repo is changing quickly in direct support of Kubernetes.
+
+### OWNERS
+
+WG Component Standard is working on this refactoring process, which is happening incrementally, starting in the v1.14 cycle.
+SIG API Machinery and SIG Cluster Lifecycle owns the code.
diff --git a/vendor/k8s.io/component-base/go.mod b/vendor/k8s.io/component-base/go.mod
new file mode 100644
index 000000000..81833c480
--- /dev/null
+++ b/vendor/k8s.io/component-base/go.mod
@@ -0,0 +1,33 @@
+// This is a generated file. Do not edit directly.
+
+module k8s.io/component-base
+
+go 1.15
+
+require (
+ github.com/blang/semver v3.5.0+incompatible
+ github.com/go-logr/logr v0.2.0
+ github.com/google/go-cmp v0.4.0
+ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
+ github.com/moby/term v0.0.0-20200312100748-672ec06f55cd
+ github.com/prometheus/client_golang v1.7.1
+ github.com/prometheus/client_model v0.2.0
+ github.com/prometheus/common v0.10.0
+ github.com/prometheus/procfs v0.1.3
+ github.com/sirupsen/logrus v1.6.0 // indirect
+ github.com/spf13/pflag v1.0.5
+ github.com/stretchr/testify v1.4.0
+ go.uber.org/atomic v1.4.0 // indirect
+ go.uber.org/multierr v1.1.0 // indirect
+ go.uber.org/zap v1.10.0
+ k8s.io/apimachinery v0.19.2
+ k8s.io/client-go v0.19.2
+ k8s.io/klog/v2 v2.2.0
+ k8s.io/utils v0.0.0-20200729134348-d5654de09c73
+)
+
+replace (
+ k8s.io/api => k8s.io/api v0.19.2
+ k8s.io/apimachinery => k8s.io/apimachinery v0.19.2
+ k8s.io/client-go => k8s.io/client-go v0.19.2
+)
diff --git a/vendor/k8s.io/component-base/logs/logreduction/logreduction.go b/vendor/k8s.io/component-base/logs/logreduction/logreduction.go
new file mode 100644
index 000000000..6534a5a64
--- /dev/null
+++ b/vendor/k8s.io/component-base/logs/logreduction/logreduction.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package logreduction
+
+import (
+ "sync"
+ "time"
+)
+
+var nowfunc = func() time.Time { return time.Now() }
+
+// LogReduction provides a filter for consecutive identical log messages;
+// a message will be printed no more than once per interval.
+// If a string of messages is interrupted by a different message,
+// the interval timer will be reset.
+type LogReduction struct {
+ lastError map[string]string
+ errorPrinted map[string]time.Time
+ errorMapLock sync.Mutex
+ identicalErrorDelay time.Duration
+}
+
+// NewLogReduction returns an initialized LogReduction
+func NewLogReduction(identicalErrorDelay time.Duration) *LogReduction {
+ l := new(LogReduction)
+ l.lastError = make(map[string]string)
+ l.errorPrinted = make(map[string]time.Time)
+ l.identicalErrorDelay = identicalErrorDelay
+ return l
+}
+
+func (l *LogReduction) cleanupErrorTimeouts() {
+ for name, timeout := range l.errorPrinted {
+ if nowfunc().Sub(timeout) >= l.identicalErrorDelay {
+ delete(l.errorPrinted, name)
+ delete(l.lastError, name)
+ }
+ }
+}
+
+// ShouldMessageBePrinted determines whether a message should be printed based
+// on how long ago this particular message was last printed
+func (l *LogReduction) ShouldMessageBePrinted(message string, parentID string) bool {
+ l.errorMapLock.Lock()
+ defer l.errorMapLock.Unlock()
+ l.cleanupErrorTimeouts()
+ lastMsg, ok := l.lastError[parentID]
+ lastPrinted, ok1 := l.errorPrinted[parentID]
+ if !ok || !ok1 || message != lastMsg || nowfunc().Sub(lastPrinted) >= l.identicalErrorDelay {
+ l.errorPrinted[parentID] = nowfunc()
+ l.lastError[parentID] = message
+ return true
+ }
+ return false
+}
+
+// ClearID clears out log reduction records pertaining to a particular parent
+// (e. g. container ID)
+func (l *LogReduction) ClearID(parentID string) {
+ l.errorMapLock.Lock()
+ defer l.errorMapLock.Unlock()
+ delete(l.lastError, parentID)
+ delete(l.errorPrinted, parentID)
+}
diff --git a/vendor/k8s.io/cri-api/pkg/apis/services.go b/vendor/k8s.io/cri-api/pkg/apis/services.go
new file mode 100644
index 000000000..9a22ecbf0
--- /dev/null
+++ b/vendor/k8s.io/cri-api/pkg/apis/services.go
@@ -0,0 +1,119 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cri
+
+import (
+ "time"
+
+ runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
+)
+
+// RuntimeVersioner contains methods for runtime name, version and API version.
+type RuntimeVersioner interface {
+ // Version returns the runtime name, runtime version and runtime API version
+ Version(apiVersion string) (*runtimeapi.VersionResponse, error)
+}
+
+// ContainerManager contains methods to manipulate containers managed by a
+// container runtime. The methods are thread-safe.
+type ContainerManager interface {
+ // CreateContainer creates a new container in specified PodSandbox.
+ CreateContainer(podSandboxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error)
+ // StartContainer starts the container.
+ StartContainer(containerID string) error
+ // StopContainer stops a running container with a grace period (i.e., timeout).
+ StopContainer(containerID string, timeout int64) error
+ // RemoveContainer removes the container.
+ RemoveContainer(containerID string) error
+ // ListContainers lists all containers by filters.
+ ListContainers(filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error)
+ // ContainerStatus returns the status of the container.
+ ContainerStatus(containerID string) (*runtimeapi.ContainerStatus, error)
+ // UpdateContainerResources updates the cgroup resources for the container.
+ UpdateContainerResources(containerID string, resources *runtimeapi.LinuxContainerResources) error
+ // ExecSync executes a command in the container, and returns the stdout output.
+ // If command exits with a non-zero exit code, an error is returned.
+ ExecSync(containerID string, cmd []string, timeout time.Duration) (stdout []byte, stderr []byte, err error)
+ // Exec prepares a streaming endpoint to execute a command in the container, and returns the address.
+ Exec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error)
+ // Attach prepares a streaming endpoint to attach to a running container, and returns the address.
+ Attach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error)
+ // ReopenContainerLog asks runtime to reopen the stdout/stderr log file
+ // for the container. If it returns error, new container log file MUST NOT
+ // be created.
+ ReopenContainerLog(ContainerID string) error
+}
+
+// PodSandboxManager contains methods for operating on PodSandboxes. The methods
+// are thread-safe.
+type PodSandboxManager interface {
+ // RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
+ // the sandbox is in ready state.
+ RunPodSandbox(config *runtimeapi.PodSandboxConfig, runtimeHandler string) (string, error)
+ // StopPodSandbox stops the sandbox. If there are any running containers in the
+ // sandbox, they should be force terminated.
+ StopPodSandbox(podSandboxID string) error
+ // RemovePodSandbox removes the sandbox. If there are running containers in the
+ // sandbox, they should be forcibly removed.
+ RemovePodSandbox(podSandboxID string) error
+ // PodSandboxStatus returns the Status of the PodSandbox.
+ PodSandboxStatus(podSandboxID string) (*runtimeapi.PodSandboxStatus, error)
+ // ListPodSandbox returns a list of Sandbox.
+ ListPodSandbox(filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error)
+ // PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
+ PortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error)
+}
+
+// ContainerStatsManager contains methods for retrieving the container
+// statistics.
+type ContainerStatsManager interface {
+ // ContainerStats returns stats of the container. If the container does not
+ // exist, the call returns an error.
+ ContainerStats(containerID string) (*runtimeapi.ContainerStats, error)
+ // ListContainerStats returns stats of all running containers.
+ ListContainerStats(filter *runtimeapi.ContainerStatsFilter) ([]*runtimeapi.ContainerStats, error)
+}
+
+// RuntimeService interface should be implemented by a container runtime.
+// The methods should be thread-safe.
+type RuntimeService interface {
+ RuntimeVersioner
+ ContainerManager
+ PodSandboxManager
+ ContainerStatsManager
+
+ // UpdateRuntimeConfig updates runtime configuration if specified
+ UpdateRuntimeConfig(runtimeConfig *runtimeapi.RuntimeConfig) error
+ // Status returns the status of the runtime.
+ Status() (*runtimeapi.RuntimeStatus, error)
+}
+
+// ImageManagerService interface should be implemented by a container image
+// manager.
+// The methods should be thread-safe.
+type ImageManagerService interface {
+ // ListImages lists the existing images.
+ ListImages(filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error)
+ // ImageStatus returns the status of the image.
+ ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error)
+ // PullImage pulls an image with the authentication config.
+ PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error)
+ // RemoveImage removes the image.
+ RemoveImage(image *runtimeapi.ImageSpec) error
+ // ImageFsInfo returns information of the filesystem that is used to store images.
+ ImageFsInfo() ([]*runtimeapi.FilesystemUsage, error)
+}