Merge pull request #134906 from carlory/fix-kubeadm-3229

kubeadm: added container runtime version check to preflight
This commit is contained in:
Kubernetes Prow Robot
2025-11-02 23:36:04 -08:00
committed by GitHub
8 changed files with 328 additions and 78 deletions

View File

@@ -70,7 +70,7 @@ func runPreflight(c workflow.RunData) error {
if err := preflight.RunRootCheckOnly(ignorePreflightErrors); err != nil {
return err
}
if err := preflight.RunUpgradeChecks(utilsexec.New(), ignorePreflightErrors); err != nil {
if err := preflight.RunUpgradeChecks(utilsexec.New(), initCfg, ignorePreflightErrors); err != nil {
return err
}

View File

@@ -53,7 +53,7 @@ func runPreflight(c workflow.RunData) error {
if err := preflight.RunRootCheckOnly(data.IgnorePreflightErrors()); err != nil {
return err
}
if err := preflight.RunUpgradeChecks(utilsexec.New(), data.IgnorePreflightErrors()); err != nil {
if err := preflight.RunUpgradeChecks(utilsexec.New(), data.InitCfg(), data.IgnorePreflightErrors()); err != nil {
return err
}

View File

@@ -73,7 +73,10 @@ type Checker interface {
// ContainerRuntimeCheck verifies the container runtime.
type ContainerRuntimeCheck struct {
runtime utilruntime.ContainerRuntime
criSocket string
// stubbed out for testing
impl utilruntime.Impl
}
// Name returns label for RuntimeCheck.
@@ -84,13 +87,62 @@ func (ContainerRuntimeCheck) Name() string {
// Check validates the container runtime
func (crc ContainerRuntimeCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating the container runtime")
defer crc.runtime.Close()
if err := crc.runtime.IsRunning(); err != nil {
containerRuntime := utilruntime.NewContainerRuntime(crc.criSocket)
if crc.impl != nil {
containerRuntime.SetImpl(crc.impl)
}
if err := containerRuntime.Connect(); err != nil {
return nil, []error{errors.Wrap(err, "could not connect to the container runtime")}
}
defer containerRuntime.Close()
if err := containerRuntime.IsRunning(); err != nil {
errorList = append(errorList, err)
}
return warnings, errorList
}
// ContainerRuntimeVersionCheck verifies the version compatibility between installed container runtime and kubelet.
type ContainerRuntimeVersionCheck struct {
criSocket string
// stubbed out for testing
impl utilruntime.Impl
}
// Name returns label for RuntimeCheck.
func (ContainerRuntimeVersionCheck) Name() string {
return "ContainerRuntimeVersion"
}
// Check validates the container runtime version compatibility with kubelet.
func (crvc ContainerRuntimeVersionCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating the container runtime version compatibility")
containerRuntime := utilruntime.NewContainerRuntime(crvc.criSocket)
if crvc.impl != nil {
containerRuntime.SetImpl(crvc.impl)
}
if err := containerRuntime.Connect(); err != nil {
return nil, []error{errors.Wrap(err, "could not connect to the container runtime")}
}
defer containerRuntime.Close()
ok, err := containerRuntime.IsRuntimeConfigImplemented()
if err != nil {
return nil, []error{errors.Wrap(err, "could not check if the runtime config is available")}
}
if !ok {
// TODO: return an error once the kubelet version is 1.36 or higher.
// https://github.com/kubernetes/kubeadm/issues/3229
err := errors.New("You must update your container runtime to a version that supports the CRI method RuntimeConfig. " +
"Falling back to using cgroupDriver from kubelet config will be removed in 1.36. " +
"For more information, see https://git.k8s.io/enhancements/keps/sig-node/4033-group-driver-detection-over-cri")
warnings = append(warnings, err)
}
return warnings, errorList
}
// ServiceCheck verifies that the given service is enabled and active. If we do not
// detect a supported init system however, all checks are skipped and a warning is
// returned.
@@ -807,11 +859,14 @@ func getEtcdVersionResponse(client *http.Client, url string, target interface{})
// ImagePullCheck will pull container images used by kubeadm
type ImagePullCheck struct {
runtime utilruntime.ContainerRuntime
criSocket string
imageList []string
sandboxImage string
imagePullPolicy v1.PullPolicy
imagePullSerial bool
// stubbed out for testing
impl utilruntime.Impl
}
// Name returns the label for ImagePullCheck
@@ -821,6 +876,15 @@ func (ImagePullCheck) Name() string {
// Check pulls images required by kubeadm. This is a mutating check
func (ipc ImagePullCheck) Check() (warnings, errorList []error) {
containerRuntime := utilruntime.NewContainerRuntime(ipc.criSocket)
if ipc.impl != nil {
containerRuntime.SetImpl(ipc.impl)
}
if err := containerRuntime.Connect(); err != nil {
return nil, []error{errors.Wrap(err, "could not connect to the container runtime")}
}
defer containerRuntime.Close()
// Handle unsupported image pull policy and policy Never.
policy := ipc.imagePullPolicy
switch policy {
@@ -835,7 +899,7 @@ func (ipc ImagePullCheck) Check() (warnings, errorList []error) {
}
// Handle CRI sandbox image warnings.
criSandboxImage, err := ipc.runtime.SandboxImage()
criSandboxImage, err := containerRuntime.SandboxImage()
if err != nil {
klog.V(4).Infof("failed to detect the sandbox image for local container runtime, %v", err)
} else if criSandboxImage != ipc.sandboxImage {
@@ -845,7 +909,7 @@ func (ipc ImagePullCheck) Check() (warnings, errorList []error) {
// Perform parallel pulls.
if !ipc.imagePullSerial {
if err := ipc.runtime.PullImagesInParallel(ipc.imageList, policy == v1.PullIfNotPresent); err != nil {
if err := containerRuntime.PullImagesInParallel(ipc.imageList, policy == v1.PullIfNotPresent); err != nil {
errorList = append(errorList, err)
}
return warnings, errorList
@@ -855,14 +919,14 @@ func (ipc ImagePullCheck) Check() (warnings, errorList []error) {
for _, image := range ipc.imageList {
switch policy {
case v1.PullIfNotPresent:
if ipc.runtime.ImageExists(image) {
if containerRuntime.ImageExists(image) {
klog.V(1).Infof("image exists: %s", image)
continue
}
fallthrough // Proceed with pulling the image if it does not exist
case v1.PullAlways:
klog.V(1).Infof("pulling: %s", image)
if err := ipc.runtime.PullImage(image); err != nil {
if err := containerRuntime.PullImage(image); err != nil {
errorList = append(errorList, errors.WithMessagef(err, "failed to pull image %s", image))
}
}
@@ -1056,12 +1120,8 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.JoinConfigura
// addCommonChecks is a helper function to duplicate checks that are common between both the
// kubeadm init and join commands
func addCommonChecks(execer utilsexec.Interface, k8sVersion string, nodeReg *kubeadmapi.NodeRegistrationOptions, checks []Checker) []Checker {
containerRuntime := utilruntime.NewContainerRuntime(nodeReg.CRISocket)
if err := containerRuntime.Connect(); err != nil {
klog.Warningf("[preflight] WARNING: Couldn't create the interface used for talking to the container runtime: %v\n", err)
} else {
checks = append(checks, ContainerRuntimeCheck{runtime: containerRuntime})
}
checks = append(checks, ContainerRuntimeCheck{criSocket: nodeReg.CRISocket})
checks = append(checks, ContainerRuntimeVersionCheck{criSocket: nodeReg.CRISocket})
// non-windows checks
checks = addSwapCheck(checks)
@@ -1085,9 +1145,10 @@ func RunRootCheckOnly(ignorePreflightErrors sets.Set[string]) error {
}
// RunUpgradeChecks initializes checks slice of structs and call RunChecks
func RunUpgradeChecks(execer utilsexec.Interface, ignorePreflightErrors sets.Set[string]) error {
func RunUpgradeChecks(execer utilsexec.Interface, cfg *kubeadmapi.InitConfiguration, ignorePreflightErrors sets.Set[string]) error {
checks := []Checker{
SystemVerificationCheck{isUpgrade: true, exec: execer},
ContainerRuntimeVersionCheck{criSocket: cfg.NodeRegistration.CRISocket},
}
return RunChecks(checks, os.Stderr, ignorePreflightErrors)
@@ -1095,12 +1156,6 @@ func RunUpgradeChecks(execer utilsexec.Interface, ignorePreflightErrors sets.Set
// RunPullImagesCheck will pull images kubeadm needs if they are not found on the system
func RunPullImagesCheck(execer utilsexec.Interface, cfg *kubeadmapi.InitConfiguration, ignorePreflightErrors sets.Set[string]) error {
containerRuntime := utilruntime.NewContainerRuntime(cfg.NodeRegistration.CRISocket)
if err := containerRuntime.Connect(); err != nil {
return handleError(os.Stderr, err.Error())
}
defer containerRuntime.Close()
serialPull := true
if cfg.NodeRegistration.ImagePullSerial != nil {
serialPull = *cfg.NodeRegistration.ImagePullSerial
@@ -1108,7 +1163,7 @@ func RunPullImagesCheck(execer utilsexec.Interface, cfg *kubeadmapi.InitConfigur
checks := []Checker{
ImagePullCheck{
runtime: containerRuntime,
criSocket: cfg.NodeRegistration.CRISocket,
imageList: images.GetControlPlaneImages(&cfg.ClusterConfiguration),
sandboxImage: images.GetPauseImage(&cfg.ClusterConfiguration),
imagePullPolicy: cfg.NodeRegistration.ImagePullPolicy,

View File

@@ -28,6 +28,8 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/lithammer/dedent"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/version"
@@ -39,6 +41,7 @@ import (
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
"k8s.io/kubernetes/cmd/kubeadm/app/util/errors"
utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime"
)
var (
@@ -1021,3 +1024,84 @@ func TestJoinIPCheck(t *testing.T) {
})
}
}
func TestContainerRuntimeCheck(t *testing.T) {
tests := []struct {
name string
prepare func(*utilruntime.FakeImpl)
expectErrors int
expectWarnings int
}{
{
name: "ok",
},
{
name: "container runtime is not running",
prepare: func(mock *utilruntime.FakeImpl) {
mock.StatusReturns(nil, errors.New("not running"))
},
expectErrors: 1,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mock := &utilruntime.FakeImpl{}
if test.prepare != nil {
test.prepare(mock)
}
warnings, errors := ContainerRuntimeCheck{impl: mock}.Check()
if len(warnings) != test.expectWarnings {
t.Errorf("expected %d warning(s) but got %d: %q", test.expectWarnings, len(warnings), warnings)
}
if len(errors) != test.expectErrors {
t.Errorf("expected %d error(s) but got %d: %q", test.expectErrors, len(errors), errors)
}
})
}
}
func TestContainerRuntimeVersionCheck(t *testing.T) {
tests := []struct {
name string
prepare func(*utilruntime.FakeImpl)
expectErrors int
expectWarnings int
}{
{
name: "ok",
},
{
name: "runtime config not implemented",
prepare: func(mock *utilruntime.FakeImpl) {
mock.RuntimeConfigReturns(nil, status.New(codes.Unimplemented, "not implemented").Err())
},
expectWarnings: 1,
},
{
name: "call RuntimeConfig fails",
prepare: func(mock *utilruntime.FakeImpl) {
mock.RuntimeConfigReturns(nil, status.New(codes.DeadlineExceeded, "deadline exceeded").Err())
},
expectErrors: 1,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mock := &utilruntime.FakeImpl{}
if test.prepare != nil {
test.prepare(mock)
}
warnings, errors := ContainerRuntimeVersionCheck{impl: mock}.Check()
if len(warnings) != test.expectWarnings {
t.Errorf("expected %d warning(s) but got %d: %q", test.expectWarnings, len(warnings), warnings)
}
if len(errors) != test.expectErrors {
t.Errorf("expected %d error(s) but got %d: %q", test.expectErrors, len(errors), errors)
}
})
}
}

View File

@@ -24,7 +24,12 @@ import (
v1 "k8s.io/cri-api/pkg/apis/runtime/v1"
)
type fakeImpl struct {
// FakeImpl is a fake implementation of the impl interface.
type FakeImpl struct {
runtimeConfigReturns struct {
res *v1.RuntimeConfigResponse
err error
}
imageStatusReturns struct {
res *v1.ImageStatusResponse
err error
@@ -57,95 +62,125 @@ type fakeImpl struct {
}
}
func (fake *fakeImpl) ImageStatus(context.Context, cri.ImageManagerService, *v1.ImageSpec, bool) (*v1.ImageStatusResponse, error) {
// ImageStatus returns the status of the image.
func (fake *FakeImpl) ImageStatus(context.Context, cri.ImageManagerService, *v1.ImageSpec, bool) (*v1.ImageStatusResponse, error) {
fakeReturns := fake.imageStatusReturns
return fakeReturns.res, fakeReturns.err
}
func (fake *fakeImpl) ImageStatusReturns(res *v1.ImageStatusResponse, err error) {
// ImageStatusReturns sets the return values for the ImageStatus method.
func (fake *FakeImpl) ImageStatusReturns(res *v1.ImageStatusResponse, err error) {
fake.imageStatusReturns = struct {
res *v1.ImageStatusResponse
err error
}{res, err}
}
func (fake *fakeImpl) ListPodSandbox(context.Context, cri.RuntimeService, *v1.PodSandboxFilter) ([]*v1.PodSandbox, error) {
// ListPodSandbox returns the list of pod sandboxes.
func (fake *FakeImpl) ListPodSandbox(context.Context, cri.RuntimeService, *v1.PodSandboxFilter) ([]*v1.PodSandbox, error) {
fakeReturns := fake.listPodSandboxReturns
return fakeReturns.res, fakeReturns.err
}
func (fake *fakeImpl) ListPodSandboxReturns(res []*v1.PodSandbox, err error) {
// ListPodSandboxReturns sets the return values for the ListPodSandbox method.
func (fake *FakeImpl) ListPodSandboxReturns(res []*v1.PodSandbox, err error) {
fake.listPodSandboxReturns = struct {
res []*v1.PodSandbox
err error
}{res, err}
}
func (fake *fakeImpl) NewRemoteImageService(string, time.Duration) (cri.ImageManagerService, error) {
// NewRemoteImageService returns the new remote image service.
func (fake *FakeImpl) NewRemoteImageService(string, time.Duration) (cri.ImageManagerService, error) {
fakeReturns := fake.newRemoteImageServiceReturns
return fakeReturns.res, fakeReturns.err
}
func (fake *fakeImpl) NewRemoteImageServiceReturns(res cri.ImageManagerService, err error) {
// NewRemoteImageServiceReturns sets the return values for the NewRemoteImageService method.
func (fake *FakeImpl) NewRemoteImageServiceReturns(res cri.ImageManagerService, err error) {
fake.newRemoteImageServiceReturns = struct {
res cri.ImageManagerService
err error
}{res, err}
}
func (fake *fakeImpl) NewRemoteRuntimeService(string, time.Duration) (cri.RuntimeService, error) {
// NewRemoteRuntimeService returns the new remote runtime service.
func (fake *FakeImpl) NewRemoteRuntimeService(string, time.Duration) (cri.RuntimeService, error) {
fakeReturns := fake.newRemoteRuntimeServiceReturns
return fakeReturns.res, fakeReturns.err
}
func (fake *fakeImpl) NewRemoteRuntimeServiceReturns(res cri.RuntimeService, err error) {
// NewRemoteRuntimeServiceReturns sets the return values for the NewRemoteRuntimeService method.
func (fake *FakeImpl) NewRemoteRuntimeServiceReturns(res cri.RuntimeService, err error) {
fake.newRemoteRuntimeServiceReturns = struct {
res cri.RuntimeService
err error
}{res, err}
}
func (fake *fakeImpl) PullImage(context.Context, cri.ImageManagerService, *v1.ImageSpec, *v1.AuthConfig, *v1.PodSandboxConfig) (string, error) {
// PullImage returns the pull image.
func (fake *FakeImpl) PullImage(context.Context, cri.ImageManagerService, *v1.ImageSpec, *v1.AuthConfig, *v1.PodSandboxConfig) (string, error) {
fakeReturns := fake.pullImageReturns
return fakeReturns.res, fakeReturns.err
}
func (fake *fakeImpl) PullImageReturns(res string, err error) {
// PullImageReturns sets the return values for the PullImage method.
func (fake *FakeImpl) PullImageReturns(res string, err error) {
fake.pullImageReturns = struct {
res string
err error
}{res, err}
}
func (fake *fakeImpl) RemovePodSandbox(context.Context, cri.RuntimeService, string) error {
// RemovePodSandbox removes the pod sandbox.
func (fake *FakeImpl) RemovePodSandbox(context.Context, cri.RuntimeService, string) error {
fakeReturns := fake.removePodSandboxReturns
return fakeReturns.res
}
func (fake *fakeImpl) RemovePodSandboxReturns(res error) {
// RemovePodSandboxReturns sets the return values for the RemovePodSandbox method.
func (fake *FakeImpl) RemovePodSandboxReturns(res error) {
fake.removePodSandboxReturns = struct {
res error
}{res}
}
func (fake *fakeImpl) Status(context.Context, cri.RuntimeService, bool) (*v1.StatusResponse, error) {
// RuntimeConfig returns the runtime config.
func (fake *FakeImpl) RuntimeConfig(context.Context, cri.RuntimeService) (*v1.RuntimeConfigResponse, error) {
fakeReturns := fake.runtimeConfigReturns
return fakeReturns.res, fakeReturns.err
}
// RuntimeConfigReturns sets the return values for the RuntimeConfig method.
func (fake *FakeImpl) RuntimeConfigReturns(res *v1.RuntimeConfigResponse, err error) {
fake.runtimeConfigReturns = struct {
res *v1.RuntimeConfigResponse
err error
}{res, err}
}
// Status returns the status of the runtime.
func (fake *FakeImpl) Status(context.Context, cri.RuntimeService, bool) (*v1.StatusResponse, error) {
fakeReturns := fake.statusReturns
return fakeReturns.res, fakeReturns.err
}
func (fake *fakeImpl) StatusReturns(res *v1.StatusResponse, err error) {
// StatusReturns sets the return values for the Status method.
func (fake *FakeImpl) StatusReturns(res *v1.StatusResponse, err error) {
fake.statusReturns = struct {
res *v1.StatusResponse
err error
}{res, err}
}
func (fake *fakeImpl) StopPodSandbox(context.Context, cri.RuntimeService, string) error {
// StopPodSandbox stops the pod sandbox.
func (fake *FakeImpl) StopPodSandbox(context.Context, cri.RuntimeService, string) error {
fakeReturns := fake.stopPodSandboxReturns
return fakeReturns.res
}
func (fake *fakeImpl) StopPodSandboxReturns(res error) {
// StopPodSandboxReturns sets the return values for the StopPodSandbox method.
func (fake *FakeImpl) StopPodSandboxReturns(res error) {
fake.stopPodSandboxReturns = struct {
res error
}{res}

View File

@@ -27,9 +27,11 @@ import (
type defaultImpl struct{}
type impl interface {
// Impl is an interface for the container runtime implementation.
type Impl interface {
NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration) (criapi.RuntimeService, error)
NewRemoteImageService(endpoint string, connectionTimeout time.Duration) (criapi.ImageManagerService, error)
RuntimeConfig(ctx context.Context, runtimeService criapi.RuntimeService) (*runtimeapi.RuntimeConfigResponse, error)
Status(ctx context.Context, runtimeService criapi.RuntimeService, verbose bool) (*runtimeapi.StatusResponse, error)
ListPodSandbox(ctx context.Context, runtimeService criapi.RuntimeService, filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error)
StopPodSandbox(ctx context.Context, runtimeService criapi.RuntimeService, sandboxID string) error
@@ -46,6 +48,10 @@ func (*defaultImpl) NewRemoteImageService(endpoint string, connectionTimeout tim
return criclient.NewRemoteImageService(endpoint, connectionTimeout, nil, nil)
}
func (*defaultImpl) RuntimeConfig(ctx context.Context, runtimeService criapi.RuntimeService) (*runtimeapi.RuntimeConfigResponse, error) {
return runtimeService.RuntimeConfig(ctx)
}
func (*defaultImpl) Status(ctx context.Context, runtimeService criapi.RuntimeService, verbose bool) (*runtimeapi.StatusResponse, error) {
return runtimeService.Status(ctx, verbose)
}

View File

@@ -23,6 +23,9 @@ import (
"strings"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
errorsutil "k8s.io/apimachinery/pkg/util/errors"
criapi "k8s.io/cri-api/pkg/apis"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
@@ -43,7 +46,7 @@ var defaultKnownCRISockets = []string{
type ContainerRuntime interface {
Connect() error
Close()
SetImpl(impl)
SetImpl(Impl)
IsRunning() error
ListKubeContainers() ([]string, error)
RemoveContainers(containers []string) error
@@ -51,11 +54,12 @@ type ContainerRuntime interface {
PullImagesInParallel(images []string, ifNotPresent bool) error
ImageExists(image string) bool
SandboxImage() (string, error)
IsRuntimeConfigImplemented() (bool, error)
}
// CRIRuntime is a struct that interfaces with the CRI
type CRIRuntime struct {
impl impl
impl Impl
criSocket string
runtimeService criapi.RuntimeService
imageService criapi.ImageManagerService
@@ -73,7 +77,7 @@ func NewContainerRuntime(criSocket string) ContainerRuntime {
}
// SetImpl can be used to set the internal implementation for testing purposes.
func (runtime *CRIRuntime) SetImpl(impl impl) {
func (runtime *CRIRuntime) SetImpl(impl Impl) {
runtime.impl = impl
}
@@ -314,3 +318,18 @@ func (runtime *CRIRuntime) SandboxImage() (string, error) {
return c.SandboxImage, nil
}
// IsRuntimeConfigImplemented checks if the container runtime supports the RuntimeConfig gRPC method
func (runtime *CRIRuntime) IsRuntimeConfigImplemented() (bool, error) {
ctx, cancel := defaultContext()
defer cancel()
_, err := runtime.impl.RuntimeConfig(ctx, runtime.runtimeService)
if err != nil {
s, ok := status.FromError(err)
if !ok || s.Code() != codes.Unimplemented {
return false, errors.Wrap(err, "failed to call RuntimeConfig gRPC method")
}
return false, nil
}
return true, nil
}

View File

@@ -24,6 +24,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
v1 "k8s.io/cri-api/pkg/apis/runtime/v1"
@@ -32,11 +34,12 @@ import (
)
var errTest = errors.New("test")
var errNotImplemented = status.New(codes.Unimplemented, "not implemented").Err()
func TestNewContainerRuntime(t *testing.T) {
for _, tc := range []struct {
name string
prepare func(*fakeImpl)
prepare func(*FakeImpl)
shouldError bool
}{
{
@@ -45,14 +48,14 @@ func TestNewContainerRuntime(t *testing.T) {
},
{
name: "invalid: new runtime service fails",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.NewRemoteRuntimeServiceReturns(nil, errTest)
},
shouldError: true,
},
{
name: "invalid: new image service fails",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.NewRemoteImageServiceReturns(nil, errTest)
},
shouldError: true,
@@ -60,7 +63,7 @@ func TestNewContainerRuntime(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &fakeImpl{}
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
@@ -76,7 +79,7 @@ func TestNewContainerRuntime(t *testing.T) {
func TestIsRunning(t *testing.T) {
for _, tc := range []struct {
name string
prepare func(*fakeImpl)
prepare func(*FakeImpl)
shouldError bool
}{
{
@@ -85,14 +88,14 @@ func TestIsRunning(t *testing.T) {
},
{
name: "invalid: runtime status fails",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.StatusReturns(nil, errTest)
},
shouldError: true,
},
{
name: "invalid: runtime condition status not 'true'",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.StatusReturns(&v1.StatusResponse{Status: &v1.RuntimeStatus{
Conditions: []*v1.RuntimeCondition{
{
@@ -107,7 +110,7 @@ func TestIsRunning(t *testing.T) {
},
{
name: "valid: runtime condition type does not match",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.StatusReturns(&v1.StatusResponse{Status: &v1.RuntimeStatus{
Conditions: []*v1.RuntimeCondition{
{
@@ -123,7 +126,7 @@ func TestIsRunning(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &fakeImpl{}
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
@@ -140,12 +143,12 @@ func TestListKubeContainers(t *testing.T) {
for _, tc := range []struct {
name string
expected []string
prepare func(*fakeImpl)
prepare func(*FakeImpl)
shouldError bool
}{
{
name: "valid",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.ListPodSandboxReturns([]*v1.PodSandbox{
{Id: "first"},
{Id: "second"},
@@ -156,7 +159,7 @@ func TestListKubeContainers(t *testing.T) {
},
{
name: "invalid: list pod sandbox fails",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.ListPodSandboxReturns(nil, errTest)
},
shouldError: true,
@@ -164,7 +167,7 @@ func TestListKubeContainers(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &fakeImpl{}
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
@@ -181,12 +184,12 @@ func TestListKubeContainers(t *testing.T) {
func TestSandboxImage(t *testing.T) {
for _, tc := range []struct {
name, expected string
prepare func(*fakeImpl)
prepare func(*FakeImpl)
shouldError bool
}{
{
name: "valid",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.StatusReturns(&v1.StatusResponse{Info: map[string]string{
"config": `{"sandboxImage": "pause"}`,
}}, nil)
@@ -196,14 +199,14 @@ func TestSandboxImage(t *testing.T) {
},
{
name: "invalid: runtime status fails",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.StatusReturns(nil, errTest)
},
shouldError: true,
},
{
name: "invalid: no config JSON",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.StatusReturns(&v1.StatusResponse{Info: map[string]string{
"config": "wrong",
}}, nil)
@@ -212,7 +215,7 @@ func TestSandboxImage(t *testing.T) {
},
{
name: "invalid: no config",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.StatusReturns(&v1.StatusResponse{Info: map[string]string{}}, nil)
},
shouldError: true,
@@ -220,7 +223,7 @@ func TestSandboxImage(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &fakeImpl{}
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
@@ -238,7 +241,7 @@ func TestRemoveContainers(t *testing.T) {
for _, tc := range []struct {
name string
containers []string
prepare func(*fakeImpl)
prepare func(*FakeImpl)
shouldError bool
}{
{
@@ -252,7 +255,7 @@ func TestRemoveContainers(t *testing.T) {
{
name: "invalid: remove pod sandbox fails",
containers: []string{"1"},
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.RemovePodSandboxReturns(errTest)
},
shouldError: true,
@@ -260,7 +263,7 @@ func TestRemoveContainers(t *testing.T) {
{
name: "invalid: stop pod sandbox fails",
containers: []string{"1"},
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.StopPodSandboxReturns(errTest)
},
shouldError: true,
@@ -268,7 +271,7 @@ func TestRemoveContainers(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &fakeImpl{}
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
@@ -284,7 +287,7 @@ func TestRemoveContainers(t *testing.T) {
func TestPullImage(t *testing.T) {
for _, tc := range []struct {
name string
prepare func(*fakeImpl)
prepare func(*FakeImpl)
shouldError bool
}{
{
@@ -292,7 +295,7 @@ func TestPullImage(t *testing.T) {
},
{
name: "invalid: pull image fails",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.PullImageReturns("", errTest)
},
shouldError: true,
@@ -300,7 +303,7 @@ func TestPullImage(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &fakeImpl{}
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
@@ -317,11 +320,11 @@ func TestImageExists(t *testing.T) {
for _, tc := range []struct {
name string
expected bool
prepare func(*fakeImpl)
prepare func(*FakeImpl)
}{
{
name: "valid",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.ImageStatusReturns(&v1.ImageStatusResponse{
Image: &v1.Image{},
}, nil)
@@ -330,7 +333,7 @@ func TestImageExists(t *testing.T) {
},
{
name: "invalid: image status fails",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.ImageStatusReturns(nil, errTest)
},
expected: false,
@@ -338,7 +341,7 @@ func TestImageExists(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &fakeImpl{}
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
@@ -466,7 +469,7 @@ func TestPullImagesInParallel(t *testing.T) {
for _, tc := range []struct {
name string
ifNotPresent bool
prepare func(*fakeImpl)
prepare func(*FakeImpl)
shouldError bool
}{
{
@@ -478,7 +481,7 @@ func TestPullImagesInParallel(t *testing.T) {
},
{
name: "invalid: pull fails",
prepare: func(mock *fakeImpl) {
prepare: func(mock *FakeImpl) {
mock.PullImageReturns("", errTest)
},
shouldError: true,
@@ -486,7 +489,7 @@ func TestPullImagesInParallel(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &fakeImpl{}
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
@@ -498,3 +501,51 @@ func TestPullImagesInParallel(t *testing.T) {
})
}
}
func TestIsRuntimeConfigEnabled(t *testing.T) {
for _, tc := range []struct {
name string
prepare func(*FakeImpl)
shouldError bool
expected bool
}{
{
name: "valid: RuntimeConfig gRPC method is implemented",
prepare: func(mock *FakeImpl) {
mock.RuntimeConfigReturns(&v1.RuntimeConfigResponse{}, nil)
},
shouldError: false,
expected: true,
},
{
name: "invalid: RuntimeConfig gRPC method is not implemented",
prepare: func(mock *FakeImpl) {
mock.RuntimeConfigReturns(nil, errNotImplemented)
},
shouldError: false,
expected: false,
},
{
name: "invalid: RuntimeConfig gRPC method returns an error",
prepare: func(mock *FakeImpl) {
mock.RuntimeConfigReturns(nil, errTest)
},
shouldError: true,
expected: false,
},
} {
t.Run(tc.name, func(t *testing.T) {
containerRuntime := NewContainerRuntime("")
mock := &FakeImpl{}
if tc.prepare != nil {
tc.prepare(mock)
}
containerRuntime.SetImpl(mock)
enabled, err := containerRuntime.IsRuntimeConfigImplemented()
assert.Equal(t, tc.shouldError, err != nil)
assert.Equal(t, tc.expected, enabled)
})
}
}