summaryrefslogtreecommitdiff
path: root/vendor/k8s.io/kubernetes/pkg/kubelet/container
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/k8s.io/kubernetes/pkg/kubelet/container')
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/cache.go199
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/container_gc.go84
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/container_reference_manager.go62
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers.go367
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/os.go107
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_linux.go30
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_unsupported.go28
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/ref.go73
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/resize.go46
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime.go646
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache.go96
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_fake.go45
-rw-r--r--vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result.go128
13 files changed, 1911 insertions, 0 deletions
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/cache.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/cache.go
new file mode 100644
index 000000000..82852a9d9
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/cache.go
@@ -0,0 +1,199 @@
+/*
+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 container
+
+import (
+ "sync"
+ "time"
+
+ "k8s.io/apimachinery/pkg/types"
+)
+
+// Cache stores the PodStatus for the pods. It represents *all* the visible
+// pods/containers in the container runtime. All cache entries are at least as
+// new or newer than the global timestamp (set by UpdateTime()), while
+// individual entries may be slightly newer than the global timestamp. If a pod
+// has no states known by the runtime, Cache returns an empty PodStatus object
+// with ID populated.
+//
+// Cache provides two methods to retrive the PodStatus: the non-blocking Get()
+// and the blocking GetNewerThan() method. The component responsible for
+// populating the cache is expected to call Delete() to explicitly free the
+// cache entries.
+type Cache interface {
+ Get(types.UID) (*PodStatus, error)
+ Set(types.UID, *PodStatus, error, time.Time)
+ // GetNewerThan is a blocking call that only returns the status
+ // when it is newer than the given time.
+ GetNewerThan(types.UID, time.Time) (*PodStatus, error)
+ Delete(types.UID)
+ UpdateTime(time.Time)
+}
+
+type data struct {
+ // Status of the pod.
+ status *PodStatus
+ // Error got when trying to inspect the pod.
+ err error
+ // Time when the data was last modified.
+ modified time.Time
+}
+
+type subRecord struct {
+ time time.Time
+ ch chan *data
+}
+
+// cache implements Cache.
+type cache struct {
+ // Lock which guards all internal data structures.
+ lock sync.RWMutex
+ // Map that stores the pod statuses.
+ pods map[types.UID]*data
+ // A global timestamp represents how fresh the cached data is. All
+ // cache content is at the least newer than this timestamp. Note that the
+ // timestamp is nil after initialization, and will only become non-nil when
+ // it is ready to serve the cached statuses.
+ timestamp *time.Time
+ // Map that stores the subscriber records.
+ subscribers map[types.UID][]*subRecord
+}
+
+// NewCache creates a pod cache.
+func NewCache() Cache {
+ return &cache{pods: map[types.UID]*data{}, subscribers: map[types.UID][]*subRecord{}}
+}
+
+// Get returns the PodStatus for the pod; callers are expected not to
+// modify the objects returned.
+func (c *cache) Get(id types.UID) (*PodStatus, error) {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ d := c.get(id)
+ return d.status, d.err
+}
+
+func (c *cache) GetNewerThan(id types.UID, minTime time.Time) (*PodStatus, error) {
+ ch := c.subscribe(id, minTime)
+ d := <-ch
+ return d.status, d.err
+}
+
+// Set sets the PodStatus for the pod.
+func (c *cache) Set(id types.UID, status *PodStatus, err error, timestamp time.Time) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ defer c.notify(id, timestamp)
+ c.pods[id] = &data{status: status, err: err, modified: timestamp}
+}
+
+// Delete removes the entry of the pod.
+func (c *cache) Delete(id types.UID) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ delete(c.pods, id)
+}
+
+// UpdateTime modifies the global timestamp of the cache and notify
+// subscribers if needed.
+func (c *cache) UpdateTime(timestamp time.Time) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ c.timestamp = &timestamp
+ // Notify all the subscribers if the condition is met.
+ for id := range c.subscribers {
+ c.notify(id, *c.timestamp)
+ }
+}
+
+func makeDefaultData(id types.UID) *data {
+ return &data{status: &PodStatus{ID: id}, err: nil}
+}
+
+func (c *cache) get(id types.UID) *data {
+ d, ok := c.pods[id]
+ if !ok {
+ // Cache should store *all* pod/container information known by the
+ // container runtime. A cache miss indicates that there are no states
+ // regarding the pod last time we queried the container runtime.
+ // What this *really* means is that there are no visible pod/containers
+ // associated with this pod. Simply return an default (mostly empty)
+ // PodStatus to reflect this.
+ return makeDefaultData(id)
+ }
+ return d
+}
+
+// getIfNewerThan returns the data it is newer than the given time.
+// Otherwise, it returns nil. The caller should acquire the lock.
+func (c *cache) getIfNewerThan(id types.UID, minTime time.Time) *data {
+ d, ok := c.pods[id]
+ globalTimestampIsNewer := (c.timestamp != nil && c.timestamp.After(minTime))
+ if !ok && globalTimestampIsNewer {
+ // Status is not cached, but the global timestamp is newer than
+ // minTime, return the default status.
+ return makeDefaultData(id)
+ }
+ if ok && (d.modified.After(minTime) || globalTimestampIsNewer) {
+ // Status is cached, return status if either of the following is true.
+ // * status was modified after minTime
+ // * the global timestamp of the cache is newer than minTime.
+ return d
+ }
+ // The pod status is not ready.
+ return nil
+}
+
+// notify sends notifications for pod with the given id, if the requirements
+// are met. Note that the caller should acquire the lock.
+func (c *cache) notify(id types.UID, timestamp time.Time) {
+ list, ok := c.subscribers[id]
+ if !ok {
+ // No one to notify.
+ return
+ }
+ newList := []*subRecord{}
+ for i, r := range list {
+ if timestamp.Before(r.time) {
+ // Doesn't meet the time requirement; keep the record.
+ newList = append(newList, list[i])
+ continue
+ }
+ r.ch <- c.get(id)
+ close(r.ch)
+ }
+ if len(newList) == 0 {
+ delete(c.subscribers, id)
+ } else {
+ c.subscribers[id] = newList
+ }
+}
+
+func (c *cache) subscribe(id types.UID, timestamp time.Time) chan *data {
+ ch := make(chan *data, 1)
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ d := c.getIfNewerThan(id, timestamp)
+ if d != nil {
+ // If the cache entry is ready, send the data and return immediately.
+ ch <- d
+ return ch
+ }
+ // Add the subscription record.
+ c.subscribers[id] = append(c.subscribers[id], &subRecord{time: timestamp, ch: ch})
+ return ch
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/container_gc.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/container_gc.go
new file mode 100644
index 000000000..be2fac4b9
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/container_gc.go
@@ -0,0 +1,84 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package container
+
+import (
+ "fmt"
+ "time"
+)
+
+// Specified a policy for garbage collecting containers.
+type ContainerGCPolicy struct {
+ // Minimum age at which a container can be garbage collected, zero for no limit.
+ MinAge time.Duration
+
+ // Max number of dead containers any single pod (UID, container name) pair is
+ // allowed to have, less than zero for no limit.
+ MaxPerPodContainer int
+
+ // Max number of total dead containers, less than zero for no limit.
+ MaxContainers int
+}
+
+// Manages garbage collection of dead containers.
+//
+// Implementation is thread-compatible.
+type ContainerGC interface {
+ // Garbage collect containers.
+ GarbageCollect() error
+ // Deletes all unused containers, including containers belonging to pods that are terminated but not deleted
+ DeleteAllUnusedContainers() error
+}
+
+// SourcesReadyProvider knows how to determine if configuration sources are ready
+type SourcesReadyProvider interface {
+ // AllReady returns true if the currently configured sources have all been seen.
+ AllReady() bool
+}
+
+// TODO(vmarmol): Preferentially remove pod infra containers.
+type realContainerGC struct {
+ // Container runtime
+ runtime Runtime
+
+ // Policy for garbage collection.
+ policy ContainerGCPolicy
+
+ // sourcesReadyProvider provides the readyness of kubelet configuration sources.
+ sourcesReadyProvider SourcesReadyProvider
+}
+
+// New ContainerGC instance with the specified policy.
+func NewContainerGC(runtime Runtime, policy ContainerGCPolicy, sourcesReadyProvider SourcesReadyProvider) (ContainerGC, error) {
+ if policy.MinAge < 0 {
+ return nil, fmt.Errorf("invalid minimum garbage collection age: %v", policy.MinAge)
+ }
+
+ return &realContainerGC{
+ runtime: runtime,
+ policy: policy,
+ sourcesReadyProvider: sourcesReadyProvider,
+ }, nil
+}
+
+func (cgc *realContainerGC) GarbageCollect() error {
+ return cgc.runtime.GarbageCollect(cgc.policy, cgc.sourcesReadyProvider.AllReady(), false)
+}
+
+func (cgc *realContainerGC) DeleteAllUnusedContainers() error {
+ return cgc.runtime.GarbageCollect(cgc.policy, cgc.sourcesReadyProvider.AllReady(), true)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/container_reference_manager.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/container_reference_manager.go
new file mode 100644
index 000000000..8383e77d9
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/container_reference_manager.go
@@ -0,0 +1,62 @@
+/*
+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 container
+
+import (
+ "sync"
+
+ "k8s.io/kubernetes/pkg/api/v1"
+)
+
+// RefManager manages the references for the containers.
+// The references are used for reporting events such as creation,
+// failure, etc. This manager is thread-safe, no locks are necessary
+// for the caller.
+type RefManager struct {
+ sync.RWMutex
+ containerIDToRef map[ContainerID]*v1.ObjectReference
+}
+
+// NewRefManager creates and returns a container reference manager
+// with empty contents.
+func NewRefManager() *RefManager {
+ return &RefManager{containerIDToRef: make(map[ContainerID]*v1.ObjectReference)}
+}
+
+// SetRef stores a reference to a pod's container, associating it with the given container ID.
+// TODO: move this to client-go v1.ObjectReference
+func (c *RefManager) SetRef(id ContainerID, ref *v1.ObjectReference) {
+ c.Lock()
+ defer c.Unlock()
+ c.containerIDToRef[id] = ref
+}
+
+// ClearRef forgets the given container id and its associated container reference.
+func (c *RefManager) ClearRef(id ContainerID) {
+ c.Lock()
+ defer c.Unlock()
+ delete(c.containerIDToRef, id)
+}
+
+// GetRef returns the container reference of the given ID, or (nil, false) if none is stored.
+// TODO: move this to client-go v1.ObjectReference
+func (c *RefManager) GetRef(id ContainerID) (ref *v1.ObjectReference, ok bool) {
+ c.RLock()
+ defer c.RUnlock()
+ ref, ok = c.containerIDToRef[id]
+ return ref, ok
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers.go
new file mode 100644
index 000000000..7930fed55
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers.go
@@ -0,0 +1,367 @@
+/*
+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 container
+
+import (
+ "bytes"
+ "fmt"
+ "hash/adler32"
+ "hash/fnv"
+ "strings"
+ "time"
+
+ "github.com/golang/glog"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
+ clientv1 "k8s.io/client-go/pkg/api/v1"
+ "k8s.io/client-go/tools/record"
+ "k8s.io/kubernetes/pkg/api/v1"
+ runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
+ "k8s.io/kubernetes/pkg/kubelet/events"
+ "k8s.io/kubernetes/pkg/kubelet/util/format"
+ "k8s.io/kubernetes/pkg/kubelet/util/ioutils"
+ hashutil "k8s.io/kubernetes/pkg/util/hash"
+ "k8s.io/kubernetes/third_party/forked/golang/expansion"
+)
+
+// HandlerRunner runs a lifecycle handler for a container.
+type HandlerRunner interface {
+ Run(containerID ContainerID, pod *v1.Pod, container *v1.Container, handler *v1.Handler) (string, error)
+}
+
+// RuntimeHelper wraps kubelet to make container runtime
+// able to get necessary informations like the RunContainerOptions, DNS settings, Host IP.
+type RuntimeHelper interface {
+ GenerateRunContainerOptions(pod *v1.Pod, container *v1.Container, podIP string) (contOpts *RunContainerOptions, useClusterFirstPolicy bool, err error)
+ GetClusterDNS(pod *v1.Pod) (dnsServers []string, dnsSearches []string, useClusterFirstPolicy bool, err error)
+ // GetPodCgroupParent returns the the CgroupName identifer, and its literal cgroupfs form on the host
+ // of a pod.
+ GetPodCgroupParent(pod *v1.Pod) string
+ GetPodDir(podUID types.UID) string
+ GeneratePodHostNameAndDomain(pod *v1.Pod) (hostname string, hostDomain string, err error)
+ // GetExtraSupplementalGroupsForPod returns a list of the extra
+ // supplemental groups for the Pod. These extra supplemental groups come
+ // from annotations on persistent volumes that the pod depends on.
+ GetExtraSupplementalGroupsForPod(pod *v1.Pod) []int64
+}
+
+// ShouldContainerBeRestarted checks whether a container needs to be restarted.
+// TODO(yifan): Think about how to refactor this.
+func ShouldContainerBeRestarted(container *v1.Container, pod *v1.Pod, podStatus *PodStatus) bool {
+ // Get latest container status.
+ status := podStatus.FindContainerStatusByName(container.Name)
+ // If the container was never started before, we should start it.
+ // NOTE(random-liu): If all historical containers were GC'd, we'll also return true here.
+ if status == nil {
+ return true
+ }
+ // Check whether container is running
+ if status.State == ContainerStateRunning {
+ return false
+ }
+ // Always restart container in the unknown, or in the created state.
+ if status.State == ContainerStateUnknown || status.State == ContainerStateCreated {
+ return true
+ }
+ // Check RestartPolicy for dead container
+ if pod.Spec.RestartPolicy == v1.RestartPolicyNever {
+ glog.V(4).Infof("Already ran container %q of pod %q, do nothing", container.Name, format.Pod(pod))
+ return false
+ }
+ if pod.Spec.RestartPolicy == v1.RestartPolicyOnFailure {
+ // Check the exit code.
+ if status.ExitCode == 0 {
+ glog.V(4).Infof("Already successfully ran container %q of pod %q, do nothing", container.Name, format.Pod(pod))
+ return false
+ }
+ }
+ return true
+}
+
+// HashContainer returns the hash of the container. It is used to compare
+// the running container with its desired spec.
+func HashContainer(container *v1.Container) uint64 {
+ hash := fnv.New32a()
+ hashutil.DeepHashObject(hash, *container)
+ return uint64(hash.Sum32())
+}
+
+// HashContainerLegacy returns the hash of the container. It is used to compare
+// the running container with its desired spec.
+// This is used by rktnetes and dockershim (for handling <=1.5 containers).
+// TODO: Remove this function when kubernetes version is >=1.8 AND rktnetes
+// update its hash function.
+func HashContainerLegacy(container *v1.Container) uint64 {
+ hash := adler32.New()
+ hashutil.DeepHashObject(hash, *container)
+ return uint64(hash.Sum32())
+}
+
+// EnvVarsToMap constructs a map of environment name to value from a slice
+// of env vars.
+func EnvVarsToMap(envs []EnvVar) map[string]string {
+ result := map[string]string{}
+ for _, env := range envs {
+ result[env.Name] = env.Value
+ }
+ return result
+}
+
+// V1EnvVarsToMap constructs a map of environment name to value from a slice
+// of env vars.
+func V1EnvVarsToMap(envs []v1.EnvVar) map[string]string {
+ result := map[string]string{}
+ for _, env := range envs {
+ result[env.Name] = env.Value
+ }
+
+ return result
+}
+
+// ExpandContainerCommandOnlyStatic substitutes only static environment variable values from the
+// container environment definitions. This does *not* include valueFrom substitutions.
+// TODO: callers should use ExpandContainerCommandAndArgs with a fully resolved list of environment.
+func ExpandContainerCommandOnlyStatic(containerCommand []string, envs []v1.EnvVar) (command []string) {
+ mapping := expansion.MappingFuncFor(V1EnvVarsToMap(envs))
+ if len(containerCommand) != 0 {
+ for _, cmd := range containerCommand {
+ command = append(command, expansion.Expand(cmd, mapping))
+ }
+ }
+ return command
+}
+
+func ExpandContainerCommandAndArgs(container *v1.Container, envs []EnvVar) (command []string, args []string) {
+ mapping := expansion.MappingFuncFor(EnvVarsToMap(envs))
+
+ if len(container.Command) != 0 {
+ for _, cmd := range container.Command {
+ command = append(command, expansion.Expand(cmd, mapping))
+ }
+ }
+
+ if len(container.Args) != 0 {
+ for _, arg := range container.Args {
+ args = append(args, expansion.Expand(arg, mapping))
+ }
+ }
+
+ return command, args
+}
+
+// Create an event recorder to record object's event except implicitly required container's, like infra container.
+func FilterEventRecorder(recorder record.EventRecorder) record.EventRecorder {
+ return &innerEventRecorder{
+ recorder: recorder,
+ }
+}
+
+type innerEventRecorder struct {
+ recorder record.EventRecorder
+}
+
+func (irecorder *innerEventRecorder) shouldRecordEvent(object runtime.Object) (*clientv1.ObjectReference, bool) {
+ if object == nil {
+ return nil, false
+ }
+ if ref, ok := object.(*clientv1.ObjectReference); ok {
+ if !strings.HasPrefix(ref.FieldPath, ImplicitContainerPrefix) {
+ return ref, true
+ }
+ }
+ // just in case we miss a spot, be sure that we still log something
+ if ref, ok := object.(*v1.ObjectReference); ok {
+ if !strings.HasPrefix(ref.FieldPath, ImplicitContainerPrefix) {
+ return events.ToObjectReference(ref), true
+ }
+ }
+ return nil, false
+}
+
+func (irecorder *innerEventRecorder) Event(object runtime.Object, eventtype, reason, message string) {
+ if ref, ok := irecorder.shouldRecordEvent(object); ok {
+ irecorder.recorder.Event(ref, eventtype, reason, message)
+ }
+}
+
+func (irecorder *innerEventRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
+ if ref, ok := irecorder.shouldRecordEvent(object); ok {
+ irecorder.recorder.Eventf(ref, eventtype, reason, messageFmt, args...)
+ }
+
+}
+
+func (irecorder *innerEventRecorder) PastEventf(object runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) {
+ if ref, ok := irecorder.shouldRecordEvent(object); ok {
+ irecorder.recorder.PastEventf(ref, timestamp, eventtype, reason, messageFmt, args...)
+ }
+}
+
+// Pod must not be nil.
+func IsHostNetworkPod(pod *v1.Pod) bool {
+ return pod.Spec.HostNetwork
+}
+
+// TODO(random-liu): Convert PodStatus to running Pod, should be deprecated soon
+func ConvertPodStatusToRunningPod(runtimeName string, podStatus *PodStatus) Pod {
+ runningPod := Pod{
+ ID: podStatus.ID,
+ Name: podStatus.Name,
+ Namespace: podStatus.Namespace,
+ }
+ for _, containerStatus := range podStatus.ContainerStatuses {
+ if containerStatus.State != ContainerStateRunning {
+ continue
+ }
+ container := &Container{
+ ID: containerStatus.ID,
+ Name: containerStatus.Name,
+ Image: containerStatus.Image,
+ ImageID: containerStatus.ImageID,
+ Hash: containerStatus.Hash,
+ State: containerStatus.State,
+ }
+ runningPod.Containers = append(runningPod.Containers, container)
+ }
+
+ // Populate sandboxes in kubecontainer.Pod
+ for _, sandbox := range podStatus.SandboxStatuses {
+ runningPod.Sandboxes = append(runningPod.Sandboxes, &Container{
+ ID: ContainerID{Type: runtimeName, ID: sandbox.Id},
+ State: SandboxToContainerState(sandbox.State),
+ })
+ }
+ return runningPod
+}
+
+// SandboxToContainerState converts runtimeapi.PodSandboxState to
+// kubecontainer.ContainerState.
+// This is only needed because we need to return sandboxes as if they were
+// kubecontainer.Containers to avoid substantial changes to PLEG.
+// TODO: Remove this once it becomes obsolete.
+func SandboxToContainerState(state runtimeapi.PodSandboxState) ContainerState {
+ switch state {
+ case runtimeapi.PodSandboxState_SANDBOX_READY:
+ return ContainerStateRunning
+ case runtimeapi.PodSandboxState_SANDBOX_NOTREADY:
+ return ContainerStateExited
+ }
+ return ContainerStateUnknown
+}
+
+// FormatPod returns a string representing a pod in a human readable format,
+// with pod UID as part of the string.
+func FormatPod(pod *Pod) string {
+ // Use underscore as the delimiter because it is not allowed in pod name
+ // (DNS subdomain format), while allowed in the container name format.
+ return fmt.Sprintf("%s_%s(%s)", pod.Name, pod.Namespace, pod.ID)
+}
+
+type containerCommandRunnerWrapper struct {
+ DirectStreamingRuntime
+}
+
+var _ ContainerCommandRunner = &containerCommandRunnerWrapper{}
+
+func DirectStreamingRunner(runtime DirectStreamingRuntime) ContainerCommandRunner {
+ return &containerCommandRunnerWrapper{runtime}
+}
+
+func (r *containerCommandRunnerWrapper) RunInContainer(id ContainerID, cmd []string, timeout time.Duration) ([]byte, error) {
+ var buffer bytes.Buffer
+ output := ioutils.WriteCloserWrapper(&buffer)
+ err := r.ExecInContainer(id, cmd, nil, output, output, false, nil, timeout)
+ // Even if err is non-nil, there still may be output (e.g. the exec wrote to stdout or stderr but
+ // the command returned a nonzero exit code). Therefore, always return the output along with the
+ // error.
+ return buffer.Bytes(), err
+}
+
+// GetContainerSpec gets the container spec by containerName.
+func GetContainerSpec(pod *v1.Pod, containerName string) *v1.Container {
+ for i, c := range pod.Spec.Containers {
+ if containerName == c.Name {
+ return &pod.Spec.Containers[i]
+ }
+ }
+ for i, c := range pod.Spec.InitContainers {
+ if containerName == c.Name {
+ return &pod.Spec.InitContainers[i]
+ }
+ }
+ return nil
+}
+
+// HasPrivilegedContainer returns true if any of the containers in the pod are privileged.
+func HasPrivilegedContainer(pod *v1.Pod) bool {
+ for _, c := range pod.Spec.Containers {
+ if c.SecurityContext != nil &&
+ c.SecurityContext.Privileged != nil &&
+ *c.SecurityContext.Privileged {
+ return true
+ }
+ }
+ return false
+}
+
+// MakeCapabilities creates string slices from Capability slices
+func MakeCapabilities(capAdd []v1.Capability, capDrop []v1.Capability) ([]string, []string) {
+ var (
+ addCaps []string
+ dropCaps []string
+ )
+ for _, cap := range capAdd {
+ addCaps = append(addCaps, string(cap))
+ }
+ for _, cap := range capDrop {
+ dropCaps = append(dropCaps, string(cap))
+ }
+ return addCaps, dropCaps
+}
+
+// MakePortMappings creates internal port mapping from api port mapping.
+func MakePortMappings(container *v1.Container) (ports []PortMapping) {
+ names := make(map[string]struct{})
+ for _, p := range container.Ports {
+ pm := PortMapping{
+ HostPort: int(p.HostPort),
+ ContainerPort: int(p.ContainerPort),
+ Protocol: p.Protocol,
+ HostIP: p.HostIP,
+ }
+
+ // We need to create some default port name if it's not specified, since
+ // this is necessary for rkt.
+ // http://issue.k8s.io/7710
+ if p.Name == "" {
+ pm.Name = fmt.Sprintf("%s-%s:%d", container.Name, p.Protocol, p.ContainerPort)
+ } else {
+ pm.Name = fmt.Sprintf("%s-%s", container.Name, p.Name)
+ }
+
+ // Protect against exposing the same protocol-port more than once in a container.
+ if _, ok := names[pm.Name]; ok {
+ glog.Warningf("Port name conflicted, %q is defined more than once", pm.Name)
+ continue
+ }
+ ports = append(ports, pm)
+ names[pm.Name] = struct{}{}
+ }
+ return
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/os.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/os.go
new file mode 100644
index 000000000..6126063b3
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/os.go
@@ -0,0 +1,107 @@
+/*
+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 container
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+)
+
+// OSInterface collects system level operations that need to be mocked out
+// during tests.
+type OSInterface interface {
+ MkdirAll(path string, perm os.FileMode) error
+ Symlink(oldname string, newname string) error
+ Stat(path string) (os.FileInfo, error)
+ Remove(path string) error
+ RemoveAll(path string) error
+ Create(path string) (*os.File, error)
+ Chmod(path string, perm os.FileMode) error
+ Hostname() (name string, err error)
+ Chtimes(path string, atime time.Time, mtime time.Time) error
+ Pipe() (r *os.File, w *os.File, err error)
+ ReadDir(dirname string) ([]os.FileInfo, error)
+ Glob(pattern string) ([]string, error)
+}
+
+// RealOS is used to dispatch the real system level operations.
+type RealOS struct{}
+
+// MkDir will will call os.Mkdir to create a directory.
+func (RealOS) MkdirAll(path string, perm os.FileMode) error {
+ return os.MkdirAll(path, perm)
+}
+
+// Symlink will call os.Symlink to create a symbolic link.
+func (RealOS) Symlink(oldname string, newname string) error {
+ return os.Symlink(oldname, newname)
+}
+
+// Stat will call os.Stat to get the FileInfo for a given path
+func (RealOS) Stat(path string) (os.FileInfo, error) {
+ return os.Stat(path)
+}
+
+// Remove will call os.Remove to remove the path.
+func (RealOS) Remove(path string) error {
+ return os.Remove(path)
+}
+
+// RemoveAll will call os.RemoveAll to remove the path and its children.
+func (RealOS) RemoveAll(path string) error {
+ return os.RemoveAll(path)
+}
+
+// Create will call os.Create to create and return a file
+// at path.
+func (RealOS) Create(path string) (*os.File, error) {
+ return os.Create(path)
+}
+
+// Chmod will change the permissions on the specified path or return
+// an error.
+func (RealOS) Chmod(path string, perm os.FileMode) error {
+ return os.Chmod(path, perm)
+}
+
+// Hostname will call os.Hostname to return the hostname.
+func (RealOS) Hostname() (name string, err error) {
+ return os.Hostname()
+}
+
+// Chtimes will call os.Chtimes to change the atime and mtime of the path
+func (RealOS) Chtimes(path string, atime time.Time, mtime time.Time) error {
+ return os.Chtimes(path, atime, mtime)
+}
+
+// Pipe will call os.Pipe to return a connected pair of pipe.
+func (RealOS) Pipe() (r *os.File, w *os.File, err error) {
+ return os.Pipe()
+}
+
+// ReadDir will call ioutil.ReadDir to return the files under the directory.
+func (RealOS) ReadDir(dirname string) ([]os.FileInfo, error) {
+ return ioutil.ReadDir(dirname)
+}
+
+// Glob will call filepath.Glob to return the names of all files matching
+// pattern.
+func (RealOS) Glob(pattern string) ([]string, error) {
+ return filepath.Glob(pattern)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_linux.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_linux.go
new file mode 100644
index 000000000..40906ce99
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_linux.go
@@ -0,0 +1,30 @@
+// +build linux
+
+/*
+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 container
+
+import (
+ "os"
+ "os/exec"
+
+ "github.com/kr/pty"
+)
+
+func StartPty(c *exec.Cmd) (*os.File, error) {
+ return pty.Start(c)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_unsupported.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_unsupported.go
new file mode 100644
index 000000000..24ea2f787
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_unsupported.go
@@ -0,0 +1,28 @@
+// +build !linux
+
+/*
+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 container
+
+import (
+ "os"
+ "os/exec"
+)
+
+func StartPty(c *exec.Cmd) (pty *os.File, err error) {
+ return nil, nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/ref.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/ref.go
new file mode 100644
index 000000000..0251b8134
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/ref.go
@@ -0,0 +1,73 @@
+/*
+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 container
+
+import (
+ "fmt"
+
+ "k8s.io/kubernetes/pkg/api"
+ "k8s.io/kubernetes/pkg/api/v1"
+ "k8s.io/kubernetes/pkg/api/v1/ref"
+)
+
+var ImplicitContainerPrefix string = "implicitly required container "
+
+// GenerateContainerRef returns an *v1.ObjectReference which references the given container
+// within the given pod. Returns an error if the reference can't be constructed or the
+// container doesn't actually belong to the pod.
+//
+// This function will return an error if the provided Pod does not have a selfLink,
+// but we expect selfLink to be populated at all call sites for the function.
+func GenerateContainerRef(pod *v1.Pod, container *v1.Container) (*v1.ObjectReference, error) {
+ fieldPath, err := fieldPath(pod, container)
+ if err != nil {
+ // TODO: figure out intelligent way to refer to containers that we implicitly
+ // start (like the pod infra container). This is not a good way, ugh.
+ fieldPath = ImplicitContainerPrefix + container.Name
+ }
+ ref, err := ref.GetPartialReference(api.Scheme, pod, fieldPath)
+ if err != nil {
+ return nil, err
+ }
+ return ref, nil
+}
+
+// fieldPath returns a fieldPath locating container within pod.
+// Returns an error if the container isn't part of the pod.
+func fieldPath(pod *v1.Pod, container *v1.Container) (string, error) {
+ for i := range pod.Spec.Containers {
+ here := &pod.Spec.Containers[i]
+ if here.Name == container.Name {
+ if here.Name == "" {
+ return fmt.Sprintf("spec.containers[%d]", i), nil
+ } else {
+ return fmt.Sprintf("spec.containers{%s}", here.Name), nil
+ }
+ }
+ }
+ for i := range pod.Spec.InitContainers {
+ here := &pod.Spec.InitContainers[i]
+ if here.Name == container.Name {
+ if here.Name == "" {
+ return fmt.Sprintf("spec.initContainers[%d]", i), nil
+ } else {
+ return fmt.Sprintf("spec.initContainers{%s}", here.Name), nil
+ }
+ }
+ }
+ return "", fmt.Errorf("container %#v not found in pod %#v", container, pod)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/resize.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/resize.go
new file mode 100644
index 000000000..d7b75eede
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/resize.go
@@ -0,0 +1,46 @@
+/*
+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 container
+
+import (
+ "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// handleResizing spawns a goroutine that processes the resize channel, calling resizeFunc for each
+// remotecommand.TerminalSize received from the channel. The resize channel must be closed elsewhere to stop the
+// goroutine.
+func HandleResizing(resize <-chan remotecommand.TerminalSize, resizeFunc func(size remotecommand.TerminalSize)) {
+ if resize == nil {
+ return
+ }
+
+ go func() {
+ defer runtime.HandleCrash()
+
+ for {
+ size, ok := <-resize
+ if !ok {
+ return
+ }
+ if size.Height < 1 || size.Width < 1 {
+ continue
+ }
+ resizeFunc(size)
+ }
+ }()
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime.go
new file mode 100644
index 000000000..c3403ad57
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime.go
@@ -0,0 +1,646 @@
+/*
+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 container
+
+import (
+ "fmt"
+ "io"
+ "net/url"
+ "reflect"
+ "strings"
+ "time"
+
+ "github.com/golang/glog"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/tools/remotecommand"
+ "k8s.io/client-go/util/flowcontrol"
+ "k8s.io/kubernetes/pkg/api/v1"
+ runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
+ "k8s.io/kubernetes/pkg/volume"
+)
+
+type Version interface {
+ // Compare compares two versions of the runtime. On success it returns -1
+ // if the version is less than the other, 1 if it is greater than the other,
+ // or 0 if they are equal.
+ Compare(other string) (int, error)
+ // String returns a string that represents the version.
+ String() string
+}
+
+// ImageSpec is an internal representation of an image. Currently, it wraps the
+// value of a Container's Image field, but in the future it will include more detailed
+// information about the different image types.
+type ImageSpec struct {
+ Image string
+}
+
+// ImageStats contains statistics about all the images currently available.
+type ImageStats struct {
+ // Total amount of storage consumed by existing images.
+ TotalStorageBytes uint64
+}
+
+// Runtime interface defines the interfaces that should be implemented
+// by a container runtime.
+// Thread safety is required from implementations of this interface.
+type Runtime interface {
+ // Type returns the type of the container runtime.
+ Type() string
+
+ // Version returns the version information of the container runtime.
+ Version() (Version, error)
+
+ // APIVersion returns the cached API version information of the container
+ // runtime. Implementation is expected to update this cache periodically.
+ // This may be different from the runtime engine's version.
+ // TODO(random-liu): We should fold this into Version()
+ APIVersion() (Version, error)
+ // Status returns the status of the runtime. An error is returned if the Status
+ // function itself fails, nil otherwise.
+ Status() (*RuntimeStatus, error)
+ // GetPods returns a list of containers grouped by pods. The boolean parameter
+ // specifies whether the runtime returns all containers including those already
+ // exited and dead containers (used for garbage collection).
+ GetPods(all bool) ([]*Pod, error)
+ // GarbageCollect removes dead containers using the specified container gc policy
+ // If allSourcesReady is not true, it means that kubelet doesn't have the
+ // complete list of pods from all avialble sources (e.g., apiserver, http,
+ // file). In this case, garbage collector should refrain itself from aggressive
+ // behavior such as removing all containers of unrecognized pods (yet).
+ // If evictNonDeletedPods is set to true, containers and sandboxes belonging to pods
+ // that are terminated, but not deleted will be evicted. Otherwise, only deleted pods will be GC'd.
+ // TODO: Revisit this method and make it cleaner.
+ GarbageCollect(gcPolicy ContainerGCPolicy, allSourcesReady bool, evictNonDeletedPods bool) error
+ // Syncs the running pod into the desired pod.
+ SyncPod(pod *v1.Pod, apiPodStatus v1.PodStatus, podStatus *PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) PodSyncResult
+ // KillPod kills all the containers of a pod. Pod may be nil, running pod must not be.
+ // TODO(random-liu): Return PodSyncResult in KillPod.
+ // gracePeriodOverride if specified allows the caller to override the pod default grace period.
+ // only hard kill paths are allowed to specify a gracePeriodOverride in the kubelet in order to not corrupt user data.
+ // it is useful when doing SIGKILL for hard eviction scenarios, or max grace period during soft eviction scenarios.
+ KillPod(pod *v1.Pod, runningPod Pod, gracePeriodOverride *int64) error
+ // GetPodStatus retrieves the status of the pod, including the
+ // information of all containers in the pod that are visble in Runtime.
+ GetPodStatus(uid types.UID, name, namespace string) (*PodStatus, error)
+ // Returns the filesystem path of the pod's network namespace; if the
+ // runtime does not handle namespace creation itself, or cannot return
+ // the network namespace path, it should return an error.
+ // TODO: Change ContainerID to a Pod ID since the namespace is shared
+ // by all containers in the pod.
+ GetNetNS(containerID ContainerID) (string, error)
+ // Returns the container ID that represents the Pod, as passed to network
+ // plugins. For example, if the runtime uses an infra container, returns
+ // the infra container's ContainerID.
+ // TODO: Change ContainerID to a Pod ID, see GetNetNS()
+ GetPodContainerID(*Pod) (ContainerID, error)
+ // TODO(vmarmol): Unify pod and containerID args.
+ // GetContainerLogs returns logs of a specific container. By
+ // default, it returns a snapshot of the container log. Set 'follow' to true to
+ // stream the log. Set 'follow' to false and specify the number of lines (e.g.
+ // "100" or "all") to tail the log.
+ GetContainerLogs(pod *v1.Pod, containerID ContainerID, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) (err error)
+ // Delete a container. If the container is still running, an error is returned.
+ DeleteContainer(containerID ContainerID) error
+ // ImageService provides methods to image-related methods.
+ ImageService
+ // UpdatePodCIDR sends a new podCIDR to the runtime.
+ // This method just proxies a new runtimeConfig with the updated
+ // CIDR value down to the runtime shim.
+ UpdatePodCIDR(podCIDR string) error
+}
+
+// DirectStreamingRuntime is the interface implemented by runtimes for which the streaming calls
+// (exec/attach/port-forward) should be served directly by the Kubelet.
+type DirectStreamingRuntime interface {
+ // Runs the command in the container of the specified pod using nsenter.
+ // Attaches the processes stdin, stdout, and stderr. Optionally uses a
+ // tty.
+ ExecInContainer(containerID ContainerID, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error
+ // Forward the specified port from the specified pod to the stream.
+ PortForward(pod *Pod, port int32, stream io.ReadWriteCloser) error
+ // ContainerAttach encapsulates the attaching to containers for testability
+ ContainerAttacher
+}
+
+// IndirectStreamingRuntime is the interface implemented by runtimes that handle the serving of the
+// streaming calls (exec/attach/port-forward) themselves. In this case, Kubelet should redirect to
+// the runtime server.
+type IndirectStreamingRuntime interface {
+ GetExec(id ContainerID, cmd []string, stdin, stdout, stderr, tty bool) (*url.URL, error)
+ GetAttach(id ContainerID, stdin, stdout, stderr, tty bool) (*url.URL, error)
+ GetPortForward(podName, podNamespace string, podUID types.UID, ports []int32) (*url.URL, error)
+}
+
+type ImageService interface {
+ // PullImage pulls an image from the network to local storage using the supplied
+ // secrets if necessary. It returns a reference (digest or ID) to the pulled image.
+ PullImage(image ImageSpec, pullSecrets []v1.Secret) (string, error)
+ // GetImageRef gets the reference (digest or ID) of the image which has already been in
+ // the local storage. It returns ("", nil) if the image isn't in the local storage.
+ GetImageRef(image ImageSpec) (string, error)
+ // Gets all images currently on the machine.
+ ListImages() ([]Image, error)
+ // Removes the specified image.
+ RemoveImage(image ImageSpec) error
+ // Returns Image statistics.
+ ImageStats() (*ImageStats, error)
+}
+
+type ContainerAttacher interface {
+ AttachContainer(id ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) (err error)
+}
+
+type ContainerCommandRunner interface {
+ // RunInContainer synchronously executes the command in the container, and returns the output.
+ // If the command completes with a non-0 exit code, a pkg/util/exec.ExitError will be returned.
+ RunInContainer(id ContainerID, cmd []string, timeout time.Duration) ([]byte, error)
+}
+
+// Pod is a group of containers.
+type Pod struct {
+ // The ID of the pod, which can be used to retrieve a particular pod
+ // from the pod list returned by GetPods().
+ ID types.UID
+ // The name and namespace of the pod, which is readable by human.
+ Name string
+ Namespace string
+ // List of containers that belongs to this pod. It may contain only
+ // running containers, or mixed with dead ones (when GetPods(true)).
+ Containers []*Container
+ // List of sandboxes associated with this pod. The sandboxes are converted
+ // to Container temporariliy to avoid substantial changes to other
+ // components. This is only populated by kuberuntime.
+ // TODO: use the runtimeApi.PodSandbox type directly.
+ Sandboxes []*Container
+}
+
+// PodPair contains both runtime#Pod and api#Pod
+type PodPair struct {
+ // APIPod is the v1.Pod
+ APIPod *v1.Pod
+ // RunningPod is the pod defined in pkg/kubelet/container/runtime#Pod
+ RunningPod *Pod
+}
+
+// ContainerID is a type that identifies a container.
+type ContainerID struct {
+ // The type of the container runtime. e.g. 'docker', 'rkt'.
+ Type string
+ // The identification of the container, this is comsumable by
+ // the underlying container runtime. (Note that the container
+ // runtime interface still takes the whole struct as input).
+ ID string
+}
+
+func BuildContainerID(typ, ID string) ContainerID {
+ return ContainerID{Type: typ, ID: ID}
+}
+
+// Convenience method for creating a ContainerID from an ID string.
+func ParseContainerID(containerID string) ContainerID {
+ var id ContainerID
+ if err := id.ParseString(containerID); err != nil {
+ glog.Error(err)
+ }
+ return id
+}
+
+func (c *ContainerID) ParseString(data string) error {
+ // Trim the quotes and split the type and ID.
+ parts := strings.Split(strings.Trim(data, "\""), "://")
+ if len(parts) != 2 {
+ return fmt.Errorf("invalid container ID: %q", data)
+ }
+ c.Type, c.ID = parts[0], parts[1]
+ return nil
+}
+
+func (c *ContainerID) String() string {
+ return fmt.Sprintf("%s://%s", c.Type, c.ID)
+}
+
+func (c *ContainerID) IsEmpty() bool {
+ return *c == ContainerID{}
+}
+
+func (c *ContainerID) MarshalJSON() ([]byte, error) {
+ return []byte(fmt.Sprintf("%q", c.String())), nil
+}
+
+func (c *ContainerID) UnmarshalJSON(data []byte) error {
+ return c.ParseString(string(data))
+}
+
+// DockerID is an ID of docker container. It is a type to make it clear when we're working with docker container Ids
+type DockerID string
+
+func (id DockerID) ContainerID() ContainerID {
+ return ContainerID{
+ Type: "docker",
+ ID: string(id),
+ }
+}
+
+type ContainerState string
+
+const (
+ ContainerStateCreated ContainerState = "created"
+ ContainerStateRunning ContainerState = "running"
+ ContainerStateExited ContainerState = "exited"
+ // This unknown encompasses all the states that we currently don't care.
+ ContainerStateUnknown ContainerState = "unknown"
+)
+
+// Container provides the runtime information for a container, such as ID, hash,
+// state of the container.
+type Container struct {
+ // The ID of the container, used by the container runtime to identify
+ // a container.
+ ID ContainerID
+ // The name of the container, which should be the same as specified by
+ // v1.Container.
+ Name string
+ // The image name of the container, this also includes the tag of the image,
+ // the expected form is "NAME:TAG".
+ Image string
+ // The id of the image used by the container.
+ ImageID string
+ // Hash of the container, used for comparison. Optional for containers
+ // not managed by kubelet.
+ Hash uint64
+ // State is the state of the container.
+ State ContainerState
+}
+
+// PodStatus represents the status of the pod and its containers.
+// v1.PodStatus can be derived from examining PodStatus and v1.Pod.
+type PodStatus struct {
+ // ID of the pod.
+ ID types.UID
+ // Name of the pod.
+ Name string
+ // Namspace of the pod.
+ Namespace string
+ // IP of the pod.
+ IP string
+ // Status of containers in the pod.
+ ContainerStatuses []*ContainerStatus
+ // Status of the pod sandbox.
+ // Only for kuberuntime now, other runtime may keep it nil.
+ SandboxStatuses []*runtimeapi.PodSandboxStatus
+}
+
+// ContainerStatus represents the status of a container.
+type ContainerStatus struct {
+ // ID of the container.
+ ID ContainerID
+ // Name of the container.
+ Name string
+ // Status of the container.
+ State ContainerState
+ // Creation time of the container.
+ CreatedAt time.Time
+ // Start time of the container.
+ StartedAt time.Time
+ // Finish time of the container.
+ FinishedAt time.Time
+ // Exit code of the container.
+ ExitCode int
+ // Name of the image, this also includes the tag of the image,
+ // the expected form is "NAME:TAG".
+ Image string
+ // ID of the image.
+ ImageID string
+ // Hash of the container, used for comparison.
+ Hash uint64
+ // Number of times that the container has been restarted.
+ RestartCount int
+ // A string explains why container is in such a status.
+ Reason string
+ // Message written by the container before exiting (stored in
+ // TerminationMessagePath).
+ Message string
+}
+
+// FindContainerStatusByName returns container status in the pod status with the given name.
+// When there are multiple containers' statuses with the same name, the first match will be returned.
+func (podStatus *PodStatus) FindContainerStatusByName(containerName string) *ContainerStatus {
+ for _, containerStatus := range podStatus.ContainerStatuses {
+ if containerStatus.Name == containerName {
+ return containerStatus
+ }
+ }
+ return nil
+}
+
+// Get container status of all the running containers in a pod
+func (podStatus *PodStatus) GetRunningContainerStatuses() []*ContainerStatus {
+ runningContainerStatuses := []*ContainerStatus{}
+ for _, containerStatus := range podStatus.ContainerStatuses {
+ if containerStatus.State == ContainerStateRunning {
+ runningContainerStatuses = append(runningContainerStatuses, containerStatus)
+ }
+ }
+ return runningContainerStatuses
+}
+
+// Basic information about a container image.
+type Image struct {
+ // ID of the image.
+ ID string
+ // Other names by which this image is known.
+ RepoTags []string
+ // Digests by which this image is known.
+ RepoDigests []string
+ // The size of the image in bytes.
+ Size int64
+}
+
+type EnvVar struct {
+ Name string
+ Value string
+}
+
+type Mount struct {
+ // Name of the volume mount.
+ // TODO(yifan): Remove this field, as this is not representing the unique name of the mount,
+ // but the volume name only.
+ Name string
+ // Path of the mount within the container.
+ ContainerPath string
+ // Path of the mount on the host.
+ HostPath string
+ // Whether the mount is read-only.
+ ReadOnly bool
+ // Whether the mount needs SELinux relabeling
+ SELinuxRelabel bool
+}
+
+type PortMapping struct {
+ // Name of the port mapping
+ Name string
+ // Protocol of the port mapping.
+ Protocol v1.Protocol
+ // The port number within the container.
+ ContainerPort int
+ // The port number on the host.
+ HostPort int
+ // The host IP.
+ HostIP string
+}
+
+type DeviceInfo struct {
+ // Path on host for mapping
+ PathOnHost string
+ // Path in Container to map
+ PathInContainer string
+ // Cgroup permissions
+ Permissions string
+}
+
+// RunContainerOptions specify the options which are necessary for running containers
+type RunContainerOptions struct {
+ // The environment variables list.
+ Envs []EnvVar
+ // The mounts for the containers.
+ Mounts []Mount
+ // The host devices mapped into the containers.
+ Devices []DeviceInfo
+ // The port mappings for the containers.
+ PortMappings []PortMapping
+ // If the container has specified the TerminationMessagePath, then
+ // this directory will be used to create and mount the log file to
+ // container.TerminationMessagePath
+ PodContainerDir string
+ // The list of DNS servers for the container to use.
+ DNS []string
+ // The list of DNS search domains.
+ DNSSearch []string
+ // The parent cgroup to pass to Docker
+ CgroupParent string
+ // The type of container rootfs
+ ReadOnly bool
+ // hostname for pod containers
+ Hostname string
+ // EnableHostUserNamespace sets userns=host when users request host namespaces (pid, ipc, net),
+ // are using non-namespaced capabilities (mknod, sys_time, sys_module), the pod contains a privileged container,
+ // or using host path volumes.
+ // This should only be enabled when the container runtime is performing user remapping AND if the
+ // experimental behavior is desired.
+ EnableHostUserNamespace bool
+}
+
+// VolumeInfo contains information about the volume.
+type VolumeInfo struct {
+ // Mounter is the volume's mounter
+ Mounter volume.Mounter
+ // SELinuxLabeled indicates whether this volume has had the
+ // pod's SELinux label applied to it or not
+ SELinuxLabeled bool
+}
+
+type VolumeMap map[string]VolumeInfo
+
+// RuntimeConditionType is the types of required runtime conditions.
+type RuntimeConditionType string
+
+const (
+ // RuntimeReady means the runtime is up and ready to accept basic containers.
+ RuntimeReady RuntimeConditionType = "RuntimeReady"
+ // NetworkReady means the runtime network is up and ready to accept containers which require network.
+ NetworkReady RuntimeConditionType = "NetworkReady"
+)
+
+// RuntimeStatus contains the status of the runtime.
+type RuntimeStatus struct {
+ // Conditions is an array of current observed runtime conditions.
+ Conditions []RuntimeCondition
+}
+
+// GetRuntimeCondition gets a specified runtime condition from the runtime status.
+func (r *RuntimeStatus) GetRuntimeCondition(t RuntimeConditionType) *RuntimeCondition {
+ for i := range r.Conditions {
+ c := &r.Conditions[i]
+ if c.Type == t {
+ return c
+ }
+ }
+ return nil
+}
+
+// String formats the runtime status into human readable string.
+func (s *RuntimeStatus) String() string {
+ var ss []string
+ for _, c := range s.Conditions {
+ ss = append(ss, c.String())
+ }
+ return fmt.Sprintf("Runtime Conditions: %s", strings.Join(ss, ", "))
+}
+
+// RuntimeCondition contains condition information for the runtime.
+type RuntimeCondition struct {
+ // Type of runtime condition.
+ Type RuntimeConditionType
+ // Status of the condition, one of true/false.
+ Status bool
+ // Reason is brief reason for the condition's last transition.
+ Reason string
+ // Message is human readable message indicating details about last transition.
+ Message string
+}
+
+// String formats the runtime condition into human readable string.
+func (c *RuntimeCondition) String() string {
+ return fmt.Sprintf("%s=%t reason:%s message:%s", c.Type, c.Status, c.Reason, c.Message)
+}
+
+type Pods []*Pod
+
+// FindPodByID finds and returns a pod in the pod list by UID. It will return an empty pod
+// if not found.
+func (p Pods) FindPodByID(podUID types.UID) Pod {
+ for i := range p {
+ if p[i].ID == podUID {
+ return *p[i]
+ }
+ }
+ return Pod{}
+}
+
+// FindPodByFullName finds and returns a pod in the pod list by the full name.
+// It will return an empty pod if not found.
+func (p Pods) FindPodByFullName(podFullName string) Pod {
+ for i := range p {
+ if BuildPodFullName(p[i].Name, p[i].Namespace) == podFullName {
+ return *p[i]
+ }
+ }
+ return Pod{}
+}
+
+// FindPod combines FindPodByID and FindPodByFullName, it finds and returns a pod in the
+// pod list either by the full name or the pod ID. It will return an empty pod
+// if not found.
+func (p Pods) FindPod(podFullName string, podUID types.UID) Pod {
+ if len(podFullName) > 0 {
+ return p.FindPodByFullName(podFullName)
+ }
+ return p.FindPodByID(podUID)
+}
+
+// FindContainerByName returns a container in the pod with the given name.
+// When there are multiple containers with the same name, the first match will
+// be returned.
+func (p *Pod) FindContainerByName(containerName string) *Container {
+ for _, c := range p.Containers {
+ if c.Name == containerName {
+ return c
+ }
+ }
+ return nil
+}
+
+func (p *Pod) FindContainerByID(id ContainerID) *Container {
+ for _, c := range p.Containers {
+ if c.ID == id {
+ return c
+ }
+ }
+ return nil
+}
+
+func (p *Pod) FindSandboxByID(id ContainerID) *Container {
+ for _, c := range p.Sandboxes {
+ if c.ID == id {
+ return c
+ }
+ }
+ return nil
+}
+
+// ToAPIPod converts Pod to v1.Pod. Note that if a field in v1.Pod has no
+// corresponding field in Pod, the field would not be populated.
+func (p *Pod) ToAPIPod() *v1.Pod {
+ var pod v1.Pod
+ pod.UID = p.ID
+ pod.Name = p.Name
+ pod.Namespace = p.Namespace
+
+ for _, c := range p.Containers {
+ var container v1.Container
+ container.Name = c.Name
+ container.Image = c.Image
+ pod.Spec.Containers = append(pod.Spec.Containers, container)
+ }
+ return &pod
+}
+
+// IsEmpty returns true if the pod is empty.
+func (p *Pod) IsEmpty() bool {
+ return reflect.DeepEqual(p, &Pod{})
+}
+
+// GetPodFullName returns a name that uniquely identifies a pod.
+func GetPodFullName(pod *v1.Pod) string {
+ // Use underscore as the delimiter because it is not allowed in pod name
+ // (DNS subdomain format), while allowed in the container name format.
+ return pod.Name + "_" + pod.Namespace
+}
+
+// Build the pod full name from pod name and namespace.
+func BuildPodFullName(name, namespace string) string {
+ return name + "_" + namespace
+}
+
+// Parse the pod full name.
+func ParsePodFullName(podFullName string) (string, string, error) {
+ parts := strings.Split(podFullName, "_")
+ if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
+ return "", "", fmt.Errorf("failed to parse the pod full name %q", podFullName)
+ }
+ return parts[0], parts[1], nil
+}
+
+// Option is a functional option type for Runtime, useful for
+// completely optional settings.
+type Option func(Runtime)
+
+// Sort the container statuses by creation time.
+type SortContainerStatusesByCreationTime []*ContainerStatus
+
+func (s SortContainerStatusesByCreationTime) Len() int { return len(s) }
+func (s SortContainerStatusesByCreationTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s SortContainerStatusesByCreationTime) Less(i, j int) bool {
+ return s[i].CreatedAt.Before(s[j].CreatedAt)
+}
+
+const (
+ // MaxPodTerminationMessageLogLength is the maximum bytes any one pod may have written
+ // as termination message output across all containers. Containers will be evenly truncated
+ // until output is below this limit.
+ MaxPodTerminationMessageLogLength = 1024 * 12
+ // MaxContainerTerminationMessageLength is the upper bound any one container may write to
+ // its termination message path. Contents above this length will be truncated.
+ MaxContainerTerminationMessageLength = 1024 * 4
+ // MaxContainerTerminationMessageLogLength is the maximum bytes any one container will
+ // have written to its termination message when the message is read from the logs.
+ MaxContainerTerminationMessageLogLength = 1024 * 2
+ // MaxContainerTerminationMessageLogLines is the maximum number of previous lines of
+ // log output that the termination message can contain.
+ MaxContainerTerminationMessageLogLines = 80
+)
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache.go
new file mode 100644
index 000000000..d15852f88
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache.go
@@ -0,0 +1,96 @@
+/*
+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 container
+
+import (
+ "sync"
+ "time"
+)
+
+var (
+ // TODO(yifan): Maybe set the them as parameters for NewCache().
+ defaultCachePeriod = time.Second * 2
+)
+
+type RuntimeCache interface {
+ GetPods() ([]*Pod, error)
+ ForceUpdateIfOlder(time.Time) error
+}
+
+type podsGetter interface {
+ GetPods(bool) ([]*Pod, error)
+}
+
+// NewRuntimeCache creates a container runtime cache.
+func NewRuntimeCache(getter podsGetter) (RuntimeCache, error) {
+ return &runtimeCache{
+ getter: getter,
+ }, nil
+}
+
+// runtimeCache caches a list of pods. It records a timestamp (cacheTime) right
+// before updating the pods, so the timestamp is at most as new as the pods
+// (and can be slightly older). The timestamp always moves forward. Callers are
+// expected not to modify the pods returned from GetPods.
+type runtimeCache struct {
+ sync.Mutex
+ // The underlying container runtime used to update the cache.
+ getter podsGetter
+ // Last time when cache was updated.
+ cacheTime time.Time
+ // The content of the cache.
+ pods []*Pod
+}
+
+// GetPods returns the cached pods if they are not outdated; otherwise, it
+// retrieves the latest pods and return them.
+func (r *runtimeCache) GetPods() ([]*Pod, error) {
+ r.Lock()
+ defer r.Unlock()
+ if time.Since(r.cacheTime) > defaultCachePeriod {
+ if err := r.updateCache(); err != nil {
+ return nil, err
+ }
+ }
+ return r.pods, nil
+}
+
+func (r *runtimeCache) ForceUpdateIfOlder(minExpectedCacheTime time.Time) error {
+ r.Lock()
+ defer r.Unlock()
+ if r.cacheTime.Before(minExpectedCacheTime) {
+ return r.updateCache()
+ }
+ return nil
+}
+
+func (r *runtimeCache) updateCache() error {
+ pods, timestamp, err := r.getPodsWithTimestamp()
+ if err != nil {
+ return err
+ }
+ r.pods, r.cacheTime = pods, timestamp
+ return nil
+}
+
+// getPodsWithTimestamp records a timestamp and retrieves pods from the getter.
+func (r *runtimeCache) getPodsWithTimestamp() ([]*Pod, time.Time, error) {
+ // Always record the timestamp before getting the pods to avoid stale pods.
+ timestamp := time.Now()
+ pods, err := r.getter.GetPods(false)
+ return pods, timestamp, err
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_fake.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_fake.go
new file mode 100644
index 000000000..59a6288d5
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_fake.go
@@ -0,0 +1,45 @@
+/*
+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 container
+
+// TestRunTimeCache embeds runtimeCache with some additional methods for testing.
+// It must be declared in the container package to have visibility to runtimeCache.
+// It cannot be in a "..._test.go" file in order for runtime_cache_test.go to have cross-package visibility to it.
+// (cross-package declarations in test files cannot be used from dot imports if this package is vendored)
+type TestRuntimeCache struct {
+ runtimeCache
+}
+
+func (r *TestRuntimeCache) UpdateCacheWithLock() error {
+ r.Lock()
+ defer r.Unlock()
+ return r.updateCache()
+}
+
+func (r *TestRuntimeCache) GetCachedPods() []*Pod {
+ r.Lock()
+ defer r.Unlock()
+ return r.pods
+}
+
+func NewTestRuntimeCache(getter podsGetter) *TestRuntimeCache {
+ return &TestRuntimeCache{
+ runtimeCache: runtimeCache{
+ getter: getter,
+ },
+ }
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result.go b/vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result.go
new file mode 100644
index 000000000..0d4563303
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result.go
@@ -0,0 +1,128 @@
+/*
+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 container
+
+import (
+ "errors"
+ "fmt"
+
+ utilerrors "k8s.io/apimachinery/pkg/util/errors"
+)
+
+// TODO(random-liu): We need to better organize runtime errors for introspection.
+
+// Container Terminated and Kubelet is backing off the restart
+var ErrCrashLoopBackOff = errors.New("CrashLoopBackOff")
+
+var (
+ // ErrContainerNotFound returned when a container in the given pod with the
+ // given container name was not found, amongst those managed by the kubelet.
+ ErrContainerNotFound = errors.New("no matching container")
+)
+
+var (
+ ErrRunContainer = errors.New("RunContainerError")
+ ErrKillContainer = errors.New("KillContainerError")
+ ErrVerifyNonRoot = errors.New("VerifyNonRootError")
+ ErrRunInitContainer = errors.New("RunInitContainerError")
+ ErrCreatePodSandbox = errors.New("CreatePodSandboxError")
+ ErrConfigPodSandbox = errors.New("ConfigPodSandboxError")
+ ErrKillPodSandbox = errors.New("KillPodSandboxError")
+)
+
+var (
+ ErrSetupNetwork = errors.New("SetupNetworkError")
+ ErrTeardownNetwork = errors.New("TeardownNetworkError")
+)
+
+// SyncAction indicates different kind of actions in SyncPod() and KillPod(). Now there are only actions
+// about start/kill container and setup/teardown network.
+type SyncAction string
+
+const (
+ StartContainer SyncAction = "StartContainer"
+ KillContainer SyncAction = "KillContainer"
+ SetupNetwork SyncAction = "SetupNetwork"
+ TeardownNetwork SyncAction = "TeardownNetwork"
+ InitContainer SyncAction = "InitContainer"
+ CreatePodSandbox SyncAction = "CreatePodSandbox"
+ ConfigPodSandbox SyncAction = "ConfigPodSandbox"
+ KillPodSandbox SyncAction = "KillPodSandbox"
+)
+
+// SyncResult is the result of sync action.
+type SyncResult struct {
+ // The associated action of the result
+ Action SyncAction
+ // The target of the action, now the target can only be:
+ // * Container: Target should be container name
+ // * Network: Target is useless now, we just set it as pod full name now
+ Target interface{}
+ // Brief error reason
+ Error error
+ // Human readable error reason
+ Message string
+}
+
+// NewSyncResult generates new SyncResult with specific Action and Target
+func NewSyncResult(action SyncAction, target interface{}) *SyncResult {
+ return &SyncResult{Action: action, Target: target}
+}
+
+// Fail fails the SyncResult with specific error and message
+func (r *SyncResult) Fail(err error, msg string) {
+ r.Error, r.Message = err, msg
+}
+
+// PodSyncResult is the summary result of SyncPod() and KillPod()
+type PodSyncResult struct {
+ // Result of different sync actions
+ SyncResults []*SyncResult
+ // Error encountered in SyncPod() and KillPod() that is not already included in SyncResults
+ SyncError error
+}
+
+// AddSyncResult adds multiple SyncResult to current PodSyncResult
+func (p *PodSyncResult) AddSyncResult(result ...*SyncResult) {
+ p.SyncResults = append(p.SyncResults, result...)
+}
+
+// AddPodSyncResult merges a PodSyncResult to current one
+func (p *PodSyncResult) AddPodSyncResult(result PodSyncResult) {
+ p.AddSyncResult(result.SyncResults...)
+ p.SyncError = result.SyncError
+}
+
+// Fail fails the PodSyncResult with an error occurred in SyncPod() and KillPod() itself
+func (p *PodSyncResult) Fail(err error) {
+ p.SyncError = err
+}
+
+// Error returns an error summarizing all the errors in PodSyncResult
+func (p *PodSyncResult) Error() error {
+ errlist := []error{}
+ if p.SyncError != nil {
+ errlist = append(errlist, fmt.Errorf("failed to SyncPod: %v\n", p.SyncError))
+ }
+ for _, result := range p.SyncResults {
+ if result.Error != nil {
+ errlist = append(errlist, fmt.Errorf("failed to %q for %q with %v: %q\n", result.Action, result.Target,
+ result.Error, result.Message))
+ }
+ }
+ return utilerrors.NewAggregate(errlist)
+}