mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			540 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			540 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2014 The Kubernetes Authors All rights reserved.
 | 
						|
 | 
						|
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 dockertools
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"math/rand"
 | 
						|
	"os"
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	dockertypes "github.com/docker/engine-api/types"
 | 
						|
	dockercontainer "github.com/docker/engine-api/types/container"
 | 
						|
	docker "github.com/fsouza/go-dockerclient"
 | 
						|
 | 
						|
	"k8s.io/kubernetes/pkg/api"
 | 
						|
	"k8s.io/kubernetes/pkg/util/sets"
 | 
						|
)
 | 
						|
 | 
						|
// FakeDockerClient is a simple fake docker client, so that kubelet can be run for testing without requiring a real docker setup.
 | 
						|
type FakeDockerClient struct {
 | 
						|
	sync.Mutex
 | 
						|
	RunningContainerList []dockertypes.Container
 | 
						|
	ExitedContainerList  []dockertypes.Container
 | 
						|
	ContainerMap         map[string]*dockertypes.ContainerJSON
 | 
						|
	Image                *docker.Image
 | 
						|
	Images               []docker.APIImages
 | 
						|
	Errors               map[string]error
 | 
						|
	called               []string
 | 
						|
	pulled               []string
 | 
						|
	// Created, Stopped and Removed all container docker ID
 | 
						|
	Created       []string
 | 
						|
	Stopped       []string
 | 
						|
	Removed       []string
 | 
						|
	RemovedImages sets.String
 | 
						|
	VersionInfo   docker.Env
 | 
						|
	Information   docker.Env
 | 
						|
	ExecInspect   *docker.ExecInspect
 | 
						|
	execCmd       []string
 | 
						|
	EnableSleep   bool
 | 
						|
}
 | 
						|
 | 
						|
// We don't check docker version now, just set the docker version of fake docker client to 1.8.1.
 | 
						|
// Notice that if someday we also have minimum docker version requirement, this should also be updated.
 | 
						|
const fakeDockerVersion = "1.8.1"
 | 
						|
 | 
						|
func NewFakeDockerClient() *FakeDockerClient {
 | 
						|
	return NewFakeDockerClientWithVersion(fakeDockerVersion, minimumDockerAPIVersion)
 | 
						|
}
 | 
						|
 | 
						|
