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 .
* /
2017-05-03 17:46:35 +00:00
package libdocker
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-28 20:54:51 +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-28 20:54:51 +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
2017-06-29 20:21:17 +00:00
dockertypes "github.com/docker/docker/api/types"
dockercontainer "github.com/docker/docker/api/types/container"
2017-09-05 19:38:57 +00:00
dockerimagetypes "github.com/docker/docker/api/types/image"
2017-02-18 15:06:24 +00:00
2017-06-22 17:25:57 +00:00
"k8s.io/api/core/v1"
2017-06-22 18:24:23 +00:00
"k8s.io/apimachinery/pkg/util/clock"
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
2017-03-02 01:18:02 +00:00
ImageInspects map [ string ] * dockertypes . ImageInspect
2017-06-29 20:21:17 +00:00
Images [ ] dockertypes . ImageSummary
2017-05-20 01:07:52 +00:00
ImageIDsNeedingAuth map [ string ] dockertypes . AuthConfig
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
2017-03-02 01:18:02 +00:00
Created [ ] string
Started [ ] string
Stopped [ ] string
Removed [ ] string
// Images pulled by ref (name or ID).
ImagesPulled [ ] string
2016-03-29 00:05:02 +00:00
VersionInfo dockertypes . Version
Information dockertypes . Info
ExecInspect * dockertypes . ContainerExecInspect
execCmd [ ] string
EnableSleep bool
2017-09-05 19:38:57 +00:00
ImageHistoryMap map [ string ] [ ] dockerimagetypes . HistoryResponseItem
2015-11-19 21:59:18 +00:00
}
2017-02-28 20:54:51 +00:00
const (
// Notice that if someday we also have minimum docker version requirement, this should also be updated.
2017-05-03 17:46:35 +00:00
fakeDockerVersion = "1.11.2"
2017-02-28 20:54:51 +00:00
fakeImageSize = 1024
2017-09-08 23:23:43 +00:00
// Docker prepends '/' to the container name.
dockerNamePrefix = "/"
2017-02-28 20:54:51 +00:00
)
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 {
2017-05-03 17:46:35 +00:00
// Docker's API version does not include the patch number.
VersionInfo : dockertypes . Version { Version : fakeDockerVersion , APIVersion : strings . TrimSuffix ( MinimumDockerAPIVersion , ".0" ) } ,
2017-01-12 21:24:35 +00:00
Errors : make ( map [ string ] error ) ,
ContainerMap : make ( map [ string ] * dockertypes . ContainerJSON ) ,
Clock : clock . RealClock { } ,
// default this to true, so that we trace calls, image pulls and container lifecycle
2017-05-20 01:07:52 +00:00
EnableTrace : true ,
ImageInspects : make ( map [ string ] * dockertypes . ImageInspect ) ,
ImageIDsNeedingAuth : make ( map [ string ] dockertypes . AuthConfig ) ,
2017-01-12 21:24:35 +00:00
}
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 {
2017-03-02 01:18:02 +00:00
ID : f . ID ,
Name : f . Name ,
Image : f . Config . Image ,
2016-04-04 22:27:20 +00:00
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-28 20:54:51 +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 {
2017-09-08 23:23:43 +00:00
names = append ( names , strings . TrimPrefix ( f . ContainerMap [ id ] . Name , dockerNamePrefix ) )
2017-02-28 20:54:51 +00:00
}
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-28 20:54:51 +00:00
func ( f * FakeDockerClient ) AssertCreatedByName ( created [ ] string ) error {
2016-07-25 23:25:36 +00:00
f . Lock ( )
defer f . Unlock ( )
2017-02-28 20:54:51 +00:00
actualCreated , err := f . idsToNames ( f . Created )
if err != nil {
return err
}
2017-03-02 01:18:02 +00:00
return sortedStringSlicesEqual ( created , actualCreated )
2016-07-25 23:25:36 +00:00
}
2017-02-28 20:54:51 +00:00
func ( f * FakeDockerClient ) AssertStoppedByName ( stopped [ ] string ) error {
2015-04-08 02:03:43 +00:00
f . Lock ( )
defer f . Unlock ( )
2017-02-28 20:54:51 +00:00
actualStopped , err := f . idsToNames ( f . Stopped )
if err != nil {
return err
}
2017-03-02 01:18:02 +00:00
return sortedStringSlicesEqual ( stopped , actualStopped )
2015-04-08 02:03:43 +00:00
}
2017-02-28 20:54:51 +00:00
func ( f * FakeDockerClient ) AssertStopped ( stopped [ ] string ) error {
2017-01-12 21:24:35 +00:00
f . Lock ( )
defer f . Unlock ( )
2017-02-28 20:54:51 +00:00
// Copy stopped to avoid modifying it.
actualStopped := append ( [ ] string { } , f . Stopped ... )
2017-03-02 01:18:02 +00:00
return sortedStringSlicesEqual ( stopped , actualStopped )
}
func ( f * FakeDockerClient ) AssertImagesPulled ( pulled [ ] string ) error {
f . Lock ( )
defer f . Unlock ( )
// Copy pulled to avoid modifying it.
actualPulled := append ( [ ] string { } , f . ImagesPulled ... )
return sortedStringSlicesEqual ( pulled , actualPulled )
}
2017-05-03 17:46:35 +00:00
func ( f * FakeDockerClient ) AssertImagesPulledMsgs ( expected [ ] string ) error {
f . Lock ( )
defer f . Unlock ( )
// Copy pulled to avoid modifying it.
actual := append ( [ ] string { } , f . pulled ... )
return sortedStringSlicesEqual ( expected , actual )
}
2017-03-02 01:18:02 +00:00
func sortedStringSlicesEqual ( expected , actual [ ] string ) error {
sort . StringSlice ( expected ) . Sort ( )
sort . StringSlice ( actual ) . Sort ( )
if ! reflect . DeepEqual ( expected , actual ) {
return fmt . Errorf ( "expected %#v, got %#v" , expected , actual )
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
}
2017-10-08 14:30:37 +00:00
return nil
2015-04-09 18:57:53 +00:00
}
2017-05-03 17:46:35 +00:00
// ListContainers is a test-spy implementation of Interface.ListContainers.
2014-07-10 12:26:24 +00:00
// 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-06-29 20:21:17 +00:00
// Filters containers with id, only support 1 id.
idFilters := options . Filters . Get ( "id" )
2017-02-28 20:54:51 +00:00
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
}
2017-06-29 20:21:17 +00:00
// Filters containers with status, only support 1 status.
statusFilters := options . Filters . Get ( "status" )
2017-02-28 20:54:51 +00:00
if len ( statusFilters ) == 1 {
var filtered [ ] dockertypes . Container
for _ , container := range containerList {
for _ , statusFilter := range statusFilters {
2017-11-28 23:32:18 +00:00
if toDockerContainerStatus ( container . Status ) == statusFilter {
2017-02-28 20:54:51 +00:00
filtered = append ( filtered , container )
break
}
}
}
containerList = filtered
}
2017-06-29 20:21:17 +00:00
// Filters containers with label filter.
labelFilters := options . Filters . Get ( "label" )
2017-02-28 20:54:51 +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-28 20:54:51 +00:00
if match {
filtered = append ( filtered , container )
2017-01-31 02:57:29 +00:00
}
}
2017-02-28 20:54:51 +00:00
containerList = filtered
2017-01-31 02:57:29 +00:00
}
2017-02-28 20:54:51 +00:00
return containerList , err
2014-06-24 01:28:06 +00:00
}
2017-11-28 23:32:18 +00:00
func toDockerContainerStatus ( state string ) string {
switch {
case strings . HasPrefix ( state , StatusCreatedPrefix ) :
return "created"
case strings . HasPrefix ( state , StatusRunningPrefix ) :
return "running"
case strings . HasPrefix ( state , StatusExitedPrefix ) :
return "exited"
default :
return "unknown"
}
}
2017-05-03 17:46:35 +00:00
// InspectContainer is a test-spy implementation of Interface.InspectContainer.
2014-07-10 12:26:24 +00:00
// 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
}
2017-08-21 20:28:44 +00:00
// InspectContainerWithSize is a test-spy implementation of Interface.InspectContainerWithSize.
// It adds an entry "inspect" to the internal method call record.
func ( f * FakeDockerClient ) InspectContainerWithSize ( id string ) ( * dockertypes . ContainerJSON , error ) {
f . Lock ( )
defer f . Unlock ( )
f . appendCalled ( calledDetail { name : "inspect_container_withsize" } )
err := f . popError ( "inspect_container_withsize" )
if container , ok := f . ContainerMap [ id ] ; ok {
return container , err
}
if err != nil {
// Use the custom error if it exists.
return nil , err
}
return nil , fmt . Errorf ( "container %q not found" , id )
}
2017-05-03 17:46:35 +00:00
// InspectImageByRef is a test-spy implementation of Interface.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" } )
2017-03-02 01:18:02 +00:00
if err := f . popError ( "inspect_image" ) ; err != nil {
return nil , err
}
if result , ok := f . ImageInspects [ name ] ; ok {
return result , nil
}
return nil , ImageNotFoundError { name }
2016-09-30 18:30:48 +00:00
}
2017-05-03 17:46:35 +00:00
// InspectImageByID is a test-spy implementation of Interface.InspectImageByID.
2016-09-30 18:30:48 +00:00
// 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" } )
2017-03-02 01:18:02 +00:00
if err := f . popError ( "inspect_image" ) ; err != nil {
return nil , err
}
if result , ok := f . ImageInspects [ name ] ; ok {
return result , nil
}
return nil , ImageNotFoundError { name }
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-28 20:54:51 +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 )
}
2017-05-03 17:46:35 +00:00
// CreateContainer is a test-spy implementation of Interface.CreateContainer.
2014-07-10 12:26:24 +00:00
// It adds an entry "create" to the internal method call record.
2017-06-29 20:21:17 +00:00
func ( f * FakeDockerClient ) CreateContainer ( c dockertypes . ContainerCreateConfig ) ( * dockercontainer . ContainerCreateCreatedBody , 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.
2017-09-08 23:23:43 +00:00
name := dockerNamePrefix + c . Name
2017-02-28 20:54:51 +00:00
id := GetFakeContainerID ( name )
f . appendContainerTrace ( "Created" , id )
2017-03-08 18:09:43 +00:00
timestamp := f . Clock . Now ( )
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-05-03 17:46:35 +00:00
{ ID : id , Names : [ ] string { name } , Image : c . Config . Image , Created : timestamp . Unix ( ) , State : StatusCreatedPrefix , Labels : c . Config . Labels } ,
2016-04-04 08:56:49 +00:00
} , f . RunningContainerList ... )
2017-02-28 20:54:51 +00:00
f . ContainerMap [ id ] = convertFakeContainer ( & FakeContainer {
2017-03-08 18:09:43 +00:00
ID : id , Name : name , Config : c . Config , HostConfig : c . HostConfig , CreatedAt : timestamp } )
2017-03-02 01:18:02 +00:00
2016-01-11 11:15:11 +00:00
f . normalSleep ( 100 , 25 , 25 )
2017-03-02 01:18:02 +00:00
2017-06-29 20:21:17 +00:00
return & dockercontainer . ContainerCreateCreatedBody { ID : id } , nil
2014-06-24 01:28:06 +00:00
}
2017-05-03 17:46:35 +00:00
// StartContainer is a test-spy implementation of Interface.StartContainer.
2014-07-10 12:26:24 +00:00
// 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 ]
2017-11-28 23:32:18 +00:00
if container . HostConfig . NetworkMode . IsContainer ( ) {
hostContainerID := container . HostConfig . NetworkMode . ConnectedContainer ( )
found := false
for _ , container := range f . RunningContainerList {
if container . ID == hostContainerID {
found = true
}
}
if ! found {
return fmt . Errorf ( "failed to start container \"%s\": Error response from daemon: cannot join network of a non running container: %s" , id , hostContainerID )
}
}
2017-03-08 18:09:43 +00:00
timestamp := f . Clock . Now ( )
2015-11-19 21:59:18 +00:00
if ! ok {
2017-03-08 18:09:43 +00:00
container = convertFakeContainer ( & FakeContainer { ID : id , Name : id , CreatedAt : timestamp } )
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 ( )
2017-03-08 18:09:43 +00:00
container . State . StartedAt = dockerTimestampToString ( timestamp )
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
2017-05-03 17:46:35 +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
}
2017-05-03 17:46:35 +00:00
// StopContainer is a test-spy implementation of Interface.StopContainer.
2014-07-10 12:26:24 +00:00
// It adds an entry "stop" to the internal method call record.
2017-06-29 20:21:17 +00:00
func ( f * FakeDockerClient ) StopContainer ( id string , timeout time . Duration ) 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
2017-05-03 17:46:35 +00:00
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
}
2017-05-19 17:20:11 +00:00
func ( f * FakeDockerClient ) UpdateContainerResources ( id string , updateConfig dockercontainer . UpdateConfig ) error {
return nil
}
2017-05-03 17:46:35 +00:00
// Logs is a test-spy implementation of Interface.Logs.
2014-08-27 19:41:32 +00:00
// 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
}
2017-05-20 01:07:52 +00:00
func ( f * FakeDockerClient ) isAuthorizedForImage ( image string , auth dockertypes . AuthConfig ) bool {
if reqd , exists := f . ImageIDsNeedingAuth [ image ] ; ! exists {
return true // no auth needed
} else {
return auth . Username == reqd . Username && auth . Password == reqd . Password
}
}
2017-05-03 17:46:35 +00:00
// PullImage is a test-spy implementation of Interface.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 {
2017-05-20 01:07:52 +00:00
if ! f . isAuthorizedForImage ( image , auth ) {
return ImageNotFoundError { ID : image }
}
2015-05-08 17:30:59 +00:00
authJson , _ := json . Marshal ( auth )
2017-03-02 01:18:02 +00:00
inspect := createImageInspectFromRef ( image )
f . ImageInspects [ image ] = inspect
2017-01-12 21:24:35 +00:00
f . appendPulled ( fmt . Sprintf ( "%s using %s" , image , string ( authJson ) ) )
2017-03-02 01:18:02 +00:00
f . Images = append ( f . Images , * createImageFromImageInspect ( * inspect ) )
f . ImagesPulled = append ( f . ImagesPulled , image )
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
}
2017-06-29 20:21:17 +00:00
func ( f * FakeDockerClient ) CreateExec ( id string , opts dockertypes . ExecConfig ) ( * dockertypes . IDResponse , 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" } )
2017-06-29 20:21:17 +00:00
return & dockertypes . IDResponse { 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" )
}
2017-06-29 20:21:17 +00:00
func ( f * FakeDockerClient ) ListImages ( opts dockertypes . ImageListOptions ) ( [ ] dockertypes . ImageSummary , error ) {
2017-03-02 01:18:02 +00:00
f . Lock ( )
defer f . Unlock ( )
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
}
2017-09-05 19:38:57 +00:00
func ( f * FakeDockerClient ) RemoveImage ( image string , opts dockertypes . ImageRemoveOptions ) ( [ ] dockertypes . ImageDeleteResponseItem , error ) {
2017-03-02 01:18:02 +00:00
f . Lock ( )
defer f . Unlock ( )
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
}
2017-09-05 19:38:57 +00:00
return [ ] dockertypes . ImageDeleteResponseItem { { Deleted : image } } , err
2014-12-22 19:54:07 +00:00
}
2017-06-29 20:21:17 +00:00
func ( f * FakeDockerClient ) InjectImages ( images [ ] dockertypes . ImageSummary ) {
2016-03-29 00:05:02 +00:00
f . Lock ( )
defer f . Unlock ( )
f . Images = append ( f . Images , images ... )
2017-03-02 01:18:02 +00:00
for _ , i := range images {
f . ImageInspects [ i . ID ] = createImageInspectFromImage ( i )
}
}
2017-06-29 20:21:17 +00:00
func ( f * FakeDockerClient ) MakeImagesPrivate ( images [ ] dockertypes . ImageSummary , auth dockertypes . AuthConfig ) {
2017-05-20 01:07:52 +00:00
f . Lock ( )
defer f . Unlock ( )
for _ , i := range images {
f . ImageIDsNeedingAuth [ i . ID ] = auth
}
}
2017-03-02 01:18:02 +00:00
func ( f * FakeDockerClient ) ResetImages ( ) {
f . Lock ( )
defer f . Unlock ( )
2017-06-29 20:21:17 +00:00
f . Images = [ ] dockertypes . ImageSummary { }
2017-03-02 01:18:02 +00:00
f . ImageInspects = make ( map [ string ] * dockertypes . ImageInspect )
2017-05-20 01:07:52 +00:00
f . ImageIDsNeedingAuth = make ( map [ string ] dockertypes . AuthConfig )
2017-03-02 01:18:02 +00:00
}
func ( f * FakeDockerClient ) InjectImageInspects ( inspects [ ] dockertypes . ImageInspect ) {
f . Lock ( )
defer f . Unlock ( )
for _ , i := range inspects {
f . Images = append ( f . Images , * createImageFromImageInspect ( i ) )
f . ImageInspects [ i . ID ] = & i
}
2016-03-29 00:05:02 +00:00
}
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
}
}
}
2017-06-29 20:21:17 +00:00
func ( f * FakeDockerClient ) ResizeExecTTY ( id string , height , width uint ) error {
2016-04-18 16:54:44 +00:00
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
}
2017-06-29 20:21:17 +00:00
func ( f * FakeDockerClient ) ResizeContainerTTY ( id string , height , width uint ) error {
2016-04-18 16:54:44 +00:00
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
}
2017-03-02 01:18:02 +00:00
func createImageInspectFromRef ( ref string ) * dockertypes . ImageInspect {
return & dockertypes . ImageInspect {
ID : ref ,
RepoTags : [ ] string { ref } ,
// Image size is required to be non-zero for CRI integration.
VirtualSize : fakeImageSize ,
Size : fakeImageSize ,
Config : & dockercontainer . Config { } ,
}
2014-06-24 23:31:33 +00:00
}
2017-06-29 20:21:17 +00:00
func createImageInspectFromImage ( image dockertypes . ImageSummary ) * dockertypes . ImageInspect {
2017-03-02 01:18:02 +00:00
return & dockertypes . ImageInspect {
ID : image . ID ,
RepoTags : image . RepoTags ,
// Image size is required to be non-zero for CRI integration.
VirtualSize : fakeImageSize ,
Size : fakeImageSize ,
Config : & dockercontainer . Config { } ,
2014-06-24 23:31:33 +00:00
}
}
2014-09-26 04:53:17 +00:00
2017-06-29 20:21:17 +00:00
func createImageFromImageInspect ( inspect dockertypes . ImageInspect ) * dockertypes . ImageSummary {
return & dockertypes . ImageSummary {
2017-03-02 01:18:02 +00:00
ID : inspect . ID ,
RepoTags : inspect . RepoTags ,
// Image size is required to be non-zero for CRI integration.
VirtualSize : fakeImageSize ,
Size : fakeImageSize ,
2014-10-02 18:58:58 +00:00
}
2014-09-26 04:53:17 +00:00
}
2017-03-02 01:18:02 +00:00
// dockerTimestampToString converts the timestamp to string
func dockerTimestampToString ( t time . Time ) string {
return t . Format ( time . RFC3339Nano )
}
2017-09-05 19:38:57 +00:00
func ( f * FakeDockerClient ) ImageHistory ( id string ) ( [ ] dockerimagetypes . HistoryResponseItem , error ) {
2016-03-29 00:05:02 +00:00
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
}
2017-09-05 19:38:57 +00:00
func ( f * FakeDockerClient ) InjectImageHistory ( data map [ string ] [ ] dockerimagetypes . HistoryResponseItem ) {
2016-03-29 00:05:02 +00:00
f . Lock ( )
defer f . Unlock ( )
f . ImageHistoryMap = data
}
2017-03-03 00:28:54 +00:00
// FakeDockerPuller is meant to be a simple wrapper around FakeDockerClient.
// Please do not add more functionalities to it.
type FakeDockerPuller struct {
2017-05-03 17:46:35 +00:00
client Interface
2017-03-03 00:28:54 +00:00
}
func ( f * FakeDockerPuller ) Pull ( image string , _ [ ] v1 . Secret ) error {
return f . client . PullImage ( image , dockertypes . AuthConfig { } , dockertypes . ImagePullOptions { } )
}
func ( f * FakeDockerPuller ) GetImageRef ( image string ) ( string , error ) {
_ , err := f . client . InspectImageByRef ( image )
if err != nil && IsImageNotFoundError ( err ) {
return "" , nil
}
return image , err
}
2017-08-21 20:28:44 +00:00
func ( f * FakeDockerClient ) GetContainerStats ( id string ) ( * dockertypes . StatsJSON , error ) {
f . Lock ( )
defer f . Unlock ( )
f . appendCalled ( calledDetail { name : "getContainerStats" } )
return nil , fmt . Errorf ( "not implemented" )
}