mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-02-22 07:03:28 +00:00
Merge pull request #134906 from carlory/fix-kubeadm-3229
kubeadm: added container runtime version check to preflight
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user