func NewFakeDockerClientWithVersion(version, apiVersion string) *FakeDockerClient {
 | 
						|
	return &FakeDockerClient{
 | 
						|
		VersionInfo:   docker.Env{fmt.Sprintf("Version=%s", version), fmt.Sprintf("ApiVersion=%s", apiVersion)},
 | 
						|
		Errors:        make(map[string]error),
 | 
						|
		RemovedImages: sets.String{},
 | 
						|
		ContainerMap:  make(map[string]*dockertypes.ContainerJSON),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) InjectError(fn string, err error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.Errors[fn] = err
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) InjectErrors(errs map[string]error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	for fn, err := range errs {
 | 
						|
		f.Errors[fn] = err
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) ClearErrors() {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.Errors = map[string]error{}
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) ClearCalls() {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = []string{}
 | 
						|
	f.Stopped = []string{}
 | 
						|
	f.pulled = []string{}
 | 
						|
	f.Created = []string{}
 | 
						|
	f.Removed = []string{}
 | 
						|
}
 | 
						|
 | 
						|
// Because the new data type returned by engine-api is too complex to manually initialize, we need a
 | 
						|
// fake container which is easier to initialize.
 | 
						|
type FakeContainer struct {
 | 
						|
	ID         string
 | 
						|
	Name       string
 | 
						|
	Running    bool
 | 
						|
	ExitCode   int
 | 
						|
	Pid        int
 | 
						|
	CreatedAt  time.Time
 | 
						|
	StartedAt  time.Time
 | 
						|
	FinishedAt time.Time
 | 
						|
	Config     *dockercontainer.Config
 | 
						|
	HostConfig *dockercontainer.HostConfig
 | 
						|
}
 | 
						|
 | 
						|
// convertFakeContainer converts the fake container to real container
 | 
						|
func convertFakeContainer(f *FakeContainer) *dockertypes.ContainerJSON {
 | 
						|
	if f.Config == nil {
 | 
						|
		f.Config = &dockercontainer.Config{}
 | 
						|
	}
 | 
						|
	if f.HostConfig == nil {
 | 
						|
		f.HostConfig = &dockercontainer.HostConfig{}
 | 
						|
	}
 | 
						|
	return &dockertypes.ContainerJSON{
 | 
						|
		ContainerJSONBase: &dockertypes.ContainerJSONBase{
 | 
						|
			ID:   f.ID,
 | 
						|
			Name: f.Name,
 | 
						|
			State: &dockertypes.ContainerState{
 | 
						|
				Running:    f.Running,
 | 
						|
				ExitCode:   f.ExitCode,
 | 
						|
				Pid:        f.Pid,
 | 
						|
				StartedAt:  dockerTimestampToString(f.StartedAt),
 | 
						|
				FinishedAt: dockerTimestampToString(f.FinishedAt),
 | 
						|
			},
 | 
						|
			Created:    dockerTimestampToString(f.CreatedAt),
 | 
						|
			HostConfig: f.HostConfig,
 | 
						|
		},
 | 
						|
		Config:          f.Config,
 | 
						|
		NetworkSettings: &dockertypes.NetworkSettings{},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) SetFakeContainers(containers []*FakeContainer) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	// Reset the lists and the map.
 | 
						|
	f.ContainerMap = map[string]*dockertypes.ContainerJSON{}
 | 
						|
	f.RunningContainerList = []dockertypes.Container{}
 | 
						|
	f.ExitedContainerList = []dockertypes.Container{}
 | 
						|
 | 
						|
	for i := range containers {
 | 
						|
		c := containers[i]
 | 
						|
		f.ContainerMap[c.ID] = convertFakeContainer(c)
 | 
						|
		container := dockertypes.Container{
 | 
						|
			Names: []string{c.Name},
 | 
						|
			ID:    c.ID,
 | 
						|
		}
 | 
						|
		if c.Running {
 | 
						|
			f.RunningContainerList = append(f.RunningContainerList, container)
 | 
						|
		} else {
 | 
						|
			f.ExitedContainerList = append(f.ExitedContainerList, container)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) SetFakeRunningContainers(containers []*FakeContainer) {
 | 
						|
	for _, c := range containers {
 | 
						|
		c.Running = true
 | 
						|
	}
 | 
						|
	f.SetFakeContainers(containers)
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) AssertCalls(calls []string) (err error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
 | 
						|
	if !reflect.DeepEqual(calls, f.called) {
 | 
						|
		err = fmt.Errorf("expected %#v, got %#v", calls, f.called)
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) AssertCreated(created []string) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
 | 
						|
	actualCreated := []string{}
 | 
						|
	for _, c := range f.Created {
 | 
						|
		dockerName, _, err := ParseDockerName(c)
 | 
						|
		if err != nil {
 | 
						|
			return fmt.Errorf("unexpected error: %v", err)
 | 
						|
		}
 | 
						|
		actualCreated = append(actualCreated, dockerName.ContainerName)
 | 
						|
	}
 | 
						|
	sort.StringSlice(created).Sort()
 | 
						|
	sort.StringSlice(actualCreated).Sort()
 | 
						|
	if !reflect.DeepEqual(created, actualCreated) {
 | 
						|
		return fmt.Errorf("expected %#v, got %#v", created, actualCreated)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) AssertStopped(stopped []string) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	sort.StringSlice(stopped).Sort()
 | 
						|
	sort.StringSlice(f.Stopped).Sort()
 | 
						|
	if !reflect.DeepEqual(stopped, f.Stopped) {
 | 
						|
		return fmt.Errorf("expected %#v, got %#v", stopped, f.Stopped)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) AssertUnorderedCalls(calls []string) (err error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
 | 
						|
	expected := make([]string, len(calls))
 | 
						|
	actual := make([]string, len(f.called))
 | 
						|
	copy(expected, calls)
 | 
						|
	copy(actual, f.called)
 | 
						|
 | 
						|
	sort.StringSlice(expected).Sort()
 | 
						|
	sort.StringSlice(actual).Sort()
 | 
						|
 | 
						|
	if !reflect.DeepEqual(actual, expected) {
 | 
						|
		err = fmt.Errorf("expected(sorted) %#v, got(sorted) %#v", expected, actual)
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) popError(op string) error {
 | 
						|
	if f.Errors == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	err, ok := f.Errors[op]
 | 
						|
	if ok {
 | 
						|
		delete(f.Errors, op)
 | 
						|
		return err
 | 
						|
	} else {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// ListContainers is a test-spy implementation of DockerInterface.ListContainers.
 | 
						|
// It adds an entry "list" to the internal method call record.
 | 
						|
func (f *FakeDockerClient) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "list")
 | 
						|
	err := f.popError("list")
 | 
						|
	containerList := append([]dockertypes.Container{}, f.RunningContainerList...)
 | 
						|
	if options.All {
 | 
						|
		// Although the container is not sorted, but the container with the same name should be in order,
 | 
						|
		// that is enough for us now.
 | 
						|
		// TODO(random-liu): Is a fully sorted array needed?
 | 
						|
		containerList = append(containerList, f.ExitedContainerList...)
 | 
						|
	}
 | 
						|
	return containerList, err
 | 
						|
}
 | 
						|
 | 
						|
// InspectContainer is a test-spy implementation of DockerInterface.InspectContainer.
 | 
						|
// It adds an entry "inspect" to the internal method call record.
 | 
						|
func (f *FakeDockerClient) InspectContainer(id string) (*dockertypes.ContainerJSON, error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "inspect_container")
 | 
						|
	err := f.popError("inspect_container")
 | 
						|
	if container, ok := f.ContainerMap[id]; ok {
 | 
						|
		return container, err
 | 
						|
	}
 | 
						|
	return nil, err
 | 
						|
}
 | 
						|
 | 
						|
// InspectImage is a test-spy implementation of DockerInterface.InspectImage.
 | 
						|
// It adds an entry "inspect" to the internal method call record.
 | 
						|
func (f *FakeDockerClient) InspectImage(name string) (*docker.Image, error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "inspect_image")
 | 
						|
	err := f.popError("inspect_image")
 | 
						|
	return f.Image, err
 | 
						|
}
 | 
						|
 | 
						|
// Sleeps random amount of time with the normal distribution with given mean and stddev
 | 
						|
// (in milliseconds), we never sleep less than cutOffMillis
 | 
						|
func (f *FakeDockerClient) normalSleep(mean, stdDev, cutOffMillis int) {
 | 
						|
	if !f.EnableSleep {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	cutoff := (time.Duration)(cutOffMillis) * time.Millisecond
 | 
						|
	delay := (time.Duration)(rand.NormFloat64()*float64(stdDev)+float64(mean)) * time.Millisecond
 | 
						|
	if delay < cutoff {
 | 
						|
		delay = cutoff
 | 
						|
	}
 | 
						|
	time.Sleep(delay)
 | 
						|
}
 | 
						|
 | 
						|
// CreateContainer is a test-spy implementation of DockerInterface.CreateContainer.
 | 
						|
// It adds an entry "create" to the internal method call record.
 | 
						|
func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "create")
 | 
						|
	if err := f.popError("create"); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	// This is not a very good fake. We'll just add this container's name to the list.
 | 
						|
	// Docker likes to add a '/', so copy that behavior.
 | 
						|
	name := "/" + c.Name
 | 
						|
	id := name
 | 
						|
	f.Created = append(f.Created, name)
 | 
						|
	// The newest container should be in front, because we assume so in GetPodStatus()
 | 
						|
	f.RunningContainerList = append([]dockertypes.Container{
 | 
						|
		{ID: name, Names: []string{name}, Image: c.Config.Image, Labels: c.Config.Labels},
 | 
						|
	}, f.RunningContainerList...)
 | 
						|
	f.ContainerMap[name] = convertFakeContainer(&FakeContainer{ID: id, Name: name, Config: c.Config, HostConfig: c.HostConfig})
 | 
						|
	f.normalSleep(100, 25, 25)
 | 
						|
	return &dockertypes.ContainerCreateResponse{ID: id}, nil
 | 
						|
}
 | 
						|
 | 
						|
// StartContainer is a test-spy implementation of DockerInterface.StartContainer.
 | 
						|
// It adds an entry "start" to the internal method call record.
 | 
						|
func (f *FakeDockerClient) StartContainer(id string) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "start")
 | 
						|
	if err := f.popError("start"); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	container, ok := f.ContainerMap[id]
 | 
						|
	if !ok {
 | 
						|
		container = convertFakeContainer(&FakeContainer{ID: id, Name: id})
 | 
						|
	}
 | 
						|
	container.State.Running = true
 | 
						|
	container.State.Pid = os.Getpid()
 | 
						|
	container.State.StartedAt = dockerTimestampToString(time.Now())
 | 
						|
	container.NetworkSettings.IPAddress = "2.3.4.5"
 | 
						|
	f.ContainerMap[id] = container
 | 
						|
	f.updateContainerStatus(id, statusRunningPrefix)
 | 
						|
	f.normalSleep(200, 50, 50)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// StopContainer is a test-spy implementation of DockerInterface.StopContainer.
 | 
						|
// It adds an entry "stop" to the internal method call record.
 | 
						|
func (f *FakeDockerClient) StopContainer(id string, timeout int) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "stop")
 | 
						|
	if err := f.popError("stop"); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	f.Stopped = append(f.Stopped, id)
 | 
						|
	// Container status should be Updated before container moved to ExitedContainerList
 | 
						|
	f.updateContainerStatus(id, statusExitedPrefix)
 | 
						|
	var newList []dockertypes.Container
 | 
						|
	for _, container := range f.RunningContainerList {
 | 
						|
		if container.ID == id {
 | 
						|
			// The newest exited container should be in front. Because we assume so in GetPodStatus()
 | 
						|
			f.ExitedContainerList = append([]dockertypes.Container{container}, f.ExitedContainerList...)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		newList = append(newList, container)
 | 
						|
	}
 | 
						|
	f.RunningContainerList = newList
 | 
						|
	container, ok := f.ContainerMap[id]
 | 
						|
	if !ok {
 | 
						|
		container = convertFakeContainer(&FakeContainer{
 | 
						|
			ID:         id,
 | 
						|
			Name:       id,
 | 
						|
			Running:    false,
 | 
						|
			StartedAt:  time.Now().Add(-time.Second),
 | 
						|
			FinishedAt: time.Now(),
 | 
						|
		})
 | 
						|
	} else {
 | 
						|
		container.State.FinishedAt = dockerTimestampToString(time.Now())
 | 
						|
		container.State.Running = false
 | 
						|
	}
 | 
						|
	f.ContainerMap[id] = container
 | 
						|
	f.normalSleep(200, 50, 50)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) RemoveContainer(id string, opts dockertypes.ContainerRemoveOptions) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "remove")
 | 
						|
	err := f.popError("remove")
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	for i := range f.ExitedContainerList {
 | 
						|
		if f.ExitedContainerList[i].ID == id {
 | 
						|
			delete(f.ContainerMap, id)
 | 
						|
			f.ExitedContainerList = append(f.ExitedContainerList[:i], f.ExitedContainerList[i+1:]...)
 | 
						|
			f.Removed = append(f.Removed, id)
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
	// To be a good fake, report error if container is not stopped.
 | 
						|
	return fmt.Errorf("container not stopped")
 | 
						|
}
 | 
						|
 | 
						|
// Logs is a test-spy implementation of DockerInterface.Logs.
 | 
						|
// It adds an entry "logs" to the internal method call record.
 | 
						|
func (f *FakeDockerClient) Logs(opts docker.LogsOptions) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "logs")
 | 
						|
	return f.popError("logs")
 | 
						|
}
 | 
						|
 | 
						|
