2014-06-24 01:28:06 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors.
|
2014-06-24 01:28:06 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2014-09-09 04:33:17 +00:00
|
|
|
package dockertools
|
2014-06-24 01:28:06 +00:00
|
|
|
|
|
|
|
import (
|
2015-05-08 17:30:59 +00:00
|
|
|
"encoding/json"
|
2014-06-19 12:29:42 +00:00
|
|
|
"fmt"
|
2017-02-16 00:54:41 +00:00
|
|
|
"hash/fnv"
|
2015-10-05 15:45:36 +00:00
|
|
|
"math/rand"
|
2015-04-09 21:31:44 +00:00
|
|
|
"os"
|
2014-09-09 04:33:17 +00:00
|
|
|
"reflect"
|
2015-03-26 22:06:27 +00:00
|
|
|
"sort"
|
2017-02-16 00:54:41 +00:00
|
|
|
"strconv"
|
2017-01-31 02:57:29 +00:00
|
|
|
"strings"
|
2014-07-23 16:53:31 +00:00
|
|
|
"sync"
|
2015-09-14 23:18:21 +00:00
|
|
|
"time"
|
2014-06-19 12:29:42 +00:00
|
|
|
|
2016-04-04 08:56:49 +00:00
|
|
|
dockertypes "github.com/docker/engine-api/types"
|
2016-04-04 22:27:20 +00:00
|
|
|
dockercontainer "github.com/docker/engine-api/types/container"
|
2015-05-08 17:30:59 +00:00
|
|
|
|
2017-02-16 00:54:41 +00:00
|
|
|
"k8s.io/client-go/util/clock"
|
2016-11-18 20:50:58 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/v1"
|
2014-06-24 01:28:06 +00:00
|
|
|
)
|
|
|
|
|
2016-07-20 00:36:48 +00:00
|
|
|
type calledDetail struct {
|
|
|
|
name string
|
|
|
|
arguments []interface{}
|
|
|
|
}
|
|
|
|
|
2016-10-08 04:35:18 +00:00
|
|
|
// NewCalledDetail create a new call detail item.
|
|
|
|
func NewCalledDetail(name string, arguments []interface{}) calledDetail {
|
|
|
|
return calledDetail{name: name, arguments: arguments}
|
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// FakeDockerClient is a simple fake docker client, so that kubelet can be run for testing without requiring a real docker setup.
|
2014-06-24 01:28:06 +00:00
|
|
|
type FakeDockerClient struct {
|
2014-09-09 04:33:17 +00:00
|
|
|
sync.Mutex
|
2016-08-30 00:40:28 +00:00
|
|
|
Clock clock.Clock
|
2016-04-04 08:56:49 +00:00
|
|
|
RunningContainerList []dockertypes.Container
|
|
|
|
ExitedContainerList []dockertypes.Container
|
2016-04-04 22:27:20 +00:00
|
|
|
ContainerMap map[string]*dockertypes.ContainerJSON
|
2016-04-06 14:15:38 +00:00
|
|
|
Image *dockertypes.ImageInspect
|
2016-04-03 15:40:22 +00:00
|
|
|
Images []dockertypes.Image
|
2016-04-04 08:56:49 +00:00
|
|
|
Errors map[string]error
|
2016-07-20 00:36:48 +00:00
|
|
|
called []calledDetail
|
2016-04-04 08:56:49 +00:00
|
|
|
pulled []string
|
2017-01-12 21:24:35 +00:00
|
|
|
EnableTrace bool
|
2016-04-03 15:40:22 +00:00
|
|
|
|
2017-01-12 21:24:35 +00:00
|
|
|
// Created, Started, Stopped and Removed all contain container docker ID
|
2016-03-29 00:05:02 +00:00
|
|
|
Created []string
|
2016-07-25 23:25:36 +00:00
|
|
|
Started []string
|
2016-03-29 00:05:02 +00:00
|
|
|
Stopped []string
|
|
|
|
Removed []string
|
|
|
|
VersionInfo dockertypes.Version
|
|
|
|
Information dockertypes.Info
|
|
|
|
ExecInspect *dockertypes.ContainerExecInspect
|
|
|
|
execCmd []string
|
|
|
|
EnableSleep bool
|
|
|
|
ImageHistoryMap map[string][]dockertypes.ImageHistory
|
2015-11-19 21:59:18 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 23:19:33 +00:00
|
|
|
const (
|
|
|
|
// 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.
|
|
|
|
fakeDockerVersion = "1.8.1"
|
|
|
|
|
|
|
|
fakeImageSize = 1024
|
|
|
|
)
|
2016-04-04 23:01:11 +00:00
|
|
|
|
2015-11-19 21:59:18 +00:00
|
|
|
func NewFakeDockerClient() *FakeDockerClient {
|
2017-01-12 21:24:35 +00:00
|
|
|
return &FakeDockerClient{
|
|
|
|
VersionInfo: dockertypes.Version{Version: fakeDockerVersion, APIVersion: minimumDockerAPIVersion},
|
|
|
|
Errors: make(map[string]error),
|
|
|
|
ContainerMap: make(map[string]*dockertypes.ContainerJSON),
|
|
|
|
Clock: clock.RealClock{},
|
|
|
|
// default this to an empty result, so that we never have a nil non-error response from InspectImage
|
|
|
|
Image: &dockertypes.ImageInspect{},
|
|
|
|
// default this to true, so that we trace calls, image pulls and container lifecycle
|
|
|
|
EnableTrace: true,
|
|
|
|
}
|
2016-02-23 21:27:28 +00:00
|
|
|
}
|
|
|
|
|
2017-01-12 21:24:35 +00:00
|
|
|
func (f *FakeDockerClient) WithClock(c clock.Clock) *FakeDockerClient {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
f.Clock = c
|
|
|
|
return f
|
2016-08-30 00:40:28 +00:00
|
|
|
}
|
|
|
|
|
2017-01-12 21:24:35 +00:00
|
|
|
func (f *FakeDockerClient) WithVersion(version, apiVersion string) *FakeDockerClient {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
f.VersionInfo = dockertypes.Version{Version: version, APIVersion: apiVersion}
|
|
|
|
return f
|
2016-08-30 00:40:28 +00:00
|
|
|
}
|
|
|
|
|
2017-01-12 21:24:35 +00:00
|
|
|
func (f *FakeDockerClient) WithTraceDisabled() *FakeDockerClient {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
f.EnableTrace = false
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FakeDockerClient) appendCalled(callDetail calledDetail) {
|
|
|
|
if f.EnableTrace {
|
|
|
|
f.called = append(f.called, callDetail)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FakeDockerClient) appendPulled(pull string) {
|
|
|
|
if f.EnableTrace {
|
|
|
|
f.pulled = append(f.pulled, pull)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FakeDockerClient) appendContainerTrace(traceCategory string, containerName string) {
|
|
|
|
if !f.EnableTrace {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch traceCategory {
|
|
|
|
case "Created":
|
|
|
|
f.Created = append(f.Created, containerName)
|
|
|
|
case "Started":
|
|
|
|
f.Started = append(f.Started, containerName)
|
|
|
|
case "Stopped":
|
|
|
|
f.Stopped = append(f.Stopped, containerName)
|
|
|
|
case "Removed":
|
|
|
|
f.Removed = append(f.Removed, containerName)
|
2015-11-19 21:59:18 +00:00
|
|
|
}
|
2014-06-24 01:28:06 +00:00
|
|
|
}
|
|
|
|
|
2016-03-03 10:01:15 +00:00
|
|
|
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{}
|
|
|
|
}
|
|
|
|
|
2014-12-17 05:11:27 +00:00
|
|
|
func (f *FakeDockerClient) ClearCalls() {
|
2014-09-09 04:33:17 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2016-07-20 00:36:48 +00:00
|
|
|
f.called = []calledDetail{}
|
2014-12-17 05:11:27 +00:00
|
|
|
f.pulled = []string{}
|
|
|
|
f.Created = []string{}
|
2017-01-12 21:24:35 +00:00
|
|
|
f.Started = []string{}
|
|
|
|
f.Stopped = []string{}
|
2014-12-17 05:11:27 +00:00
|
|
|
f.Removed = []string{}
|
2014-06-24 01:28:06 +00:00
|
|
|
}
|
|
|
|
|
2016-07-20 00:36:48 +00:00
|
|
|
func (f *FakeDockerClient) getCalledNames() []string {
|
|
|
|
names := []string{}
|
|
|
|
for _, detail := range f.called {
|
|
|
|
names = append(names, detail.name)
|
|
|
|
}
|
|
|
|
return names
|
|
|
|
}
|
|
|
|
|
2016-04-04 22:27:20 +00:00
|
|
|
// 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) {
|
2015-11-11 00:51:35 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
// Reset the lists and the map.
|
2016-04-04 22:27:20 +00:00
|
|
|
f.ContainerMap = map[string]*dockertypes.ContainerJSON{}
|
2016-04-04 08:56:49 +00:00
|
|
|
f.RunningContainerList = []dockertypes.Container{}
|
|
|
|
f.ExitedContainerList = []dockertypes.Container{}
|
2015-11-11 00:51:35 +00:00
|
|
|
|
|
|
|
for i := range containers {
|
|
|
|
c := containers[i]
|
2016-04-04 22:27:20 +00:00
|
|
|
f.ContainerMap[c.ID] = convertFakeContainer(c)
|
2016-04-04 08:56:49 +00:00
|
|
|
container := dockertypes.Container{
|
2015-11-11 00:51:35 +00:00
|
|
|
Names: []string{c.Name},
|
|
|
|
ID: c.ID,
|
|
|
|
}
|
2017-01-31 02:57:29 +00:00
|
|
|
if c.Config != nil {
|
|
|
|
container.Labels = c.Config.Labels
|
|
|
|
}
|
2016-04-04 22:27:20 +00:00
|
|
|
if c.Running {
|
2016-04-04 08:56:49 +00:00
|
|
|
f.RunningContainerList = append(f.RunningContainerList, container)
|
2015-11-11 00:51:35 +00:00
|
|
|
} else {
|
2016-04-04 08:56:49 +00:00
|
|
|
f.ExitedContainerList = append(f.ExitedContainerList, container)
|
2015-11-11 00:51:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-04 22:27:20 +00:00
|
|
|
func (f *FakeDockerClient) SetFakeRunningContainers(containers []*FakeContainer) {
|
2015-11-11 00:51:35 +00:00
|
|
|
for _, c := range containers {
|
2016-04-04 22:27:20 +00:00
|
|
|
c.Running = true
|
2015-11-11 00:51:35 +00:00
|
|
|
}
|
|
|
|
f.SetFakeContainers(containers)
|
|
|
|
}
|
|
|
|
|
2014-09-09 04:33:17 +00:00
|
|
|
func (f *FakeDockerClient) AssertCalls(calls []string) (err error) {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
|
2016-07-20 00:36:48 +00:00
|
|
|
if !reflect.DeepEqual(calls, f.getCalledNames()) {
|
|
|
|
err = fmt.Errorf("expected %#v, got %#v", calls, f.getCalledNames())
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-10-08 04:35:18 +00:00
|
|
|
func (f *FakeDockerClient) AssertCallDetails(calls ...calledDetail) (err error) {
|
2016-07-20 00:36:48 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
|
2014-09-09 04:33:17 +00:00
|
|
|
if !reflect.DeepEqual(calls, f.called) {
|
|
|
|
err = fmt.Errorf("expected %#v, got %#v", calls, f.called)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-02-16 00:54:41 +00:00
|
|
|
// idsToNames converts container ids into names. The caller must hold the lock.
|
|
|
|
func (f *FakeDockerClient) idsToNames(ids []string) ([]string, error) {
|
|
|
|
names := []string{}
|
|
|
|
for _, id := range ids {
|
|
|
|
dockerName, _, err := ParseDockerName(f.ContainerMap[id].Name)
|
2015-04-08 02:03:43 +00:00
|
|
|
if err != nil {
|
2017-02-16 00:54:41 +00:00
|
|
|
return nil, fmt.Errorf("unexpected error: %v", err)
|
2015-04-08 02:03:43 +00:00
|
|
|
}
|
2017-02-16 00:54:41 +00:00
|
|
|
names = append(names, dockerName.ContainerName)
|
|
|
|
}
|
|
|
|
return names, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FakeDockerClient) AssertCreatedByNameWithOrder(created []string) error {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
actualCreated, err := f.idsToNames(f.Created)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-04-08 02:03:43 +00:00
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(created, actualCreated) {
|
|
|
|
return fmt.Errorf("expected %#v, got %#v", created, actualCreated)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-16 00:54:41 +00:00
|
|
|
func (f *FakeDockerClient) AssertCreatedByName(created []string) error {
|
2016-07-25 23:25:36 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-02-16 00:54:41 +00:00
|
|
|
|
|
|
|
actualCreated, err := f.idsToNames(f.Created)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sort.StringSlice(created).Sort()
|
|
|
|
sort.StringSlice(actualCreated).Sort()
|
|
|
|
if !reflect.DeepEqual(created, actualCreated) {
|
|
|
|
return fmt.Errorf("expected %#v, got %#v", created, actualCreated)
|
2016-07-25 23:25:36 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-16 00:54:41 +00:00
|
|
|
func (f *FakeDockerClient) AssertStoppedByName(stopped []string) error {
|
2015-04-08 02:03:43 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-02-16 00:54:41 +00:00
|
|
|
|
|
|
|
actualStopped, err := f.idsToNames(f.Stopped)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-04-08 02:03:43 +00:00
|
|
|
sort.StringSlice(stopped).Sort()
|
2017-02-16 00:54:41 +00:00
|
|
|
sort.StringSlice(actualStopped).Sort()
|
|
|
|
if !reflect.DeepEqual(stopped, actualStopped) {
|
|
|
|
return fmt.Errorf("expected %#v, got %#v", stopped, actualStopped)
|
2015-04-08 02:03:43 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-16 00:54:41 +00:00
|
|
|
func (f *FakeDockerClient) AssertStopped(stopped []string) error {
|
2017-01-12 21:24:35 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-02-16 00:54:41 +00:00
|
|
|
// Copy stopped to avoid modifying it.
|
|
|
|
actualStopped := append([]string{}, f.Stopped...)
|
|
|
|
sort.StringSlice(stopped).Sort()
|
|
|
|
sort.StringSlice(actualStopped).Sort()
|
|
|
|
if !reflect.DeepEqual(stopped, actualStopped) {
|
|
|
|
return fmt.Errorf("expected %#v, got %#v", stopped, actualStopped)
|
2017-01-12 21:24:35 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-04-09 18:57:53 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// ListContainers is a test-spy implementation of DockerInterface.ListContainers.
|
|
|
|
// It adds an entry "list" to the internal method call record.
|
2016-04-04 08:56:49 +00:00
|
|
|
func (f *FakeDockerClient) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) {
|
2014-09-09 04:33:17 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "list"})
|
2015-04-09 18:57:53 +00:00
|
|
|
err := f.popError("list")
|
2016-04-04 08:56:49 +00:00
|
|
|
containerList := append([]dockertypes.Container{}, f.RunningContainerList...)
|
2015-04-08 02:03:43 +00:00
|
|
|
if options.All {
|
2016-02-12 19:33:32 +00:00
|
|
|
// Although the container is not sorted, but the container with the same name should be in order,
|
2015-10-23 20:02:32 +00:00
|
|
|
// that is enough for us now.
|
2015-12-31 08:41:05 +00:00
|
|
|
// TODO(random-liu): Is a fully sorted array needed?
|
2015-10-30 05:42:25 +00:00
|
|
|
containerList = append(containerList, f.ExitedContainerList...)
|
2015-04-08 02:03:43 +00:00
|
|
|
}
|
2017-02-16 23:19:33 +00:00
|
|
|
// Filter containers with id, only support 1 id.
|
|
|
|
idFilters := options.Filter.Get("id")
|
|
|
|
if len(idFilters) != 0 {
|
|
|
|
var filtered []dockertypes.Container
|
|
|
|
for _, container := range containerList {
|
|
|
|
for _, idFilter := range idFilters {
|
|
|
|
if container.ID == idFilter {
|
|
|
|
filtered = append(filtered, container)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
containerList = filtered
|
|
|
|
}
|
|
|
|
// Filter containers with status, only support 1 status.
|
|
|
|
statusFilters := options.Filter.Get("status")
|
|
|
|
if len(statusFilters) == 1 {
|
|
|
|
var filtered []dockertypes.Container
|
|
|
|
for _, container := range containerList {
|
|
|
|
for _, statusFilter := range statusFilters {
|
|
|
|
if container.Status == statusFilter {
|
|
|
|
filtered = append(filtered, container)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
containerList = filtered
|
|
|
|
}
|
2017-01-31 02:57:29 +00:00
|
|
|
// Filter containers with label filter.
|
|
|
|
labelFilters := options.Filter.Get("label")
|
2017-02-16 23:19:33 +00:00
|
|
|
if len(labelFilters) != 0 {
|
|
|
|
var filtered []dockertypes.Container
|
|
|
|
for _, container := range containerList {
|
|
|
|
match := true
|
|
|
|
for _, labelFilter := range labelFilters {
|
|
|
|
kv := strings.Split(labelFilter, "=")
|
|
|
|
if len(kv) != 2 {
|
|
|
|
return nil, fmt.Errorf("invalid label filter %q", labelFilter)
|
|
|
|
}
|
|
|
|
if container.Labels[kv[0]] != kv[1] {
|
|
|
|
match = false
|
|
|
|
break
|
|
|
|
}
|
2017-01-31 02:57:29 +00:00
|
|
|
}
|
2017-02-16 23:19:33 +00:00
|
|
|
if match {
|
|
|
|
filtered = append(filtered, container)
|
2017-01-31 02:57:29 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-16 23:19:33 +00:00
|
|
|
containerList = filtered
|
2017-01-31 02:57:29 +00:00
|
|
|
}
|
2017-02-16 23:19:33 +00:00
|
|
|
return containerList, err
|
2014-06-24 01:28:06 +00:00
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// InspectContainer is a test-spy implementation of DockerInterface.InspectContainer.
|
|
|
|
// It adds an entry "inspect" to the internal method call record.
|
2016-04-04 22:27:20 +00:00
|
|
|
func (f *FakeDockerClient) InspectContainer(id string) (*dockertypes.ContainerJSON, error) {
|
2014-09-09 04:33:17 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "inspect_container"})
|
2015-04-09 18:57:53 +00:00
|
|
|
err := f.popError("inspect_container")
|
2015-11-19 21:59:18 +00:00
|
|
|
if container, ok := f.ContainerMap[id]; ok {
|
|
|
|
return container, err
|
2014-10-28 00:29:55 +00:00
|
|
|
}
|
2016-08-30 00:40:28 +00:00
|
|
|
if err != nil {
|
|
|
|
// Use the custom error if it exists.
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("container %q not found", id)
|
2014-06-24 01:28:06 +00:00
|
|
|
}
|
|
|
|
|
2016-09-30 18:30:48 +00:00
|
|
|
// InspectImageByRef is a test-spy implementation of DockerInterface.InspectImageByRef.
|
2014-10-03 07:34:18 +00:00
|
|
|
// It adds an entry "inspect" to the internal method call record.
|
2016-09-30 18:30:48 +00:00
|
|
|
func (f *FakeDockerClient) InspectImageByRef(name string) (*dockertypes.ImageInspect, error) {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "inspect_image"})
|
2016-09-30 18:30:48 +00:00
|
|
|
err := f.popError("inspect_image")
|
|
|
|
return f.Image, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// InspectImageByID is a test-spy implementation of DockerInterface.InspectImageByID.
|
|
|
|
// It adds an entry "inspect" to the internal method call record.
|
|
|
|
func (f *FakeDockerClient) InspectImageByID(name string) (*dockertypes.ImageInspect, error) {
|
2014-10-03 07:34:18 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "inspect_image"})
|
2015-04-09 18:57:53 +00:00
|
|
|
err := f.popError("inspect_image")
|
|
|
|
return f.Image, err
|
2014-10-03 07:34:18 +00:00
|
|
|
}
|
|
|
|
|
2015-10-05 15:45:36 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2017-02-16 00:54:41 +00:00
|
|
|
// GetFakeContainerID generates a fake container id from container name with a hash.
|
|
|
|
func GetFakeContainerID(name string) string {
|
|
|
|
hash := fnv.New64a()
|
|
|
|
hash.Write([]byte(name))
|
|
|
|
return strconv.FormatUint(hash.Sum64(), 16)
|
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// CreateContainer is a test-spy implementation of DockerInterface.CreateContainer.
|
|
|
|
// It adds an entry "create" to the internal method call record.
|
2016-04-14 17:36:13 +00:00
|
|
|
func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error) {
|
2014-09-09 04:33:17 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "create"})
|
2015-10-05 15:45:36 +00:00
|
|
|
if err := f.popError("create"); err != nil {
|
|
|
|
return nil, err
|
2015-04-09 18:57:53 +00:00
|
|
|
}
|
2015-10-05 15:45:36 +00:00
|
|
|
// 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
|
2017-02-16 00:54:41 +00:00
|
|
|
id := GetFakeContainerID(name)
|
|
|
|
f.appendContainerTrace("Created", id)
|
2016-01-27 01:46:15 +00:00
|
|
|
// The newest container should be in front, because we assume so in GetPodStatus()
|
2016-04-04 08:56:49 +00:00
|
|
|
f.RunningContainerList = append([]dockertypes.Container{
|
2017-02-16 00:54:41 +00:00
|
|
|
{ID: id, Names: []string{name}, Image: c.Config.Image, Labels: c.Config.Labels},
|
2016-04-04 08:56:49 +00:00
|
|
|
}, f.RunningContainerList...)
|
2017-02-16 00:54:41 +00:00
|
|
|
f.ContainerMap[id] = convertFakeContainer(&FakeContainer{
|
2016-08-30 00:40:28 +00:00
|
|
|
ID: id, Name: name, Config: c.Config, HostConfig: c.HostConfig, CreatedAt: f.Clock.Now()})
|
2016-01-11 11:15:11 +00:00
|
|
|
f.normalSleep(100, 25, 25)
|
2016-04-14 17:36:13 +00:00
|
|
|
return &dockertypes.ContainerCreateResponse{ID: id}, nil
|
2014-06-24 01:28:06 +00:00
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// StartContainer is a test-spy implementation of DockerInterface.StartContainer.
|
|
|
|
// It adds an entry "start" to the internal method call record.
|
2016-04-14 17:37:35 +00:00
|
|
|
func (f *FakeDockerClient) StartContainer(id string) error {
|
2014-09-09 04:33:17 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "start"})
|
2015-10-05 15:45:36 +00:00
|
|
|
if err := f.popError("start"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendContainerTrace("Started", id)
|
2015-11-19 21:59:18 +00:00
|
|
|
container, ok := f.ContainerMap[id]
|
|
|
|
if !ok {
|
2016-04-04 22:27:20 +00:00
|
|
|
container = convertFakeContainer(&FakeContainer{ID: id, Name: id})
|
2015-10-05 15:45:36 +00:00
|
|
|
}
|
2016-04-04 22:27:20 +00:00
|
|
|
container.State.Running = true
|
|
|
|
container.State.Pid = os.Getpid()
|
2016-08-30 00:40:28 +00:00
|
|
|
container.State.StartedAt = dockerTimestampToString(f.Clock.Now())
|
2016-04-04 22:27:20 +00:00
|
|
|
container.NetworkSettings.IPAddress = "2.3.4.5"
|
2015-11-19 21:59:18 +00:00
|
|
|
f.ContainerMap[id] = container
|
2016-01-05 19:37:26 +00:00
|
|
|
f.updateContainerStatus(id, statusRunningPrefix)
|
2015-10-05 15:45:36 +00:00
|
|
|
f.normalSleep(200, 50, 50)
|
|
|
|
return nil
|
2014-06-24 01:28:06 +00:00
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// StopContainer is a test-spy implementation of DockerInterface.StopContainer.
|
|
|
|
// It adds an entry "stop" to the internal method call record.
|
2016-04-14 17:37:35 +00:00
|
|
|
func (f *FakeDockerClient) StopContainer(id string, timeout int) error {
|
2014-09-09 04:33:17 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "stop"})
|
2015-10-05 15:45:36 +00:00
|
|
|
if err := f.popError("stop"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendContainerTrace("Stopped", id)
|
2016-02-23 01:44:38 +00:00
|
|
|
// Container status should be Updated before container moved to ExitedContainerList
|
|
|
|
f.updateContainerStatus(id, statusExitedPrefix)
|
2016-04-04 08:56:49 +00:00
|
|
|
var newList []dockertypes.Container
|
|
|
|
for _, container := range f.RunningContainerList {
|
2015-10-05 15:45:36 +00:00
|
|
|
if container.ID == id {
|
2016-01-27 01:46:15 +00:00
|
|
|
// The newest exited container should be in front. Because we assume so in GetPodStatus()
|
2016-04-04 08:56:49 +00:00
|
|
|
f.ExitedContainerList = append([]dockertypes.Container{container}, f.ExitedContainerList...)
|
2015-10-05 15:45:36 +00:00
|
|
|
continue
|
2014-07-03 05:35:50 +00:00
|
|
|
}
|
2015-10-05 15:45:36 +00:00
|
|
|
newList = append(newList, container)
|
|
|
|
}
|
2016-04-04 08:56:49 +00:00
|
|
|
f.RunningContainerList = newList
|
2015-11-19 21:59:18 +00:00
|
|
|
container, ok := f.ContainerMap[id]
|
|
|
|
if !ok {
|
2016-04-04 22:27:20 +00:00
|
|
|
container = convertFakeContainer(&FakeContainer{
|
|
|
|
ID: id,
|
|
|
|
Name: id,
|
|
|
|
Running: false,
|
|
|
|
StartedAt: time.Now().Add(-time.Second),
|
|
|
|
FinishedAt: time.Now(),
|
|
|
|
})
|
2015-11-19 21:59:18 +00:00
|
|
|
} else {
|
2016-08-30 00:40:28 +00:00
|
|
|
container.State.FinishedAt = dockerTimestampToString(f.Clock.Now())
|
2015-11-19 21:59:18 +00:00
|
|
|
container.State.Running = false
|
2014-07-03 05:35:50 +00:00
|
|
|
}
|
2015-11-19 21:59:18 +00:00
|
|
|
f.ContainerMap[id] = container
|
2015-10-05 15:45:36 +00:00
|
|
|
f.normalSleep(200, 50, 50)
|
|
|
|
return nil
|
2014-06-24 01:28:06 +00:00
|
|
|
}
|
2014-06-24 23:31:33 +00:00
|
|
|
|
2016-04-14 17:37:35 +00:00
|
|
|
func (f *FakeDockerClient) RemoveContainer(id string, opts dockertypes.ContainerRemoveOptions) error {
|
2014-10-28 00:29:55 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "remove"})
|
2015-04-09 18:57:53 +00:00
|
|
|
err := f.popError("remove")
|
2016-02-23 01:44:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for i := range f.ExitedContainerList {
|
2016-04-14 17:37:35 +00:00
|
|
|
if f.ExitedContainerList[i].ID == id {
|
|
|
|
delete(f.ContainerMap, id)
|
2016-02-23 01:44:38 +00:00
|
|
|
f.ExitedContainerList = append(f.ExitedContainerList[:i], f.ExitedContainerList[i+1:]...)
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendContainerTrace("Removed", id)
|
2016-02-23 01:44:38 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-04-09 18:57:53 +00:00
|
|
|
}
|
2016-02-23 01:44:38 +00:00
|
|
|
// To be a good fake, report error if container is not stopped.
|
|
|
|
return fmt.Errorf("container not stopped")
|
2014-10-28 00:29:55 +00:00
|
|
|
}
|
|
|
|
|
2014-08-27 19:41:32 +00:00
|
|
|
// Logs is a test-spy implementation of DockerInterface.Logs.
|
|
|
|
// It adds an entry "logs" to the internal method call record.
|
2016-04-17 20:00:52 +00:00
|
|
|
func (f *FakeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error {
|
2014-08-27 19:41:32 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "logs"})
|
2015-04-09 18:57:53 +00:00
|
|
|
return f.popError("logs")
|
2014-08-27 19:41:32 +00:00
|
|
|
}
|
|
|
|
|
2016-04-14 17:37:35 +00:00
|
|
|
// PullImage is a test-spy implementation of DockerInterface.PullImage.
|
2014-07-10 12:26:24 +00:00
|
|
|
// It adds an entry "pull" to the internal method call record.
|
2016-04-25 19:40:59 +00:00
|
|
|
func (f *FakeDockerClient) PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error {
|
2014-09-09 04:33:17 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "pull"})
|
2015-04-09 18:57:53 +00:00
|
|
|
err := f.popError("pull")
|
|
|
|
if err == nil {
|
2015-05-08 17:30:59 +00:00
|
|
|
authJson, _ := json.Marshal(auth)
|
2016-12-30 02:48:49 +00:00
|
|
|
f.Image = &dockertypes.ImageInspect{
|
|
|
|
ID: image,
|
|
|
|
RepoTags: []string{image},
|
2017-02-16 23:19:33 +00:00
|
|
|
// Image size is required to be non-zero for CRI integration.
|
|
|
|
VirtualSize: fakeImageSize,
|
|
|
|
Size: fakeImageSize,
|
|
|
|
Config: &dockercontainer.Config{},
|
2016-12-30 02:48:49 +00:00
|
|
|
}
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendPulled(fmt.Sprintf("%s using %s", image, string(authJson)))
|
2015-03-17 00:51:20 +00:00
|
|
|
}
|
2015-04-09 18:57:53 +00:00
|
|
|
return err
|
2014-06-19 12:29:42 +00:00
|
|
|
}
|
|
|
|
|
2016-04-18 04:27:39 +00:00
|
|
|
func (f *FakeDockerClient) Version() (*dockertypes.Version, error) {
|
2016-03-14 08:35:49 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-02-03 02:28:19 +00:00
|
|
|
v := f.VersionInfo
|
|
|
|
return &v, f.popError("version")
|
2014-10-14 10:08:48 +00:00
|
|
|
}
|
|
|
|
|
2016-04-18 04:27:39 +00:00
|
|
|
func (f *FakeDockerClient) Info() (*dockertypes.Info, error) {
|
2015-04-21 00:26:40 +00:00
|
|
|
return &f.Information, nil
|
|
|
|
}
|
|
|
|
|
2016-04-17 19:58:47 +00:00
|
|
|
func (f *FakeDockerClient) CreateExec(id string, opts dockertypes.ExecConfig) (*dockertypes.ContainerExecCreateResponse, error) {
|
2015-05-06 03:50:45 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
f.execCmd = opts.Cmd
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "create_exec"})
|
2016-04-17 19:58:47 +00:00
|
|
|
return &dockertypes.ContainerExecCreateResponse{ID: "12345678"}, nil
|
2014-10-14 10:08:48 +00:00
|
|
|
}
|
2014-12-22 19:54:07 +00:00
|
|
|
|
2016-04-17 19:58:47 +00:00
|
|
|
func (f *FakeDockerClient) StartExec(startExec string, opts dockertypes.ExecStartCheck, sopts StreamOptions) error {
|
2015-05-06 03:50:45 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "start_exec"})
|
2014-10-14 10:08:48 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-17 20:00:52 +00:00
|
|
|
func (f *FakeDockerClient) AttachToContainer(id string, opts dockertypes.ContainerAttachOptions, sopts StreamOptions) error {
|
2015-07-28 04:48:55 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "attach"})
|
2015-07-28 04:48:55 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-17 19:58:47 +00:00
|
|
|
func (f *FakeDockerClient) InspectExec(id string) (*dockertypes.ContainerExecInspect, error) {
|
2015-05-08 16:48:31 +00:00
|
|
|
return f.ExecInspect, f.popError("inspect_exec")
|
|
|
|
}
|
|
|
|
|
2016-04-03 15:40:22 +00:00
|
|
|
func (f *FakeDockerClient) ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error) {
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "list_images"})
|
2015-04-09 18:57:53 +00:00
|
|
|
err := f.popError("list_images")
|
|
|
|
return f.Images, err
|
2014-12-22 19:54:07 +00:00
|
|
|
}
|
|
|
|
|
2016-04-06 15:06:37 +00:00
|
|
|
func (f *FakeDockerClient) RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error) {
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "remove_image", arguments: []interface{}{image, opts}})
|
2015-04-09 18:57:53 +00:00
|
|
|
err := f.popError("remove_image")
|
|
|
|
if err == nil {
|
2016-05-27 20:42:31 +00:00
|
|
|
for i := range f.Images {
|
|
|
|
if f.Images[i].ID == image {
|
|
|
|
f.Images = append(f.Images[:i], f.Images[i+1:]...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2015-04-09 18:57:53 +00:00
|
|
|
}
|
2016-04-06 15:06:37 +00:00
|
|
|
return []dockertypes.ImageDelete{{Deleted: image}}, err
|
2014-12-22 19:54:07 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 00:05:02 +00:00
|
|
|
func (f *FakeDockerClient) InjectImages(images []dockertypes.Image) {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
f.Images = append(f.Images, images...)
|
|
|
|
}
|
|
|
|
|
2016-01-05 19:37:26 +00:00
|
|
|
func (f *FakeDockerClient) updateContainerStatus(id, status string) {
|
2016-04-04 08:56:49 +00:00
|
|
|
for i := range f.RunningContainerList {
|
|
|
|
if f.RunningContainerList[i].ID == id {
|
|
|
|
f.RunningContainerList[i].Status = status
|
2016-01-05 19:37:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-18 16:54:44 +00:00
|
|
|
func (f *FakeDockerClient) ResizeExecTTY(id string, height, width int) error {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "resize_exec"})
|
2016-04-18 16:54:44 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FakeDockerClient) ResizeContainerTTY(id string, height, width int) error {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "resize_container"})
|
2016-04-18 16:54:44 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// FakeDockerPuller is a stub implementation of DockerPuller.
|
2014-06-24 23:31:33 +00:00
|
|
|
type FakeDockerPuller struct {
|
2014-09-09 04:33:17 +00:00
|
|
|
sync.Mutex
|
|
|
|
|
2014-10-02 18:58:58 +00:00
|
|
|
HasImages []string
|
2014-06-24 23:31:33 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-07-10 12:26:24 +00:00
|
|
|
// Pull records the image pull attempt, and optionally injects an error.
|
2016-11-18 20:50:58 +00:00
|
|
|
func (f *FakeDockerPuller) Pull(image string, secrets []v1.Secret) (err error) {
|
2014-09-09 04:33:17 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2014-06-24 23:31:33 +00:00
|
|
|
f.ImagesPulled = append(f.ImagesPulled, image)
|
|
|
|
|
2014-07-23 16:53:31 +00:00
|
|
|
if len(f.ErrorsToInject) > 0 {
|
|
|
|
err = f.ErrorsToInject[0]
|
|
|
|
f.ErrorsToInject = f.ErrorsToInject[1:]
|
2014-06-24 23:31:33 +00:00
|
|
|
}
|
2014-07-23 16:53:31 +00:00
|
|
|
return err
|
2014-06-24 23:31:33 +00:00
|
|
|
}
|
2014-09-26 04:53:17 +00:00
|
|
|
|
2016-12-31 00:36:55 +00:00
|
|
|
func (f *FakeDockerPuller) GetImageRef(name string) (string, error) {
|
2014-10-02 18:58:58 +00:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
if f.HasImages == nil {
|
2016-12-29 07:26:03 +00:00
|
|
|
return name, nil
|
2014-10-02 18:58:58 +00:00
|
|
|
}
|
|
|
|
for _, s := range f.HasImages {
|
|
|
|
if s == name {
|
2016-12-29 07:26:03 +00:00
|
|
|
return s, nil
|
2014-10-02 18:58:58 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-29 07:26:03 +00:00
|
|
|
return "", nil
|
2014-09-26 04:53:17 +00:00
|
|
|
}
|
2016-03-29 00:05:02 +00:00
|
|
|
func (f *FakeDockerClient) ImageHistory(id string) ([]dockertypes.ImageHistory, error) {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2017-01-12 21:24:35 +00:00
|
|
|
f.appendCalled(calledDetail{name: "image_history"})
|
2016-03-29 00:05:02 +00:00
|
|
|
history := f.ImageHistoryMap[id]
|
|
|
|
return history, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FakeDockerClient) InjectImageHistory(data map[string][]dockertypes.ImageHistory) {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
f.ImageHistoryMap = data
|
|
|
|
}
|
2016-04-14 17:36:13 +00:00
|
|
|
|
|
|
|
// dockerTimestampToString converts the timestamp to string
|
|
|
|
func dockerTimestampToString(t time.Time) string {
|
|
|
|
return t.Format(time.RFC3339Nano)
|
|
|
|
}
|