Move cri base plugin to CRI runtime service

Create new plugin type for CRI runtime and image services.

Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
Derek McGowan
2024-01-23 21:53:37 -08:00
parent 22e1a2e65c
commit 9795677fe9
20 changed files with 205 additions and 104 deletions

View File

@@ -319,8 +319,6 @@ type PluginConfig struct {
ContainerdConfig `toml:"containerd" json:"containerd"`
// CniConfig contains config related to cni
CniConfig `toml:"cni" json:"cni"`
// DisableTCPService disables serving CRI on the TCP server.
DisableTCPService bool `toml:"disable_tcp_service" json:"disableTCPService"`
// StreamServerAddress is the ip address streaming server is listening on.
StreamServerAddress string `toml:"stream_server_address" json:"streamServerAddress"`
// StreamServerPort is the port streaming server is listening on.
@@ -433,6 +431,12 @@ type Config struct {
StateDir string `json:"stateDir"`
}
// ServiceConfig contains all the configuration for the CRI API server.
type ServiceConfig struct {
// DisableTCPService disables serving CRI on the TCP server.
DisableTCPService bool `toml:"disable_tcp_service" json:"disableTCPService"`
}
const (
// RuntimeUntrusted is the implicit runtime defined for ContainerdConfig.UntrustedWorkloadRuntime
RuntimeUntrusted = "untrusted"

View File

@@ -89,7 +89,6 @@ func DefaultConfig() PluginConfig {
},
},
},
DisableTCPService: true,
StreamServerAddress: "127.0.0.1",
StreamServerPort: "0",
StreamIdleTimeout: streaming.DefaultConfig.StreamIdleTimeout.String(), // 4 hour

View File

@@ -78,7 +78,6 @@ func DefaultConfig() PluginConfig {
},
},
},
DisableTCPService: true,
StreamServerAddress: "127.0.0.1",
StreamServerPort: "0",
StreamIdleTimeout: streaming.DefaultConfig.StreamIdleTimeout.String(), // 4 hour

View File