// PullImage is a test-spy implementation of DockerInterface.PullImage.
 | 
						|
// It adds an entry "pull" to the internal method call record.
 | 
						|
func (f *FakeDockerClient) PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "pull")
 | 
						|
	err := f.popError("pull")
 | 
						|
	if err == nil {
 | 
						|
		registry := opts.Registry
 | 
						|
		if len(registry) != 0 {
 | 
						|
			registry = registry + "/"
 | 
						|
		}
 | 
						|
		authJson, _ := json.Marshal(auth)
 | 
						|
		f.pulled = append(f.pulled, fmt.Sprintf("%s%s:%s using %s", registry, opts.Repository, opts.Tag, string(authJson)))
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) Version() (*docker.Env, error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	return &f.VersionInfo, f.popError("version")
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) Info() (*docker.Env, error) {
 | 
						|
	return &f.Information, nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) CreateExec(opts docker.CreateExecOptions) (*docker.Exec, error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.execCmd = opts.Cmd
 | 
						|
	f.called = append(f.called, "create_exec")
 | 
						|
	return &docker.Exec{ID: "12345678"}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) StartExec(_ string, _ docker.StartExecOptions) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "start_exec")
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) AttachToContainer(opts docker.AttachToContainerOptions) error {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.called = append(f.called, "attach")
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) InspectExec(id string) (*docker.ExecInspect, error) {
 | 
						|
	return f.ExecInspect, f.popError("inspect_exec")
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) ListImages(opts docker.ListImagesOptions) ([]docker.APIImages, error) {
 | 
						|
	err := f.popError("list_images")
 | 
						|
	return f.Images, err
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) RemoveImage(image string) error {
 | 
						|
	err := f.popError("remove_image")
 | 
						|
	if err == nil {
 | 
						|
		f.RemovedImages.Insert(image)
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerClient) updateContainerStatus(id, status string) {
 | 
						|
	for i := range f.RunningContainerList {
 | 
						|
		if f.RunningContainerList[i].ID == id {
 | 
						|
			f.RunningContainerList[i].Status = status
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// FakeDockerPuller is a stub implementation of DockerPuller.
 | 
						|
type FakeDockerPuller struct {
 | 
						|
	sync.Mutex
 | 
						|
 | 
						|
	HasImages    []string
 | 
						|
	ImagesPulled []string
 | 
						|
 | 
						|
	// Every pull will return the first error here, and then reslice
 | 
						|
	// to remove it. Will give nil errors if this slice is empty.
 | 
						|
	ErrorsToInject []error
 | 
						|
}
 | 
						|
 | 
						|
// Pull records the image pull attempt, and optionally injects an error.
 | 
						|
func (f *FakeDockerPuller) Pull(image string, secrets []api.Secret) (err error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	f.ImagesPulled = append(f.ImagesPulled, image)
 | 
						|
 | 
						|
	if len(f.ErrorsToInject) > 0 {
 | 
						|
		err = f.ErrorsToInject[0]
 | 
						|
		f.ErrorsToInject = f.ErrorsToInject[1:]
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (f *FakeDockerPuller) IsImagePresent(name string) (bool, error) {
 | 
						|
	f.Lock()
 | 
						|
	defer f.Unlock()
 | 
						|
	if f.HasImages == nil {
 | 
						|
		return true, nil
 | 
						|
	}
 | 
						|
	for _, s := range f.HasImages {
 | 
						|
		if s == name {
 | 
						|
			return true, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false, nil
 | 
						|
}
 | 
						|
 | 
						|
// dockerTimestampToString converts the timestamp to string
 | 
						|
func dockerTimestampToString(t time.Time) string {
 | 
						|
	return t.Format(time.RFC3339Nano)
 | 
						|
}
 |