123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849 |
- /*
- Copyright 2014 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package libdocker
- import (
- "encoding/json"
- "fmt"
- "hash/fnv"
- "math/rand"
- "os"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "time"
- dockertypes "github.com/docker/docker/api/types"
- dockercontainer "github.com/docker/docker/api/types/container"
- dockerimagetypes "github.com/docker/docker/api/types/image"
- "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/util/clock"
- )
- type CalledDetail struct {
- name string
- arguments []interface{}
- }
- // NewCalledDetail create a new call detail item.
- func NewCalledDetail(name string, arguments []interface{}) CalledDetail {
- return CalledDetail{name: name, arguments: arguments}
- }
- // 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
- Clock clock.Clock
- RunningContainerList []dockertypes.Container
- ExitedContainerList []dockertypes.Container
- ContainerMap map[string]*dockertypes.ContainerJSON
- ImageInspects map[string]*dockertypes.ImageInspect
- Images []dockertypes.ImageSummary
- ImageIDsNeedingAuth map[string]dockertypes.AuthConfig
- Errors map[string]error
- called []CalledDetail
- pulled []string
- EnableTrace bool
- RandGenerator *rand.Rand
- // Created, Started, Stopped and Removed all contain container docker ID
- Created []string
- Started []string
- Stopped []string
- Removed []string
- // Images pulled by ref (name or ID).
- ImagesPulled []string
- VersionInfo dockertypes.Version
- Information dockertypes.Info
- ExecInspect *dockertypes.ContainerExecInspect
- execCmd []string
- EnableSleep bool
- ImageHistoryMap map[string][]dockerimagetypes.HistoryResponseItem
- ContainerStatsMap map[string]*dockertypes.StatsJSON
- }
- const (
- // Notice that if someday we also have minimum docker version requirement, this should also be updated.
- fakeDockerVersion = "1.13.1"
- fakeImageSize = 1024
- // Docker prepends '/' to the container name.
- dockerNamePrefix = "/"
- )
- func NewFakeDockerClient() *FakeDockerClient {
- return &FakeDockerClient{
- // Docker's API version does not include the patch number.
- VersionInfo: dockertypes.Version{Version: fakeDockerVersion, APIVersion: strings.TrimSuffix(MinimumDockerAPIVersion, ".0")},
- 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
- EnableTrace: true,
- ExecInspect: &dockertypes.ContainerExecInspect{},
- ImageInspects: make(map[string]*dockertypes.ImageInspect),
- ImageIDsNeedingAuth: make(map[string]dockertypes.AuthConfig),
- RandGenerator: rand.New(rand.NewSource(time.Now().UnixNano())),
- }
- }
- func (f *FakeDockerClient) WithClock(c clock.Clock) *FakeDockerClient {
- f.Lock()
- defer f.Unlock()
- f.Clock = c
- return f
- }
- func (f *FakeDockerClient) WithVersion(version, apiVersion string) *FakeDockerClient {
- f.Lock()
- defer f.Unlock()
- f.VersionInfo = dockertypes.Version{Version: version, APIVersion: apiVersion}
- return f
- }
- func (f *FakeDockerClient) WithTraceDisabled() *FakeDockerClient {
- f.Lock()
- defer f.Unlock()
- f.EnableTrace = false
- return f
- }
- func (f *FakeDockerClient) WithRandSource(source rand.Source) *FakeDockerClient {
- f.Lock()
- defer f.Unlock()
- f.RandGenerator = rand.New(source)
- 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)
- }
- }
- 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 = []CalledDetail{}
- f.pulled = []string{}
- f.Created = []string{}
- f.Started = []string{}
- f.Stopped = []string{}
- f.Removed = []string{}
- }
- func (f *FakeDockerClient) getCalledNames() []string {
- names := []string{}
- for _, detail := range f.called {
- names = append(names, detail.name)
- }
- return names
- }
- // 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,
- Image: f.Config.Image,
- 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.Config != nil {
- container.Labels = c.Config.Labels
- }
- if c.Running {
- f.RunningContainerList = append(f.RunningContainerList, container)
- } else {
- f.ExitedContainerList = append(f.ExitedContainerList, container)
- }
- }
- }
- func (f *FakeDockerClient) AssertCalls(calls []string) (err error) {
- f.Lock()
- defer f.Unlock()
- if !reflect.DeepEqual(calls, f.getCalledNames()) {
- err = fmt.Errorf("expected %#v, got %#v", calls, f.getCalledNames())
- }
- return
- }
- func (f *FakeDockerClient) AssertCallDetails(calls ...CalledDetail) (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) popError(op string) error {
- if f.Errors == nil {
- return nil
- }
- err, ok := f.Errors[op]
- if ok {
- delete(f.Errors, op)
- return err
- }
- return nil
- }
- // ListContainers is a test-spy implementation of Interface.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.appendCalled(CalledDetail{name: "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.
- containerList = append(containerList, f.ExitedContainerList...)
- }
- // Filters containers with id, only support 1 id.
- idFilters := options.Filters.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
- }
- // Filters containers with status, only support 1 status.
- statusFilters := options.Filters.Get("status")
- if len(statusFilters) == 1 {
- var filtered []dockertypes.Container
- for _, container := range containerList {
- for _, statusFilter := range statusFilters {
- if toDockerContainerStatus(container.Status) == statusFilter {
- filtered = append(filtered, container)
- break
- }
- }
- }
- containerList = filtered
- }
- // Filters containers with label filter.
- labelFilters := options.Filters.Get("label")
- 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
- }
- }
- if match {
- filtered = append(filtered, container)
- }
- }
- containerList = filtered
- }
- return containerList, err
- }
- 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"
- }
- }
- // InspectContainer is a test-spy implementation of Interface.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.appendCalled(CalledDetail{name: "inspect_container"})
- err := f.popError("inspect_container")
- 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)
- }
- // 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)
- }
- // InspectImageByRef is a test-spy implementation of Interface.InspectImageByRef.
- // It adds an entry "inspect" to the internal method call record.
- func (f *FakeDockerClient) InspectImageByRef(name string) (*dockertypes.ImageInspect, error) {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "inspect_image"})
- 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}
- }
- // InspectImageByID is a test-spy implementation of Interface.InspectImageByID.
- // It adds an entry "inspect" to the internal method call record.
- func (f *FakeDockerClient) InspectImageByID(name string) (*dockertypes.ImageInspect, error) {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "inspect_image"})
- 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}
- }
- // 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)(f.RandGenerator.NormFloat64()*float64(stdDev)+float64(mean)) * time.Millisecond
- if delay < cutoff {
- delay = cutoff
- }
- time.Sleep(delay)
- }
- // 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)
- }
- // CreateContainer is a test-spy implementation of Interface.CreateContainer.
- // It adds an entry "create" to the internal method call record.
- func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig) (*dockercontainer.ContainerCreateCreatedBody, error) {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "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.
- name := dockerNamePrefix + c.Name
- id := GetFakeContainerID(name)
- f.appendContainerTrace("Created", id)
- timestamp := f.Clock.Now()
- // The newest container should be in front, because we assume so in GetPodStatus()
- f.RunningContainerList = append([]dockertypes.Container{
- {ID: id, Names: []string{name}, Image: c.Config.Image, Created: timestamp.Unix(), State: StatusCreatedPrefix, Labels: c.Config.Labels},
- }, f.RunningContainerList...)
- f.ContainerMap[id] = convertFakeContainer(&FakeContainer{
- ID: id, Name: name, Config: c.Config, HostConfig: c.HostConfig, CreatedAt: timestamp})
- f.normalSleep(100, 25, 25)
- return &dockercontainer.ContainerCreateCreatedBody{ID: id}, nil
- }
- // StartContainer is a test-spy implementation of Interface.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.appendCalled(CalledDetail{name: "start"})
- if err := f.popError("start"); err != nil {
- return err
- }
- f.appendContainerTrace("Started", id)
- container, ok := f.ContainerMap[id]
- 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)
- }
- }
- timestamp := f.Clock.Now()
- if !ok {
- container = convertFakeContainer(&FakeContainer{ID: id, Name: id, CreatedAt: timestamp})
- }
- container.State.Running = true
- container.State.Pid = os.Getpid()
- container.State.StartedAt = dockerTimestampToString(timestamp)
- r := f.RandGenerator.Uint32()
- container.NetworkSettings.IPAddress = fmt.Sprintf("10.%d.%d.%d", byte(r>>16), byte(r>>8), byte(r))
- f.ContainerMap[id] = container
- f.updateContainerStatus(id, StatusRunningPrefix)
- f.normalSleep(200, 50, 50)
- return nil
- }
- // StopContainer is a test-spy implementation of Interface.StopContainer.
- // It adds an entry "stop" to the internal method call record.
- func (f *FakeDockerClient) StopContainer(id string, timeout time.Duration) error {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "stop"})
- if err := f.popError("stop"); err != nil {
- return err
- }
- f.appendContainerTrace("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(f.Clock.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.appendCalled(CalledDetail{name: "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.appendContainerTrace("Removed", id)
- return nil
- }
- }
- for i := range f.RunningContainerList {
- // allow removal of running containers which are not running
- if f.RunningContainerList[i].ID == id && !f.ContainerMap[id].State.Running {
- delete(f.ContainerMap, id)
- f.RunningContainerList = append(f.RunningContainerList[:i], f.RunningContainerList[i+1:]...)
- f.appendContainerTrace("Removed", id)
- return nil
- }
- }
- // To be a good fake, report error if container is not stopped.
- return fmt.Errorf("container not stopped")
- }
- func (f *FakeDockerClient) UpdateContainerResources(id string, updateConfig dockercontainer.UpdateConfig) error {
- return nil
- }
- // Logs is a test-spy implementation of Interface.Logs.
- // It adds an entry "logs" to the internal method call record.
- func (f *FakeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "logs"})
- return f.popError("logs")
- }
- 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
- }
- }
- // PullImage is a test-spy implementation of Interface.PullImage.
- // It adds an entry "pull" to the internal method call record.
- func (f *FakeDockerClient) PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "pull"})
- err := f.popError("pull")
- if err == nil {
- if !f.isAuthorizedForImage(image, auth) {
- return ImageNotFoundError{ID: image}
- }
- authJson, _ := json.Marshal(auth)
- inspect := createImageInspectFromRef(image)
- f.ImageInspects[image] = inspect
- f.appendPulled(fmt.Sprintf("%s using %s", image, string(authJson)))
- f.Images = append(f.Images, *createImageFromImageInspect(*inspect))
- f.ImagesPulled = append(f.ImagesPulled, image)
- }
- return err
- }
- func (f *FakeDockerClient) Version() (*dockertypes.Version, error) {
- f.Lock()
- defer f.Unlock()
- v := f.VersionInfo
- return &v, f.popError("version")
- }
- func (f *FakeDockerClient) Info() (*dockertypes.Info, error) {
- return &f.Information, nil
- }
- func (f *FakeDockerClient) CreateExec(id string, opts dockertypes.ExecConfig) (*dockertypes.IDResponse, error) {
- f.Lock()
- defer f.Unlock()
- f.execCmd = opts.Cmd
- f.appendCalled(CalledDetail{name: "create_exec"})
- return &dockertypes.IDResponse{ID: "12345678"}, nil
- }
- func (f *FakeDockerClient) StartExec(startExec string, opts dockertypes.ExecStartCheck, sopts StreamOptions) error {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "start_exec"})
- return nil
- }
- func (f *FakeDockerClient) AttachToContainer(id string, opts dockertypes.ContainerAttachOptions, sopts StreamOptions) error {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "attach"})
- return nil
- }
- func (f *FakeDockerClient) InspectExec(id string) (*dockertypes.ContainerExecInspect, error) {
- return f.ExecInspect, f.popError("inspect_exec")
- }
- func (f *FakeDockerClient) ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.ImageSummary, error) {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "list_images"})
- err := f.popError("list_images")
- return f.Images, err
- }
- func (f *FakeDockerClient) RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDeleteResponseItem, error) {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "remove_image", arguments: []interface{}{image, opts}})
- err := f.popError("remove_image")
- if err == nil {
- for i := range f.Images {
- if f.Images[i].ID == image {
- f.Images = append(f.Images[:i], f.Images[i+1:]...)
- break
- }
- }
- }
- return []dockertypes.ImageDeleteResponseItem{{Deleted: image}}, err
- }
- func (f *FakeDockerClient) InjectImages(images []dockertypes.ImageSummary) {
- f.Lock()
- defer f.Unlock()
- f.Images = append(f.Images, images...)
- for _, i := range images {
- f.ImageInspects[i.ID] = createImageInspectFromImage(i)
- }
- }
- func (f *FakeDockerClient) MakeImagesPrivate(images []dockertypes.ImageSummary, auth dockertypes.AuthConfig) {
- f.Lock()
- defer f.Unlock()
- for _, i := range images {
- f.ImageIDsNeedingAuth[i.ID] = auth
- }
- }
- func (f *FakeDockerClient) ResetImages() {
- f.Lock()
- defer f.Unlock()
- f.Images = []dockertypes.ImageSummary{}
- f.ImageInspects = make(map[string]*dockertypes.ImageInspect)
- f.ImageIDsNeedingAuth = make(map[string]dockertypes.AuthConfig)
- }
- 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
- }
- }
- func (f *FakeDockerClient) updateContainerStatus(id, status string) {
- for i := range f.RunningContainerList {
- if f.RunningContainerList[i].ID == id {
- f.RunningContainerList[i].Status = status
- }
- }
- }
- func (f *FakeDockerClient) ResizeExecTTY(id string, height, width uint) error {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "resize_exec"})
- return nil
- }
- func (f *FakeDockerClient) ResizeContainerTTY(id string, height, width uint) error {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "resize_container"})
- return nil
- }
- 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{},
- }
- }
- func createImageInspectFromImage(image dockertypes.ImageSummary) *dockertypes.ImageInspect {
- 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{},
- }
- }
- func createImageFromImageInspect(inspect dockertypes.ImageInspect) *dockertypes.ImageSummary {
- return &dockertypes.ImageSummary{
- ID: inspect.ID,
- RepoTags: inspect.RepoTags,
- // Image size is required to be non-zero for CRI integration.
- VirtualSize: fakeImageSize,
- Size: fakeImageSize,
- }
- }
- // dockerTimestampToString converts the timestamp to string
- func dockerTimestampToString(t time.Time) string {
- return t.Format(time.RFC3339Nano)
- }
- func (f *FakeDockerClient) ImageHistory(id string) ([]dockerimagetypes.HistoryResponseItem, error) {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "image_history"})
- history := f.ImageHistoryMap[id]
- return history, nil
- }
- func (f *FakeDockerClient) InjectImageHistory(data map[string][]dockerimagetypes.HistoryResponseItem) {
- f.Lock()
- defer f.Unlock()
- f.ImageHistoryMap = data
- }
- // FakeDockerPuller is meant to be a simple wrapper around FakeDockerClient.
- // Please do not add more functionalities to it.
- type FakeDockerPuller struct {
- client Interface
- }
- 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
- }
- func (f *FakeDockerClient) InjectContainerStats(data map[string]*dockertypes.StatsJSON) {
- f.Lock()
- defer f.Unlock()
- f.ContainerStatsMap = data
- }
- func (f *FakeDockerClient) GetContainerStats(id string) (*dockertypes.StatsJSON, error) {
- f.Lock()
- defer f.Unlock()
- f.appendCalled(CalledDetail{name: "get_container_stats"})
- stats, ok := f.ContainerStatsMap[id]
- if !ok {
- return nil, fmt.Errorf("container %q not found", id)
- }
- return stats, nil
- }
|