@@ -17,6 +17,7 @@
package cri
import (
"context"
"fmt"
"io"
@@ -25,13 +26,13 @@ import (
"github.com/containerd/plugin/registry"
containerd "github.com/containerd/containerd/v2/client"
srvconfig "github.com/containerd/containerd/v2/cmd/containerd/server/config"
"github.com/containerd/containerd/v2/core/sandbox"
criconfig "github.com/containerd/containerd/v2/pkg/cri/config"
"github.com/containerd/containerd/v2/pkg/cri/constants"
"github.com/containerd/containerd/v2/pkg/cri/instrument"
"github.com/containerd/containerd/v2/pkg/cri/nri"
"github.com/containerd/containerd/v2/pkg/cri/server"
"github.com/containerd/containerd/v2/pkg/cri/server/base"
nriservice "github.com/containerd/containerd/v2/pkg/nri"
"github.com/containerd/containerd/v2/plugins"
"github.com/containerd/platforms"
@@ -43,13 +44,11 @@ import (
// Register CRI service plugin
func init() {
registry.Register(&plugin.Registration{
Type: plugins.GRPCPlugin,
ID: "cri",
Requires: []plugin.Type{
plugins.CRIImagePlugin,
plugins.InternalPlugin,
plugins.CRIServicePlugin,
plugins.SandboxControllerPlugin,
plugins.NRIApiPlugin,
plugins.EventPlugin,
@@ -58,23 +57,46 @@ func init() {
plugins.SandboxStorePlugin,
plugins.TransferPlugin,
},
Config: &criconfig.ServiceConfig{
DisableTCPService: true,
},
ConfigMigration: func(ctx context.Context, version int, pluginConfigs map[string]interface{}) error {
if version >= srvconfig.CurrentConfigVersion {
return nil
}
const pluginName = string(plugins.GRPCPlugin) + ".cri"
original, ok := pluginConfigs[pluginName]
if !ok {
return nil
}
src := original.(map[string]interface{})
// Currently only a single key migrated
if val, ok := src["disable_tcp_service"]; ok {
pluginConfigs[pluginName] = map[string]interface{}{
"disable_tcp_service": val,
}
} else {
delete(pluginConfigs, pluginName)
}
return nil
},
InitFn: initCRIService,
})
}
func initCRIService(ic *plugin.InitContext) (interface{}, error) {
ctx := ic.Context
config := ic.Config.(*criconfig.ServiceConfig)
// Get base CRI dependencies.
criBasePlugin, err := ic.GetByID(plugins.InternalPlugin, "cri")
// Get runtime service.
criRuntimePlugin, err := ic.GetByID(plugins.CRIServicePlugin, "runtime")
if err != nil {
return nil, fmt.Errorf("unable to load CRI service base dependencies: %w", err)
return nil, fmt.Errorf("unable to load CRI runtime service plugin dependency: %w", err)
}
criBase := criBasePlugin.(*base.CRIBase)
c := criBase.Config
// Get image service.
criImagePlugin, err := ic.GetSingle(plugins.CRIImagePlugin)
criImagePlugin, err := ic.GetByID(plugins.CRIServicePlugin, "images")
if err != nil {
return nil, fmt.Errorf("unable to load CRI image service plugin dependency: %w", err)
}
@@ -98,15 +120,16 @@ func initCRIService(ic *plugin.InitContext) (interface{}, error) {
}
options := &server.CRIServiceOptions{
RuntimeService: criRuntimePlugin.(server.RuntimeService),
ImageService: criImagePlugin.(server.ImageService),
NRI: getNRIAPI(ic),
Client: client,
SandboxControllers: sbControllers,
BaseOCISpecs: criBase.BaseOCISpecs,
}
is := criImagePlugin.(imageService).GRPCService()
s, rs, err := server.NewCRIService(criBase.Config, options)
// TODO: More options specifically for grpc service?
s, rs, err := server.NewCRIService(options)
if err != nil {
return nil, fmt.Errorf("failed to create CRI service: %w", err)
}
@@ -127,7 +150,7 @@ func initCRIService(ic *plugin.InitContext) (interface{}, error) {
initializer: s,
}
if c.DisableTCPService {
if config.DisableTCPService {
return service, nil
}

View File

@@ -1,203 +0,0 @@
/*
Copyright The containerd 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 base
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
"github.com/containerd/log"
"github.com/containerd/plugin"
"github.com/containerd/plugin/registry"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"k8s.io/klog/v2"
srvconfig "github.com/containerd/containerd/v2/cmd/containerd/server/config"
criconfig "github.com/containerd/containerd/v2/pkg/cri/config"
"github.com/containerd/containerd/v2/pkg/cri/constants"
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/containerd/v2/plugins"
"github.com/containerd/containerd/v2/plugins/services/warning"
"github.com/containerd/platforms"
)
// CRIBase contains common dependencies for CRI's runtime, image, and podsandbox services.
type CRIBase struct {
// Config contains all configurations.
Config criconfig.Config
// BaseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
BaseOCISpecs map[string]*oci.Spec
}
func init() {
config := criconfig.DefaultConfig()
// Base plugin that other CRI services depend on.
registry.Register(&plugin.Registration{
Type: plugins.InternalPlugin,
ID: "cri",
Config: &config,
Requires: []plugin.Type{
plugins.WarningPlugin,
},
ConfigMigration: func(ctx context.Context, version int, plugins map[string]interface{}) error {
if version >= srvconfig.CurrentConfigVersion {
return nil
}
c, ok := plugins["io.containerd.grpc.v1.cri"]
if !ok {
return nil
}
conf := c.(map[string]interface{})
migrateConfig(conf)
plugins["io.containerd.internal.v1.cri"] = conf
delete(plugins, "io.containerd.grpc.v1.cri")
return nil
},
InitFn: initCRIBase,
})
}
func initCRIBase(ic *plugin.InitContext) (interface{}, error) {
ic.Meta.Platforms = []imagespec.Platform{platforms.DefaultSpec()}
ic.Meta.Exports = map[string]string{"CRIVersion": constants.CRIVersion}
ctx := ic.Context
pluginConfig := ic.Config.(*criconfig.PluginConfig)
if warnings, err := criconfig.ValidatePluginConfig(ctx, pluginConfig); err != nil {
return nil, fmt.Errorf("invalid plugin config: %w", err)
} else if len(warnings) > 0 {
ws, err := ic.GetSingle(plugins.WarningPlugin)
if err != nil {
return nil, err
}
warn := ws.(warning.Service)
for _, w := range warnings {
warn.Emit(ctx, w)
}
}
// For backward compatibility, we have to keep the rootDir and stateDir the same as before.
containerdRootDir := filepath.Dir(ic.Properties[plugins.PropertyRootDir])
rootDir := filepath.Join(containerdRootDir, "io.containerd.grpc.v1.cri")
containerdStateDir := filepath.Dir(ic.Properties[plugins.PropertyStateDir])
stateDir := filepath.Join(containerdStateDir, "io.containerd.grpc.v1.cri")
c := criconfig.Config{
PluginConfig: *pluginConfig,
ContainerdRootDir: containerdRootDir,
ContainerdEndpoint: ic.Properties[plugins.PropertyGRPCAddress],
RootDir: rootDir,
StateDir: stateDir,
}
log.G(ctx).Infof("Start cri plugin with config %+v", c)
if err := setGLogLevel(); err != nil {
return nil, fmt.Errorf("failed to set glog level: %w", err)
}
ociSpec, err := loadBaseOCISpecs(&c)
if err != nil {
return nil, fmt.Errorf("failed to create load basic oci spec: %w", err)
}
return &CRIBase{
Config: c,
BaseOCISpecs: ociSpec,
}, nil
}
func loadBaseOCISpecs(config *criconfig.Config) (map[string]*oci.Spec, error) {
specs := map[string]*oci.Spec{}
for _, cfg := range config.Runtimes {
if cfg.BaseRuntimeSpec == "" {
continue
}
// Don't load same file twice
if _, ok := specs[cfg.BaseRuntimeSpec]; ok {
continue
}
spec, err := loadOCISpec(cfg.BaseRuntimeSpec)
if err != nil {
return nil, fmt.Errorf("failed to load base OCI spec from file: %s: %w", cfg.BaseRuntimeSpec, err)
}
specs[cfg.BaseRuntimeSpec] = spec
}
return specs, nil
}
func loadOCISpec(filename string) (*oci.Spec, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("failed to open base OCI spec: %s: %w", filename, err)
}
defer file.Close()
spec := oci.Spec{}
if err := json.NewDecoder(file).Decode(&spec); err != nil {
return nil, fmt.Errorf("failed to parse base OCI spec file: %w", err)
}
return &spec, nil
}
// Set glog level.
func setGLogLevel() error {
l := log.GetLevel()
fs := flag.NewFlagSet("klog", flag.PanicOnError)
klog.InitFlags(fs)
if err := fs.Set("logtostderr", "true"); err != nil {
return err
}
switch l {
case log.TraceLevel:
return fs.Set("v", "5")
case log.DebugLevel:
return fs.Set("v", "4")
case log.InfoLevel:
return fs.Set("v", "2")
default:
// glog doesn't support other filters. Defaults to v=0.
}
return nil
}
func migrateConfig(conf map[string]interface{}) {
containerdConf, ok := conf["containerd"]
if !ok {
return
}
runtimesConf, ok := containerdConf.(map[string]interface{})["runtimes"]
if !ok {
return
}
for _, v := range runtimesConf.(map[string]interface{}) {
runtimeConf := v.(map[string]interface{})
if sandboxMode, ok := runtimeConf["sandbox_mode"]; ok {
if _, ok := runtimeConf["sandboxer"]; !ok {
runtimeConf["sandboxer"] = sandboxMode
}
}
}
}

View File

@@ -1,60 +0,0 @@
/*
Copyright The containerd 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 base
import (
"encoding/json"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
criconfig "github.com/containerd/containerd/v2/pkg/cri/config"
"github.com/containerd/containerd/v2/pkg/oci"
)
func TestLoadBaseOCISpec(t *testing.T) {
spec := oci.Spec{Version: "1.0.2", Hostname: "default"}
file, err := os.CreateTemp("", "spec-test-")
require.NoError(t, err)
defer func() {
assert.NoError(t, file.Close())
assert.NoError(t, os.RemoveAll(file.Name()))
}()
err = json.NewEncoder(file).Encode(&spec)
assert.NoError(t, err)
config := criconfig.Config{}
config.Runtimes = map[string]criconfig.Runtime{
"runc": {BaseRuntimeSpec: file.Name()},
}
specs, err := loadBaseOCISpecs(&config)
assert.NoError(t, err)
assert.Len(t, specs, 1)
out, ok := specs[file.Name()]
assert.True(t, ok, "expected spec with file name %q", file.Name())
assert.Equal(t, "1.0.2", out.Version)
assert.Equal(t, "default", out.Hostname)
}

View File

@@ -394,9 +394,9 @@ func (c *criService) runtimeSpec(id string, platform platforms.Platform, baseSpe
container := &containers.Container{ID: id}
if baseSpecFile != "" {
baseSpec, ok := c.baseOCISpecs[baseSpecFile]
if !ok {
return nil, fmt.Errorf("can't find base OCI spec %q", baseSpecFile)
baseSpec, err := c.LoadOCISpec(baseSpecFile)
if err != nil {
return nil, fmt.Errorf("can't load base OCI spec %q: %w", baseSpecFile, err)
}
spec := oci.Spec{}

View File

@@ -1680,23 +1680,24 @@ func TestPrivilegedDevices(t *testing.T) {
}
func TestBaseOCISpec(t *testing.T) {
c := newTestCRIService()
baseLimit := int64(100)
c.baseOCISpecs = map[string]*oci.Spec{
"/etc/containerd/cri-base.json": {
Process: &runtimespec.Process{
User: runtimespec.User{AdditionalGids: []uint32{9999}},
Capabilities: &runtimespec.LinuxCapabilities{
Permitted: []string{"CAP_SETUID"},
c := newTestCRIService(withRuntimeService(&fakeRuntimeService{
ocispecs: map[string]*oci.Spec{
"/etc/containerd/cri-base.json": {
Process: &runtimespec.Process{
User: runtimespec.User{AdditionalGids: []uint32{9999}},
Capabilities: &runtimespec.LinuxCapabilities{
Permitted: []string{"CAP_SETUID"},
},
},
},
Linux: &runtimespec.Linux{
Resources: &runtimespec.LinuxResources{
Memory: &runtimespec.LinuxMemory{Limit: &baseLimit}, // Will be overwritten by `getCreateContainerTestData`
Linux: &runtimespec.Linux{
Resources: &runtimespec.LinuxResources{
Memory: &runtimespec.LinuxMemory{Limit: &baseLimit}, // Will be overwritten by `getCreateContainerTestData`
},
},
},
},
}
}))
ociRuntime := config.Runtime{}
ociRuntime.BaseRuntimeSpec = "/etc/containerd/cri-base.json"

View File

@@ -524,13 +524,14 @@ func TestContainerAnnotationPassthroughContainerSpec(t *testing.T) {
}
func TestBaseRuntimeSpec(t *testing.T) {
c := newTestCRIService()
c.baseOCISpecs = map[string]*oci.Spec{
"/etc/containerd/cri-base.json": {
Version: "1.0.2",
Hostname: "old",
c := newTestCRIService(withRuntimeService(&fakeRuntimeService{
ocispecs: map[string]*oci.Spec{
"/etc/containerd/cri-base.json": {
Version: "1.0.2",
Hostname: "old",
},
},
}
}))
out, err := c.runtimeSpec(
"id1",
@@ -546,8 +547,10 @@ func TestBaseRuntimeSpec(t *testing.T) {
assert.Equal(t, "new-domain", out.Domainname)
// Make sure original base spec not changed
assert.NotEqual(t, out, c.baseOCISpecs["/etc/containerd/cri-base.json"])
assert.Equal(t, c.baseOCISpecs["/etc/containerd/cri-base.json"].Hostname, "old")
spec, err := c.LoadOCISpec("/etc/containerd/cri-base.json")
assert.NoError(t, err)
assert.NotEqual(t, out, spec)
assert.Equal(t, spec.Hostname, "old")
assert.Equal(t, filepath.Join("/", constants.K8sContainerdNamespace, "id1"), out.Linux.CgroupsPath)
}

View File

@@ -31,7 +31,6 @@ import (
"github.com/containerd/containerd/v2/core/sandbox"
criconfig "github.com/containerd/containerd/v2/pkg/cri/config"
"github.com/containerd/containerd/v2/pkg/cri/constants"
"github.com/containerd/containerd/v2/pkg/cri/server/base"
"github.com/containerd/containerd/v2/pkg/cri/server/podsandbox/types"
imagestore "github.com/containerd/containerd/v2/pkg/cri/store/image"
ctrdutil "github.com/containerd/containerd/v2/pkg/cri/util"
@@ -51,8 +50,7 @@ func init() {
plugins.EventPlugin,
plugins.LeasePlugin,
plugins.SandboxStorePlugin,
plugins.InternalPlugin,
plugins.CRIImagePlugin,
plugins.CRIServicePlugin,
plugins.ServicePlugin,
},
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
@@ -66,26 +64,26 @@ func init() {
return nil, fmt.Errorf("unable to init client for podsandbox: %w", err)
}
// Get base CRI dependencies.
criBasePlugin, err := ic.GetByID(plugins.InternalPlugin, "cri")
// Get runtime service.
criRuntimePlugin, err := ic.GetByID(plugins.CRIServicePlugin, "runtime")
if err != nil {
return nil, fmt.Errorf("unable to load CRI service base dependencies: %w", err)
return nil, fmt.Errorf("unable to load CRI runtime service plugin dependency: %w", err)
}
criBase := criBasePlugin.(*base.CRIBase)
runtimeService := criRuntimePlugin.(RuntimeService)
// Get image service.
criImagePlugin, err := ic.GetSingle(plugins.CRIImagePlugin)
criImagePlugin, err := ic.GetByID(plugins.CRIServicePlugin, "images")
if err != nil {
return nil, fmt.Errorf("unable to load CRI image service plugin dependency: %w", err)
}
c := Controller{
client: client,
config: criBase.Config,
os: osinterface.RealOS{},
baseOCISpecs: criBase.BaseOCISpecs,
imageService: criImagePlugin.(ImageService),
store: NewStore(),
client: client,
config: runtimeService.Config(),
os: osinterface.RealOS{},
runtimeService: runtimeService,
imageService: criImagePlugin.(ImageService),
store: NewStore(),
}
return &c, nil
},
@@ -99,6 +97,12 @@ type CRIService interface {
BackOffEvent(id string, event interface{})
}
// RuntimeService specifies dependencies to CRI runtime service.
type RuntimeService interface {
Config() criconfig.Config
LoadOCISpec(string) (*oci.Spec, error)
}
// ImageService specifies dependencies to CRI image service.
type ImageService interface {
LocalResolve(refOrID string) (imagestore.Image, error)
@@ -113,14 +117,14 @@ type Controller struct {
config criconfig.Config
// client is an instance of the containerd client
client *containerd.Client
// runtimeService is a dependency to CRI runtime service.
runtimeService RuntimeService
// imageService is a dependency to CRI image service.
imageService ImageService
// os is an interface for all required os operations.
os osinterface.OS
// cri is CRI service that provides missing gaps needed by controller.
cri CRIService
// baseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
baseOCISpecs map[string]*oci.Spec
store *Store
}

View File

@@ -159,9 +159,9 @@ func (c *Controller) runtimeSpec(id string, baseSpecFile string, opts ...oci.Spe
container := &containers.Container{ID: id}
if baseSpecFile != "" {
baseSpec, ok := c.baseOCISpecs[baseSpecFile]
if !ok {
return nil, fmt.Errorf("can't find base OCI spec %q", baseSpecFile)
baseSpec, err := c.runtimeService.LoadOCISpec(baseSpecFile)
if err != nil {
return nil, fmt.Errorf("can't load base OCI spec %q: %w", baseSpecFile, err)
}
spec := oci.Spec{}

View File

@@ -65,6 +65,15 @@ type sandboxService interface {
SandboxController(config *runtime.PodSandboxConfig, runtimeHandler string) (sandbox.Controller, error)
}
// RuntimeService specifies dependencies to runtime service which provides
// the runtime configuration and OCI spec loading.
type RuntimeService interface {
Config() criconfig.Config
// LoadCISpec loads cached OCI specs via `Runtime.BaseRuntimeSpec`
LoadOCISpec(string) (*oci.Spec, error)
}
// ImageService specifies dependencies to image service.
type ImageService interface {
RuntimeSnapshotter(ctx context.Context, ociRuntime criconfig.Runtime) string
@@ -84,6 +93,7 @@ type ImageService interface {
// criService implements CRIService.
type criService struct {
RuntimeService
ImageService
// config contains all configurations.
config criconfig.Config
@@ -115,8 +125,6 @@ type criService struct {
// cniNetConfMonitor is used to reload cni network conf if there is
// any valid fs change events from cni network conf dir.
cniNetConfMonitor map[string]*cniNetConfSyncer
// baseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
baseOCISpecs map[string]*oci.Spec
// allCaps is the list of the capabilities.
// When nil, parsed from CapEff of /proc/self/status.
allCaps []string //nolint:nolintlint,unused // Ignore on non-Linux
@@ -130,6 +138,8 @@ type criService struct {
}
type CRIServiceOptions struct {
RuntimeService RuntimeService
ImageService ImageService
NRI *nri.API
@@ -137,9 +147,6 @@ type CRIServiceOptions struct {
// SandboxControllers is a map of all the loaded sandbox controllers
SandboxControllers map[string]sandbox.Controller
// BaseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
BaseOCISpecs map[string]*oci.Spec
// Client is the base containerd client used for accessing services,
//
// TODO: Replace this gradually with directly configured instances
@@ -147,18 +154,18 @@ type CRIServiceOptions struct {
}
// NewCRIService returns a new instance of CRIService
// TODO: Add criBase.BaseOCISpecs to options
func NewCRIService(config criconfig.Config, options *CRIServiceOptions) (CRIService, runtime.RuntimeServiceServer, error) {
func NewCRIService(options *CRIServiceOptions) (CRIService, runtime.RuntimeServiceServer, error) {
var err error
labels := label.NewStore()
config := options.RuntimeService.Config()
c := &criService{
RuntimeService: options.RuntimeService,
ImageService: options.ImageService,
config: config,
client: options.Client,
imageFSPaths: options.ImageService.ImageFSPaths(),
os: osinterface.RealOS{},
baseOCISpecs: options.BaseOCISpecs,
sandboxStore: sandboxstore.NewStore(labels),
containerStore: containerstore.NewStore(labels),
sandboxNameIndex: registrar.NewRegistrar(),

View File

@@ -25,10 +25,12 @@ import (
"github.com/containerd/containerd/v2/api/types"
"github.com/containerd/containerd/v2/core/sandbox"
"github.com/containerd/containerd/v2/internal/registrar"
criconfig "github.com/containerd/containerd/v2/pkg/cri/config"
containerstore "github.com/containerd/containerd/v2/pkg/cri/store/container"
"github.com/containerd/containerd/v2/pkg/cri/store/label"
sandboxstore "github.com/containerd/containerd/v2/pkg/cri/store/sandbox"
servertesting "github.com/containerd/containerd/v2/pkg/cri/testing"
"github.com/containerd/containerd/v2/pkg/oci"
ostesting "github.com/containerd/containerd/v2/pkg/os/testing"
"github.com/containerd/errdefs"
"github.com/containerd/platforms"
@@ -74,11 +76,34 @@ func (f fakeSandboxController) Metrics(ctx context.Context, sandboxID string) (*
return &types.Metric{}, errdefs.ErrNotImplemented
}
type fakeRuntimeService struct {
ocispecs map[string]*oci.Spec
}
func (f fakeRuntimeService) Config() criconfig.Config {
return testConfig
}
func (f fakeRuntimeService) LoadOCISpec(filename string) (*oci.Spec, error) {
spec, ok := f.ocispecs[filename]
if !ok {
return nil, errdefs.ErrNotFound
}
return spec, nil
}
type testOpt func(*criService)
func withRuntimeService(rs RuntimeService) testOpt {
return func(service *criService) {
service.RuntimeService = rs
}
}
// newTestCRIService creates a fake criService for test.
func newTestCRIService() *criService {
func newTestCRIService(opts ...testOpt) *criService {
labels := label.NewStore()
return &criService{
ImageService: &fakeImageService{},
service := &criService{
config: testConfig,
os: ostesting.NewFakeOS(),
sandboxStore: sandboxstore.NewStore(labels),
@@ -90,4 +115,15 @@ func newTestCRIService() *criService {
},
sandboxService: &fakeSandboxService{},
}
for _, opt := range opts {
opt(service)
}
if service.RuntimeService == nil {
service.RuntimeService = &fakeRuntimeService{}
}
if service.ImageService == nil {
service.ImageService = &fakeImageService{}
}
return service
}