Merge pull request #115610 from SataQiu/detect-sandbox-20230208

kubeadm: show a warning message when detecting that the sandbox image of the container runtime is inconsistent with that used by kubeadm
This commit is contained in:
Kubernetes Prow Robot 2023-02-14 19:26:23 -08:00 committed by GitHub
commit f545ff3ba8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 1 deletions

View File

@ -813,6 +813,7 @@ func getEtcdVersionResponse(client *http.Client, url string, target interface{})
type ImagePullCheck struct { type ImagePullCheck struct {
runtime utilruntime.ContainerRuntime runtime utilruntime.ContainerRuntime
imageList []string imageList []string
sandboxImage string
imagePullPolicy v1.PullPolicy imagePullPolicy v1.PullPolicy
} }
@ -826,6 +827,15 @@ func (ipc ImagePullCheck) Check() (warnings, errorList []error) {
policy := ipc.imagePullPolicy policy := ipc.imagePullPolicy
klog.V(1).Infof("using image pull policy: %s", policy) klog.V(1).Infof("using image pull policy: %s", policy)
for _, image := range ipc.imageList { for _, image := range ipc.imageList {
if image == ipc.sandboxImage {
criSandboxImage, err := ipc.runtime.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 {
klog.Warningf("detected that the sandbox image %q of the container runtime is inconsistent with that used by kubeadm. It is recommended that using %q as the CRI sandbox image.",
criSandboxImage, ipc.sandboxImage)
}
}
switch policy { switch policy {
case v1.PullNever: case v1.PullNever:
klog.V(1).Infof("skipping pull of image: %s", image) klog.V(1).Infof("skipping pull of image: %s", image)
@ -1036,7 +1046,12 @@ func RunPullImagesCheck(execer utilsexec.Interface, cfg *kubeadmapi.InitConfigur
} }
checks := []Checker{ checks := []Checker{
ImagePullCheck{runtime: containerRuntime, imageList: images.GetControlPlaneImages(&cfg.ClusterConfiguration), imagePullPolicy: cfg.NodeRegistration.ImagePullPolicy}, ImagePullCheck{
runtime: containerRuntime,
imageList: images.GetControlPlaneImages(&cfg.ClusterConfiguration),
sandboxImage: images.GetPauseImage(&cfg.ClusterConfiguration),
imagePullPolicy: cfg.NodeRegistration.ImagePullPolicy,
},
} }
return RunChecks(checks, os.Stderr, ignorePreflightErrors) return RunChecks(checks, os.Stderr, ignorePreflightErrors)
} }

View File

@ -44,6 +44,7 @@ type ContainerRuntime interface {
RemoveContainers(containers []string) error RemoveContainers(containers []string) error
PullImage(image string) error PullImage(image string) error
ImageExists(image string) (bool, error) ImageExists(image string) (bool, error)
SandboxImage() (string, error)
} }
// CRIRuntime is a struct that interfaces with the CRI // CRIRuntime is a struct that interfaces with the CRI
@ -173,3 +174,19 @@ func detectCRISocketImpl(isSocket func(string) bool, knownCRISockets []string) (
func DetectCRISocket() (string, error) { func DetectCRISocket() (string, error) {
return detectCRISocketImpl(isExistingSocket, defaultKnownCRISockets) return detectCRISocketImpl(isExistingSocket, defaultKnownCRISockets)
} }
// SandboxImage returns the sandbox image used by the container runtime
func (runtime *CRIRuntime) SandboxImage() (string, error) {
args := []string{"-D=false", "info", "-o", "go-template", "--template", "{{.config.sandboxImage}}"}
out, err := runtime.crictl(args...).CombinedOutput()
if err != nil {
return "", errors.Wrapf(err, "output: %s, error", string(out))
}
sandboxImage := strings.TrimSpace(string(out))
if len(sandboxImage) > 0 {
return sandboxImage, nil
}
return "", errors.Errorf("the detected sandbox image is empty")
}

View File

@ -161,6 +161,56 @@ func TestListKubeContainers(t *testing.T) {
} }
} }
func TestSandboxImage(t *testing.T) {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeAction{
func() ([]byte, []byte, error) { return []byte("registry.k8s.io/pause:3.9"), nil, nil },
func() ([]byte, []byte, error) { return []byte("registry.k8s.io/pause:3.9\n"), nil, nil },
func() ([]byte, []byte, error) { return nil, nil, nil },
func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
},
}
execer := &fakeexec.FakeExec{
CommandScript: genFakeActions(&fcmd, len(fcmd.CombinedOutputScript)),
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
}
cases := []struct {
name string
expected string
isError bool
}{
{"valid: read sandbox image normally", "registry.k8s.io/pause:3.9", false},
{"valid: read sandbox image with leading/trailing white spaces", "registry.k8s.io/pause:3.9", false},
{"invalid: read empty sandbox image", "", true},
{"invalid: failed to read sandbox image", "", true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
runtime, err := NewContainerRuntime(execer, "unix:///some/socket.sock")
if err != nil {
t.Fatalf("unexpected NewContainerRuntime error: %v", err)
}
sandboxImage, err := runtime.SandboxImage()
if tc.isError {
if err == nil {
t.Errorf("unexpected SandboxImage success")
}
return
} else if err != nil {
t.Errorf("unexpected SandboxImage error: %v", err)
}
if sandboxImage != tc.expected {
t.Errorf("expected sandbox image %v, but got %v", tc.expected, sandboxImage)
}
})
}
}
func TestRemoveContainers(t *testing.T) { func TestRemoveContainers(t *testing.T) {
fakeOK := func() ([]byte, []byte, error) { return nil, nil, nil } fakeOK := func() ([]byte, []byte, error) { return nil, nil, nil }
fakeErr := func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} } fakeErr := func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} }