diff --git a/Makefile b/Makefile index 5a6780adf3c..ead994e1357 100644 --- a/Makefile +++ b/Makefile @@ -368,7 +368,7 @@ mock-criostorage: ${MOCKGEN} ${MOCKGEN} \ -package criostoragemock \ -destination ${MOCK_PATH}/criostorage/criostorage.go \ - github.com/cri-o/cri-o/internal/storage ImageServer,RuntimeServer + github.com/cri-o/cri-o/internal/storage ImageServer,ImageServerList,RuntimeServer mock-lib-config: ${MOCKGEN} ${MOCKGEN} \ diff --git a/go.mod b/go.mod index 018c7064982..4364f6f9385 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,7 @@ require ( github.com/opencontainers/runtime-spec v1.1.0-rc.1 github.com/opencontainers/runtime-tools v0.9.1-0.20230110161035-a6a073817ab0 github.com/opencontainers/selinux v1.11.0 + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 github.com/psampaz/go-mod-outdated v0.9.0 github.com/seccomp/libseccomp-golang v0.10.0 @@ -305,7 +306,6 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pjbgf/sha1cd v0.2.3 // indirect github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pkg/sftp v1.13.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect diff --git a/internal/factory/sandbox/sandbox.go b/internal/factory/sandbox/sandbox.go index 133c7ae7f59..2b7d1ff4a58 100644 --- a/internal/factory/sandbox/sandbox.go +++ b/internal/factory/sandbox/sandbox.go @@ -94,6 +94,17 @@ func (s *sandbox) SetConfig(config *types.PodSandboxConfig) error { return nil } +// ConstructSandboxNameFromConfig returns the pod sandbox name from the configuration +func ConstructSandboxNameFromConfig(config *types.PodSandboxConfig) string { + return strings.Join([]string{ + "k8s", + config.Metadata.Name, + config.Metadata.Namespace, + config.Metadata.Uid, + fmt.Sprintf("%d", config.Metadata.Attempt), + }, "_") +} + // SetNameAndID sets the sandbox name and ID func (s *sandbox) SetNameAndID() error { if s.config == nil { @@ -109,13 +120,7 @@ func (s *sandbox) SetNameAndID() error { } s.id = stringid.GenerateNonCryptoID() - s.name = strings.Join([]string{ - "k8s", - s.config.Metadata.Name, - s.config.Metadata.Namespace, - s.config.Metadata.Uid, - fmt.Sprintf("%d", s.config.Metadata.Attempt), - }, "_") + s.name = ConstructSandboxNameFromConfig(s.config) return nil } diff --git a/internal/lib/checkpoint.go b/internal/lib/checkpoint.go index dee9bfdc09d..9249ac9d163 100644 --- a/internal/lib/checkpoint.go +++ b/internal/lib/checkpoint.go @@ -236,7 +236,7 @@ func (c *ContainerServer) exportCheckpoint(ctx context.Context, ctr *oci.Contain if err != nil { return fmt.Errorf("error exporting root file-system diff for %q: %w", id, err) } - mountPoint, err := c.StorageImageServer().GetStore().Mount(id, specgen.Linux.MountLabel) + mountPoint, err := c.StorageImageServer(ctr.ID()).GetStore().Mount(id, specgen.Linux.MountLabel) if err != nil { return fmt.Errorf("not able to get mountpoint for container %q: %w", id, err) } diff --git a/internal/lib/container_server.go b/internal/lib/container_server.go index e91c17c5931..5184d897b8a 100644 --- a/internal/lib/container_server.go +++ b/internal/lib/container_server.go @@ -38,7 +38,7 @@ const ContainerManagerCRIO = "cri-o" type ContainerServer struct { runtime *oci.Runtime store cstorage.Store - storageImageServer storage.ImageServer + storageImageServers storage.ImageServerList storageRuntimeServer storage.RuntimeServer ctrNameIndex *registrar.Registrar ctrIDIndex *truncindex.TruncIndex @@ -63,8 +63,13 @@ func (c *ContainerServer) Store() cstorage.Store { } // StorageImageServer returns the ImageServer for the ContainerServer -func (c *ContainerServer) StorageImageServer() storage.ImageServer { - return c.storageImageServer +func (c *ContainerServer) StorageImageServer(containerID string) storage.ImageServer { + return c.storageImageServers.GetImageServer(containerID) +} + +// ImageServerList returns the list of ImageServer instances available +func (c *ContainerServer) ImageServerList() storage.ImageServerList { + return c.storageImageServers } // CtrIDIndex returns the TruncIndex for the ContainerServer @@ -107,9 +112,11 @@ func New(ctx context.Context, configIface libconfig.Iface) (*ContainerServer, er return nil, err } - storageRuntimeService := storage.GetRuntimeService(ctx, imageService) + imageServers := storage.GetImageServiceList(imageService) + + storageRuntimeService := storage.GetRuntimeService(ctx, imageServers) - runtime, err := oci.New(config) + runtime, err := oci.New(config, imageServers) if err != nil { return nil, err } @@ -122,7 +129,7 @@ func New(ctx context.Context, configIface libconfig.Iface) (*ContainerServer, er c := &ContainerServer{ runtime: runtime, store: store, - storageImageServer: imageService, + storageImageServers: imageServers, storageRuntimeServer: storageRuntimeService, ctrNameIndex: registrar.NewRegistrar(), ctrIDIndex: truncindex.NewTruncIndex([]string{}), diff --git a/internal/lib/container_server_test.go b/internal/lib/container_server_test.go index 2134bbc5887..83319a512fc 100644 --- a/internal/lib/container_server_test.go +++ b/internal/lib/container_server_test.go @@ -100,7 +100,7 @@ var _ = t.Describe("ContainerServer", func() { It("should succeed to get the StorageImageServer", func() { // Given // When - res := sut.StorageImageServer() + res := sut.StorageImageServer("") // Then Expect(res).NotTo(BeNil()) diff --git a/internal/lib/container_server_test_inject.go b/internal/lib/container_server_test_inject.go index 7b76d807c50..ac7d28009b3 100644 --- a/internal/lib/container_server_test_inject.go +++ b/internal/lib/container_server_test_inject.go @@ -17,5 +17,5 @@ func (c *ContainerServer) SetStorageRuntimeServer(server storage.RuntimeServer) // SetStorageImageServer sets the ImageServer for the ContainerServer func (c *ContainerServer) SetStorageImageServer(server storage.ImageServer) { - c.storageImageServer = server + c.storageImageServers.SetDefaultImageServer(server) } diff --git a/internal/lib/restore.go b/internal/lib/restore.go index 982453e7964..770f411d0e6 100644 --- a/internal/lib/restore.go +++ b/internal/lib/restore.go @@ -45,7 +45,8 @@ func (c *ContainerServer) ContainerRestore( return "", err } // During checkpointing the container is unmounted. This mounts the container again. - mountPoint, err := c.StorageImageServer().GetStore().Mount(ctr.ID(), ctrSpec.Config.Linux.MountLabel) + store := c.StorageImageServer(ctr.ID()).GetStore() + mountPoint, err := store.Mount(ctr.ID(), ctrSpec.Config.Linux.MountLabel) if err != nil { log.Debugf(ctx, "Failed to mount container %q: %v", ctr.ID(), err) return "", err @@ -61,13 +62,13 @@ func (c *ContainerServer) ContainerRestore( if ctr.RestoreArchive() != "" { if ctr.RestoreIsOCIImage() { log.Debugf(ctx, "Restoring from %v", ctr.RestoreArchive()) - imageMountPoint, err := c.StorageImageServer().GetStore().MountImage(ctr.RestoreArchive(), nil, "") + imageMountPoint, err := store.MountImage(ctr.RestoreArchive(), nil, "") if err != nil { return "", err } logrus.Debugf("Checkpoint image mounted at %v", imageMountPoint) defer func() { - _, err := c.StorageImageServer().GetStore().UnmountImage(ctr.RestoreArchive(), true) + _, err := store.UnmountImage(ctr.RestoreArchive(), true) if err != nil { log.Errorf(ctx, "Failed to unmount checkpoint image: %q", err) } diff --git a/internal/oci/oci.go b/internal/oci/oci.go index eeb062739f0..07b0d2ac509 100644 --- a/internal/oci/oci.go +++ b/internal/oci/oci.go @@ -11,6 +11,7 @@ import ( "time" "github.com/cri-o/cri-o/internal/log" + "github.com/cri-o/cri-o/internal/storage" "github.com/cri-o/cri-o/pkg/config" rspec "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/net/context" @@ -47,6 +48,7 @@ type Runtime struct { config *config.Config runtimeImplMap map[string]RuntimeImpl runtimeImplMapMutex sync.RWMutex + imageServers storage.ImageServerList } // RuntimeImpl is an interface used by the caller to interact with the @@ -80,7 +82,7 @@ type RuntimeImpl interface { } // New creates a new Runtime with options provided -func New(c *config.Config) (*Runtime, error) { +func New(c *config.Config, is storage.ImageServerList) (*Runtime, error) { execNotifyDir := filepath.Join(c.ContainerAttachSocketDir, "exec-pid-dir") if err := os.MkdirAll(execNotifyDir, 0o750); err != nil { return nil, fmt.Errorf("create oci runtime pid dir: %w", err) @@ -89,6 +91,7 @@ func New(c *config.Config) (*Runtime, error) { return &Runtime{ config: c, runtimeImplMap: make(map[string]RuntimeImpl), + imageServers: is, }, nil } @@ -176,7 +179,10 @@ func (r *Runtime) newRuntimeImpl(c *Container) (RuntimeImpl, error) { } if rh.RuntimeType == config.RuntimeTypeVM { - return newRuntimeVM(rh.RuntimePath, rh.RuntimeRoot, rh.RuntimeConfigPath, r.config.RuntimeConfig.ContainerExitsDir), nil + impl := newRuntimeVM(r.imageServers.GetDefaultImageServer(), rh.RuntimePath, rh.RuntimeRoot, rh.RuntimeConfigPath, r.config.RuntimeConfig.ContainerExitsDir) + // TODO: for peer-pods, register the runtimeVM as the ImageServer for the container + // r.imageServers.SetImageServer(c.ID(), impl) + return impl, nil } if rh.RuntimeType == config.RuntimeTypePod { @@ -302,6 +308,7 @@ func (r *Runtime) DeleteContainer(ctx context.Context, c *Container) (err error) r.runtimeImplMapMutex.Lock() delete(r.runtimeImplMap, c.ID()) r.runtimeImplMapMutex.Unlock() + r.imageServers.DeleteImageServer(c.ID()) } }() } diff --git a/internal/oci/oci_test.go b/internal/oci/oci_test.go index 04e5217aa78..9b1adeafc2f 100644 --- a/internal/oci/oci_test.go +++ b/internal/oci/oci_test.go @@ -24,7 +24,7 @@ var _ = t.Describe("Oci", func() { c.ContainerAttachSocketDir = t.MustTempDir("crio") // When - runtime, err := oci.New(c) + runtime, err := oci.New(c, nil) Expect(err).To(BeNil()) // Then @@ -86,7 +86,7 @@ var _ = t.Describe("Oci", func() { // so we have permission to make a directory within it config.ContainerAttachSocketDir = t.MustTempDir("crio") - sut, err = oci.New(config) + sut, err = oci.New(config, nil) Expect(err).To(BeNil()) Expect(sut).NotTo(BeNil()) }) diff --git a/internal/oci/runtime_vm.go b/internal/oci/runtime_vm.go index 1a9d34fb673..5963907b9e0 100644 --- a/internal/oci/runtime_vm.go +++ b/internal/oci/runtime_vm.go @@ -27,8 +27,11 @@ import ( "github.com/containerd/ttrpc" "github.com/containerd/typeurl" conmonconfig "github.com/containers/conmon/runner/config" + systemCtxTypes "github.com/containers/image/v5/types" + "github.com/containers/storage" "github.com/cri-o/cri-o/internal/config/cgmgr" "github.com/cri-o/cri-o/internal/log" + criostorage "github.com/cri-o/cri-o/internal/storage" "github.com/cri-o/cri-o/server/metrics" "github.com/cri-o/cri-o/utils" "github.com/cri-o/cri-o/utils/errdefs" @@ -55,6 +58,8 @@ type runtimeVM struct { client *ttrpc.Client task task.TaskService + defaultIs criostorage.ImageServer + sync.Mutex ctrs map[string]containerInfo } @@ -69,7 +74,7 @@ const ( ) // newRuntimeVM creates a new runtimeVM instance -func newRuntimeVM(path, root, configPath, exitsPath string) RuntimeImpl { +func newRuntimeVM(defaultImageServer criostorage.ImageServer, path, root, configPath, exitsPath string) *runtimeVM { logrus.Debug("oci.newRuntimeVM() start") defer logrus.Debug("oci.newRuntimeVM() end") @@ -92,6 +97,7 @@ func newRuntimeVM(path, root, configPath, exitsPath string) RuntimeImpl { fifoDir: filepath.Join(root, "crio", "fifo"), ctx: context.Background(), ctrs: make(map[string]containerInfo), + defaultIs: defaultImageServer, } } @@ -1071,3 +1077,51 @@ func (r *runtimeVM) RestoreContainer(ctx context.Context, c *Container, cgroupPa return errors.New("restoring not implemented for runtimeVM") } + +func (r *runtimeVM) ListImages(systemContext *systemCtxTypes.SystemContext, filter string) ([]criostorage.ImageResult, error) { + log.Debugf(r.ctx, "RuntimeVM.ListImages() start") + defer log.Debugf(r.ctx, "RuntimeVM.ListImages() end") + // Do not call the default ImageServer here: it is already called for ListImages + // so this would just create duplicates + return []criostorage.ImageResult{}, nil +} + +func (r *runtimeVM) ImageStatus(systemContext *systemCtxTypes.SystemContext, nameOrID string) (*criostorage.ImageResult, error) { + log.Debugf(r.ctx, "RuntimeVM.ImageStatus() start") + defer log.Debugf(r.ctx, "RuntimeVM.ImageStatus() end") + // Returning ImageUnknown on purpose, to make the caller call PullImage next + // To be replaced with actual Image Status information. + return nil, storage.ErrImageUnknown +} + +func (r *runtimeVM) PrepareImage(inputSystemContext *systemCtxTypes.SystemContext, imageName string) (systemCtxTypes.ImageCloser, error) { + log.Debugf(r.ctx, "RuntimeVM.PrepareImage() start") + defer log.Debugf(r.ctx, "RuntimeVM.PrepareImage() end") + return r.defaultIs.PrepareImage(inputSystemContext, imageName) +} + +func (r *runtimeVM) PullImage(systemContext *systemCtxTypes.SystemContext, imageName string, inputOptions *criostorage.ImageCopyOptions) (systemCtxTypes.ImageReference, error) { + log.Debugf(r.ctx, "RuntimeVM.PullImage() start") + defer log.Debugf(r.ctx, "RuntimeVM.PullImage() end") + + // if peer_pods, call the shim's PullImage + return r.defaultIs.PullImage(systemContext, imageName, inputOptions) +} + +func (r *runtimeVM) UntagImage(systemContext *systemCtxTypes.SystemContext, nameOrID string) error { + log.Debugf(r.ctx, "RuntimeVM.UntagImage() start") + defer log.Debugf(r.ctx, "RuntimeVM.UntagImage() end") + return r.defaultIs.UntagImage(systemContext, nameOrID) +} + +func (r *runtimeVM) GetStore() storage.Store { + log.Debugf(r.ctx, "RuntimeVM.GetStore() start") + defer log.Debugf(r.ctx, "RuntimeVM.GetStore() end") + return r.defaultIs.GetStore() +} + +func (r *runtimeVM) ResolveNames(systemContext *systemCtxTypes.SystemContext, imageName string) ([]string, error) { + log.Debugf(r.ctx, "RuntimeVM.ResolveNames() start") + defer log.Debugf(r.ctx, "RuntimeVM.ResolveNames() end") + return r.defaultIs.ResolveNames(systemContext, imageName) +} diff --git a/internal/storage/image.go b/internal/storage/image.go index 3821e948d39..6433930a380 100644 --- a/internal/storage/image.go +++ b/internal/storage/image.go @@ -3,7 +3,6 @@ package storage import ( "bufio" "context" - "errors" "fmt" "io" "net" @@ -33,6 +32,7 @@ import ( json "github.com/json-iterator/go" digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -98,6 +98,11 @@ type imageService struct { ctx context.Context } +type imageServiceList struct { + defaultImageServer ImageServer + imageServers map[string]ImageServer +} + // ImageBeingPulled map[string]bool to keep track of the images haven't done pulling. var ImageBeingPulled sync.Map @@ -141,6 +146,38 @@ type ImageServer interface { ResolveNames(systemContext *types.SystemContext, imageName string) ([]string, error) } +// ImageServerList provides a way to access ImageServer instances. +// A default ImageServer is used for most containers, and a specific ImageServer +// can be registered for some containers. +type ImageServerList interface { + // GetDefaultImageServer returns the default ImageServer + GetDefaultImageServer() ImageServer + // SetDefaultImageServer sets the default ImageServer for the ImageServerList + SetDefaultImageServer(is ImageServer) + // GetImageServer returns the ImageServer associated to the given container ID. + // If there is none, it returns the default ImageServer. + GetImageServer(containerID string) ImageServer + // SetImageServer associates an ImageServer to the given container ID. + SetImageServer(containerID string, is ImageServer) + // DeleteImageServer removes the ImageServer associated to the given + // containerID (if any) + DeleteImageServer(containerID string) + // ResolveNames makes a call to ResolveName() on each ImageServer and returns + // the data returned by the first ImageServer that can resolve the name. + ResolveNames(systemContext *types.SystemContext, imageName string) ([]string, error) + // ListImages makes a call to ListImages() on each ImageServer, and returns + // the combined list of images. + ListImages(systemContext *types.SystemContext, filter string) ([]ImageResult, error) + // UntagImage makes a call to UntagImage on each ImageServer that contains + // the target image. + UntagImage(systemContext *types.SystemContext, nameOrID string) (lastError error) + // ImageStatus makes a call to ImageStatus on each ImageServer, starting + // with the default, and returns the status from the first ImageServer that + // can find the image. + // It returns storage.ErrImageUnknown if no ImageServer can find the image. + ImageStatus(systemContext *types.SystemContext, filter string) (*ImageResult, error) +} + func (svc *imageService) getRef(name string) (types.ImageReference, error) { ref, err := alltransports.ParseImageName(name) if err != nil { @@ -826,6 +863,126 @@ func (svc *imageService) ResolveNames(systemContext *types.SystemContext, imageN return images, nil } +func (isl *imageServiceList) GetDefaultImageServer() ImageServer { + return isl.defaultImageServer +} + +func (isl *imageServiceList) SetDefaultImageServer(is ImageServer) { + isl.defaultImageServer = is +} + +func (isl *imageServiceList) GetImageServer(containerID string) ImageServer { + is, ok := isl.imageServers[containerID] + if ok { + return is + } + return isl.defaultImageServer +} + +func (isl *imageServiceList) SetImageServer(containerID string, is ImageServer) { + isl.imageServers[containerID] = is +} + +func (isl *imageServiceList) DeleteImageServer(containerID string) { + delete(isl.imageServers, containerID) +} + +// ResolveNames will call ResolveNames on each registered ImageServer, starting +// with the default. +// It returns the data provided by the first ImageServer that provides valid +// data. +// It returns an error if no ImageServer could resolve the name. +func (isl *imageServiceList) ResolveNames(systemContext *types.SystemContext, imageName string) ([]string, error) { + // check the default ImageServer first + names, err := isl.defaultImageServer.ResolveNames(systemContext, imageName) + if err == nil { + return names, nil + } + if err != nil && (err == ErrCannotParseImageID || err == ErrImageMultiplyTagged) { + return []string{}, err + } + + // if no answer came from it, try the other registered servers + for _, is := range isl.imageServers { + names, err := is.ResolveNames(systemContext, imageName) + if err != nil { + if err == ErrCannotParseImageID || err == ErrImageMultiplyTagged { + return []string{}, err + } + continue + } + return names, nil + } + return []string{}, fmt.Errorf("failed resolving image name for %s - not found", imageName) +} + +func (isl *imageServiceList) ListImages(systemContext *types.SystemContext, filter string) (imageResults []ImageResult, lastError error) { + // first call ListImages on the default ImageServer + imageResults, lastError = isl.defaultImageServer.ListImages(systemContext, filter) + + // then call it on each container-specific ImageServer + for _, is := range isl.imageServers { + images, err := is.ListImages(systemContext, filter) + if err != nil { + if lastError == nil { + lastError = err + } else { + lastError = errors.Wrap(lastError, err.Error()) + } + continue + } + imageResults = append(imageResults, images...) + } + return imageResults, lastError +} + +func (isl *imageServiceList) UntagImage(systemContext *types.SystemContext, nameOrID string) (lastError error) { + // start with the default ImageServer + _, e := isl.defaultImageServer.GetStore().Image(nameOrID) + if e == nil { + lastError = isl.defaultImageServer.UntagImage(systemContext, nameOrID) + } + + for _, is := range isl.imageServers { + _, e := is.GetStore().Image(nameOrID) + if e != nil { + continue + } + err := is.UntagImage(systemContext, nameOrID) + if err != nil { + if lastError == nil { + lastError = err + } else { + lastError = errors.Wrap(lastError, err.Error()) + } + } + } + return lastError +} + +// ImageStatus returns the image status for the given image. +func (isl *imageServiceList) ImageStatus(systemContext *types.SystemContext, filter string) (*ImageResult, error) { + // start with the default ImageServer + status, lastError := isl.defaultImageServer.ImageStatus(systemContext, filter) + if lastError == nil { + return status, nil + } + + for _, is := range isl.imageServers { + status, err := is.ImageStatus(systemContext, filter) + if err == nil { + return status, nil + } + if lastError == nil { + lastError = err + } else { + lastError = errors.Wrap(lastError, err.Error()) + } + } + + return nil, lastError +} + // GetImageService returns an ImageServer that uses the passed-in store, and // which will prepend the passed-in DefaultTransport value to an image name if // a name that's passed to its PullImage() method can't be resolved to an image @@ -873,3 +1030,10 @@ func GetImageService(ctx context.Context, sc *types.SystemContext, store storage return is, nil } + +func GetImageServiceList(is ImageServer) ImageServerList { + return &imageServiceList{ + defaultImageServer: is, + imageServers: make(map[string]ImageServer), + } +} diff --git a/internal/storage/imagelist_test.go b/internal/storage/imagelist_test.go new file mode 100644 index 00000000000..26cd1059462 --- /dev/null +++ b/internal/storage/imagelist_test.go @@ -0,0 +1,280 @@ +package storage_test + +import ( + "os" + + "github.com/containers/image/v5/types" + "github.com/cri-o/cri-o/internal/storage" + criostoragemock "github.com/cri-o/cri-o/test/mocks/criostorage" + "github.com/golang/mock/gomock" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// The actual test suite +var _ = t.Describe("Image", func() { + // Test constants + const ( + firstContainerID = "containerID1" + secondContainerID = "containerID2" + unknownContainerID = "unknownContainerID" + ) + + var ( + mockCtrl *gomock.Controller + dfltImageServerMock *criostoragemock.MockImageServer + ctridImageServerMock *criostoragemock.MockImageServer + + // The system under test + sut storage.ImageServerList + + // The empty system context + ctx *types.SystemContext + ) + + // Prepare the system under test + BeforeEach(func() { + // Setup the mocks + mockCtrl = gomock.NewController(GinkgoT()) + dfltImageServerMock = criostoragemock.NewMockImageServer(mockCtrl) + ctridImageServerMock = criostoragemock.NewMockImageServer(mockCtrl) + + // Setup the SUT + ctx = &types.SystemContext{ + SystemRegistriesConfPath: t.MustTempFile("registries"), + } + + sut = storage.GetImageServiceList(dfltImageServerMock) + Expect(sut).NotTo(BeNil()) + + sut.SetImageServer(firstContainerID, ctridImageServerMock) + }) + AfterEach(func() { + mockCtrl.Finish() + Expect(os.Remove(ctx.SystemRegistriesConfPath)).To(BeNil()) + }) + + // mockGetRef := func() mockSequence { + // return inOrder( + // // parseStoreReference ("@"+testImageName) will fail, recognizing it as an invalid image ID + // storeMock.EXPECT().Image(testImageName). + // Return(&cs.Image{ID: testSHA256}, nil), + + // mockParseStoreReference(storeMock, testImageName), + // ) + // } + + t.Describe("GetImageServiceList", func() { + It("should succeed to retrieve an image service", func() { + // Given + // When + imageServiceList := storage.GetImageServiceList(dfltImageServerMock) + + // Then + Expect(imageServiceList).NotTo(BeNil()) + }) + }) + + t.Describe("GetDefaultImageServer", func() { + It("should succeed to retrieve the default ImageServer", func() { + // Given + gomock.InOrder( + dfltImageServerMock.EXPECT().GetStore().Return(nil), + ) + + // When + is := sut.GetDefaultImageServer() + + // Then + Expect(is).NotTo(BeNil()) + Expect(is.GetStore()).To(BeNil()) + }) + }) + + t.Describe("SetDefaultImageServer", func() { + It("should succeed to change default ImageServer", func() { + // Given + newImageServerMock := criostoragemock.NewMockImageServer(mockCtrl) + gomock.InOrder( + newImageServerMock.EXPECT().GetStore().Return(nil), + ) + + // When + sut.SetDefaultImageServer(newImageServerMock) + is := sut.GetDefaultImageServer() + + // Then + Expect(is).NotTo(BeNil()) + Expect(is.GetStore()).To(BeNil()) + }) + }) + + t.Describe("SetImageServer", func() { + It("should succeed to register a new image server for a container", func() { + // Given + newImageServerMock := criostoragemock.NewMockImageServer(mockCtrl) + gomock.InOrder( + newImageServerMock.EXPECT().GetStore().Return(nil), + ) + + // When + sut.SetImageServer(secondContainerID, newImageServerMock) + is := sut.GetImageServer(secondContainerID) + + // Then + Expect(is).NotTo(BeNil()) + Expect(is.GetStore()).To(BeNil()) + }) + }) + + t.Describe("GetImageServer", func() { + It("should return the default ImageServer for unknown container", func() { + // Given + gomock.InOrder( + dfltImageServerMock.EXPECT().GetStore().Return(nil), + ) + + // When + is := sut.GetImageServer(unknownContainerID) + + // Then + Expect(is).NotTo(BeNil()) + Expect(is.GetStore()).To(BeNil()) + }) + + It("should return the ImageServer associated to a container ID", func() { + // Given + gomock.InOrder( + ctridImageServerMock.EXPECT().GetStore().Return(nil), + ) + + // When + is := sut.GetImageServer(firstContainerID) + + // Then + Expect(is).NotTo(BeNil()) + Expect(is.GetStore()).To(BeNil()) + }) + }) + + t.Describe("DeleteImageServer", func() { + It("should succeed to remove an ImageServer", func() { + // Given + gomock.InOrder( + dfltImageServerMock.EXPECT().GetStore().Return(nil), + ) + + // When + sut.DeleteImageServer(firstContainerID) + is := sut.GetImageServer(firstContainerID) // should return the default IS + + // Then + Expect(is).NotTo(BeNil()) + Expect(is.GetStore()).To(BeNil()) + }) + }) + + t.Describe("ResolveNames", func() { + It("should succeed to resolve with default ImageServer", func() { + // Given + gomock.InOrder( + dfltImageServerMock.EXPECT().ResolveNames(gomock.Any(), gomock.Any()). + Return([]string{}, nil), + ) + + // When + names, err := sut.ResolveNames(nil, "") + + // Then + Expect(err).To(BeNil()) + Expect(names).NotTo(BeNil()) + }) + + It("should succeed to resolve, and return resolving ImageServer", func() { + // Given + gomock.InOrder( + dfltImageServerMock.EXPECT().ResolveNames(gomock.Any(), gomock.Any()). + Return(nil, t.TestError), + ctridImageServerMock.EXPECT().ResolveNames(gomock.Any(), gomock.Any()). + Return([]string{}, nil), + ) + + // When + names, err := sut.ResolveNames(nil, "") + + // Then + Expect(err).To(BeNil()) + Expect(names).NotTo(BeNil()) + }) + + It("should fail when no ImageServer can resolve", func() { + // Given + gomock.InOrder( + dfltImageServerMock.EXPECT().ResolveNames(gomock.Any(), gomock.Any()). + Return(nil, t.TestError), + ctridImageServerMock.EXPECT().ResolveNames(gomock.Any(), gomock.Any()). + Return(nil, t.TestError), + ) + + // When + _, err := sut.ResolveNames(nil, "") + + // Then + Expect(err).NotTo(BeNil()) + }) + }) + + t.Describe("ListImages", func() { + It("should list images from all registered ImageServer", func() { + gomock.InOrder( + dfltImageServerMock.EXPECT().ListImages(gomock.Any(), gomock.Any()). + Return([]storage.ImageResult{{}}, nil), + ctridImageServerMock.EXPECT().ListImages(gomock.Any(), gomock.Any()). + Return([]storage.ImageResult{{}}, nil), + ) + // When + images, err := sut.ListImages(nil, "") + // Then + Expect(err).To(BeNil()) + Expect(len(images)).To(Equal(2)) + }) + It("should return the list even if the default ImageServer fail", func() { + gomock.InOrder( + dfltImageServerMock.EXPECT().ListImages(gomock.Any(), gomock.Any()). + Return([]storage.ImageResult{}, t.TestError), + ctridImageServerMock.EXPECT().ListImages(gomock.Any(), gomock.Any()). + Return([]storage.ImageResult{{}}, nil), + ) + // When + images, err := sut.ListImages(nil, "") + // Then + Expect(err).NotTo(BeNil()) + Expect(len(images)).To(Equal(1)) + }) + It("should return the list even if an additional ImageServer fail", func() { + gomock.InOrder( + dfltImageServerMock.EXPECT().ListImages(gomock.Any(), gomock.Any()). + Return([]storage.ImageResult{{}}, nil), + ctridImageServerMock.EXPECT().ListImages(gomock.Any(), gomock.Any()). + Return([]storage.ImageResult{}, t.TestError), + ) + // When + images, err := sut.ListImages(nil, "") + // Then + Expect(err).NotTo(BeNil()) + Expect(len(images)).To(Equal(1)) + }) + It("should fail when all ImageServer fail", func() { + gomock.InOrder( + dfltImageServerMock.EXPECT().ListImages(gomock.Any(), gomock.Any()). + Return([]storage.ImageResult{}, t.TestError), + ctridImageServerMock.EXPECT().ListImages(gomock.Any(), gomock.Any()). + Return([]storage.ImageResult{}, t.TestError), + ) + // When + _, err := sut.ListImages(nil, "") + // Then + Expect(err).NotTo(BeNil()) + }) + }) +}) diff --git a/internal/storage/runtime.go b/internal/storage/runtime.go index 5152d3e8665..a14a24d0af6 100644 --- a/internal/storage/runtime.go +++ b/internal/storage/runtime.go @@ -40,7 +40,7 @@ var ( ) type runtimeService struct { - storageImageServer ImageServer + storageImageServer ImageServerList ctx context.Context } @@ -158,22 +158,23 @@ func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.System } // Check if we have the specified image. - ref, err := istorage.Transport.ParseStoreReference(r.storageImageServer.GetStore(), imageName) + is := r.storageImageServer.GetImageServer(containerID) + ref, err := istorage.Transport.ParseStoreReference(is.GetStore(), imageName) if err != nil { // Maybe it's some other transport's copy of the image? otherRef, err2 := alltransports.ParseImageName(imageName) if err2 == nil && otherRef.DockerReference() != nil { - ref, err = istorage.Transport.ParseStoreReference(r.storageImageServer.GetStore(), otherRef.DockerReference().String()) + ref, err = istorage.Transport.ParseStoreReference(is.GetStore(), otherRef.DockerReference().String()) } if err != nil { // Maybe the image ID is sufficient? - ref, err = istorage.Transport.ParseStoreReference(r.storageImageServer.GetStore(), "@"+imageID) + ref, err = istorage.Transport.ParseStoreReference(is.GetStore(), "@"+imageID) if err != nil { return ContainerInfo{}, err } } } - img, err := istorage.Transport.GetStoreImage(r.storageImageServer.GetStore(), ref) + img, err := istorage.Transport.GetStoreImage(is.GetStore(), ref) if img == nil && errors.Is(err, storage.ErrImageUnknown) && isPauseImage { image := imageID if imageName != "" { @@ -190,14 +191,14 @@ func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.System if imageAuthFile != "" { sourceCtx.AuthFilePath = imageAuthFile } - ref, err = r.storageImageServer.PullImage(systemContext, image, &ImageCopyOptions{ + ref, err = is.PullImage(systemContext, image, &ImageCopyOptions{ SourceCtx: &sourceCtx, DestinationCtx: systemContext, }) if err != nil { return ContainerInfo{}, err } - img, err = istorage.Transport.GetStoreImage(r.storageImageServer.GetStore(), ref) + img, err = istorage.Transport.GetStoreImage(is.GetStore(), ref) if err != nil { return ContainerInfo{}, err } @@ -264,7 +265,7 @@ func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.System if idMappingsOptions != nil { coptions.IDMappingOptions = *idMappingsOptions } - container, err := r.storageImageServer.GetStore().CreateContainer(containerID, names, img.ID, "", string(mdata), &coptions) + container, err := is.GetStore().CreateContainer(containerID, names, img.ID, "", string(mdata), &coptions) if err != nil { if metadata.Pod { logrus.Debugf("Failed to create pod sandbox %s(%s): %v", metadata.PodName, metadata.PodID, err) @@ -286,7 +287,7 @@ func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.System // container before returning. defer func() { if retErr != nil { - if err2 := r.storageImageServer.GetStore().DeleteContainer(container.ID); err2 != nil { + if err2 := is.GetStore().DeleteContainer(container.ID); err2 != nil { if metadata.Pod { logrus.Debugf("%v deleting partially-created pod sandbox %q", err2, container.ID) } else { @@ -301,18 +302,18 @@ func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.System // Add a name to the container's layer so that it's easier to follow // what's going on if we're just looking at the storage-eye view of things. layerName := metadata.ContainerName + "-layer" - names, err = r.storageImageServer.GetStore().Names(container.LayerID) + names, err = is.GetStore().Names(container.LayerID) if err != nil { return ContainerInfo{}, err } names = append(names, layerName) - err = r.storageImageServer.GetStore().SetNames(container.LayerID, names) + err = is.GetStore().SetNames(container.LayerID, names) if err != nil { return ContainerInfo{}, err } // Find out where the container work directories are, so that we can return them. - containerDir, err := r.storageImageServer.GetStore().ContainerDirectory(container.ID) + containerDir, err := is.GetStore().ContainerDirectory(container.ID) if err != nil { return ContainerInfo{}, err } @@ -322,7 +323,7 @@ func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.System logrus.Debugf("Container %q has work directory %q", container.ID, containerDir) } - containerRunDir, err := r.storageImageServer.GetStore().ContainerRunDirectory(container.ID) + containerRunDir, err := is.GetStore().ContainerRunDirectory(container.ID) if err != nil { return ContainerInfo{}, err } @@ -352,11 +353,11 @@ func (r *runtimeService) CreateContainer(systemContext *types.SystemContext, pod return r.createContainerOrPodSandbox(systemContext, podName, podID, imageName, "", imageID, containerName, containerID, metadataName, "", "", attempt, idMappingsOptions, labelOptions, false, privileged) } -func (r *runtimeService) deleteLayerIfMapped(imageID, layerID string) { +func (r *runtimeService) deleteLayerIfMapped(containerID, imageID, layerID string) { if layerID == "" { return } - store := r.storageImageServer.GetStore() + store := r.storageImageServer.GetImageServer(containerID).GetStore() image, err := store.Image(imageID) if err != nil { @@ -385,7 +386,8 @@ func (r *runtimeService) DeleteContainer(ctx context.Context, idOrName string) e if idOrName == "" { return ErrInvalidContainerID } - container, err := r.storageImageServer.GetStore().Container(idOrName) + store := r.storageImageServer.GetImageServer(idOrName).GetStore() + container, err := store.Container(idOrName) // Already deleted if errors.Is(err, storage.ErrContainerUnknown) { return nil @@ -393,17 +395,17 @@ func (r *runtimeService) DeleteContainer(ctx context.Context, idOrName string) e if err != nil { return err } - layer, err := r.storageImageServer.GetStore().Layer(container.LayerID) + layer, err := store.Layer(container.LayerID) if err != nil { log.Debugf(ctx, "Failed to retrieve layer %q: %v", container.LayerID, err) } - err = r.storageImageServer.GetStore().DeleteContainer(container.ID) + err = store.DeleteContainer(container.ID) if err != nil { log.Debugf(ctx, "Failed to delete container %q: %v", container.ID, err) return err } if layer != nil { - r.deleteLayerIfMapped(container.ImageID, layer.Parent) + r.deleteLayerIfMapped(container.ID, container.ImageID, layer.Parent) } return nil } @@ -414,12 +416,12 @@ func (r *runtimeService) SetContainerMetadata(idOrName string, metadata *Runtime logrus.Debugf("Failed to encode metadata for %q: %v", idOrName, err) return err } - return r.storageImageServer.GetStore().SetMetadata(idOrName, string(mdata)) + return r.storageImageServer.GetImageServer(idOrName).GetStore().SetMetadata(idOrName, string(mdata)) } func (r *runtimeService) GetContainerMetadata(idOrName string) (RuntimeContainerMetadata, error) { metadata := RuntimeContainerMetadata{} - mdata, err := r.storageImageServer.GetStore().Metadata(idOrName) + mdata, err := r.storageImageServer.GetImageServer(idOrName).GetStore().Metadata(idOrName) if err != nil { return metadata, err } @@ -430,7 +432,8 @@ func (r *runtimeService) GetContainerMetadata(idOrName string) (RuntimeContainer } func (r *runtimeService) StartContainer(idOrName string) (string, error) { - container, err := r.storageImageServer.GetStore().Container(idOrName) + store := r.storageImageServer.GetImageServer(idOrName).GetStore() + container, err := store.Container(idOrName) if err != nil { if errors.Is(err, storage.ErrContainerUnknown) { return "", ErrInvalidContainerID @@ -441,7 +444,7 @@ func (r *runtimeService) StartContainer(idOrName string) (string, error) { if err := json.Unmarshal([]byte(container.Metadata), &metadata); err != nil { return "", err } - mountPoint, err := r.storageImageServer.GetStore().Mount(container.ID, metadata.MountLabel) + mountPoint, err := store.Mount(container.ID, metadata.MountLabel) if err != nil { logrus.Debugf("Failed to mount container %q: %v", container.ID, err) return "", err @@ -456,7 +459,9 @@ func (r *runtimeService) StopContainer(ctx context.Context, idOrName string) err if idOrName == "" { return ErrInvalidContainerID } - container, err := r.storageImageServer.GetStore().Container(idOrName) + + store := r.storageImageServer.GetImageServer(idOrName).GetStore() + container, err := store.Container(idOrName) if err != nil { if errors.Is(err, storage.ErrContainerUnknown) { log.Infof(ctx, "Container %s not known, assuming it got already removed", idOrName) @@ -467,7 +472,7 @@ func (r *runtimeService) StopContainer(ctx context.Context, idOrName string) err return err } - if _, err := r.storageImageServer.GetStore().Unmount(container.ID, true); err != nil { + if _, err := store.Unmount(container.ID, true); err != nil { if errors.Is(err, storage.ErrLayerUnknown) { log.Infof(ctx, "Layer for container %s not known", container.ID) return nil @@ -482,31 +487,33 @@ func (r *runtimeService) StopContainer(ctx context.Context, idOrName string) err } func (r *runtimeService) GetWorkDir(id string) (string, error) { - container, err := r.storageImageServer.GetStore().Container(id) + store := r.storageImageServer.GetImageServer(id).GetStore() + container, err := store.Container(id) if err != nil { if errors.Is(err, storage.ErrContainerUnknown) { return "", ErrInvalidContainerID } return "", err } - return r.storageImageServer.GetStore().ContainerDirectory(container.ID) + return store.ContainerDirectory(container.ID) } func (r *runtimeService) GetRunDir(id string) (string, error) { - container, err := r.storageImageServer.GetStore().Container(id) + store := r.storageImageServer.GetImageServer(id).GetStore() + container, err := store.Container(id) if err != nil { if errors.Is(err, storage.ErrContainerUnknown) { return "", ErrInvalidContainerID } return "", err } - return r.storageImageServer.GetStore().ContainerRunDirectory(container.ID) + return store.ContainerRunDirectory(container.ID) } // GetRuntimeService returns a RuntimeServer that uses the passed-in image // service to pull and manage images, and its store to manage containers based // on those images. -func GetRuntimeService(ctx context.Context, storageImageServer ImageServer) RuntimeServer { +func GetRuntimeService(ctx context.Context, storageImageServer ImageServerList) RuntimeServer { return &runtimeService{ storageImageServer: storageImageServer, ctx: ctx, diff --git a/internal/storage/runtime_test.go b/internal/storage/runtime_test.go index a2615ced1d3..0a5321b1f33 100644 --- a/internal/storage/runtime_test.go +++ b/internal/storage/runtime_test.go @@ -17,9 +17,10 @@ import ( // The actual test suite var _ = t.Describe("Runtime", func() { var ( - mockCtrl *gomock.Controller - storeMock *containerstoragemock.MockStore - imageServerMock *criostoragemock.MockImageServer + mockCtrl *gomock.Controller + storeMock *containerstoragemock.MockStore + imageServerMock *criostoragemock.MockImageServer + imageServerListMock *criostoragemock.MockImageServerList ) // The system under test @@ -34,8 +35,9 @@ var _ = t.Describe("Runtime", func() { mockCtrl = gomock.NewController(GinkgoT()) storeMock = containerstoragemock.NewMockStore(mockCtrl) imageServerMock = criostoragemock.NewMockImageServer(mockCtrl) + imageServerListMock = criostoragemock.NewMockImageServerList(mockCtrl) - sut = storage.GetRuntimeService(context.Background(), imageServerMock) + sut = storage.GetRuntimeService(context.Background(), imageServerListMock) Expect(sut).NotTo(BeNil()) ctx = context.TODO() @@ -47,6 +49,7 @@ var _ = t.Describe("Runtime", func() { // The part of createContainerOrPodSandbox before a CreateContainer call, if the image already exists locally. mockCreateContainerOrPodSandboxImageExists := func() mockSequence { return inOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), mockParseStoreReference(storeMock, "imagename"), imageServerMock.EXPECT().GetStore().Return(storeMock), @@ -61,10 +64,10 @@ var _ = t.Describe("Runtime", func() { It("should succeed to retrieve the run dir", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{}, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().ContainerRunDirectory(gomock.Any()). Return("dir", nil), ) @@ -80,6 +83,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to retrieve the run dir on not existing container", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(nil, t.TestError), @@ -96,6 +100,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to retrieve the run dir on invalid container ID", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(nil, cs.ErrContainerUnknown), @@ -116,10 +121,10 @@ var _ = t.Describe("Runtime", func() { It("should succeed to retrieve the work dir", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{}, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().ContainerDirectory(gomock.Any()). Return("dir", nil), ) @@ -135,6 +140,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to retrieve the work dir on not existing container", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(nil, t.TestError), @@ -151,6 +157,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to retrieve the work dir on invalid container ID", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(nil, cs.ErrContainerUnknown), @@ -170,10 +177,10 @@ var _ = t.Describe("Runtime", func() { It("should succeed to stop a container", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{}, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Unmount(gomock.Any(), gomock.Any()). Return(true, nil), ) @@ -198,6 +205,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to stop a container on unknown container", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(nil, t.TestError), @@ -213,10 +221,10 @@ var _ = t.Describe("Runtime", func() { It("should fail to stop a container on unmount error", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{}, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Unmount(gomock.Any(), gomock.Any()). Return(false, t.TestError), ) @@ -233,10 +241,10 @@ var _ = t.Describe("Runtime", func() { It("should succeed to start a container", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{Metadata: "{}"}, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Mount(gomock.Any(), gomock.Any()). Return("mount", nil), ) @@ -252,6 +260,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to start a container on store error", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(nil, t.TestError), @@ -268,6 +277,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to start a container on unknown ID", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(nil, cs.ErrContainerUnknown), @@ -285,6 +295,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to start a container on invalid metadata", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{Metadata: "invalid"}, nil), @@ -301,10 +312,10 @@ var _ = t.Describe("Runtime", func() { It("should fail to start a container on mount error", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{Metadata: "{}"}, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Mount(gomock.Any(), gomock.Any()). Return("", t.TestError), ) @@ -322,6 +333,7 @@ var _ = t.Describe("Runtime", func() { It("should succeed to retrieve the container metadata", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Metadata(gomock.Any()). Return(`{"Pod": true}`, nil), @@ -339,6 +351,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to retrieve the container metadata on store error", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Metadata(gomock.Any()). Return("", t.TestError), @@ -355,6 +368,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to retrieve the container metadata on invalid JSON", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Metadata(gomock.Any()). Return("invalid", nil), @@ -375,6 +389,7 @@ var _ = t.Describe("Runtime", func() { metadata := &storage.RuntimeContainerMetadata{Pod: true} metadata.SetMountLabel("label") gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().SetMetadata(gomock.Any(), gomock.Any()). Return(nil), @@ -391,6 +406,7 @@ var _ = t.Describe("Runtime", func() { // Given metadata := &storage.RuntimeContainerMetadata{Pod: true} gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().SetMetadata(gomock.Any(), gomock.Any()). Return(t.TestError), @@ -408,12 +424,11 @@ var _ = t.Describe("Runtime", func() { It("should succeed to delete a container", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{}, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Layer("").Return(nil, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().DeleteContainer(gomock.Any()). Return(nil), ) @@ -438,6 +453,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to delete a container on store retrieval error", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(nil, t.TestError), @@ -453,12 +469,11 @@ var _ = t.Describe("Runtime", func() { It("should fail to delete a container on store deletion error", func() { // Given gomock.InOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Container(gomock.Any()). Return(&cs.Container{}, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Layer("").Return(nil, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().DeleteContainer(gomock.Any()). Return(t.TestError), ) @@ -748,6 +763,7 @@ var _ = t.Describe("Runtime", func() { It("should fail to create a container on error accessing local image", func() { // Given inOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), mockParseStoreReference(storeMock, "imagename"), imageServerMock.EXPECT().GetStore().Return(storeMock), @@ -784,6 +800,7 @@ var _ = t.Describe("Runtime", func() { pulledRef, err := istorage.Transport.ParseStoreReference(storeMock, "pauseimagename") Expect(err).To(BeNil()) inOrder( + imageServerListMock.EXPECT().GetImageServer(gomock.Any()).Return(imageServerMock), imageServerMock.EXPECT().GetStore().Return(storeMock), mockParseStoreReference(storeMock, "pauseimagename"), imageServerMock.EXPECT().GetStore().Return(storeMock), @@ -812,7 +829,7 @@ var _ = t.Describe("Runtime", func() { It("should pull pauseImage if not available locally, using default credentials", func() { // The system under test - sut := storage.GetRuntimeService(context.Background(), imageServerMock) + sut := storage.GetRuntimeService(context.Background(), imageServerListMock) Expect(sut).NotTo(BeNil()) // Given @@ -832,7 +849,7 @@ var _ = t.Describe("Runtime", func() { It("should pull pauseImage if not available locally, using provided credential file", func() { // The system under test - sut := storage.GetRuntimeService(context.Background(), imageServerMock) + sut := storage.GetRuntimeService(context.Background(), imageServerListMock) Expect(sut).NotTo(BeNil()) // Given diff --git a/server/container_create_linux.go b/server/container_create_linux.go index 79b6e230089..c8b64fdfadd 100644 --- a/server/container_create_linux.go +++ b/server/container_create_linux.go @@ -207,7 +207,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, ctr ctrfactory.Cont if err != nil { return nil, err } - images, err := s.StorageImageServer().ResolveNames(s.config.SystemContext, image) + images, err := s.StorageImageServer(containerID).ResolveNames(s.config.SystemContext, image) if err != nil { if err == storage.ErrCannotParseImageID { images = append(images, image) @@ -222,7 +222,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, ctr ctrfactory.Cont imgResultErr error ) for _, img := range images { - imgResult, imgResultErr = s.StorageImageServer().ImageStatus(s.config.SystemContext, img) + imgResult, imgResultErr = s.StorageImageServer(containerID).ImageStatus(s.config.SystemContext, img) if imgResultErr == nil { break } diff --git a/server/container_restore.go b/server/container_restore.go index 9e901e75879..3bf2c2b3744 100644 --- a/server/container_restore.go +++ b/server/container_restore.go @@ -73,16 +73,16 @@ func (s *Server) CRImportCheckpoint( if checkpointIsOCIImage { log.Debugf(ctx, "Restoring from oci image %s\n", input) - - imageRef, err := istorage.Transport.ParseStoreReference(s.ContainerServer.StorageImageServer().GetStore(), input) + store := s.ContainerServer.StorageImageServer(sbID).GetStore() + imageRef, err := istorage.Transport.ParseStoreReference(store, input) if err != nil { return "", fmt.Errorf("failed to parse image name: %s: %w", input, err) } - img, err := istorage.Transport.GetStoreImage(s.ContainerServer.StorageImageServer().GetStore(), imageRef) + img, err := istorage.Transport.GetStoreImage(store, imageRef) if err != nil { return "", err } - mountPoint, err = s.ContainerServer.StorageImageServer().GetStore().MountImage(img.ID, nil, "") + mountPoint, err = store.MountImage(img.ID, nil, "") if err != nil { return "", err } @@ -91,7 +91,7 @@ func (s *Server) CRImportCheckpoint( logrus.Debugf("Checkpoint image %s mounted at %v\n", input, mountPoint) defer func() { - if _, err := s.ContainerServer.StorageImageServer().GetStore().UnmountImage(input, true); err != nil { + if _, err := store.UnmountImage(input, true); err != nil { logrus.Errorf("Could not unmount checkpoint image %s: %q", input, err) } }() diff --git a/server/container_restore_test.go b/server/container_restore_test.go index f8fc16cda77..606afab1b7e 100644 --- a/server/container_restore_test.go +++ b/server/container_restore_test.go @@ -541,7 +541,6 @@ var _ = t.Describe("ContainerRestore", func() { storeMock.EXPECT().GraphDriverName().Return(""), storeMock.EXPECT().GraphRoot().Return(""), storeMock.EXPECT().RunRoot().Return(""), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().Image(gomock.Any()). Return(&cs.Image{ ID: "abcdef", @@ -549,10 +548,8 @@ var _ = t.Describe("ContainerRestore", func() { "localhost/checkpoint-image:tag1", }, }, nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().MountImage(gomock.Any(), gomock.Any(), gomock.Any()). Return("", nil), - imageServerMock.EXPECT().GetStore().Return(storeMock), storeMock.EXPECT().UnmountImage(gomock.Any(), true). Return(false, nil), ) diff --git a/server/image_fs_info.go b/server/image_fs_info.go index de1660b0f76..f7012e03fab 100644 --- a/server/image_fs_info.go +++ b/server/image_fs_info.go @@ -32,7 +32,9 @@ func getStorageFsInfo(store storage.Store) (*types.FilesystemUsage, error) { // ImageFsInfo returns information of the filesystem that is used to store images. func (s *Server) ImageFsInfo(context.Context, *types.ImageFsInfoRequest) (*types.ImageFsInfoResponse, error) { - store := s.StorageImageServer().GetStore() + // Using default ImageServer here, as we're using a single Store object + // we return the filesystem info for the default store. + store := s.ImageServerList().GetDefaultImageServer().GetStore() fsUsage, err := getStorageFsInfo(store) if err != nil { return nil, err diff --git a/server/image_list.go b/server/image_list.go index 0e1c736a907..799c48c1230 100644 --- a/server/image_list.go +++ b/server/image_list.go @@ -19,7 +19,8 @@ func (s *Server) ListImages(ctx context.Context, req *types.ListImagesRequest) ( filter = filterImage.Image } } - results, err := s.StorageImageServer().ListImages(s.config.SystemContext, filter) + // perform ListImages on all image servers available + results, err := s.ImageServerList().ListImages(s.config.SystemContext, filter) if err != nil { return nil, err } diff --git a/server/image_pull.go b/server/image_pull.go index 8b6a493428c..b6c7da68793 100644 --- a/server/image_pull.go +++ b/server/image_pull.go @@ -9,6 +9,7 @@ import ( "time" imageTypes "github.com/containers/image/v5/types" + sboxfactory "github.com/cri-o/cri-o/internal/factory/sandbox" "github.com/cri-o/cri-o/internal/log" "github.com/cri-o/cri-o/internal/storage" "github.com/cri-o/cri-o/server/metrics" @@ -36,9 +37,21 @@ func (s *Server) PullImage(ctx context.Context, req *types.PullImageRequest) (*t if req.SandboxConfig != nil && req.SandboxConfig.Linux != nil { sandboxCgroup = req.SandboxConfig.Linux.CgroupParent } + + var name, podID string + if req.SandboxConfig != nil && req.SandboxConfig.Metadata != nil { + name = sboxfactory.ConstructSandboxNameFromConfig(req.SandboxConfig) + podID, err = s.PodIDForName(name) + if err != nil { + log.Warnf(ctx, "Failed getting sandbox %s", name) + podID = "" + } + } + pullArgs := pullArguments{ image: image, sandboxCgroup: sandboxCgroup, + containerID: podID, } if req.Auth != nil { username := req.Auth.Username @@ -125,20 +138,21 @@ func (s *Server) pullImage(ctx context.Context, pullArgs *pullArguments) (string images []string pulled string ) - images, err = s.StorageImageServer().ResolveNames(s.config.SystemContext, pullArgs.image) + is := s.StorageImageServer(pullArgs.containerID) + images, err = is.ResolveNames(s.config.SystemContext, pullArgs.image) if err != nil { return "", err } for _, img := range images { var tmpImg imageTypes.ImageCloser - tmpImg, err = s.StorageImageServer().PrepareImage(&sourceCtx, img) + tmpImg, err = is.PrepareImage(&sourceCtx, img) if err != nil { // We're not able to find the image remotely, check if it's // available locally, but only for localhost/ prefixed ones. // This allows pulling localhost/ prefixed images even if the // `imagePullPolicy` is set to `Always`. if strings.HasPrefix(img, localRegistryPrefix) { - if _, err := s.StorageImageServer().ImageStatus( + if _, err := is.ImageStatus( s.config.SystemContext, img, ); err == nil { pulled = img @@ -152,7 +166,7 @@ func (s *Server) pullImage(ctx context.Context, pullArgs *pullArguments) (string defer tmpImg.Close() // nolint:gocritic var storedImage *storage.ImageResult - storedImage, err = s.StorageImageServer().ImageStatus(s.config.SystemContext, img) + storedImage, err = is.ImageStatus(s.config.SystemContext, img) if err == nil { tmpImgConfigDigest := tmpImg.ConfigInfo().Digest if tmpImgConfigDigest.String() == "" { @@ -241,7 +255,7 @@ func (s *Server) pullImage(ctx context.Context, pullArgs *pullArguments) (string } } - _, err = s.StorageImageServer().PullImage(s.config.SystemContext, img, &storage.ImageCopyOptions{ + _, err = is.PullImage(s.config.SystemContext, img, &storage.ImageCopyOptions{ SourceCtx: &sourceCtx, DestinationCtx: s.config.SystemContext, OciDecryptConfig: decryptConfig, @@ -268,7 +282,7 @@ func (s *Server) pullImage(ctx context.Context, pullArgs *pullArguments) (string // Update metric for successful image pulls metrics.Instance().MetricImagePullsSuccessesInc(pulled) - status, err := s.StorageImageServer().ImageStatus(s.config.SystemContext, pulled) + status, err := is.ImageStatus(s.config.SystemContext, pulled) if err != nil { return "", err } diff --git a/server/image_remove.go b/server/image_remove.go index 81d517a80e3..2842974f89a 100644 --- a/server/image_remove.go +++ b/server/image_remove.go @@ -32,7 +32,7 @@ func (s *Server) removeImage(ctx context.Context, imageRef string) error { ctx, span := log.StartSpan(ctx) defer span.End() - images, err := s.StorageImageServer().ResolveNames(s.config.SystemContext, imageRef) + images, err := s.ImageServerList().ResolveNames(s.config.SystemContext, imageRef) if err != nil { if err == storage.ErrCannotParseImageID { images = append(images, imageRef) @@ -41,7 +41,7 @@ func (s *Server) removeImage(ctx context.Context, imageRef string) error { } } for _, img := range images { - err = s.StorageImageServer().UntagImage(s.config.SystemContext, img) + err = s.ImageServerList().UntagImage(s.config.SystemContext, img) if err != nil { log.Debugf(ctx, "Error deleting image %s: %v", img, err) continue diff --git a/server/image_remove_test.go b/server/image_remove_test.go index fc4ab4a5a71..b40982fd694 100644 --- a/server/image_remove_test.go +++ b/server/image_remove_test.go @@ -26,6 +26,8 @@ var _ = t.Describe("ImageRemove", func() { imageServerMock.EXPECT().ResolveNames( gomock.Any(), gomock.Any()). Return([]string{"image"}, nil), + imageServerMock.EXPECT().GetStore().Return(storeMock), + storeMock.EXPECT().Image(gomock.Any()).Return(nil, nil), imageServerMock.EXPECT().UntagImage(gomock.Any(), gomock.Any()).Return(nil), ) @@ -43,6 +45,8 @@ var _ = t.Describe("ImageRemove", func() { imageServerMock.EXPECT().ResolveNames( gomock.Any(), gomock.Any()). Return(nil, storage.ErrCannotParseImageID), + imageServerMock.EXPECT().GetStore().Return(storeMock), + storeMock.EXPECT().Image(gomock.Any()).Return(nil, nil), imageServerMock.EXPECT().UntagImage(gomock.Any(), gomock.Any()).Return(nil), ) @@ -60,6 +64,8 @@ var _ = t.Describe("ImageRemove", func() { imageServerMock.EXPECT().ResolveNames( gomock.Any(), gomock.Any()). Return([]string{"image"}, nil), + imageServerMock.EXPECT().GetStore().Return(storeMock), + storeMock.EXPECT().Image(gomock.Any()).Return(nil, nil), imageServerMock.EXPECT().UntagImage(gomock.Any(), gomock.Any()).Return(t.TestError), ) diff --git a/server/image_status.go b/server/image_status.go index e926acfbc15..03756faea3a 100644 --- a/server/image_status.go +++ b/server/image_status.go @@ -30,7 +30,7 @@ func (s *Server) ImageStatus(ctx context.Context, req *types.ImageStatusRequest) } log.Infof(ctx, "Checking image status: %s", image) - images, err := s.StorageImageServer().ResolveNames(s.config.SystemContext, image) + images, err := s.ImageServerList().ResolveNames(s.config.SystemContext, image) if err != nil { if err == pkgstorage.ErrCannotParseImageID { images = append(images, image) @@ -43,7 +43,7 @@ func (s *Server) ImageStatus(ctx context.Context, req *types.ImageStatusRequest) lastErr error ) for _, image := range images { - status, err := s.StorageImageServer().ImageStatus(s.config.SystemContext, image) + status, err := s.ImageServerList().ImageStatus(s.config.SystemContext, image) if err != nil { if errors.Is(err, storage.ErrImageUnknown) { log.Debugf(ctx, "Can't find %s", image) diff --git a/server/server.go b/server/server.go index b3f5f3411d4..193604d6ea9 100644 --- a/server/server.go +++ b/server/server.go @@ -99,6 +99,7 @@ type pullArguments struct { image string sandboxCgroup string credentials imageTypes.DockerAuthConfig + containerID string } // pullOperation is used to synchronize parallel pull operations via the diff --git a/server/suite_test.go b/server/suite_test.go index 703b931cc97..83ea9e9669c 100644 --- a/server/suite_test.go +++ b/server/suite_test.go @@ -37,22 +37,23 @@ func TestServer(t *testing.T) { } var ( - libMock *libmock.MockIface - mockCtrl *gomock.Controller - serverConfig *config.Config - storeMock *containerstoragemock.MockStore - imageServerMock *criostoragemock.MockImageServer - runtimeServerMock *criostoragemock.MockRuntimeServer - imageCloserMock *imagetypesmock.MockImageCloser - cniPluginMock *ocicnitypesmock.MockCNIPlugin - ociRuntimeMock *ocimock.MockRuntimeImpl - sut *server.Server - t *TestFramework - testContainer *oci.Container - testManifest []byte - testPath string - testSandbox *sandbox.Sandbox - testStreamService server.StreamService + libMock *libmock.MockIface + mockCtrl *gomock.Controller + serverConfig *config.Config + storeMock *containerstoragemock.MockStore + imageServerMock *criostoragemock.MockImageServer + imageServerListMock *criostoragemock.MockImageServerList + runtimeServerMock *criostoragemock.MockRuntimeServer + imageCloserMock *imagetypesmock.MockImageCloser + cniPluginMock *ocicnitypesmock.MockCNIPlugin + ociRuntimeMock *ocimock.MockRuntimeImpl + sut *server.Server + t *TestFramework + testContainer *oci.Container + testManifest []byte + testPath string + testSandbox *sandbox.Sandbox + testStreamService server.StreamService emptyDir string ) @@ -71,6 +72,7 @@ var _ = BeforeSuite(func() { libMock = libmock.NewMockIface(mockCtrl) storeMock = containerstoragemock.NewMockStore(mockCtrl) imageServerMock = criostoragemock.NewMockImageServer(mockCtrl) + imageServerListMock = criostoragemock.NewMockImageServerList(mockCtrl) runtimeServerMock = criostoragemock.NewMockRuntimeServer(mockCtrl) imageCloserMock = imagetypesmock.NewMockImageCloser(mockCtrl) cniPluginMock = ocicnitypesmock.NewMockCNIPlugin(mockCtrl) diff --git a/test/mocks/criostorage/criostorage.go b/test/mocks/criostorage/criostorage.go index a0cbd691701..e84a6a4fd4d 100644 --- a/test/mocks/criostorage/criostorage.go +++ b/test/mocks/criostorage/criostorage.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/cri-o/cri-o/internal/storage (interfaces: ImageServer,RuntimeServer) +// Source: github.com/cri-o/cri-o/internal/storage (interfaces: ImageServer,ImageServerList,RuntimeServer) // Package criostoragemock is a generated GoMock package. package criostoragemock @@ -141,6 +141,152 @@ func (mr *MockImageServerMockRecorder) UntagImage(arg0, arg1 interface{}) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UntagImage", reflect.TypeOf((*MockImageServer)(nil).UntagImage), arg0, arg1) } +// MockImageServerList is a mock of ImageServerList interface. +type MockImageServerList struct { + ctrl *gomock.Controller + recorder *MockImageServerListMockRecorder +} + +// MockImageServerListMockRecorder is the mock recorder for MockImageServerList. +type MockImageServerListMockRecorder struct { + mock *MockImageServerList +} + +// NewMockImageServerList creates a new mock instance. +func NewMockImageServerList(ctrl *gomock.Controller) *MockImageServerList { + mock := &MockImageServerList{ctrl: ctrl} + mock.recorder = &MockImageServerListMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockImageServerList) EXPECT() *MockImageServerListMockRecorder { + return m.recorder +} + +// DeleteImageServer mocks base method. +func (m *MockImageServerList) DeleteImageServer(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeleteImageServer", arg0) +} + +// DeleteImageServer indicates an expected call of DeleteImageServer. +func (mr *MockImageServerListMockRecorder) DeleteImageServer(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteImageServer", reflect.TypeOf((*MockImageServerList)(nil).DeleteImageServer), arg0) +} + +// GetDefaultImageServer mocks base method. +func (m *MockImageServerList) GetDefaultImageServer() storage0.ImageServer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDefaultImageServer") + ret0, _ := ret[0].(storage0.ImageServer) + return ret0 +} + +// GetDefaultImageServer indicates an expected call of GetDefaultImageServer. +func (mr *MockImageServerListMockRecorder) GetDefaultImageServer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultImageServer", reflect.TypeOf((*MockImageServerList)(nil).GetDefaultImageServer)) +} + +// GetImageServer mocks base method. +func (m *MockImageServerList) GetImageServer(arg0 string) storage0.ImageServer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetImageServer", arg0) + ret0, _ := ret[0].(storage0.ImageServer) + return ret0 +} + +// GetImageServer indicates an expected call of GetImageServer. +func (mr *MockImageServerListMockRecorder) GetImageServer(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImageServer", reflect.TypeOf((*MockImageServerList)(nil).GetImageServer), arg0) +} + +// ImageStatus mocks base method. +func (m *MockImageServerList) ImageStatus(arg0 *types.SystemContext, arg1 string) (*storage0.ImageResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ImageStatus", arg0, arg1) + ret0, _ := ret[0].(*storage0.ImageResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ImageStatus indicates an expected call of ImageStatus. +func (mr *MockImageServerListMockRecorder) ImageStatus(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageStatus", reflect.TypeOf((*MockImageServerList)(nil).ImageStatus), arg0, arg1) +} + +// ListImages mocks base method. +func (m *MockImageServerList) ListImages(arg0 *types.SystemContext, arg1 string) ([]storage0.ImageResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListImages", arg0, arg1) + ret0, _ := ret[0].([]storage0.ImageResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListImages indicates an expected call of ListImages. +func (mr *MockImageServerListMockRecorder) ListImages(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListImages", reflect.TypeOf((*MockImageServerList)(nil).ListImages), arg0, arg1) +} + +// ResolveNames mocks base method. +func (m *MockImageServerList) ResolveNames(arg0 *types.SystemContext, arg1 string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResolveNames", arg0, arg1) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ResolveNames indicates an expected call of ResolveNames. +func (mr *MockImageServerListMockRecorder) ResolveNames(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveNames", reflect.TypeOf((*MockImageServerList)(nil).ResolveNames), arg0, arg1) +} + +// SetDefaultImageServer mocks base method. +func (m *MockImageServerList) SetDefaultImageServer(arg0 storage0.ImageServer) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetDefaultImageServer", arg0) +} + +// SetDefaultImageServer indicates an expected call of SetDefaultImageServer. +func (mr *MockImageServerListMockRecorder) SetDefaultImageServer(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDefaultImageServer", reflect.TypeOf((*MockImageServerList)(nil).SetDefaultImageServer), arg0) +} + +// SetImageServer mocks base method. +func (m *MockImageServerList) SetImageServer(arg0 string, arg1 storage0.ImageServer) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetImageServer", arg0, arg1) +} + +// SetImageServer indicates an expected call of SetImageServer. +func (mr *MockImageServerListMockRecorder) SetImageServer(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetImageServer", reflect.TypeOf((*MockImageServerList)(nil).SetImageServer), arg0, arg1) +} + +// UntagImage mocks base method. +func (m *MockImageServerList) UntagImage(arg0 *types.SystemContext, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UntagImage", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// UntagImage indicates an expected call of UntagImage. +func (mr *MockImageServerListMockRecorder) UntagImage(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UntagImage", reflect.TypeOf((*MockImageServerList)(nil).UntagImage), arg0, arg1) +} + // MockRuntimeServer is a mock of RuntimeServer interface. type MockRuntimeServer struct { ctrl *gomock.Controller